Running a remote execute PowerShell against each Windows 8 VM on your Hyper-V host can help you maintain the guest VM’s in a minimal amount of time.

With all of the work going on at the office around PowerShell I wanted to have a go and solve another problem I have. I kept running into an issue when using Hyper-V on my local computer. Every so often, to keep them relent, I need to boot each of the virtual machines and run windows updates. My first thought was to create a PowerShell that would update the them automatically, by then I thought… why can’t I push the script out to each of them.

DONERemote Execute PowerShell against each VM

DONEExecute PowerShell against VM

This will require two distinct Scripts.

Remote Execute against each VM

I am using Windows 8 with Hyper-V so all of the virtual machines are local. So the first step is to get a list of the VM’s and loop though them.

Get a list of all of your VM's

PowerShell

1

2

3

4

5

$VMs=Get-VM

foreach($vmin$VMs)

{

Write-Host" $($vm.Name)"-ForegroundColorYellow

}

Note Watch out as Get-VM does not error out when you are not running under elevated privileges it just returns no machines. Now that vexed me for a while.

You can combat this by doing a check for elevated privileges and starting a new elevated process if your are not currently running in elevated mode.

So for each VM we can now execute something. However, what I want to do is run the script on my host (My main computer) and have it execute a portion of that script on the VM. For this I choose to have a separate ps1 file that did all of the local actions. There are really two ways to do this, first with a “[remote region]” that executes anything within that code block on the remote VM and second with a “Invoke-Command -ComputerName [computerName]”. The former sounds like it will complicate my scripts, making them too long, so I went with the latter. “Invoke-Command” has an option to pass another ps1 that does the actual execution which in turn allows me test that script separately.

You will note that I have set the execution policy to ‘unrestricted’ prior to executing the code. As I am not signing the PowerShell scripts I need to do that in order to get them to run. Although all of the guest VM’s are within a domain my local computer, the one running the master script, is not. This does pose a number of interesting issues that means that you need to pass a ‘-Credential’ object into the invoke method.

This object will then allow the ‘Invoke-Command’ to authenticate correctly with the remote server. However the first time that you run this script you might get an error because the local computer is not trusted. If it was joined to the domain this would not be an issue but as it is in a workgroup the domain joined VM will reject you.

Set all hosts as trusted

PowerShell

1

Set-Itemwsman:localhostclienttrustedhosts*

Just run the above on the VM to prep it or you can explicitly trust the host name. As these are all demo and not production VM’s I am not particularly worried about locking them down.

Now we can loop through the VM’s and execute a remote script against them. This then sounds like we are nearly done, however what happens when the VM’s are off or saved or paused. Well… nothing as you can’t execute a script against a machine that is not running. I needed to find a way to start the machines and luckily hyper-v can be totally managed by PowerShell and thus there is a command for that. The current state of the machine is stored in “$vm.State” which has a number of vaues that we need to do different things for.

Case Statement for VM State

PowerShell

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

switch($vm.State)

{

default{

Write-Host" Don't need to do anything with $($compName) as it is $($vm.State) "

}

"Paused"

{

Write-Host"$($vm.Name) is paused so making it go "

Resume-VM–Name*$compName*-Confirm:$false

}

"Saved"

{

Write-Host"$($vm.Name) is paused so making it go "

Start-VM–Name*$compName*-Confirm:$false

}

"Off"

{

Write-Host"$($vm.Name) is stopped so making it go "

Start-VM–Name*$compName*-Confirm:$false

}

}

Here I use a case statement to choose what to do with each VM based on its current state. Therefore if it is ‘paused’ I need to ‘resume’ it to the running state. In addition I also store the current state in a variable so that I can reset each VM to its original state after I have run the script. This would allow me to boot each VM in turn rather than all at one and thus not overload my local computer.

So there is one last thing to do. When you change the state of VM using “Start-VM” it can take some time to load. You could guess and insert a delay but what if the VM takes a long time to do a group policy update or maybe a windows update needs to finish.

Wait for VM to be ready

PowerShell

1

2

3

4

5

6

do{

Start-Sleep-milliseconds100

Write-Progress-activity"Waiting for VM to become responsive"-SecondsRemaining-1

Write-Progress-activity"Waiting for VM to become responsive"-SecondsRemaining-1-Completed

So I monitor the ‘heartbeat’ of the VM to determine when it becomes available. This will return “OK” once the VM has booted far enough to start responding to Hyper-V. This should mean that the VM will now be responsive to commands. As this can take some time I also want some sort of progress bar to be visible to let anyone monitoring the script know that we are waiting. There is a built in ‘Write-Progress’ command that lets us do this and it renders in the appropriate form for the medium that the script is executing in.

Write-Host"Unable to determin name of $($vm.Name) as it is in a crappy format. Needs to be '$nameRegex'"-ForegroundColorRed

}

}

Be warned that this is my first pass at this script and more scarily my first actual PowerShell script. As such I have no idea if this is the right way to do things, but it does seam to work. It lets you execute another PowerShell script against each of my VM’s.

Execute against VM

The second script that I need will do all of the work to configure and update the VM after it has been started. This will be a remote execution script that installs anything that it needs as well as getting and installing all of the outstanding Microsoft Updates.

I will likely go though a bunch more iterations on this script but for right now it:

Makes sure that Chocolatey is installed

Installed Chocolatey packages including ‘PSWindowsUpdate’

Validates some firewall changes that I need

Downloads and installs all Microsoft Updates

This may change and I want to test out some hierarchical PowerShell script option, but until then this makes it easy to update all of my VM’s.

Conclusion

Although I have tinkered with PowerShell now and then this is the first executable script that I have written. I am still in copy/paste mode but I can sure see the value of learning and using PowerShell for everything from installing applications to configuring systems.

You can just about do anything with PowerShell that you like.

Remote Execute PowerShell against each Windows 8 VM was last modified: May 23rd, 2013 by Martin Hinshelwood

-Every company deserves working software that successfully and consistently meets their customers needs on a regular cadence. We can help you get working software with continuous feedback so that your lean-agile teams can deliver continuous value with Visual Studio ALM, Team Foundation Server & Scrum. We have experts on hand to help improve your process and deliver more value at higher quality.