How to Patch Hyper-V virtual machines through PowerShell Direct

In my post introducing PowerShell Direct I showed how you could use PowerShell Direct to create, and use, a PowerShell remoting session over the VM bus instead of using WSMAN.

So, what else can you do with PowerShell Direct?

In truth, just about anything you need to but one thing that comes to mind is patching virtual machines. I expect that for the majority of your machines you’ll be using a patching mechanism such as WSUS, SCCM or a third-party product. This is great when you have full connectivity and your VMs can talk directly to your patching server. But what about non-domain machines or those VMs tucked away in their own little corner of the network that can’t communicate with your patching server. Or even those machines that need a set of patches applying before you can allow them on the network.

You can use PowerShell Direct in two ways to help resolve some of these patching issues.

Copy patch and install

The first way is to copy the required patch files to the new system and install them directly. First step is to download the patch, or patches, you need to apply – don’t forget to unlock them. Then you can create a remoting session to the virtual machine:

PS> $computer = 'W16ND01' PS> $cred = Get-Credential -Credential "$computer\Administrator" PS> $s = New-PSSession -VMName $computer -Credential $cred PS> $s Id Name ComputerName ComputerType State ConfigurationName Availability -- ---- ------------ ------------ ----- ----------------- ------------ 2 Session2 W16ND01 VirtualMachine Opened Available 1 2 3 4 5 6 7 8 PS > $computer = 'W16ND01' PS > $cred = Get-Credential -Credential "$computer\Administrator" PS > $s = New-PSSession -VMName $computer -Credential $cred PS > $s Id Name ComputerName ComputerType State ConfigurationName Availability -- ---- ------------ ------------ ----- ----------------- ------------ 2 Session2 W16ND01 VirtualMachine Opened Available

Copy patch

You can then copy the patch to the virtual machine

PS> Copy-Item -Path C:\Source\windows10.0-kb4013418-x64_b7165b95be791aeb025135efe60e00db2800c5c6.msu -Destination C:\Source\ -ToSession $s 1 PS > Copy-Item -Path C : \ Source \ windows10 . 0 -kb4013418 -x64_b7165b95be791aeb025135efe60e00db2800c5c6 . msu -Destination C : \ Source \ -ToSession $s

You can check that the file is present on the remote machine

PS> Invoke-Command -Session $s -ScriptBlock {Test-Path -Path C:\Source\windows10.0-kb4013418-x64_b7165b95be791aeb025135efe60e00db2800c5c6.msu} True 1 2 PS > Invoke-Command -Session $s -ScriptBlock { Test-Path -Path C : \ Source \ windows10 . 0 -kb4013418 -x64_b7165b95be791aeb025135efe60e00db2800c5c6 . msu } True

Install patch

And now it’s time to apply the patch:

PS> Enter-PSSession -Session $s [W16ND01]: PS C:\Users\Administrator\Documents> wusa.exe C:\Source\windows10.0-kb4013418-x64_b7165b95be791aeb025135efe60e00db2800c5c6.msu /quiet [W16ND01]: PS C:\Users\Administrator\Documents> get-hotfix Source Description HotFixID InstalledBy InstalledOn ------ ----------- -------- ----------- ----------- W16ND01 Update KB3192137 NT AUTHORITY\SYSTEM 12/09/2016 00:00:00 W16ND01 Update KB3199986 W16ND01\Administr... 31/03/2017 00:00:00 W16ND01 Update KB4013418 W16ND01\Administr... 12/05/2017 00:00:00 W16ND01 Update KB3213522 NT AUTHORITY\SYSTEM 31/03/2017 00:00:00 [W16ND01]: PS C:\Users\Administrator\Documents> Exit-PSSession PS> 1 2 3 4 5 6 7 8 9 10 11 12 13 PS > Enter-PSSession -Session $s [ W16ND01 ] : PS C : \ Users \ Administrator \ Documents > wusa . exe C : \ Source \ windows10 . 0 -kb4013418 -x64_b7165b95be791aeb025135efe60e00db2800c5c6 . msu / quiet [ W16ND01 ] : PS C : \ Users \ Administrator \ Documents > get -hotfix Source Description HotFixID InstalledBy InstalledOn ------ ----------- -------- ----------- ----------- W16ND01 Update KB3192137 NT AUTHORITY \ SYSTEM 12/09/2016 00 : 00 : 00 W16ND01 Update KB3199986 W16ND01 \ Administr . . . 31/03/2017 00 : 00 : 00 W16ND01 Update KB4013418 W16ND01 \ Administr . . . 12/05/2017 00 : 00 : 00 W16ND01 Update KB3213522 NT AUTHORITY \ SYSTEM 31/03/2017 00 : 00 : 00 [ W16ND01 ] : PS C : \ Users \ Administrator \ Documents > Exit-PSSession PS >

The easiest way is to enter the remoting session you’ve created to the remote machine – note how the prompt changes. Use wusa.exe to install the patch. The /quiet switch ensures that there’s no prompts for user interaction. You can test if the patch was installed by using Get-Hotfix. The new patch will show in the list if it installed correctly. Leave the interactive session by typing Exit-PSSession.

Using Windows Update or WSUS

Ideally you want to be downloading patches directly to the machine and installing them. Windows Server 2016 and Windows 10 supply CIM classes that enable you to download and install patches from Microsoft’s update site or a WSUS server if you’ve configured the machine to use WSUS.

Discover available patches

First, create a script block that creates an instance of a MSFT_WUOperationsSession class. You then pipe that into Invoke-CimMethod using the ScanForUpdates method with arguments to scan for updates that aren’t installed.

PS> $sb = { $avup = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession | Invoke-CimMethod -MethodName ScanForUpdates -Arguments @{SearchCriteria="IsInstalled=0";OnlineScan=$true} $avup.Updates } 1 2 3 4 5 PS > $sb = { $avup = New-CimInstance -Namespace root / Microsoft / Windows / WindowsUpdate -ClassName MSFT_WUOperationsSession | Invoke-CimMethod -MethodName ScanForUpdates -Arguments @ { SearchCriteria = "IsInstalled=0" ; OnlineScan = $true } $avup . Updates }

Use Invoke-Command to run the scriptblock against the remoting session.

PS> Invoke-Command -Session $s -ScriptBlock $sb PSComputerName : W16ND01 RunspaceId : 62001f7d-ae58-4cee-9f6e-a638e34d16d3 Description : After the download, this tool runs one time to check your computer for infection by specific, prevalent malicious software (including Blaster, Sasser, and Mydoom) and helps remove any infection that is found. If an infection is found, the tool will display a status report the next time that you start your computer. A new version of the tool will be offered every month. If you want to manually run the tool on your computer, you can download a copy from the Microsoft Download Center, or you can run an online version from microsoft.com. This tool is not a replacement for an antivirus product. To help protect your computer, you should use an antivirus product. KBArticleID : MsrcSeverity : RevisionNumber : 202 Title : Windows Malicious Software Removal Tool for Windows 8, 8.1, 10 and Windows Server 2012, 2012 R2, 2016 x64 Edition - May 2017 (KB890830) UpdateID : f989a181-f72a-4cff-b3d9-f3a4412edaa0 PSComputerName : W16ND01 RunspaceId : 62001f7d-ae58-4cee-9f6e-a638e34d16d3 Description : A security issue has been identified in a Microsoft software product that could affect your system. You can help protect your system by installing this update from Microsoft. For a complete listing of the issues that are included in this update, see the associated Microsoft Knowledge Base article. After you install this update, you may have to restart your system. KBArticleID : MsrcSeverity : Moderate RevisionNumber : 202 Title : 2017-05 Cumulative Update for Windows Server 2016 for x64-based Systems (KB4019472) UpdateID : 95ff788a-8fe4-4584-bfca-7051b92405b2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 PS > Invoke-Command -Session $s -ScriptBlock $sb PSComputerName : W16ND01 RunspaceId : 62001f7d -ae58 -4cee -9f6e -a638e34d16d3 Description : After the download , this tool runs one time to check your computer for infection by specific , prevalent malicious software ( including Blaster , Sasser , and Mydoom ) and helps remove any infection that is found . If an infection is found , the tool will display a status report the next time that you start your computer . A new version of the tool will be offered every month . If you want to manually run the tool on your computer , you can download a copy from the Microsoft Download Center , or you can run an online version from microsoft . com . This tool is not a replacement for an antivirus product . To help protect your computer , you should use an antivirus product . KBArticleID : MsrcSeverity : RevisionNumber : 202 Title : Windows Malicious Software Removal Tool for Windows 8 , 8 . 1 , 10 and Windows Server 2012 , 2012 R2 , 2016 x64 Edition - May 2017 ( KB890830 ) UpdateID : f989a181 -f72a -4cff -b3d9 -f3a4412edaa0 PSComputerName : W16ND01 RunspaceId : 62001f7d -ae58 -4cee -9f6e -a638e34d16d3 Description : A security issue has been identified in a Microsoft software product that could affect your system . You can help protect your system by installing this update from Microsoft . For a complete listing of the issues that are included in this update , see the associated Microsoft Knowledge Base article . After you install this update , you may have to restart your system . KBArticleID : MsrcSeverity : Moderate RevisionNumber : 202 Title : 2017 -05 Cumulative Update for Windows Server 2016 for x64 -based Systems ( KB4019472 ) UpdateID : 95ff788a -8fe4 -4584 -bfca -7051b92405b2

In this case two updates are required.

Install patches

Installing them is a similar action to discovery. Create the script block that will use the MSFT_WUOperationsSession class but this time call the ApplyApplicableUpdates method. In this case we’re applying all available updates.

$sb = { New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession | Invoke-CimMethod -MethodName ApplyApplicableUpdates } 1 2 3 4 $sb = { New-CimInstance -Namespace root / Microsoft / Windows / WindowsUpdate -ClassName MSFT_WUOperationsSession | Invoke-CimMethod -MethodName ApplyApplicableUpdates }

You can then use the scriptblock in your remoting session

PS> Invoke-Command -Session $s -ScriptBlock $sb HResult : 0 ReturnValue : 0 PSComputerName : W16ND01 RunspaceId : 62001f7d-ae58-4cee-9f6e-a638e34d16d3 1 2 3 4 5 6 PS > Invoke-Command -Session $s -ScriptBlock $sb HResult : 0 ReturnValue : 0 PSComputerName : W16ND01 RunspaceId : 62001f7d -ae58 -4cee -9f6e -a638e34d16d3

A return value of 0 indicates success. Anything else means something has failed.

View installed updates

You can view the installed updates

$sb = { $inup = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession | Invoke-CimMethod -MethodName ScanForUpdates -Arguments @{SearchCriteria="IsInstalled=1";OnlineScan=$true} $inup.Updates } 1 2 3 4 5 $sb = { $inup = New-CimInstance -Namespace root / Microsoft / Windows / WindowsUpdate -ClassName MSFT_WUOperationsSession | Invoke-CimMethod -MethodName ScanForUpdates -Arguments @ { SearchCriteria = "IsInstalled=1" ; OnlineScan = $true } $inup . Updates }

PS> Invoke-Command -Session $s -ScriptBlock $sb 1 PS > Invoke-Command -Session $s -ScriptBlock $sb

When we retrieved the list of available updates the description for KB4019472 stated that an update may be required. You can test by examining the installed updates

PS> Invoke-Command -Session $s -ScriptBlock {Get-HotFix} Source Description HotFixID InstalledBy InstalledOn PSComputerName ------ ----------- -------- ----------- ----------- -------------- W16ND01 Update KB3192137 NT AUTHORITY\SYSTEM 12/09/2016 00:00:00 W16ND01 W16ND01 Update KB3199986 W16ND01\Administr... 31/03/2017 00:00:00 W16ND01 W16ND01 Update KB4013418 W16ND01\Administr... 12/05/2017 00:00:00 W16ND01 W16ND01 Security Update KB4019472 W16ND01 W16ND01 Update KB3213522 NT AUTHORITY\SYSTEM 31/03/2017 00:00:00 W16ND01 1 2 3 4 5 6 7 8 9 PS > Invoke-Command -Session $s -ScriptBlock { Get-HotFix } Source Description HotFixID InstalledBy InstalledOn PSComputerName ------ ----------- -------- ----------- ----------- -------------- W16ND01 Update KB3192137 NT AUTHORITY \ SYSTEM 12/09/2016 00 : 00 : 00 W16ND01 W16ND01 Update KB3199986 W16ND01 \ Administr . . . 31/03/2017 00 : 00 : 00 W16ND01 W16ND01 Update KB4013418 W16ND01 \ Administr . . . 12/05/2017 00 : 00 : 00 W16ND01 W16ND01 Security Update KB4019472 W16ND01 W16ND01 Update KB3213522 NT AUTHORITY \ SYSTEM 31/03/2017 00 : 00 : 00 W16ND01

Notice that KB4019472 doesn’t show any data for InstalledBy on InstalledOn – this means a reboot is required.

PS> Invoke-Command -Session $s -ScriptBlock {Restart-Computer -Force} 1 PS > Invoke-Command -Session $s -ScriptBlock { Restart-Computer -Force }

When the machine has finished rebooting you’ll need to re-create the remoting session – it breaks on a reboot. You can then view the installed updates

PS> Invoke-Command -Session $s -ScriptBlock {Get-HotFix} Source Description HotFixID InstalledBy InstalledOn PSComputerName ------ ----------- -------- ----------- ----------- -------------- W16ND01 Update KB3192137 NT AUTHORITY\SYSTEM 12/09/2016 00:00:00 W16ND01 W16ND01 Update KB3199986 W16ND01\Administr... 31/03/2017 00:00:00 W16ND01 W16ND01 Update KB4013418 W16ND01\Administr... 12/05/2017 00:00:00 W16ND01 W16ND01 Security Update KB4019472 NT AUTHORITY\SYSTEM 14/05/2017 00:00:00 W16ND01 1 2 3 4 5 6 7 8 PS > Invoke-Command -Session $s -ScriptBlock { Get-HotFix } Source Description HotFixID InstalledBy InstalledOn PSComputerName ------ ----------- -------- ----------- ----------- -------------- W16ND01 Update KB3192137 NT AUTHORITY \ SYSTEM 12/09/2016 00 : 00 : 00 W16ND01 W16ND01 Update KB3199986 W16ND01 \ Administr . . . 31/03/2017 00 : 00 : 00 W16ND01 W16ND01 Update KB4013418 W16ND01 \ Administr . . . 12/05/2017 00 : 00 : 00 W16ND01 W16ND01 Security Update KB4019472 NT AUTHORITY \ SYSTEM 14/05/2017 00 : 00 : 00 W16ND01

Now KB4019472 shows who installed it and when. You’ll also notice that KB3213522 has dropped off the list – it’s been superseded by the update you installed.

This technique works for Windows Server 2016 and Windows 10. Unfortunately, there isn’t a comparable technique for earlier versions of Windows Server. There is a COM object you can use to manage updates but it won’t work through a remoting session – you have to logon onto the machine and run the code locally.

PowerShell Direct reminder

There are a few rules you need to remember when you’re using PowerShell Direct:

Host is Windows 10 or Server 2016 running Hyper-V

Guest is Windows 10 or Server 2016

Guest must be on the host – no cross-host access

Guest must be running

Must log onto host as Hyper-V admin

Must supply valid credentials for LOCAL account on guest VM.

At present, you can’t connect to a Linux virtual machine using PowerShell Direct but I fully expect this functionality to arrive in the future given PowerShell 6.0 will be available for many Linux distributions.