andi@abow: ~/microsoft/ $ cat powershell-einen-prozess-auf-remotecomputern-beenden.md
>_abow

how do you do IT

Alle möglichen Handkniffe, die ich mir so zusammentrage im Berufsalltag

← cd ~/

Powershell // Einen Prozess auf Remotecomputern beenden

Powershell // Einen Prozess auf Remotecomputern beenden

Du sitzt an deinem Admin-PC und ein Prozess hängt – aber nicht auf deiner Maschine, sondern auf einem halben Dutzend Servern gleichzeitig. Jeden einzeln per RDP aufmachen? Spar dir das. In diesem Artikel zeige ich dir ein PowerShell-Skript, mit dem du einen Prozess auf einem oder mehreren Remote-Computern in einem Rutsch beendest – wahlweise per Hostliste, per Textdatei oder direkt aus einer Active-Directory-OU heraus.

Gegenüber meiner ersten Version (2022) ist das Skript komplett überarbeitet: zuverlässiges Remoting über Invoke-Command, sauberer Parameter-Block, Sicherheitsabfrage über das native -Confirm von PowerShell statt einer Windows-Forms-Box und eine echte Erfolgskontrolle pro Computer.

Getestet auf: Windows 10 / 11 und Windows Server 2016–2022.


Voraussetzungen

Damit das Skript funktioniert, müssen ein paar Dinge stimmen:

  • PowerShell-Remoting (WinRM) ist auf den Zielcomputern aktiv (Enable-PSRemoting -Force).
  • Die Remotecomputer sind aus deinem Netzwerk erreichbar.
  • Du hast Admin-Rechte auf den Zielen, z. B. als Domain-Administrator.
  • Die ExecutionPolicy lässt die Ausführung zu. Für einen einmaligen Lauf reicht:
  powershell.exe -ExecutionPolicy Bypass -File .\Stop-RemoteProcess.ps1

(Wie man ExecutionPolicys sauber per GPO für Benutzer oder Computer setzt, schreibe ich gerne in einem eigenen Artikel.)


Drei Wege, die Zielcomputer anzugeben

Das Skript arbeitet mit Parameter-Sets – du wählst genau einen der drei Wege:

WegParameterWann sinnvoll
Direkte Liste-ComputerName SRV01,SRV02Du kennst die Hosts
Textdatei-Path C:\temp\hosts.txtFeste Maschinenlisten, ein Host pro Zeile
AD-Suche-SearchBase ... -NameFilter ...Dynamisch alle Server einer OU

Den zu beendenden Prozess gibst du immer über -ProcessName an (ohne .exe).


Anwendungsbeispiele

# Direkt zwei Hosts
.\Stop-RemoteProcess.ps1 -ComputerName SRV01,SRV02 -ProcessName notepad

# Hostliste aus Textdatei
.\Stop-RemoteProcess.ps1 -Path C:\temp\hosts.txt -ProcessName Teams

# Alle Server einer OU, deren Name "SQL" enthält
.\Stop-RemoteProcess.ps1 -SearchBase 'OU=Server,DC=abow,DC=local' -NameFilter 'SQL' -ProcessName sqlservr

Vor dem Beenden zeigt dir das Skript alle gefundenen Computer an und fragt pro Maschine nach (-Confirm). Erst nach deinem OK wird der Prozess wirklich gekillt – und anschließend geprüft, ob er auch tatsächlich weg ist.


Das Skript

#Requires -Version 5.1
<#
.SYNOPSIS
    Beendet einen Prozess auf einem oder mehreren Remote-Computern via Invoke-Command.

.NOTES
    Name:    Stop-RemoteProcess
    Author:  Andreas Bowitz (Andi Bow)
    Version: 1.0
#>

[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High', DefaultParameterSetName = 'ComputerName')]
param(
    [Parameter(Mandatory, ParameterSetName = 'ComputerName')]
    [string[]]$ComputerName,

    [Parameter(Mandatory, ParameterSetName = 'File')]
    [ValidateScript({ Test-Path $_ })]
    [string]$Path,

    [Parameter(Mandatory, ParameterSetName = 'AD')]
    [string]$SearchBase,

    [Parameter(Mandatory, ParameterSetName = 'AD')]
    [string]$NameFilter,

    [Parameter(Mandatory)]
    [string]$ProcessName,

    [System.Management.Automation.PSCredential]$Credential
)

# --- Admin-Rechte pruefen ---
$identity  = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($identity)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    Write-Warning "Keine Administrator-Rechte. Bitte PowerShell als Administrator starten."
    return
}

# --- Zielcomputer ermitteln ---
$targets = switch ($PSCmdlet.ParameterSetName) {
    'ComputerName' { $ComputerName }
    'File'         { Get-Content -Path $Path | Where-Object { $_.Trim() } }
    'AD' {
        Import-Module ActiveDirectory -ErrorAction Stop
        Get-ADComputer -Filter "Name -like '*$NameFilter*'" -SearchBase $SearchBase |
            Select-Object -ExpandProperty Name
    }
}

if (-not $targets) {
    Write-Warning "Keine Computer gefunden. Abbruch."
    return
}

Write-Host "Folgende Computer wurden gefunden:" -ForegroundColor Cyan
$targets | ForEach-Object { Write-Host "  - $_" }

# --- Credentials holen ---
if (-not $Credential) {
    $Credential = Get-Credential -Message "Bitte mit einem Administrator-Konto anmelden."
}

# --- Prozess remote beenden ---
foreach ($computer in $targets) {

    if (-not (Test-Connection -ComputerName $computer -Count 1 -Quiet)) {
        Write-Warning "$computer ist nicht erreichbar - uebersprungen."
        continue
    }

    if (-not $PSCmdlet.ShouldProcess($computer, "Prozess '$ProcessName' beenden")) {
        continue
    }

    try {
        $result = Invoke-Command -ComputerName $computer -Credential $Credential -ScriptBlock {
            param($name)
            $procs = Get-Process -Name $name -ErrorAction SilentlyContinue
            if (-not $procs) { return 'not-running' }
            $procs | Stop-Process -Force -ErrorAction Stop
            Start-Sleep -Seconds 1
            if (Get-Process -Name $name -ErrorAction SilentlyContinue) { 'still-running' } else { 'stopped' }
        } -ArgumentList $ProcessName

        switch ($result) {
            'stopped'       { Write-Host "[OK]   $computer : '$ProcessName' wurde beendet." -ForegroundColor Green }
            'not-running'   { Write-Host "[INFO] $computer : '$ProcessName' war nicht aktiv." -ForegroundColor Yellow }
            'still-running' { Write-Warning "$computer : '$ProcessName' konnte NICHT beendet werden." }
        }
    }
    catch {
        Write-Warning "$computer : Fehler - $($_.Exception.Message)"
    }
}

Was sich gegenüber der alten Version verbessert hat

Für alle, die die 2022er-Fassung kennen – die wichtigsten Änderungen und warum:

  • Invoke-Command statt Enter-PSSession in der Schleife. Enter-PSSession ist für interaktive Sitzungen gedacht und lässt sich nicht sinnvoll automatisiert in einer Schleife abarbeiten. Invoke-Command führt den Block remote aus und gibt ein Ergebnis zurück.
  • -using/Argument-Übergabe. Die lokale Variable $ProcessName landet jetzt über -ArgumentList korrekt im Remote-Scriptblock – vorher war sie dort schlicht leer.
  • Get-Content statt [IO.File]::ReadAllText. Letzteres liefert einen String, sodass die Schleife nur einmal über den Gesamttext lief. Get-Content gibt ein Array – eine Zeile pro Host.
  • Select-Object -ExpandProperty Name statt Format-Table. Format-Table erzeugt Anzeigeobjekte, keine verwertbaren Strings – die Schleife lief damit ins Leere.
  • Native Sicherheitsabfrage über ShouldProcess/-Confirm statt einer System.Windows.Forms.MessageBox. Funktioniert auch ohne GUI / in Remote-Sessions.
  • Erreichbarkeitsprüfung pro Host (Test-Connection), saubere try/catch-Fehlerbehandlung und ein klares Status-Ergebnis je Computer.

Hier könnt ihr das Skript herunterladen:

Wie immer berichte ich hier nur von meinen persönlichen Erfahrungen und Ergebnissen. Dies ist keine offizielle Anleitung von Microsoft. Das Nutzen von Skripten geschieht auf eigene Gefahr. 😉

Weiterlesen

$ Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert