I’ve been working on a project recently that required me to remotely apply snapshots to multiple VMs. This is with Windows Server 2008 R2 so I didn’t have access to the nice Server 2012 PowerShell commands like Get-VM so this all had to be done through WMI instead.
First I set up credentials and PowerShell session:
$pwd = ConvertTo-SecureString -AsPlainText -Force –String “your password”
$cred= New-Object System.Management.Automation.PSCredential ("domain\user", $pwd)
$sesh = new-pssession -computername "remoteComputer" -credential $cred
Now that my session is set up, I can send over a block of commands using Invoke-Command, like so:
1 invoke-command -session $sesh -scriptblock {
2 #script to be run on remote computer
3 }
Next, I set up the VM names that I want to revert:
1 $servers = “server1”, “server2”, “server3”
Now I need to set a variable for the snapshot name. It’s important to note here that for this specific script, all three servers have a snapshot with the exact same name that I’ll be reverting to.
1 $SnapshotName = “dat snapshot”
Next I need to issue a shutdown command to each server. As far as I know, you CANNOT apply a snapshot to a running VM this way. You can do so manually through Hyper-V, but I ran into issues applying snapshots through PowerShell while the target VM is running.
1 foreach ($server in $servers) {
2
3 $query = "SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" + $server + "'"
4 $VM = get-wmiobject -query $query -namespace "root\virtualization" -computername "."
5 $query = "SELECT * FROM Msvm_ShutdownComponent WHERE SystemName='" + $VM.name + "'"
6 $Shutdown = get-wmiobject -query $query -namespace "root\virtualization" -computername "."
7 $Shutdown.InitiateShutdown($true,"Because I said so")
8 }
Wait for each server to start back up:
1 foreach ($server in $servers) {
2 $query = "SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" + $server + "'"
3 $VM = get-wmiobject -query $query -namespace "root\virtualization" -computername "."
4
5 while ($VM.OnTimeInMilliseconds -ne 0)
6 {
7 $VM = get-wmiobject -query $query -namespace "root\virtualization" -computername "."
8 Start-Sleep -s 5
9 }
10 }
At this point, I needed to code in a 60-second delay once this loop finished (you can do that with this: Start-Sleep –s 60). This is due to how I had to query the VMs’ status. I simply looked at the VMs “OnTimeInMilliseconds” and figured that when that was reset to 0, the VM would be shutdown. That’s not quite the case, however. When that “OnTimeInMilliseconds” is set to 0, the VM has reached a stopping state, not a stopped state. I assume that the stopping state will only remain for a few seconds at worst but give it 60 seconds for good measure. I haven’t run into any issues with this yet and I’m not in any hurry.
The last step is to apply the snapshot and start the VM back up. I do both of those in one step for each VM:
1 $VMMS = gwmi -namespace root\virtualization Msvm_VirtualSystemManagementService -computername "."
2
3 foreach ($server in $servers)
4 {
5 # Get the virtual machine object
6 $VM = gwmi MSVM_ComputerSystem -filter "ElementName='$server'" -namespace "root\virtualization" -computername "."
7
8 # Find the snapshot that we want to apply
9 $Snapshot = gwmi -Namespace root\virtualization -Query "Associators Of {$VM} Where AssocClass=Msvm_ElementSettingData ResultClass=Msvm_VirtualSystemSettingData" | where {$_.ElementName -eq $SnapshotName} | select -first 1
10
11 # Apply the snapshot
12 $VMMS.ApplyVirtualSystemSnapshot($VM, $Snapshot)
13
14 $VM.requeststatechange(2)
15 }
And we’re done!
Credit to Ben Armstrong for parts of this script.
No comments:
Post a Comment