Free Script: Gracefully Restart Virtual Machines on Hyper-V

There is one function whose absence in all official Hyper-V tools is blatantly obvious, and that is the ability to gracefully restart virtual machines. A Restart-VM function was introduced in 2012, but it just hard stops the VM before turning it back on. I suppose it has uses, but I’ve never found a good one. So, I set out to create my own. Here are the results.

Script details

The core functionality of the script is very basic. It performs a graceful shutdown of the VM, waits for it to be in an Off state, then turns it back on. It can work against multiple VMs at once, and runs each process in a separate background job so that the virtual machine restarts occur simultaneously. There are a number of things to be aware of with this script, so please read carefully before using.

This Script Should be Dot-Sourced

The first is that it was scripted to be dot-sourced. If you simply execute it, nothing will happen. The reason I created it that way is that this script uses the same name as the existing Restart-VM function, so it will create a shadow of it. This means that while this script is loaded, calling Restart-VM will use this function instead of the built-in cmdlet.

In order to dot-source a script, you just enter a period, a space, and the path to the script file. So, if you save this as C:ScriptsRestart-VM.ps1:

. C:ScriptsRestart-VM.ps1

From that point onward, this script would be called any time you use Restart-VM within the same session. What I think would work best is if you automatically loaded it into your PowerShell profile so that it’s always present rather than accidentally running the built-in cmdlet and crashing VMs.

There are a lot of resources available to help you auto-load scripts into your profile. I use something similar to method #5 on this blog post. Simple instructions are to go to %userprofile%Documents and create a subfolder named WindowsPowerShellProfile. Inside that folder, create a file named Microsoft.PowerShell_profile.ps1. If you also want it to load when you start the PowerShell ISE, you have to create a file named Microsoft.PowerShellISE_profile.ps1. Inside the file(s), insert a line that dot-sources the script file. You can also do this with PowerShell:

New-Item -Path $env:USERPROFILEDocumentsWindowsPowerShell -Type Directory
Set-Content -Path $env:USERPROFILEDocumentsWindowsPowerShellMicrosoft.PowerShell.profile.ps1 -Content ". C:ScriptsRestart-VM.ps1"

If you use PowerShell Remoting, you’ll need a workaround to get a profile to load in remote sessions. Directions are included in Kiquenet’s response in this Stack Overflow thread, which is, unfortunately, not the accepted answer.

All of the Base Cmdlet’s Parameters are Shadowed, but Some Work Differently

I read somewhere that if you’re going to write a function that shadows a built-in cmdlet that it should include all the same parameters so that people using it don’t have to change any scripts that depend on it. I did that with this function. However, the output isn’t going to be identical because the function behaves differently.

Force: The built-in cmdlet uses the Force parameter to suppress confirmation prompts. Otherwise, it asks you on each and every VM if you want to perform the restart. Since this script is much less dangerous and because of the way that I implemented the restart function, it will not ask you for confirmation. If the virtual machine has the Shutdown integration service enabled and responding, it will restart the VM without question. If the VM does not have the service enabled or it is not responding, then it will throw a warning and skip the VM. You will need to specify Force if you want VMs like that to restart, because it won’t be a graceful shutdown. Be aware that this only uses the built-in Stop-VM cmdlet. If the containing vmwp.exe process is hung, you’ll need to deal with that the hard way.

AsJob: I didn’t try this with the built-in cmdlet, but I think it just returns a single job no matter how many VMs are specified. My function returns a separate job for each VM that it is restarting. These jobs will be named in the format: Restart-VM-HostNameVMNameVMId.

Verbose: I didn’t look at their verbose output, but I rolled my own for this one. It’s not as descriptive as I would like it to be, but that’s because I don’t know if there’s a way to return verbose output from a background job.

The Script

There is some more explanatory text to come, but if you’ve absorbed the above then you’re ready for the script itself. Save the contents of the following to a .PS1 file. I wrote it with the expectation that you would use Restart-VM.ps1, but it doesn’t really matter.

#requires -Version 3
#requires -Modules Hyper-V
function Restart-VM {
	<#
	.SYNOPSIS
		Restarts one or more virtual machines by performing an orderly shutdown and startup sequence.
	.DESCRIPTION
		Restarts one or more virtual machines by performing an orderly shutdown and startup sequence.
		Shadows the built-in Restart-VM, which does not contain the same functionality.
	.PARAMETER Name
		A string array containing the name(s) of the virtual machine to restart.
	.PARAMETER ComputerName
		The name(s) of the computer(s) that host(s) the target virtual machine(s). If not specified, the local host will be used.
	.PARAMETER AsJob
		Places the restart operation in a job and returns immediately.
		Cannot be included with Passthru.
	.PARAMETER Force
		If the virtual machine is not running integration services, or if the integration services do not respond promptly, performs a Turn Off operation.
		If not specified, only virtual machines with responding integration services will be restarted.
	.PARAMETER VM
		The virtual machine object to be restarted.
	.PARAMETER Timeout
		Maximum number of seconds to wait for the target system to shut down gracefully before performing a Turn Off operation. Default is eternity.
		If multiple virtual machines are specified, each has its own separate timer.
	.PARAMETER ForceTimeout
		Number of seconds to wait for a forced turn off to work before assuming the worker process is hung. Default and minimum is 5 seconds.
		If multiple virtual machines are specified, each has its own separate timer.
		Has no effect if Force is not also specified.
	.PARAMETER Passthru
		Causes the script to emit a virtual machine object that represents the VM that was restarted.
		Cannot be included with AsJob.
	.OUTPUTS
		None by default.
		If -Passthru is specified, Microsoft.HyperV.PowerShell.VirtualMachine.
		If -AsJob is specified, System.Management.Automation.PSRemotingJob.
	.NOTES
	   Author: Eric Siron
	   Copyright: (C) 2014 Altaro Software
	   Version 1.0.2
	   Authored Date: November 15, 2014
		1.0.2 revision: April 27, 2016: Minor update to VM selection logic.
		1.0.1 revision: November 26, 2014: Minor fix to timeout logic.
	.LINK
	    https://www.altaro.com/hyper-v/free-script-restart-vm/
	.EXAMPLE
		C:PS> Restart-VM svtest
		Description
		-----------
		Restarts the virtual machine named test on the local host.
	.EXAMPLE
		C:PS> Restart-VM svhungsystem -Force
		Description
		-----------
		Restarts the virtual machine named svhungsystem on the local host even if it is not responding.
	.EXAMPLE
		C:PS> Get-VM -Name svtrouble | Restart-VM -Force -ForceTimeout 30
		Description
		-----------
		Forces the virtual machine named svtrouble to shut down, but extends the maximum wait time from 5 seconds to 30 seconds.
	.EXAMPLE
		C:PS> Restart-VM svserver1, svserver2 -ComputerName svhv1 -Timeout 60
		Description
		-----------
		Restarts VMs named svserver1 and svserver2 on Hyper-V host svhv1. If either does not shut down within 60 seconds, it is forcefully turned off.
	.EXAMPLE
		C:PS> Get-ClusterVM | Restart-VM
		Description
		-----------
		Using the output of Get-ClusterVM (another free script from Altaro), restarts all cluster VMs.
	#>
	[CmdletBinding(DefaultParameterSetName='ByName', SupportsShouldProcess=$true)]
	param(
		[Alias("VMName")]
		[Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ParameterSetName='ByName', Mandatory=$true, Position=1)]
		[String[]]$Name,
		[Alias("VMHost")]
		[Parameter(ValueFromPipelineByPropertyName=$true, Position=2, ParameterSetName='ByName')]
		[String[]]$ComputerName = @(, $env:COMPUTERNAME),
		[Parameter(ValueFromPipelineByPropertyName=$true)]
		[Switch]$AsJob,
		[Parameter(ValueFromPipelineByPropertyName=$true)]
		[Switch]$Force,
		[Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ParameterSetName='ByVM')]
		[Microsoft.HyperV.PowerShell.VirtualMachine[]]$VM,
		[Parameter(ValueFromPipelineByPropertyName=$true)]
		[UInt32]$Timeout = 0,
		[Parameter(ValueFromPipelineByPropertyName=$true)]
		[UInt32]$ForceTimeout = 5,
		[Parameter(ValueFromPipelineByPropertyName=$true)]
		[Switch]$Passthru
	)
	BEGIN {
		### Globals ###
		$JobList = @()
		### Script blocks ###
		$RestartScript = {
			param(
				[Parameter(Position=1)][Object]$VMObject,	# deserialized and cannot be used as a VM object
				[Parameter(Position=2)][UInt32]$Timeout,
				[Parameter(Position=3)][UInt32]$ForceTimeout,
				[Parameter(Position=4)][Switch]$Force,
				[Parameter(Position=5)][Switch]$Passthru
			)
			### Constants ###
			New-Variable -Name VMStateRunning -Value 2 -Option Constant
			New-Variable -Name VMStateTurnedOff -Value 3 -Option Constant
			New-Variable -Name MinimumForceTimeout -Value 5 -Option Constant
			### Functions ###
			function TurnOff {
				param(
					[Parameter()][Object]$VMObject,	# deserialized and cannot be used as a VM object
					[Parameter()][UInt32]$Timeout,
					[Parameter()][ValidateSet("Shutdown", "TurnOff")][String]$Mode
				)
				switch($Mode)
				{
					"Shutdown" {
						Stop-VM -ComputerName $VMObject.ComputerName -Name $VMObject.Name -Force
					}
					"TurnOff" {
						Stop-VM -ComputerName $VMObject.ComputerName -Name $VMObject.Name -Force -TurnOff
					}
				}
				$TimeoutCounter = 0
				$Continue = $true
				while($Continue)
				{
					Start-Sleep -Seconds 1
					$Continue = ((Get-WmiObject -Namespace rootvirtualizationv2 -Class Msvm_ComputerSystem -ComputerName $VMObject.ComputerName -Filter "Name = '$($VMObject.VMId.Guid.ToString().ToUpper())'").EnabledState -ne $VMStateTurnedOff)
					if($Timeout -and $Continue)
					{
						$TimeoutCounter++
						if($TimeoutCounter -ge $Timeout)
						{
							$false
							return
						}
					}
				}
				$true
			}
			if($ForceTimeout -lt $MinimumForceTimeout) { $ForceTimeout = $MinimumForceTimeout }
			$ForceRequired = $true	# this will be set off if a graceful shutdown is successful
			$StopAttempted = $false
			# output from get-vm isn't always reliable, ask WMI if the VM is on
			if((Get-WmiObject -Namespace rootvirtualizationv2 -Class Msvm_ComputerSystem -ComputerName $VMObject.ComputerName -Filter "Name = '$($VMObject.VMId.Guid.ToString().ToUpper())'").EnabledState -eq $VMStateRunning)
			{
				# The VM is on. Check for the shutdown integration service.
				$ShutdownIntegrationStatus = Get-VMIntegrationService -Name "Shutdown" -ComputerName $VMObject.ComputerName -VMName $VMObject.VMName
				if($ShutdownIntegrationStatus.Enabled)
				{
					if($ShutdownIntegrationStatus.PrimaryOperationalStatus -eq "Ok")
					{
						$StopAttempted = $true
						if(TurnOff -VMObject $VMObject -Timeout $Timeout -Mode "Shutdown")
						{
							$ForceRequired = $false
						}
						else
						{
							Write-Warning "Graceful shutdown for $($VMObject.Name) was unsuccessful."
						}
					}
				}
				if($Force -and $ForceRequired)
				{
					$StopAttempted = $true
					Write-Verbose -Message "Attempting to turn off (hard shut down) $($VMObject.VMName) on $($VMObject.ComputerName)"
					if(-not(TurnOff -VMObject $VMObject -Timeout $ForceTimeout -Mode "TurnOff"))
					{
						Write-Warning -Message "Unable to turn off $($VMObject.VMName)"
					}
				}
				if($ForceRequired -and -not $Force)
				{
					Write-Warning -Message "VM $($VMObject.VMName) can only be restarted with the -Force switch"
				}
				# start the VM if the script tried to turn it off and its status is off
				if((Get-WmiObject -Namespace rootvirtualizationv2 -Class Msvm_ComputerSystem -ComputerName $VMObject.ComputerName -Filter "Name = '$($VMObject.VMId.Guid.ToString().ToUpper())'").EnabledState -eq $VMStateTurnedOff)
				{
					Start-VM -ComputerName $VMObject.ComputerName -Name $VMObject.Name
					if($Passthru)
					{
						# just passing the object back will return a deserialized object. allow the calling thread to retrieve the object
						[String]::Join(",", ($VMObject.Computername, $VMObject.Name))
					}
				}
				elseif($StopAttempted)
				{
					Write-Error "VM $($VMObject.Name) cannot be started because it was not successfully turned off"
				}
			}
		}
	}
	PROCESS {
		if($Name -eq $null -and $VM -eq $null)
		{
			throw("Must select at least one virtual machine to restart.")
		}
		if($AsJob -and $Passthru)
		{
			throw("AsJob and Passthru cannot be specified together")
		}

		if ($PSCmdlet.ParameterSetName -eq 'ByName')
		{
			$VM = @()
			foreach($VMHost in $ComputerName)
			{
				foreach($VMName in $Name) # $Name is the input parameter for virtual machine name(s)
				{
					try
					{
						Write-Verbose -Message "Checking for $VMName on $VMHost"
						$VM += Get-VM -ComputerName $VMHost -Name $VMName -ErrorAction Stop
					}
					catch
					{
						Write-Verbose -Message "No VM with the name $VMName is present on host $VMHost"
					}
				}
			}
		}

		foreach($VMObject in $VM)
		{
			Write-Verbose -Message "Performing restart of $($VMObject.Name) on $($VMObject.ComputerName)"
			if($PSCmdlet.ShouldProcess("$($VMObject.Name) on $($VMObject.ComputerName)", "Restart"))
			{
				$JobList += Start-Job -ScriptBlock $RestartScript -Name "Restart-VM-$($VMObject.ComputerName)-$($VMObject.Name)-$($VMObject.VMId)" -ArgumentList $VMObject, $Timeout, $ForceTimeout, $Force, $Passthru
			}
		}
	}
	END {
		if($JobList)
		{
			if($AsJob)
			{
				$JobList
			}
			else
			{
				$ResultsArray = @()
				Wait-Job -Job $JobList | Out-Null
				foreach($Job in $JobList)
				{
					$ResultsArray += Receive-Job -Job $Job
				}
				$JobList | Remove-Job
				if($Passthru)
				{
					foreach($Result in $ResultsArray)
					{
						try
						{
							$VMItem = $Result.Split(",")
							Get-VM -ComputerName $VMItem[0] -Name $VMItem[1] -ErrorAction Stop
						}
						catch
						{
							; # really nothing to do about it
						}
					}
				}
				else
				{
					$ResultsArray
				}
			}
		}
	}
}

More Information

This script does include complete help. However, you’ll discover that you have to use it a little differently than you’re accustomed to. Just calling Get-Help Restart-VM results in the following:

Get-Help Restart-VM Output

What you need to do is: Get-Help Restart-VM -Category Function . This will then show you the help as you expect. You can use -Full, -Example, etc.

Parameters

  • AsJob: Returns to the PowerShell prompt immediately. The jobs for all VMs that are being restarted will be left in the pipeline.
  • ComputerName: The host of the virtual machine(s) to be restarted. You can use an array, and each will be scanned for the named VMs. Cannot be used with -VM.
  • Confirm: If you use this as -Confirm:$true, it will prompt before restarting any VM.
  • Force: If any virtual machine does not have the Shutdown integration service installed or enabled, or if it is not responding, then this parameter will cause that virtual machine to be forcibly turned off.
  • ForceTimeout: By default, the script will wait up to 5 seconds after instructing a VM to be forcibly turned off to attempt to turn it back on. ForceTimeout allows you to set a higher maximum timeout, in seconds.
  • Name: The name(s) of the virtual machine(s) to be restarted. Cannot be used with -VM.
  • Passthru: All restarted VM objects will be left in the pipeline.
  • VM: One or more virtual machine object(s) to be restarted. You can pipeline items such as the output from Get-VM. Cannot be used with -ComputerName or -Name.
  • Whatif: Shows you which virtual machine(s) the script would attempt to restart.

 

Altaro Hyper-V Backup
Share this post

Not a DOJO Member yet?

Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!

26 thoughts on "Free Script: Gracefully Restart Virtual Machines on Hyper-V"

  • Kevin Bloomfield says:

    Hi, why wouldnt you just use the Shutdown /r command?

    • Eric Siron says:

      Linux VMs, VMs that won’t respond to shutdown /r for some reason, VMs that aren’t in the same domain, VMs that you aren’t the administrator of, VMs that may or may not be hung up and you’d like to guarantee they restart with a single command, VMs that are isolated, VMs that have the firewall settings turned to max, and any time you want to control virtual machines from the hypervisor layer instead of the guest OS layer, to name a few reasons.

  • Mr B says:

    Hi,

    I am a newbie to PS scripting. I have about 40 VMs to restart as a job on a schedule (daily or weekly).
    How can i specify a list of VMs to restart and how or where do i need to tweak the script to restart as a batch?

    • Eric Siron says:

      This is a bit more than I can fully explain in a single posting, although I do have it planned for a future article. Basically, you store a list in a text file, usually in CSV format, import it into PowerShell when you need it. You wouldn’t tweak the script at all. You just pipe the imported list to it.

  • Mr B says:

    Hi,

    I am a newbie to PS scripting. I have about 40 VMs to restart as a job on a schedule (daily or weekly).
    How can i specify a list of VMs to restart and how or where do i need to tweak the script to restart as a batch?

  • Ray says:

    Great script. How might you run this as a scheduled task?

  • neo says:

    Background Info:
    I have the PowerShell profile setup correctly as per your instructions. So that the default Restart-VM function is always overridden by your custom function.
    I also did set exectionpolilcy bypass for PowerShell and PowerShell ISE

    Problem:
    When I run either PowerShell as Administrator I type the following
    PS> get-vm test
    //I see test is in a running state

    Then I do:
    PS C:WINDOWSsystem32> restart-vm test -verbose
    VERBOSE: Checking for test on USERPC
    VERBOSE: No VM with the name test is present on host USERPC

    any idea what I’m missing?

    • neo says:

      Note I’ve been trying to debug the script myself with slight modifications
      Background Info I left out. I’m on Windows 10 which has PowerShell 5.0, if you made this script for an older version, maybe the newer version broke your script?

      foreach($VMName in $Name) # $Name is the input parameter for virtual machine name(s)
      {
      #try
      #{
      # Write-Verbose -Message “Checking for $VMName on $VMHost”
      $VM = Get-VM -ComputerName $VMHost -Name $VMName -ErrorAction Stop
      #}
      #catch
      #{
      # Write-Verbose -Message “No VM with the name $VMName is present on host $VMHost”
      #}
      =================This results in========================
      PS C:WINDOWSsystem32> restart-vm test -verbose
      Cannot convert the “VirtualMachine (Name = ‘test’) [Id = ‘ab67a6aa-6394-4dcb-b1a0-dd891da337e5’]” value of type
      “Microsoft.HyperV.PowerShell.VirtualMachine” to type “Microsoft.HyperV.PowerShell.VirtualMachine”.
      At C:ScriptsRestart-VM.ps1:216 char:7
      … $VM = Get-VM -ComputerName $VMHost -Name $VMName -ErrorA …
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      CategoryInfo : InvalidArgument: (:) [], RuntimeException
      FullyQualifiedErrorId : ConvertToFinalInvalidCastException

      • Eric Siron says:

        I don’t see why it’s even attempting a conversion and why it wouldn’t work. Try Get-VM test | Restart-VM -Verbose and see if that works differently.

  • neo says:

    Background Info:
    I have the PowerShell profile setup correctly as per your instructions. So that the default Restart-VM function is always overridden by your custom function.
    I also did set exectionpolilcy bypass for PowerShell and PowerShell ISE

    Problem:
    When I run either PowerShell as Administrator I type the following
    PS> get-vm test
    //I see test is in a running state

    Then I do:
    PS C:WINDOWSsystem32> restart-vm test -verbose
    VERBOSE: Checking for test on USERPC
    VERBOSE: No VM with the name test is present on host USERPC

    any idea what I’m missing?

    • neo says:

      Note I’ve been trying to debug the script myself with slight modifications
      Background Info I left out. I’m on Windows 10 which has PowerShell 5.0, if you made this script for an older version, maybe the newer version broke your script?

      foreach($VMName in $Name) # $Name is the input parameter for virtual machine name(s)
      {
      #try
      #{
      # Write-Verbose -Message “Checking for $VMName on $VMHost”
      $VM = Get-VM -ComputerName $VMHost -Name $VMName -ErrorAction Stop
      #}
      #catch
      #{
      # Write-Verbose -Message “No VM with the name $VMName is present on host $VMHost”
      #}
      =================This results in========================
      PS C:WINDOWSsystem32> restart-vm test -verbose
      Cannot convert the “VirtualMachine (Name = ‘test’) [Id = ‘ab67a6aa-6394-4dcb-b1a0-dd891da337e5’]” value of type
      “Microsoft.HyperV.PowerShell.VirtualMachine” to type “Microsoft.HyperV.PowerShell.VirtualMachine”.
      At C:ScriptsRestart-VM.ps1:216 char:7
      … $VM = Get-VM -ComputerName $VMHost -Name $VMName -ErrorA …
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      CategoryInfo : InvalidArgument: (:) [], RuntimeException
      FullyQualifiedErrorId : ConvertToFinalInvalidCastException

  • neo says:

    Update: Your Script works fine with Windows 10 Powershell 5.0
    However: Somethings funky happens when your script is launched from the auto-run functionality of Powershell Profile

    Problem occurs when:
    Microsoft.PowerShell_profile.ps1
    Calls . C:ScriptsRestart-VM.ps1

    I renamed/deleted Microsoft.PowerShell_profile.ps1
    so that it wouldn’t autorun your script

    Then I manually opened powershell as admin
    manually typed . C:ScriptsRestart-VM.ps1
    manually typed restart-vm test
    and everything worked as expected

    I really can’t figure this out, also trying to set it up to work as a batch file. If you have any ideas please share, If I figure it out I’ll post.

  • neo says:

    Update: Your Script works fine with Windows 10 Powershell 5.0
    However: Somethings funky happens when your script is launched from the auto-run functionality of Powershell Profile

    Problem occurs when:
    Microsoft.PowerShell_profile.ps1
    Calls . C:ScriptsRestart-VM.ps1

    I renamed/deleted Microsoft.PowerShell_profile.ps1
    so that it wouldn’t autorun your script

    Then I manually opened powershell as admin
    manually typed . C:ScriptsRestart-VM.ps1
    manually typed restart-vm test
    and everything worked as expected

    I really can’t figure this out, also trying to set it up to work as a batch file. If you have any ideas please share, If I figure it out I’ll post.

  • Braden Pintar says:

    Hello,

    I have a 3-host cluster and am using your script to try to restart a specific VM. the command line I am using is:

    Restart-VM -ComputerName hv1,hv2,hv3 -Name windows81

    The script appears to run properly, however I get an error:
    The property ‘Count’ cannot be found on this object. Verify that the property exists.
    At \sofsscriptsRestart-VM.ps1:205 char:7
    if(-not $VM.Count)
    ~~~~~~~~~~~~~~
    CategoryInfo : NotSpecified: (:) [], PropertyNotFoundException
    FullyQualifiedErrorId : PropertyNotFoundStrict

    What am I missing?

    Thank you.

    Braden

    • Eric Siron says:

      Not your fault at all. The line throwing the error was mostly a basic sanity check that would ensure that upcoming lines would work. The sanity check itself apparently doesn’t work in all situations, but PowerShell knows how to deal with the situation anyway.
      I have modified the script so that it should work in all situations without throwing that error.

  • Braden Pintar says:

    Hello,

    I have a 3-host cluster and am using your script to try to restart a specific VM. the command line I am using is:

    Restart-VM -ComputerName hv1,hv2,hv3 -Name windows81

    The script appears to run properly, however I get an error:
    The property ‘Count’ cannot be found on this object. Verify that the property exists.
    At sofsscriptsRestart-VM.ps1:205 char:7
    if(-not $VM.Count)
    ~~~~~~~~~~~~~~
    CategoryInfo : NotSpecified: (:) [], PropertyNotFoundException
    FullyQualifiedErrorId : PropertyNotFoundStrict

    What am I missing?

    Thank you.

    Braden

  • John T says:

    This is a great script but have a couple of points to discuss:

    1. Does the script actually cycle through the VMs one by one? As it seems to turn them all off and on at the same time and this can be undesirable when you have over 100 VMs trying to start at the same time.

    2. Is there a way to enable ProcessorCompatibility if it is currently off?

    Kind regards,
    John

    • Eric Siron says:

      It initiates a shut down of all VMs simultaneously. Whether or not they all turn on at once depends on how quickly they shut down. I don’t have the capacity to test this on a 100 VM system. Rebooting 100 VMs in short proximity would be messy whether you take the entire storm at once or over time anyway. I think the best control would be to not ship all 100 VMs to the script at the same time.

      It would be possible to change the compatibility setting. Anything inserted between line 180/181 could operate against the VM while it was down. To prevent a reliance on the Hyper-V PowerShell module whose presence I cannot guarantee, the script currently operates solely via WMI. I don’t currently know how difficult it is to manage the processor compatibility setting that way.

  • Daniel Costa says:

    Hi Eric.

    Your script saved my day. Thanks.

    Is there a way to run without prompt for a confirmation?

    best regards

  • DeepReplier says:

    Hi Folks,

    Just be mindful that you’re utilizing Get-WmiObject, because it is being deprecated in favor of Get-CIMInstance in future versions.

    • Eric Siron says:

      Do you know of any official sources for that? I’ve seen rumors, but no official designation. Either way, “deprecated” is not a synonym for “deleted”. “Deprecated” means that Microsoft will not develop it further, which makes sense because there’s nothing else that it can do. I suspect that the functionality that this script calls upon will die before Get-WmiObject does. But, if I turn out to be wrong and gwmi dies while I’m still in the industry, I’ll come back and update my scripts. Hopefully, the CIM cmdlets will have filled in the remaining functionality gaps by then.

  • B Gergious says:

    Hi guys,

    I am very new is PowerShell scripts and I fairly understand the above script and I have amend it for Start/Stop VM’s, however in little further way. So I got an environment with a few servers and I need a script to check if some VM’s are running to Shut it down them and the other way if the VM’s are off to turn it ON.
    Woudl you be able to help me please ?

    • Eric Siron says:

      You might want to post this question in the dojo forums where we have more space and tools.
      But basically, it’s something like get-vm | where State -eq 'Running' | ---> your shutdown script and get-vm | where State -ne 'Running' | start-vm

  • QingshengHe says:

    Very useful script. great thanks!

Leave a comment or ask a question

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

Your email address will not be published.

Notify me of follow-up replies via email

Yes, I would like to receive new blog posts by email

What is the color of grass?

Please note: If you’re not already a member on the Dojo Forums you will create a new account and receive an activation email.

Microsoft 365 Security checklist - free eBook