Free PowerShell Script for Hyper-V: Integration Services on Older Systems

Save to My DOJO

Free PowerShell Script for Hyper-V: Integration Services on Older Systems

Windows Update should cover Integration Services updates for most of us, however, I still have an active need to update Windows 7 guests of a 2012 R2 host, and these machines frequently get re-imaged from a source that I do not control. I looked for a pre-built script, but the ones that I found ranged from non-functional to not adequate. So, I rolled one up and now it’s also yours to enjoy too!

Try Windows Updates for Hyper-V Integration Services Updates

Most people should not need this script anymore. Unlike their predecessors, Windows Server 2016 and later do not include the necessary files. Guests running recent Windows Server operating systems should receive updates in normal Microsoft Update cycles. For all other Windows systems, you’ll need to locate a copy of the necessary Integration Services CAB file(s). You can use them with this script to update a VM offline or copy their installer into the guest operating system’s file system and run them there.

Microsoft Senior PFE Chuck Timon has written an article that discusses which operating systems will receive updates as guests.

Integration Services Updates Work Only for Windows

Linux and BSD guests use “Integration Components”, not Integration Services. I do not know of any external scripts to update them in offline guests. The Linux and BSD kernels have included the base components natively for several versions, so most current distributions will automatically update them along with kernel updates. You can find information on enabling any additional components on the related Microsoft docs pages. For other operating systems, or if you prefer to install the updates out-of-band from kernel upgrades, you can download and install the Linux Integration Services separately.

Script Notes

I included the initial file listing here for convenience. I will publish any future updates exclusively on my GitHub page.

Notes:

  • The script only works on local virtual machines
  • The local machine must have the Hyper-V PowerShell cmdlets installed
  • You must run the script as an administrator
  • The script only requires that you supply one or more VMs (output from Get-VM), or their names
  • By default, the script uses the files from the host. These might be out of date or not even present. Use -Path to designate your preferred files.
  • By default, it will only attempt to install the 64-bit components. Use -x86 for 32-bit components, or -Try32And64 to try both.
  • By default, it looks in the local Windows folder for the necessary CABs. If you’re using on something newer than 2012 R2, you’ll need to provide them yourself. Read the help (in Get-Help or online) for details.
  • The script was built for 2012 R2, so those are the file versions that it expects. If you intend to use different file names, then you’ll need to edit the script file. I left the necessary lines near the top so they’ll be easy to find.
  • The script outputs nothing when it works. If you need verification, use -Verbose. You can safely re-apply the same CAB.

Script File Listing

The text of the script’s initial release appears below:

<#
.SYNOPSIS
Installs the Hyper-V integration services into offline local virtual machines.
.DESCRIPTION
Installs the Hyper-V integration services into offline local virtual machines.
Built specifically to work on Windows Server 2012 R2 guests. Modify the default filenames and override $Path to use a different set of Integration Services.
Use the -Verbose switch for verification of successful installations.
.PARAMETER VM
The name or virtual machine object(s) (from Get-VM, etc.) to update.
.PARAMETER Path
A valid path to the update CABs.
MUST have sub-folders names amd64 and x86 with the necessary Windows6.x-HyperVIntegrationServices-PLATFORM.cab.
You can override the file names by editing the script.
.PARAMETER x86
Use the x86 update instead of x64.
.PARAMETER Try32and64
Attempt to install both the 32-bit and 64-bit updates. Use if you're not sure of the bitness of the contained guest.
.EXAMPLE
C:PS> Update-VMIntegrationServices -VMName vm04
Installs the x64 updates on the VM named vm04.
.EXAMPLE
C:PS> Get-VM | Update-VMIntegrationServices -Try32and64
Attempts to update all VMs. Will try to apply both 32 and 64 bit to see if either is applicable.
.NOTES
Author: Eric Siron
Version 1.0, December 11, 2018
Released under MIT license
.LINK
https://github.com/ejsiron/Posher-V/blob/master/Docs/Update-VMIntegrationServices.md
#>
[CmdletBinding(DefaultParameterSetName='Default')]
param(
    [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $true, ParameterSetName='Default')]
    [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $true, ParameterSetName='x86Only')]
    [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $true, ParameterSetName='TryBoth')]
    [psobject[]]$VM,
    [Parameter(ParameterSetName='Default')]
    [Parameter(ParameterSetName = 'x86Only')]
    [Parameter(ParameterSetName = 'TryBoth')]
    [String]$Path = [String]::Empty,
	[Parameter(ParameterSetName = 'x86Only')][Switch]$x86,
	[Parameter(ParameterSetName = 'TryBoth')][Switch]$Try32and64
)

#requires -Version 4
#requires -RunAsAdministrator
#requires -Module Hyper-V

begin
{
	Set-StrictMode -Version Latest
	$DefaultPath = Join-Path -Path $env:SystemRoot -ChildPath 'vmguestsupport'
	$x64UpdateFile = 'amd64Windows6.x-HyperVIntegrationServices-x64.cab'
	$x86UpdateFile = 'x86Windows6.x-HyperVIntegrationServices-x86.cab'

	if ([String]::IsNullOrEmpty($Path)) { $Path = $DefaultPath }
	$Path = (Resolve-Path -Path $Path -ErrorAction Stop).Path
	$UpdateFiles = New-Object -TypeName System.Collections.ArrayList
	if ($x86 -or $Try32and64)
	{
		$OutNull = $UpdateFiles.Add((Resolve-Path -Path (Join-Path -Path $Path -ChildPath $x86UpdateFile) -ErrorAction Stop).Path)
	}
	if (-not $Try32and64)
	{
		$OutNull = $UpdateFiles.Add((Resolve-Path -Path (Join-Path -Path $Path -ChildPath $x64UpdateFile) -ErrorAction Stop).Path)
	}
}

process
{
    if($VM.Count -eq 0) { exit 0 }
    $VMParamType = $VM[0].GetType().FullName
    switch($VMParamType)
    {
        'Microsoft.HyperV.PowerShell.VirtualMachine' {
            # preferred condition so do nothing; just capture the condition
        }
        'System.String' {
            $VM = Get-VM -Name $VM
        }
        default {
            Write-Error -Message ('Cannot work with objects of type {0}' -f $VMParamType) -ErrorAction Stop
        }
    }

	foreach ($Machine in $VM)
	{
		Write-Progress -Activity 'Adding current integration components to VMs' -Status $Machine.Name -Id 7 # ID just so it doesn't collide with Add-WindowsPackage or *-DiskImage
		if ($Machine.State -eq [Microsoft.HyperV.PowerShell.VMState]::Off)
		{
			$VMHDParams = @{
				VM                 = $Machine;
				ControllerType     = [Microsoft.HyperV.PowerShell.ControllerType]::IDE;
				ControllerNumber   = 0;
				ControllerLocation = 0
			}

			if ($Machine.Generation -eq 2)
			{
				$VMHDParams.ControllerType = [Microsoft.HyperV.PowerShell.ControllerType]::SCSI
			}

			$VHDPath = [String]::Empty
			try
			{
				$VHDPath = (Get-VMHardDiskDrive @VMHDParams).Path	
			}
			catch
			{
				Write-Warning ('VM "{0}" has no primary hard drive' -f $Machine.Name)
			}

            $DiskNum = (Mount-VHD -Path $VHDPath -Passthru).DiskNumber
            $DriveLetters = (Get-Disk $DiskNum | Get-Partition).DriveLetter
            if ((Get-Disk $DiskNum).OperationalStatus -ne 'Online')
            {
                Set-Disk $MountedVHD.Number -IsOffline:$false -IsReadOnly:$false
                Set-Disk -Number $DiskNum -IsOffline $false
                Set-Disk -Number $DiskNum -IsReadOnly $false
            }

            #Install the patch
            $TargetDriveLetter = ''
            foreach ($DriveLetter in $DriveLetters)
            {
                if (Test-Path ($DriveLetter + ':Windows'))
                {
                    $TargetDriveLetter = $DriveLetter
                }
            }

            if($DriveLetter)
            {
                foreach ($UpdateFile in $UpdateFiles)
                {
                    try
                    {
                        $OutNull = Add-WindowsPackage -PackagePath $UpdateFile -Path ($TargetDriveLetter + ':') -ErrorAction Stop	
                    }
                    catch
                    {
                        # Add-WindowsPackage writes to the warning and the error stream on errors so let its warning speak for itself
                        # Only include more information for an unnecessary patch
                        if ($_.Exception.ErrorCode -eq 0x800f081e)
                        {
                            Write-Warning 'This package is not applicable'
                        }
                    }
                }
            }
            else
            {
                Write-Error -Message ('No drive on VM {0} has a Windows folder' -f $Machine.Name) -ErrorAction Continue
            }

            Dismount-VHD -Path $VHDPath
		}
		else
		{
			Write-Warning -Message ('{0} cannot be updated because it is not in an Off state' -f $Machine.Name)
		}
	}
}

The script is fairly straight-forward, but its help does include a couple of examples to get you started.

Bug Reports and Enhancement Requests

This script is not very complicated and I did test the entire thing with all possible parameter settings. Let me know here or on the GitHub issues page.

If you need it to do something that it doesn’t, let me know. I won’t promise feature updates (especially if they’re complicated), but I’ll at least listen to requests. And, it’s open source, so you can add updates of your own.

I recently uploaded another free PowerShell script which detects problems with MAC address in Hyper-V which is proving very popular with you guys.

If you just can’t get enough, here are more free PowerShell scripts for you.

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!

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.