Foreach Powershell Schleife parallel abarbeiten
Hallo,
Wie kann ich denn eine Foreach Schleife in Powershell parallel abarbeiten?
Aktuell werden auf mehreren Computern nacheinander bestimmte Dinge ausgeführt:
Die abarbeitung duaer teils sehr lange, da es meist über 10 Computer sind, die nacheinander abgearbeitet werden. In manchen Fällen werden auch länger laufende Aktionen auf den einzelnen Computern ausgeführt, was dazu führt, dass das Script mehrere Stunden benötigt, bis es mit allen Servern durch ist.
Schön wäre es, wenn die foreach Schleife immer 5 oder mehr Server parallell abarbeitet .
ich hab da jetzt was zu gefunden, wie das mit Jobs funktioniert :https://stackoverflow.com/questions/43685522/running-tasks-parallel-in-p ...
Bin aber soweit in Powershell nicht fit genug um zu wissen, wie ich die Schleife dahingehend umbauen kann.
Wie kann ich denn eine Foreach Schleife in Powershell parallel abarbeiten?
Aktuell werden auf mehreren Computern nacheinander bestimmte Dinge ausgeführt:
foreach ($computer in $computers) {
mach was
}
Die abarbeitung duaer teils sehr lange, da es meist über 10 Computer sind, die nacheinander abgearbeitet werden. In manchen Fällen werden auch länger laufende Aktionen auf den einzelnen Computern ausgeführt, was dazu führt, dass das Script mehrere Stunden benötigt, bis es mit allen Servern durch ist.
Schön wäre es, wenn die foreach Schleife immer 5 oder mehr Server parallell abarbeitet .
ich hab da jetzt was zu gefunden, wie das mit Jobs funktioniert :https://stackoverflow.com/questions/43685522/running-tasks-parallel-in-p ...
Bin aber soweit in Powershell nicht fit genug um zu wissen, wie ich die Schleife dahingehend umbauen kann.
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 24139179460
Url: https://administrator.de/contentid/24139179460
Ausgedruckt am: 22.11.2024 um 05:11 Uhr
11 Kommentare
Neuester Kommentar
Hi.
Ab PowerShell 7 gibt es den Parameter -parallel für foreach-object der das für dich übernimmt.
Jobs wurde ich persönlich nicht nutzen wollen da die in der Initialisierung doch um einiges langsamer als Runspaces sind, wenn dich das nicht stört kannst du die natürlich auch nutzen mittels Start-Job und dein Skript im Skriptblock.
Lies dir dazu mal diese Serie durch
Beginning Use of PowerShell Runspaces: Part 1
Beginning Use of PowerShell Runspaces: Part 2
Beginning Use of PowerShell Runspaces: Part 3
Gruß schrick
Ab PowerShell 7 gibt es den Parameter -parallel für foreach-object der das für dich übernimmt.
Jobs wurde ich persönlich nicht nutzen wollen da die in der Initialisierung doch um einiges langsamer als Runspaces sind, wenn dich das nicht stört kannst du die natürlich auch nutzen mittels Start-Job und dein Skript im Skriptblock.
Lies dir dazu mal diese Serie durch
Beginning Use of PowerShell Runspaces: Part 1
Beginning Use of PowerShell Runspaces: Part 2
Beginning Use of PowerShell Runspaces: Part 3
Gruß schrick
Oben wäre eine Form der Anmeldung. Hier mit Keyfile.
Nur so mal eingeworfen. Wenn man direkt davor sitzt kann Kennwort auch selber kurz eingeben.
Hier sieht man nochmal die Methoden wie man Namen und Passwörter übergeben könnte. Die Keyfile wird so generiert, dass sie auf anderen Windows Rechnern lesbar ist.
Klar ist der Punkt Sicherheit nur so Lala. Hat man die Datei, kann man die verwurschten. Wir haben es auch nun anders gelöst.
Aber so könnte man Windows Creds generieren und damit Script bedienen.
Kern ist und bleibt aber Start-Job.
Nur so mal eingeworfen. Wenn man direkt davor sitzt kann Kennwort auch selber kurz eingeben.
Hier sieht man nochmal die Methoden wie man Namen und Passwörter übergeben könnte. Die Keyfile wird so generiert, dass sie auf anderen Windows Rechnern lesbar ist.
Klar ist der Punkt Sicherheit nur so Lala. Hat man die Datei, kann man die verwurschten. Wir haben es auch nun anders gelöst.
Aber so könnte man Windows Creds generieren und damit Script bedienen.
Kern ist und bleibt aber Start-Job.
$KeyFile = "D:\test\pass_admin.key"
$Key = New-Object Byte[] 32 # You can use 16, 24, or 32 for AES
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($Key)
$Key | out-file $KeyFile
$PasswordFile = "D:\test\pass_super_geheim.txt"
$KeyFile = "D:\test\pw.key"
$Key = Get-Content $KeyFile
$Password = "SuperGeheimesKennwort" | ConvertTo-SecureString -AsPlainText -Force
$Password | ConvertFrom-SecureString -key $Key | Out-File $PasswordFile
$USERNAME = $WinUserName
$DOMUSER = $($WinDom)+"\"+$($WinUserName)
$PasswordFile = $WinUserPwdFile
$KeyFile = $WinUserKeyFile
$key = Get-Content $KeyFile
$MyCredential = New-Object -TypeName System.Management.Automation.PSCredential `
-ArgumentList $USERNAME, (Get-Content $($PasswordFile) | ConvertTo-SecureString -Key $($key))
$PASSWORD = $MyCredential.GetNetworkCredential().password
$passwdsec = convertto-securestring -AsPlainText -Force -String $PASSWORD
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $DOMUSER,$passwdsec
Invoke-Command -ComputerName $WinSrvHost -Credential $cred `
-ScriptBlock { `
$JobA = Start-Job { `
stop-service -name 'SQLSERVERAGENT' -force -passThru -ErrorAction Continue `
}; `
Start-Sleep 30 ; `
$JobS = Start-Job { `
stop-service -name 'MSSQLSERVER' -force -passThru -ErrorAction Continue `
}; `
Start-Sleep 30; `
cmd /c taskkill /f /im sql*; `
Start-Sleep 5; `
Start-Service -Name 'MSSQLSERVER'; `
#Start-Sleep 30; `
Start-Service -Name 'SQLSERVERAGENT'; `
}
Wenn man sowieso mit Invoke-Command arbeitet und statt mit einer Foreach Schleife alle Computer im Parameter -computername als Array übergibt werden die Computer automatisch parallel abgearbeitet.
Es braucht dann also keinerlei Schleife mehr.
Mit dem Parameter -ThrottleLimit lässt sich dann auch festlegen wie viele Computer max. parallel verarbeitet werden
Es braucht dann also keinerlei Schleife mehr.
Mit dem Parameter -ThrottleLimit lässt sich dann auch festlegen wie viele Computer max. parallel verarbeitet werden
Invoke-Command -ComputerName "pc1","pc2","pc3" -ThrottleLimit 10 -Scriptblock {
# mach was
}
Man kann das auch so lösen.
Du hast ein Hauptscript, hier liest du z.B. eine Liste mit Computernamen aus.
Dann machst du ein Subscript, welches nur mit Variablen arbeitet (also Voll Dynamisch ist).
Nun rufst du im for each bereich das subscript auf
nach dem moto >> subscript.ps1 /ComputerName
wobei der Computername Dynamisch mitgegeben wird.
Man muss es aber so starten das nicht drauf gewartet wird das es sich beendet.
Somit starten dann dutzende Subscripts parallel und werden gleichzeitig abgearbeitet.
Nun tut das subscript checken welcher parameter mitgegeben wurde (der Computername)
und führt dann mit dem Parameter >> Variable dann entsprchend alles aus.
Wie das im Detail mit Powershell zu bauen ist, keine Ahnung, hab sowas nur unter VBS Scripts gebaut gehabt.
VBS Code:
Übergebenen Parameter im Subscript auslesen:
Du hast ein Hauptscript, hier liest du z.B. eine Liste mit Computernamen aus.
Dann machst du ein Subscript, welches nur mit Variablen arbeitet (also Voll Dynamisch ist).
Nun rufst du im for each bereich das subscript auf
nach dem moto >> subscript.ps1 /ComputerName
wobei der Computername Dynamisch mitgegeben wird.
Man muss es aber so starten das nicht drauf gewartet wird das es sich beendet.
Somit starten dann dutzende Subscripts parallel und werden gleichzeitig abgearbeitet.
Nun tut das subscript checken welcher parameter mitgegeben wurde (der Computername)
und führt dann mit dem Parameter >> Variable dann entsprchend alles aus.
Wie das im Detail mit Powershell zu bauen ist, keine Ahnung, hab sowas nur unter VBS Scripts gebaut gehabt.
VBS Code:
Set objShell = WScript.CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
CommandLine = "%comspec% /c cscript.exe " & """" & "C:\subscript.vbs" & """" & " /" & PCName
'wscript.echo CommandLine
objShell.run(CommandLine),0,false
Übergebenen Parameter im Subscript auslesen:
Set Args = WScript.Arguments
sPCNameTemp = Replace(Args(0), "/", "")
sPCNameTemp = Replace(sPCNameTemp, " ", "")
wscript.echo sPCNameTemp
Allerdings wird der Servername / IP nicht ausgegeben/ eingelesen
Du hast die Parameterdefinition im Skriptblock vergessen zu übernehmen. Ein Skriptblock ist wie eine Funktion die braucht entweder eine Parameterdefinition oder die Verwendung von der Variablen $args für die übergebenen Werte. Der Skriptblock wird ja in einem separaten Thread ausgeführt und kennt somit die Variablen des Host-Skriptes nicht.$update_job = {
Param([string]$computer)
sleep 10
Write-host "Skript auf PC $computer erfolgreich" -ForegroundColor Yellow
}
$update_job = {
Param(
[string]$computer,
[string]$rootPath
)
# ....
#...
und beim Aufruf des Jobs die Variable mit übergeben:
Start-Job -Scriptblock $update_job -ArgumentList $computer,$RootPath