Monitoring Startup,Shutdown and restart of a Virtual machine With PowerShell 3.0

During scripting some maintenance PowerShell scripts for Hyper-V guests I felt the need for a more accurate way to monitor the startup of a virtual machine. Pings, telnet to a known open port it all doesn’t do the job accurately enough as I want to know when CTRL+AL+DEL appears on the screen. So I pinged Jeff Wouters who told me I could monitor Get-VM -Name DC01 | Get-VMIntegrationService  to detect when PrimaryStatusDescription goes to “OK”.

Now when you look at the Integration services there are 5 of them.

image

Which one is the best to use for our purpose? Well,I tested them out and after some experimenting with the various services I concluded that the PrimaryStatusDescription of the  Key-Value Pair Exchange works best for this purpose. All others become available a bit to soon in the process of starting a VM, which seems logical.

Monitor a starting virtual machine

So how to use this in a script? We’ll here’s a snippet to monitor the boot process of a guest.

$Vm = Get-VM "MyVM"
start-VM "$Vm"
#This means the VM is now shutting down ...    
$Counter = 0
$ProgressCount = 0
Do
{    
    $Operational = Get-VM -Name $VM | Get-VMIntegrationService -Name "Key-Value Pair Exchange"
    $Counter = $Counter + 1 
    $ProgressCount =  $ProgressCount +1
    $PercentComplete = ($ProgressCount * 20)
    Write-Progress -Activity "$VM" -status "VM starting up: $Status - Progressbar indicates activity, not a percent of completion: ($Counter Seconds)"  -percentComplete ($PercentComplete / 100 *100)
    if ($PercentComplete -gt 90) {$ProgressCount = 0}
    sleep 1
}
While ($Operational.PrimaryStatusDescription -ne "OK")
$Status = (Get-VM -Name $VM | Get-VMIntegrationService -Name "Key-Value Pair Exchange").PrimaryStatusDescription
Write-Progress -Activity "VM $VM is up and running" -status "VM status: $Status - We're done here. Completed in a total of $Counter seconds."  -percentComplete (100)

 

Monitor a stopping virtual machine

Likewise, sometime we want to monitor a VM shutting down, which is the same code as above but with reverse logic.

$Vm = Get-VM "MyVM"
stop-VM "$Vm"
$Counter = 0
$ProgressCount = 0
#This means the VM is now shutting down  in the retart cycle ...    
   Do
   {            
    $Operational = Get-VM -Name $Vm | Get-VMIntegrationService -Name "Key-Value Pair Exchange"
    $Counter = $Counter + 1 
    $Status = (Get-VM -Name $Vm | Get-VMIntegrationService -Name "Key-Value Pair Exchange").PrimaryStatusDescription
    $ProgressCount =  $ProgressCount + 1
    $PercentComplete = ($ProgressCount * 20)
    Write-Progress -Activity "$VM" -status "VM shutting down : $Status - Progressbar indicates activity, not a percent of completion: ($Counter Seconds)"  -percentComplete ($PercentComplete / 100 *100)
    if ($PercentComplete -gt 90) {$ProgressCount = 0}
    sleep 1
   }
   While ($Operational.PrimaryStatusDescription -eq "OK")
   $Status = (Get-VM -Name $Vm | Get-VMIntegrationService -Name "Key-Value Pair Exchange").PrimaryStatusDescription
   Write-Progress -Activity "VM $Vm has now been shutdown" -status "VM status: $Status - We're done here. Completed in a total of $Counter seconds."  -percentComplete (100)

Monitor a restarting a virtual machine.

When in a PowerShell script you want to monitor progress of a virtual machine restarting you can combine both. You monitor shutdown and you monitor startup.

$VmThatRestarts = Get-VM "MyVM"
#Restart the VM
#This means the VM is now shutting down  in the retart cycle ...
 $Counter = 0
 $ProgressCount = 0
Do
{            
    $Operational = Get-VM -Name $Vm | Get-VMIntegrationService -Name "Key-Value Pair Exchange"
    $Counter = $Counter + 1 
    $Status = (Get-VM -Name $Vm | Get-VMIntegrationService -Name "Key-Value Pair Exchange").PrimaryStatusDescription
    $ProgressCount =  $ProgressCount + 1
    $PercentComplete = ($ProgressCount * 20)
    Write-Progress -Activity "$VM" -status "VM restarting - Shutdown phase : $Status - Progressbar indicates activity, not a percent of completion: ($Counter Seconds)"  -percentComplete ($PercentComplete / 100 *100)
    if ($PercentComplete -gt 90) {$ProgressCount = 0}
    sleep 1
}
While ($Operational.PrimaryStatusDescription -eq "OK")
$Status = (Get-VM -Name $Vm | Get-VMIntegrationService -Name "Key-Value Pair Exchange").PrimaryStatusDescription
Write-Progress -Activity "VM $Vm has now been shutdown in restart cycle" -status "VM status: $Status - VM has shut down in $Counter Seconds"  -percentComplete (100)
   
#Any thing worthwhile is worth adding 1 second of waiting for good measure :-)
Sleep 1

#This means the VM is now starting  ...    
$ProgressCount = 0
Do
{    
    $Operational = Get-VM -Name $VM | Get-VMIntegrationService -Name "Key-Value Pair Exchange"
    $Counter = $Counter + 1 
    $ProgressCount =  $ProgressCount +1
    $PercentComplete = ($ProgressCount * 20)
    Write-Progress -Activity "$VM" -status "VM restarting - Startup phase: $Status - Progressbar indicates activity, not a percent of completion: ($Counter Seconds)"  -percentComplete ($PercentComplete / 100 *100)
    if ($PercentComplete -gt 90) {$ProgressCount = 0}
    sleep 1
}
While ($Operational.PrimaryStatusDescription -ne "OK")
$Status = (Get-VM -Name $VM | Get-VMIntegrationService -Name "Key-Value Pair Exchange").PrimaryStatusDescription
Write-Progress -Activity "VM $VM is up and running again" -status "VM status: $Status - We're done here. Completed in a total of $Counter seconds."  -percentComplete (100)

Note that in all the above snippets  I’ve thrown some logic in to us the progress bar as an activity bar as I know of no way to calculate real % done in a startup, shutdown, restart process. It looks something like this in ISE

image

or like this in a PowerShell prompt

image

Checking Host Integration Services Version on all Nodes of A Windows Server 2012 Hyper-V Cluster With PowerShell

It’s important to keep our Hyper-V cluster hosts and the virtual machines running on them up to date. Whilst we have great and free solutions to achieve this there are some things missing like centralized reporting on the Integration Services component version running on all of the nodes in a cluster and way to upgrade all the virtual machines to version running on the host. This post deals with the first issue.

Before we upgrade the Integration Services components on the virtual machines we always check if all nodes in the cluster are on the same version themselves. Sure this should not happen if you mange them right but my world isn’t perfect. So trust but verify.With cluster sizes now up to 64 nodes it’s ever more important to keep an eye on them. But even for smaller cluster the task of determining the Integration Services components manually via the GUI, event viewer and/or registry is rather tedious. Out of sync Integration Services components can be troublesome and cause many issues and if you have out of sync virtual machines, imagine the extra mess you’ll be in when even the cluster nodes are running different versions.

To make live easier I threw a little PowerShell script together to check the host Integration Services component version on all nodes of a Window Server 2012 Hyper-V Cluster With PowerShell. I’m far from a PowerShell guru, but you’ll see that you can do a lot of things  done even if you’re not. I’m sharing it here for you to use, adapt for your own needs and get some inspiration. It basically allows you to optionally pass an expected version of the IS components and a cluster name like this

CheckHyperVClusterHostsICVersion -Version 6.2.9200.16433 -cluster "MyClusterName"

It does the following:

  • It will list per Integration Services component version found on cluster nodes what version was found on what nodes. This gives you a nice overview. I hope this never becomes to much of a list in your clusters.
  • If you don’t specify a cluster it will try to connect to the cluster to which the host you’re running on belongs, if any.
  • If the host does not belong to a cluster it will just provide feedback on the IS version of that Hyper-V host you’re running the script on.

Here’s a screen shot of when you run this on a none clustered host, without Hyper-V installed:

image

This is the result of running it against a well maintained cluster without any parameters that has been updated with KB2770917:

image

The same but now with the expected version and cluster name passed as parameters

image

So, there you go, I hope you find it useful.

#===========================================================
# # Microsoft PowerShell Source File 
# 
# NAME:    CheckISCOnNodesOfHyperVCluster.ps1
# VERSION:    1.0.0.0

# AUTHOR:    Didier Van Hoye
# DATE :    17/11/2012
# 
# COMMENT:     This script is intended to be run 
#              against Windows Server 2012 and assumes
#            the use of PowerShell 3.0
#            The parameters are optional but if you
#            leave out some the remainder should be named.
# # =======================================================
 
cls
$ErrorActionPreference = "Stop"

 
function CheckHyperVClusterHostsICVersion
{
    Param
    (
        #Param help description
        [Version]
        $ExpectedISCVersion,
        #Param help description
        [String]
        $Cluster
    )

    Write-Host "This script will check the IS components on all nodes of a cluster." -ForegroundColor Green
 
    If ($ExpectedISCVersion) {Write-Host "You specified the expected IS component version to be $ExpectedISCVersion" -ForegroundColor Green}
    Else {Write-host "You did not specify an expected IS component version." -ForegroundColor Green}
    
    If ($Cluster)
    {
        Try
        {
            $ClusterObject= Get-Cluster -Name $Cluster
        }
        Catch
        {     
            Write-Host "We cannot contact the cluster you specified"
        }
    }
    Else
    {    
        write-Host "`n`n"
        Write-host "You did not specify a cluster to connect to. We'll use the cluster to which the node this script is running on belongs if any." -ForegroundColor Yellow
        write-Host "`n`n"
  
        Try
        {
            $ClusterObject = Get-Cluster
        }
  
        Catch
        {
            $LocalHost = $env:computername
            Write-Host
            Write-Host "The current node ($LocalHost) is not a member of a cluster. As a courtsey to you we'll check the IS components for current host" -foregroundcolor Magenta
            Write-Host
        }
 
    }
  
    If ($ClusterObject) {$ToCheck= "the nodes of cluster $ClusterObject"} Else { $ToCheck = "server $env:computername"}
 
    write-Host "Attempting to running Integration Components version check on" $ToCheck -ForegroundColor Green
    Write-Host


    If ($ClusterObject)
    {

        $ClusterNodes = Get-Clusternode -cluster $ClusterObject.Name
        
        #Declare an hashtable to hold all host/IS version values. The hosts are the key here.
        $HostISVersions = @{}
 
        foreach ($ClusterNode in $ClusterNodes)
        {
            Try
            {
                 $HostISVersions[$ClusterNode.Name]=Get-ItemProperty "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersionVirtualizationGuestInstallerVersion" | select -ExpandProperty Microsoft-Hyper-V-Guest-Installer
            }
            Catch
            {
            Write-Host "We could not determine the version of the Integration Services on this host, probably due to this not being a Hyper-V host" -ForegroundColor Orange
            Write-Host "We'll check this for you right now" -ForegroundColor Orange
            $HyperVFeature = Get-WindowsFeature Hyper-V
            If ($HyperVFeature.Installstate -eq "Installed")
            {
              Write-Host "Hyper-V seems to be installed on this node. Something else is wrong." -ForegroundColor Red
            }
            Else
            {
                Write-Host "Hyper-V is indeed not installed on this node." -ForegroundColor Orange
            }
            }
        }
         #Use GetEnumerator or thise sorting thing doesn't work out well on an hash tabel :-)
        $UniqueIcVersions = $HostISVersions.GetEnumerator() | Sort-Object -Property Value -Unique
 
        Write-Host "We've found " $UniqueIcVersions.count "versions on the" $HostISVersions.count "nodes of your cluster" $ClusterObject.Name
 
        ForEach ($IcVersion in $UniqueIcVersions )
        {
            $Counter = 1
            $IcVersionValue = $IcVersion.value
            "IC version " + $IcVersion.value + " is found in:"
            foreach ($Key in ($HostISVersions.GetEnumerator()| Where-Object { $_.value -eq $IcVersionValue}))
            {
                "`t" + "$Counter : " + $Key.Name
                $Counter= $Counter + 1
            }
 
            If ($ExpectedISCVersion)
            {
               
                $CompareVersions = ([Version]$IcVersion.Value).CompareTo([Version]$ExpectedISCVersion)
                        
                switch ($CompareVersions)
                {
                    0 {Write-Host "This version ($IcVersionValue) is equal to the expected version ($ExpectedISCVersion)." -ForegroundColor Green}
                    1 {Write-Host "This version ($IcVersionValue) is higher than the expected version ($ExpectedISCVersion). Please ensure all hosts run the same IC version level." -ForegroundColor Yellow}
                    -1 {Write-Host "This version ($IcVersionValue) is lower than the expected version ($ExpectedISCVersion). Please ensure all hosts run the same IC version level." -ForegroundColor Red}
                }
            }

        }
    }

    Else
    {
        Try
        {
            $HostIcVersion = Get-ItemProperty "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersionVirtualizationGuestInstallerVersion" | select -ExpandProperty Microsoft-Hyper-V-Guest-Installer
            Write-Host "The IS component version on server $localhost is $HostIcVersion"
            If ($ExpectedISCVersion)
            {
               
                   $CompareVersions = ([Version]$HostIcVersion).CompareTo([Version]$ExpectedISCVersion)
                        
                switch ($CompareVersions)
                {
                0 {Write-Host "This version ($HostIcVersion) is equal to the expected version ($ExpectedISCVersion)." -ForegroundColor Green}
                1 {Write-Host "This version ($HostIcVersion) is higher than the expected version ($ExpectedISCVersion). Please check if you need to downgrade your host or if the expected version is correct." -ForegroundColor Yellow}
                -1 {Write-Host "This version ($HostIcVersion) is lower than the expected version ($ExpectedISCVersion). Please check if you need to upgrade your host or if the expected version is correct." -ForegroundColor Red}
                }
            }
        }
        Catch
        {
            Write-Host "We could not determine the version of the Integration Services on this host, probably due to this not being a Hyper-V host" -ForegroundColor yellow
            Write-Host "We'll check this for you right now" -ForegroundColor yellow
            $HyperVFeature = Get-WindowsFeature Hyper-V
            If ($HyperVFeature.Installstate -eq "Installed")
            {
                Write-Host "Hyper-V seems to be installed on this node. Something else is wrong." -ForegroundColor Red
            }
            Else
            {
                Write-Host "Hyper-V is indeed not installed on this node." -ForegroundColor yellow
            }
        }
    }
}
 
CheckHyperVClusterHostsICVersion -Version 6.2.9200.16433 -cluster "MyClusterName"

KB2770917 Updating Host & Guest Integration Services Components – Most Current Version Depends on Guest OS

As after installing http://support.microsoft.com/kb/2770917 on Windows Server 2012 Hyper-V hosts the integration services components are upgraded from 6.2.9200.16384 to 6.2.9200.16433. Windows Server 2012 guest get that same upgrade and as such also the newer integration services components. The guest with older OS version needed a different approach. So I turned to all the great PowerShell support now available for Hyper-V to automate this. Pretty pleased with the results of our adventures in PowerShell scripting I let the script go on Hyper-V cluster dedicated to test & development. As such there are some virtual machines on there running Windows 2003 SP2 (X64) and Windows XP SP3 (x86).  Guess what, after running my script and verifying the integration services version I see that those VM still report version 6.2.9200.16384 . No update. Didn’t my new scripting achievement “take” on those older guests?

So I try the install manually and this is what I get:

clip_image001

 

Why is there no upgrade for these guests?  Are they not needed or do I have an issue? So I mount the ISO and dig around in the files to find a clue in the date:

clip_image001[10]

 

It looks like there are indeed no update components in there for Windows XP/ W2K3. So then I look at the following registry key on the host where I normally use the Microsoft-Hyper-V-Guest-Installer-Win6x-Package value to find out what integration services version my hosts are running:

image

 

Bingo, there it seems indicated that we indeed need version for XP/W2K3 and version for W2K8(R2)/W2K12 and Vista/Windows 7/Windows 8. Cool, but I had to check if this was indeed as it should be and I’m happy to confirm all is well. Ben Armstrong (http://blogs.msdn.com/b/virtual_pc_guy/) confirmed that this is how it should be. There was a update needed for backup that only applied to Windows 8 / Windows Server 2012 guests.  As this fix was in a common component for Windows Server 2008 and later they all got the update. But for the older OS versions this was not the case and hence no update is need. Which is reflected in all the above. In short, this means your XP SP3 & W2K3SP2 VMs are just fine running the version of the integration services and are not in any kind of trouble.

This does leave me with an another task. I was planning to do enhancements to my script like feedback on progress, some logging, some better logic for clustered and non clustered environments, but now I have to also address this possibility and verify using the registry keys on the host which IC version I should check against per OS version. Checking against just for the one related to the host isn’t good enough Smile.

Don’t Be Afraid To Learn PowerShell, You’ll Need It & You’ll Learn To Love It!

As I started my career in IT doing data crunching, OLAP & development in VBA & VB5.00/6.0 it isn’t that surprising I’ve done most of my automation in VBScript. I’m very familiar with it and even in the Windows 2008 Core era it was very useful as we didn’t have .NET in it at that time, meaning no PowerShell (no, the unsupported hack to get it on there does not count).

The first real PowerShell use for me came with Exchange 2007. That worked out pretty well, but at the time we didn’t use it for much more than Exchange. Today, more and more I’m starting to use PowerShell versus VBScript. For one sometimes VBScript can’t get it done, it’s not being developed any more in its capabilities and two, PowerShell commandlets do pack a serious punch!

Since Windows Server 2008 R2 PowerShell has gotten better overall support and with Windows 8 it is everywhere, natively. That’s very good and it means we can do all we need to do now (well a whole lot of it) in PowerShell. And then I haven’t even mentioned the entire workflow support in Windows 8 PowerShell!  As an old VB guy I had to get over my dislike for curly braces. I also need to earn the different syntax and especially the way in which to use the programming constructs (control flow, sub routines, operators, data types).

A lot of people tend to focus on the one liners. These are great and powerful, but they often reminded me of the old discussion with C/C++ developers about code readability & maintainability.  For that purpose we don’t mind that the code is more verbose. One liners are not the goal  but they are fun. Just remember that all code, how small it may be, one day will have to be maintained. The more readable, logical and easy to understand it is, the better. The whole “self documenting” thingy Smile.  One liners do not always fit in here. But to demonstrate I have nothing against them I’ll show you a real easy example for all you out there dealing with the jump to PowerShell. If you get hooked on one liners just be sure to use them with reason and go visit the blog of Jeff Wouters , you’ll become good scripting buddies.

Let’s say in VBScript you needed to format a date in a specific way. In VBScript you have a very limited number of format option to use. So when you want something funky like “20120414” (YearMonthDay) as a date format you’d use a function that builds that string and pads the numbers with zero if needed. You can either write a generic function to handle all possible date needs or a custom/purpose built one for just the needs at hand.

Just to get the gist of this, it could look a bit like this:

WScript.echo FormatDateForMyNeed() PrivateFunction FormatDateForMyNeed () Dim sDate, sYear, sMonth, sDay sDate =Now() sYear =Year(sDate) sMonth =Month(sDate) IfLen(sMonth) =1Then sMonth ="0"& sMonth sDay =Day(sDate) IfLen (sDay) =1Then sDay ="0"& sDay FormatDateForMyNeed = sYear & sMonth & sDay End Function

Driven by “routine” & a VBScript background you could mash up some functions in PowerShell and make it a convoluted scripting exercise:

function BuildDate { $date=Get-Date $String= [string]$date.year $MONTH= [String]$date.month $String+= PadString "0"2$Month$DAY= [String]$date.day $String+= PadString "0"2$DAYReturn$String } function PadString ($PadChar, $PaddedLength, [String]$StringToPad) { $StringToPad= ($padChar* ($PaddedLength-$stringToPad.length)) +$stringToPadReturn$StringToPad } BuildDate

But PowerShell has way better date format support than VBScript and you can just write this:

Get-Date -format "yyyyMMdd"

Now that’s a one liner I have nothing against and yes, it saves a whole lot of effort. Sure this is a real simple example but it proves a point. Do your self a favor, take out a couple of hours a week and dabble around in PowerShell. You’ll add a valuable time saving tool to your inventory and gain a precious skillset Smile for a bright future! Need a good example? See this blog post by Janssen Jones to see some workflow goodness and what it can do.