4 min read

Introduction

A while ago, I wrote a blog post on how to automatic activate Windows Server 2012 R2 Virtual Machines using PowerShell, you can read about it here.

The script was written based on Windows Server 2012 R2 guest virtual machines and requires PowerShell Remoting to be set for each VM. In other words, you need to have networking setup for each VM.

In Windows Server 2016 Hyper-V, Microsoft introduced an awesome feature called PowerShell Direct. For more information about PowerShell Direct, check here.

I updated the script to support Windows Server 2016 guest virtual machines, which does not require any network configuration for the guest operation system or any type of network at all.

In this post, I will show you how to leverage PowerShell Direct and Automatic Virtual Machine Activation (AVMA) to activate Windows Server 2016 guest operation systems.

Automatic Virtual Machine Activation

Automatic Virtual Machine Activation (AVMA) is a feature that was introduced in Windows Server 2012 R2. AVMA binds the virtual machine activation to the licensed virtualization server and activates the virtual machine when it starts up. This eliminates the need to enter licensing information and activate each virtual machine individually.

Microsoft has update the generic AVMA Keys to support Windows Server 2016 (Datacenter/Standard/Essentials) according to the Guest OS Edition that you want to activate, for more information, check here.

In order to get benefits of Automatic Virtual Machine Activation, AVMA requires that the host is running Windows Server 2016 Datacenter and that the guest virtual machine OS is either Windows Server 2016 Datacenter, Windows Server 2016 Standard or Windows Server 2016 Essentials.

Scenario

You have deployed several Windows Server 2016 virtual machines on top of Windows Server 2016 Hyper-V (Datacenter Edition) with or without SCVMM or using a deployment tool such as MDT where you can add below script during the deployment to activate all VMs.

Of course you could use the manual approach, but the easiest way is to use PowerShell, for this reason I have created a small function that takes care of all the steps for you:

Here we go:

<# .SYNOPSIS Activate Windows Server 2016 VM. .DESCRIPTION Activate Windows Server 2016 Virtual Machines using Automatic Virtual Machine Activation (AVMA). Guest OSs (Windows Server 2016 Standard, Standard Core, Datacenter and Datacenter Core). .NOTES =========================================================================== File Name : ActivateVM-WS2016.ps1 Author : Charbel Nemnom Version : 2.0 Date created : 26.March.2017 Last modified: 31.March.2017 Requires : PowerShell Version 5.0 or above OS : Windows Server 2016 Hyper-V Module : Hyper-V-PowerShell =========================================================================== .LINK To provide feedback or for further assistance please visit: https://charbelnemnom.com .PARAMETER HyperVHost The Hyper-V host that own the virtual machine(s) to be activated. .PARAMETER VMName The name of a single virtual machine. .PARAMETER ALLVMS If this parameter is specified, then it will activate all virtual machines. .EXAMPLE .\ActivateVM-WS2016.ps1 -HyperVHost <Hyper-V Host> -VMName <VMName> This example will connect to a specified Hyper-V host and activate a single VM. .EXAMPLE .\ActivateVM-WS2016.ps1 -HyperVHost <Hyper-V Host> -ALLVMS This example will connect to a specified Hyper-V host and activate all Virtual Machines. #> [CmdletBinding()] param( [Parameter(Mandatory, HelpMessage= 'Enter Hyper-V Host Name')] [ValidateNotNullOrEmpty()] [Alias('Hyper-V Host Name')] [String]$HyperVHost, [Parameter(HelpMessage= 'Enter Virtual Machine Name')] [ValidateNotNullOrEmpty()] [Alias('Virtual Machine Name')] [String]$VMName, [Parameter(HelpMessage= 'All Virtual Machines, ParameterSetName = "ALLVMS"')] [Alias('All Virtual Machines')] [Switch]$ALLVMS ) # Environment Configuration $Script:clearTextPassword = “[email protected]” $Script:passwordSecureString = ConvertTo-SecureString -AsPlainText $Script:clearTextPassword -Force $Script:localAdminCred = New-Object System.Management.Automation.PSCredential (".\Administrator", $Script:passwordSecureString) Function ActivateVM { param( [Parameter(Mandatory=$true)] [String]$HyperVHost, [Parameter(Mandatory=$true)] [String]$VirtualMachine, [Parameter(Mandatory=$true)] [System.Management.Automation.PSCredential] $Credential ) Invoke-Command -ComputerName $HyperVHost -Credential $Credential -ScriptBlock { Wait-VM -Name $Using:VirtualMachine -For Heartbeat Invoke-Command -VMName $Using:VirtualMachine -Credential $Using:Credential -ScriptBlock { param ( $VirtualMachine ) # Define initial license status $LicenseStatus = @("Unlicensed","Licensed","OOB Grace", "OOT Grace","Non-Genuine Grace","Notification","Extended Grace") # Check if the guest OS is licensed before activation $WINLIC=Get-CimInstance -ClassName SoftwareLicensingProduct |` Where{$_.PartialProductKey -and $_.Name -like "*Windows*"} | Select-Object ` @{Expression={$_.Name};Name="WindowsName"},` @{Expression={$LicenseStatus[$($_.LicenseStatus)]};Name="LicenseStatus"} If ($WINLIC.LicenseStatus -ne "Licensed") { If ($WINLIC.WindowsName -like "*Standard*") { # Activate VM WS2016 Standard cmd.exe /c cscript //B "%windir%\system32\slmgr.vbs" /ipk C3RCX-M6NRP-6CXC9-TW2F2-4RHYD /ato Write-Output "Activating virtual machine: $VirtualMachine Windows Server 2016 Standard" } If ($WINLIC.WindowsName -like "*Datacenter*") { # Activate VM WS2016 Datacenter cmd.exe /c cscript //B "%windir%\system32\slmgr.vbs" /ipk TMJ3Y-NTRTM-FJYXT-T22BY-CWG3J /ato Write-Output "Activating virtual machine: $VirtualMachine Windows Server 2016 Datacenter" } If ($WINLIC.WindowsName -like "*Essentials*") { # Activate VM WS2016 Essentials cmd.exe /c cscript //B "%windir%\system32\slmgr.vbs" /ipk B4YNW-62DX9-W8V6M-82649-MHBKQ /ato Write-Output "Activating virtual machine: $VirtualMachine Windows Server 2016 Essentials" } } If ($WINLIC.LicenseStatus -eq "Licensed") { Write-Output "Virtual Machine: $VirtualMachine is already activated!" } } -ArgumentList $Using:VirtualMachine } } # If ALLVMS switch equal to true, then activate all VMs If ($ALLVMS) { $VMs = Get-VM * -ComputerName $HyperVHost | Select-Object VMName foreach ($VM in $VMs) { ActivateVM -VirtualMachine $VM.VMName -HyperVHost $HyperVHost -Credential $Script:LocalAdminCred } } Else { # Activate Single VM ActivateVM -VirtualMachine $VMName -HyperVHost $HyperVHost -Credential $Script:LocalAdminCred }

You can run above script from your management machine in the same domain, you need to specify first the Hyper-V host name this could be Nano Server host as well, and then you have two options, either you specify a single virtual machine name or you can use the switch “-ALLVMS” to activate all virtual machines on a single host.

This script will leverage PowerShell Remoting to connect to the Hyper-V host remotely and then use PowerShell Direct to activate all VMs. Welcome to Nested PowerShell Remoting (PSRemoting + PSDirect). This script will also use a new cmdlet in Hyper-V 2016 named “Wait-VM“. This cmdlet will wait for a virtual machine to respond using (heartbeat) before activating. You need to update the “Environment Configuration” section in the script to match your environment for admin credentials.

The results will look something like this:

In this example, we are activating all virtual machines on a single host, if a VM is already activated, you will be notified, and if a new VM needs to be activated, you will be notified as well.

There are different ways to accomplish the same result, but nevertheless it has worked for me and I feel that it’s a much easier than having to log in and activate each VM individually.

Enjoy your weekend!

Cheers,

[email protected]