I start with a txt file named vms.txt.
It contains 2 servers like so:
server1
server2
When I run the script shown below, the command that is invoked to install VMware tools only runs on server2 and server1 gets skipped. Does anyone have any suggestions on how to modify this script to make it run on both servers in the txt file? I will have to run this in the future for hundreds of VMs, so I am trying to find an easy way to get this to loop properly.
$cred = Get-Credential
$vms = Get-Content C:\Scripts\Tools\vms.txt
foreach($vm in $vms){
$sessions = New-PSSession -ComputerName $vm -Credential $cred
}
foreach($session in $sessions)
{
Invoke-Command -Session $session -ScriptBlock {
c:\users\jsmith\documents\VMware-tools-10.3.5.exe /v "/qn REBOOT=R Remove=AppDefense,VMCI”
}
}
In your loop-based approach, the problem is your variable assignment:
The immediate fix is to move the assignment out of the loop:
Taking a step back:
Both
New-PSSession-ComputerNameandInvoke-Command-Sessionaccept an array of computer names / sessions, so there's no need for loops.Passing multiple sessions / computer names to
Invoke-Commandhas the big advantage that the operations run in parallel.Note:
Invoke-Commandhas built-in throttling to avoid targeting too many machines at once. It defaults to 32, but can be modified with the-ThrottleLimitparameter..PSComputerNameproperty reflecting the originating computer - see the bottom section of this answer.That is, your code can be simplified to:
Important:
Remove-PSSessionwhen no longer needed.Invoke-Commandcall, you need to ensure that those operations have finished first - see the comments re potentially asynchronous execution of yourVMware-tools-10.3.5.exeapplication below.Or, even simpler, if you only need to execute one command on each machine, in which case there is no need to create sessions explicitly, pass all computer names directly to
Invoke-Command's-ComputerNameparameter:Important:
If your application (
VMware-tools-10.3.5.exe) runs asynchronously, you must ensure its synchronous execution, otherwise it may not run to completion, because the implicitly created remote session is discarded when a script block returns from a given computer.A simple trick to ensure synchronous execution of any external (GUI-subsystem) executable is to pipe it to
Write-Output, as shown above (orWait-Process, if it doesn't produce console output) - see this answer for an explanation.