Refresh Hyper-V test machines

Are you like me and use a lot of virtual machines to manage testing a.s.o, on Hyper-V platform and often end up with computers out of sync with domain due to frequent restoring of snapshots?

Then these script might help you keeping your machines to date and fresh.

This is a two part script, the first one handles the VMs on the Hyper-V side, the other one should be run inside the VMs to fix things like Updates, Computer Password refresh a.s.o.

HandleVMs.ps1

This is the main script for handling the VMs on the Hyper-V server.

# Powershell script to automate snapshot and refresh certain test VM:s.
#
# Version: 1.0

# Getting input parameters, options are Begin/Finish/Clean
param (
[Parameter(Mandatory=$true)][string]$Action
)

# Import Hyper-V module
Import-Module Hyper-V

# Requirerments: Requires that at least one checkpoint to be used to refresh the machine has
# the following suffix at the end, this will be added on the latest refreshed checkpoint automatically
$refSuffix = "Reference"

# What should we add for suffix to the backup checkpoints, enter it here.
$bckSuffix = "Backup"

# Computers to handle during the execution of this script.
# Should be like this when 2 or more machines shall be handled: $machineArray = "Machine01","Machine02","And so on..."
$machineArray = ""

# Generations of CheckPoints to keep for both reference, and backup checkpoints.
$snapGenerations = 2

#### Main script execution starts here ####

## VM functions ##

# VM turnoff function
function VM-TurnOff {
Param($VMs)
# Turn off machines to prepare for checkpoint of current state if needed to go back.
foreach ( $m in $machineArray)
{
Write-Output "Stopping $m..."
Try {
Stop-VM -Name $m -ErrorAction Stop
}
catch {
Write-Error "Error stopping $m..."
Write-Error "Aborting script..."
exit 1
}
Write-Output "Sucessfully stopped $m..."
}
}

# VM checkpoint function
function VM-Checkpoints {
Param($VMs, $Action)

# Get date and time
$curDate = Get-Date -UFormat "%Y-%m-%d %H:%M"

# Make checkpoint of current state for fallback purposes.
foreach ( $m in $machineArray)
{
Try {
if ($Action -eq "Begin"){
Write-Output "Creating checkpoint on $m..."
Checkpoint-VM -Name $m -SnapshotName "$curDate - $bckSuffix" -ErrorAction Stop
}

if ($Action -eq "Finish"){
#$curDate = Get-Date -UFormat "%Y-%m-%d %H:%M"
Write-Output "Creating checkpoint on $m..."
Checkpoint-VM -Name $m -SnapshotName "$curDate - $refSuffix" -ErrorAction Stop
}
}
catch {
Write-Error "Could not create checkpoint on $m..."
Write-Error "Aborting script..."
exit 1
}

}
Write-Output "Successfully created all checkpoints..."
}

# VM restore function
function VM-Restore {
Param($VMs)

# Restore Previous automated checkpoint to fresh it up.
foreach ( $m in $machineArray)
{
Write-Output "Restoring checkpoint on $m..."
Try {
$snapToRestore = Get-VMSnapshot $m -ErrorAction Stop | Where-Object {$_.Name -like '*' + $refSuffix} | Sort-Object -Property CreationTime -Descending | select -First 1
Restore-VMSnapshot -VMName $m -Name $snapToRestore.Name -Confirm:$false
}
catch {
Write-Error "Could not restore checkpoint on $m..."
Write-Error "Aborting script..."
exit 1
}
}
Write-Output "All VMs has been been restored..."
}

# VM start function
function VM-Start {
Param($VMs)

# Starting VMs so they can be refreshed.
try {
foreach ( $m in $machineArray){
Write-Output "Starting $m..."
Start-VM -Name $m -ErrorAction Stop
}
}
catch {
Write-Error "Could not start $m..."
Write-Error "Aborting script..."
exit 1
}
Write-Output "All VMs started..."
}

# VM cleanup function
function VM-CleanUp {
Param($VMs)

# Tell what we are doing.
Write-Output "Removing checkpoints..."

# Default status for checkpoint removals
$status = 0

# Cleaning VMs from older checkpoints.
foreach ( $m in $machineArray)
{
Try {
# Remove all the keepers from $refToDelete array so they stay untouched.
if ($(Get-VMSnapshot $m -ErrorAction Stop | Where-Object {$_.Name -like '*' + $refSuffix}).Count -gt $snapGenerations){
# Create first array for reference checkpoints to delete
[System.Collections.ArrayList]$refToDelete = Get-VMSnapshot $m -ErrorAction Stop | Where-Object {$_.Name -like '*' + $refSuffix} | Sort-Object -Property CreationTime -Descending

# Removing reference checkpoints that we are going to keep from list.
$cleanList = 0
while($cleanList -lt $snapGenerations) {
$cleanList++
$refToDelete.RemoveAt(0)
}
#Remove obsolete reference checkpoints
foreach ($s in $refToDelete){
Write-Output "Deleting reference checkpoint: $($s.Name)..."
Remove-VMSnapshot -VMName $m -Name $s.Name
}

}
else {
Write-Output "Not enough reference checkpoint generations to work with on $m..."
$status = 1
}

# Remove all the keepers from $bckToDelete array so they stay untouched.
if ($(Get-VMSnapshot $m -ErrorAction Stop | Where-Object {$_.Name -like '*' + $bckSuffix}).Count -gt $snapGenerations){
# Create second array for backup checkpoints to delete
[System.Collections.ArrayList]$bckToDelete = Get-VMSnapshot $m -ErrorAction Stop | Where-Object {$_.Name -like '*' + $bckSuffix} | Sort-Object -Property CreationTime -Descending

# Removing reference checkpoints that we are going to keep from list.
$cleanList = 0
while($cleanList -lt $snapGenerations) {
$cleanList++
$bckToDelete.RemoveAt(0)
}
#Remove obsolete reference checkpoints
foreach ($s in $bckToDelete){
Write-Output "Deleting backup checkpoint: $($s.Name)..."
Remove-VMSnapshot -VMName $m -Name $s.Name
}

}
else {
Write-Output "Not enough backup checkpoints generations to work with on $m..."
$status = 1
}
}
catch {
Write-Error "Could not remove checkpoint: $($s.Name)..."
Write-Error "Aborting script..."
exit 1
}
}
if ($status -eq 0){
Write-Output "All VMs obsolete checkpoints deleted..."
}
}

## Start to do things with all the functions we created ##

# Determine what to do based on input from you.
if ($Action -eq "Begin" ){
# Turn off machines to prepare for checkpoint of current state if needed to go back.
VM-TurnOff -VMs $machineArray

# Make checkpoint of current state for fallback purposes.
VM-CheckPoints -VMs $machineArray -Action Begin

# Restore Previous automated checkpoint to fresh it up.
VM-Restore -VMs $machineArray

# Starting VMs so they can be refreshed.
VM-Start -VMs $machineArray

# Mark execution as sucessfull if we come this far.
Exit 0
}

if ($Action -eq "Finish" ){
# Turn off machines to prepare for checkpoint of refreshed state.
VM-TurnOff -VMs $machineArray

# Make checkpoint of current state for fallback purposes.
VM-CheckPoints -VMs $machineArray -Action Finish

# Starting VMs so they can be refreshed.
VM-Start -VMs $machineArray

# Mark execution as sucessfull if we come this far.
Exit 0
}

if ($Action -eq "Clean" ){
# Clean out old genereations of ckeckpoints to keep all things a bit cleaner.
VM-CleanUp -VMs $machineArray

# Mark execution as sucessfull if we came this far.
Exit 0
}

RefreshVMs.ps1

The following script aids in keeping the VMs up to date and refresh the Computer Password to reduce the risk of getting out of sync with your domain. 

# Powershell script to refresh a virtual machine so it can be snapped again with fresh settings and updates.
# This script should be used in conjunction with the HandleVMS.ps1 script to speed things up.
# This script is built with SCCM as backend for updates and needs to be modified if you have no SCCM.
#
# Version 1.0

# Getting input parameters, options are Update/Password/All
param (
[Parameter(Mandatory=$true)][string]$Action
)

## User Variables ##

# Domain server to talk to.
$dcServer = ""

# Use credentials for resetting computer password a.s.o.
$dcCredentials = "No"
# Set to "No" it will use credentials from the account running the script.
# Set to "Ask" will require everything.
# Set to "Yes" will pre populate username field with info in $dcDomain and $dcUser.
# Options: (Yes/No/ask)

# If $dcCredentials is set to "Yes" you can pre fill with username, we will never put passwords a script.
$dcDomain = ""
$dcUser = ""

#### Main script execution starts here ####

## VM functions ##

# VM Update function
function VM-Update {

$ComputerName = "."
$SupName = "All"

$AppEvalState0 = "0"
$AppEvalState1 = "1"
$ApplicationClass = [WmiClass]"root\ccm\clientSDK:CCM_SoftwareUpdatesManager"

If ($SupName -Like "All" -or $SupName -like "all") {
Foreach ($Computer in $ComputerName) {
$Application = (Get-WmiObject -Namespace "root\ccm\clientSDK" -Class CCM_SoftwareUpdate -ComputerName $Computer | Where-Object { $_.EvaluationState -like "*$($AppEvalState0)*" -or $_.EvaluationState -like "*$($AppEvalState1)*"})
Invoke-WmiMethod -Class CCM_SoftwareUpdatesManager -Name InstallUpdates -ArgumentList (,$Application) -Namespace root\ccm\clientsdk -ComputerName $Computer
}
}
Else {
Foreach ($Computer in $ComputerName) {
$Application = (Get-WmiObject -Namespace "root\ccm\clientSDK" -Class CCM_SoftwareUpdate -ComputerName $Computer | Where-Object { $_.EvaluationState -like "*$($AppEvalState)*" -and $_.Name -like "*$($SupName)*"})
Invoke-WmiMethod -Class CCM_SoftwareUpdatesManager -Name InstallUpdates -ArgumentList (,$Application) -Namespace root\ccm\clientsdk -ComputerName $Computer
}
}
}

# VM Reset computer password
function VM-Password {
if($dcCredentials -eq "No" -or $dcCredentials -eq "no"){
Write-Output "Resetting computer password with logged in credentials..."
Reset-ComputerMachinePassword -Server "$dcServer"
}
if ($dcCredentials -eq "Yes" -or $dcCredentials -eq "yes") {
Write-Output "Resetting computer password with pre populated credentials..."
Reset-ComputerMachinePassword -Server "$dcServer" -Credential $dcDomain\$dcUser
}
if ($dcCredentials -eq "Ask" -or $dcCredentials -eq "ask") {
Write-Output "Resetting computer password asking for complete set of credentials..."
Reset-ComputerMachinePassword -Server "$dcServer" -Credential ""
}
}


# Run Windows Updates through SCCM.
if ($Action -eq "Update" ){
VM-Update
}

# Update the computer password to minimize risk of being disconnected from domain.
if ($Action -eq "Password" ){
VM-Password
}

# Run all action available.
if ($Action -eq "All" ){
VM-Update
VM-Password
}

Download

Disclaimer

As always these scripts are released AS-IS, usage of these script are at your on risk, i cannot guarantee functionality outside my environment.

Leave a Reply

Your email address will not be published. Required fields are marked *