althalus
Goto Top

Remote-PowerShell Script auf mehreren Rechnern gleichzeitig Ausführen

Hallo zusammen,

nach dem mein letztes Script (Remote-PowerShell Script gibt Probleme bei einigen Testrechnern) Dank der Hilfe von 'grexit' super läuft und die heutige Erprobung bis auf einen Anwenderfehler sehr gut lief... dauerte das Update doch über eine Stunde, bei 28 Rechnern.
Da ich dies, für die Mitbarbeiter und mich, gerne beschleunigen würde interessiert mich ob man Powershell irgendwie dazu bringen könnte das Script für die in PC.txt enthaltenen Rechner parallel starten zu lassen.

Zum Bleistift das man der Powershell sagt sie solle sich die ersten 5 Rechner nehmen, für die 5 Rechner die Installation parallel fertig stellen und dann die nächsten 5 Rechner nehmen. Da noch 80 Systeme mit einer Installation über das Script warten wäre dies natürlich extrem Hilfreich und Zeitsparend.

LG Althalus

PS: Codebeispiele würden mich sehr Freuen.

Content-ID: 287323

Url: https://administrator.de/forum/remote-powershell-script-auf-mehreren-rechnern-gleichzeitig-ausfuehren-287323.html

Ausgedruckt am: 26.12.2024 um 04:12 Uhr

114757
114757 02.11.2015 aktualisiert um 15:42:51 Uhr
Goto Top
Moin,
Kannst du z.B. über den Parameter -AsJob von Invoke-Command. Das führt den Job als separaten Thread im Hintergrund aus.

-AsJob
Runs the command as a background job on a remote computer. Use this parameter to run commands that take an extensive time to complete.
When you use AsJob, the command returns an object that represents the job, and then displays the command prompt. You can continue to work in the session while the job completes. To manage the job, use the Job cmdlets. To get the job results, use the Receive-Job cmdlet.
The AsJob parameter is similar to using the Invoke-Command cmdlet to run a Start-Job command remotely. However, with AsJob, the job is created on the local computer, even though the job runs on a remote computer, and the results of the remote job are automatically returned to the local computer.
Gruß jodel32
Althalus
Althalus 02.11.2015 um 16:14:47 Uhr
Goto Top
Hallo jodel32,

klingt zwar sehr gut, aber wie bringe ich das in eine Schleife? Das momentane Script sieht so aus. Es Funktioniert es sehr gut, aber halt dadurch das er nicht mehrere (ich sag jetzt mal 5 oder auch gerne alle) Rechner gleichzeitig macht dauert es teilweise sehr lange.
In der 1.txt stehen beim morgigen Ausrollen ca 40 Rechner. Bei 28 dauerte es ja schon über eine Stunde. Seh bezüglich des '-AsJob' vielleicht auch einfach den Wald vor lauter Bäumen nicht.

# Deklarieren der lokalen Variablen und Scripte
$username = "<Domäne\Administrator>"  
$password = "<Passwort>"  
$secstr = New-Object -TypeName System.Security.SecureString
$password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr
$PCLIST = Get-Content 'D:\Powershell\1.txt'  

$ScriptDeinstall1 = {
    $app = Get-WmiObject -Class Win32_Product |where { $_.name -EQ 'MSI-Paket 1' }  
    $app.uninstall()
}
$ScriptDeinstall2 = {
    $app = Get-WmiObject -Class Win32_Product |where { $_.name -EQ 'MSI-Paket 2' }  
    $app.uninstall()
}
$ScriptInstall1 = {
    $args = "-i C:\Temp\Patchordner\MSI-Paket 1.msi /qn /norestart ALLUSERS=1"  
    [diagnostics.process]::start("msiexec.exe", $args).WaitForExit()  
}
$ScriptInstall2 = {
    $args = "-i C:\Temp\Patchordner\MSI-Paket 2Setup.msi /qn /norestart ALLUSERS=1"  
    [diagnostics.process]::start("msiexec.exe", $args).WaitForExit()  
}
$Textmassage = {
    $CmdMessage = { C:\windows\system32\msg.exe * 'Die Installation des Updates ist beendet, es kann wieder gearbeitet werden.' }  
    $CmdMessage | Invoke-Expression
}


$ScriptCode = {        
    mkDir C:\Temp\Patchordner
    Net Use X: \\Server\Freigabe\Unterordner /USER:<Domäne\Administrator> <Passwort>
    robocopy X: C:\Temp\Patchordner /MIR
    Net Use X: /delete
}

ForEach ($computer in $PCLIST) {
# Ping ob Rechner erreichbar und Skript Ausgeführt werden kann
ping $Rechnername -n 1 | out-null
$PingErgebniscomputer = "$lastexitcode"  

    if(!($PingErgebnisRechnername -eq "0")) {   
        # Wenn Ping Erfolgreich wird Installation und Skript ausgeführt
        # Verbinden auf Remoterechner und erteilung der Berechtigungen
        Enter-PSSession -ComputerName $computer -Credential $cred
        Invoke-Command -Computername $computer { Set-ExecutionPolicy Bypass -Force }
        Invoke-Command -Computername $computer -ScriptBlock $ScriptCode -Credential $cred
        
        # Nachricht das jetzt das Update ausgeführt wird
        Invoke-Command -ComputerName $computer -scriptblock {msg * "Meldung 1: Es wird ein Update eingespielt, bitte nichts machen bis die Erfolgsmeldung kommt."}  
        
        # Deinstallation der MSI-Pakete, InternetExplorer und Outlook werden geschlossen
        Write-Host "Internet Explorer wird geschlossen"  
        Invoke-Command -Computername $computer { Stop-Process -name iexplo* -Force }
        Write-Host "MSI-Paket 1 werden Deinstalliert"  
        Invoke-Command -ComputerName $computer -scriptblock $ScriptDeinstall1 -Credential $cred
        Write-Host "Outlook wird geschlossen"  
        Invoke-Command -Computername $computer { Stop-Process -name OUTLOO* -Force }
        Write-Host "MSI-Paket 2 wird Deinstalliert"  
        Invoke-Command -ComputerName $computer -scriptblock $ScriptDeinstall2 -Credential $cred -Verbose
        
        # Installation des Updates, InternetExplorer und Outlook werden geschlossen
        Write-Host "Internet Explorer wird geschlossen"  
        Invoke-Command -Computername $computer { Stop-Process -name iexplo* -Force }
        Write-Host "Installation von MSI-Paket 1"  
        Invoke-Command -ComputerName $computer -scriptblock $ScriptInstall1 -Credential $cred
        Write-Host "Installation von MSI-Paket 2"  
        Write-Host "Outlook wird geschlossen"  
        Invoke-Command -Computername $computer { Stop-Process -name OUTLOO* -Force }
        Invoke-Command -ComputerName $computer -scriptblock $ScriptInstall2 -Credential $cred

        # Entfernen der Updatedateien
        Invoke-Command -ComputerName $computer { rd C:\Temp\Patchordner -recurse }

        # Freischaltung der NetFramework Dateien
        Invoke-Command -ComputerName $computer { C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CasPol.exe -q -machine -chggroup Trusted_Zone FullTrust }
        Invoke-Command -ComputerName $computer { C:\WINDOWS\Microsoft.NET\Framework64\v2.0.50727\CasPol.exe -q -machine -chggroup Trusted_Zone FullTrust }

        #Nachricht für die Benutzer dass das Update fertig ist
        Invoke-Command -ComputerName $computer -scriptblock {msg * "Meldung 2: Update wurde Ausgeführt!"}  
        
        #Verlassen der Remote-Session
        Exit-PSSession
	}
	ELSE{Out-File D:\Powershell\dump.txt -InputObject $computer -width 50}
}
114757
Lösung 114757 02.11.2015, aktualisiert am 03.11.2015 um 15:54:25 Uhr
Goto Top
1000 und ein Invoke-Command OMG face-confused hast wohl den Vorschlag von @122990 im letzten Post nicht übernommen, denn dann bräuchtest du nur ein einziges Invoke-Command pro Rechner, dann einfach den o.g. Parameter dran hängen Schleife, durchlaufen lassen und Kaffee trinken gehen, dann machen sich alle Rechner gleichzeitig an die Installation.

Ein bißchen Lesen kann man eigentlich schon erwarten:
Background Jobs

Beispiel:
# Array für alle Jobs
$jobs = @()
(gc 'D:\Powershell\1.txt') | %{  
    # Job erstellen und starten
    $job = Invoke-Command -ComputerName $_ -ScriptBlock {
            # hier kommen deine Befehle rein die auf dem Remote-Computer ausgeführt werden sollen
            # für deinen Test hier mal eine simple sleep Schleife
            1..(Get-Random -Min 1 -Max 10) | %{sleep(1)}
    } -AsJob -Jobname "RechnerJob_$_"  
    # Job dem Array hinzufügen
    $jobs +=$job
}

# Überprüfen welche Jobs noch laufen
$j = $jobs | ?{$_.State -eq "Running"}  
# so lange checken bis alle Jobs erledigt sind
while ($j){
    cls
    $j = $jobs | ?{$_.State -eq "Running"}  
    write-host "Noch $($j.Count) Jobs zu erledigen" -ForegroundColor Green  
    $j | ft Name,State -AutoSize
    sleep 1
}
write-host "Fertig"  
# Jobs entfernen
remove-job $jobs
Althalus
Althalus 03.11.2015 aktualisiert um 16:03:39 Uhr
Goto Top
Vielen Dank für deine Hilfe jodel32,
momentan sieht das Script noch so aus da ich heute doch nicht weiter dazu kam es zu Optimieren. Es laufen also jetzt erst einmal alle Jobs auf den Remoterechnern gleichzeitig.
Das von Dir werde ich morgen mit aufnehmen.

Noch einmal Herzlichen Dank.

# Deklarieren der lokalen Variablen und Scripte
$username = "<Domäne\Admin>"  
$password = "<Passwort>"  
$secstr = New-Object -TypeName System.Security.SecureString
$password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr
$PCLIST = Get-Content 'D:\Powershell\<Rechnerliste.txt>'  

ForEach ($computer in $PCLIST) {
    # Anzeige nicht erreichbarer Rechner mit Speicherung in Lokaler TXT-Datei
    if(!(Test-Connection -Cn $computer -Count 1 -quiet))  { Out-File D:\Powershell\Fehler.txt -Append -InputObject $computer -width 50 }

    # Wenn Ping Erfolgreich wird Installation und Skript ausgeführt
    if((Test-Connection -Cn $computer -Count 1 -quiet)) {
        # Aufbau der Verbindung zu den remoterechnern
        Invoke-Command -ComputerName $computer -Credential $cred {
		
		# Erteilung der Berechtigung auf den Remoterechnen
		Set-ExecutionPolicy Bypass -Force

		# Nachricht das jetzt das Update ausgeführt wird
		msg * "Meldung 1: Es wird ein Update eingespielt, bitte nichts machen bis die Erfolgsmeldung kommt."  
			
		# Erstellung des Patchverzeichnisses und Kopieren der Daten, mit Verbindung und Trennung des Netzlaufwerks
		mkDir C:\Patchordner
		Net Use X: \\Server\Freigabe\Unterverzeichnis /USER:<Domäne\Admin> <Passwort>
		robocopy X: C:\Patchordner /MIR
		Net Use X: /delete
			
		# Deinstallation der MSI-Pakete
        $msi = @('<Programm1>','<Programm2>')  
        foreach($deinstall in $msi){
            $app = Get-WmiObject -Class Win32_Product | Where-Object {
            $_.Name -match “$deinstall”
            }
            $app.Uninstall()
            }

		# Installation der MSI-Pakete
        [diagnostics.process]::start("msiexec.exe","-i C:\Patchordner\<Programm1.msi> /qn /norestart ALLUSERS=1").WaitForExit()  
        [diagnostics.process]::start("msiexec.exe","-i C:\Patchordner\<Programm2.msi> /qn /norestart ALLUSERS=1").WaitForExit()  
		
        # Entfernen der Updatedateien
		rd C:\Patchordner -recurse

		# Freischaltung der NetFramework Dateien
		C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CasPol.exe -q -machine -chggroup Trusted_Zone FullTrust
		C:\WINDOWS\Microsoft.NET\Framework64\v2.0.50727\CasPol.exe -q -machine -chggroup Trusted_Zone FullTrust

		#Nachricht für die Benutzer dass das Update fertig ist
		msg * "Meldung 2: Update wurde Ausgeführt!"  
		}  -AsJob
    }
}