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:
| Weg | Parameter | Wann sinnvoll |
|---|---|---|
| Direkte Liste | -ComputerName SRV01,SRV02 | Du kennst die Hosts |
| Textdatei | -Path C:\temp\hosts.txt | Feste 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-CommandstattEnter-PSSessionin der Schleife.Enter-PSSessionist für interaktive Sitzungen gedacht und lässt sich nicht sinnvoll automatisiert in einer Schleife abarbeiten.Invoke-Commandführt den Block remote aus und gibt ein Ergebnis zurück.-using/Argument-Übergabe. Die lokale Variable$ProcessNamelandet jetzt über-ArgumentListkorrekt im Remote-Scriptblock – vorher war sie dort schlicht leer.Get-Contentstatt[IO.File]::ReadAllText. Letzteres liefert einen String, sodass die Schleife nur einmal über den Gesamttext lief.Get-Contentgibt ein Array – eine Zeile pro Host.Select-Object -ExpandProperty NamestattFormat-Table.Format-Tableerzeugt Anzeigeobjekte, keine verwertbaren Strings – die Schleife lief damit ins Leere.- Native Sicherheitsabfrage über
ShouldProcess/-Confirmstatt einerSystem.Windows.Forms.MessageBox. Funktioniert auch ohne GUI / in Remote-Sessions. - Erreichbarkeitsprüfung pro Host (
Test-Connection), sauberetry/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. 😉
