Querying, managing and updating Windows Licensing with Powershell

A lot of people build their images with MAK keys, and as they grow, they want to switch over to using KMS keys aka Volume License Keys.  There are other scenarios that I have come across when you have the right keys, but for some reason or another, the license has not activated, and the only way you find out is that your machine starts shutting down every hour.

First thing to check of course is that the KMS SRV record exists in DNS and is pointing to the correct KMS server

nslookup -type=srv _vlmcs._tcp

I happened to run into a scenario, where a MAK key that was being used in the image, but it had gone past it’s maximum activation limit. So I decided to write a little script to find out which machines were impacted (weren’t activated and what key they were using (it only gives you partial key, but helpful in looking it up).


$dt = (Get-Date).AddDays(-45)
#Get Servers from a server OU that have been recently active and updated their password in AD in lst 45 days
$allservers = Get-ADComputer -SearchBase "OU=Servers,DC=ad,DC=example,DC=com" -Filter * -Properties PasswordLastSet, DNSHostName, IPV4Address | where {$_.PasswordLastSet -gt $dt}
#ping them to see if they are at least reachable. Not a while lot we can do here to deal with WinRM issues
$upservers = $allservers | where {(Test-Connection -ComputerName $_.DNSHostName -Count 1 -Quiet) -eq $true }
# Try to connect to each of the servers and get the information. This will take some time, get a coffee
$licinfo= $upservers | %{ Get-CimInstance -ComputerName $_.Name -ClassName SoftwareLicensingProduct -Filter "ApplicationID = '55c92734-d682-4d71-983e-d6ec3f16059f'" -OperationTimeoutSec 5 |
where {$_.PartialProductKey -ne $null} }
#Gather info and put it in a CSV to share out findings with team members if needed
$licinfo | select -Property PSComputername, ProductKeyChannel, LicenseStatus, PartialProductKey, Name, KeyManagementServiceMachine, GenuineStatus, VLActivationType |
where {$_.PSCOMputerName} | sort -Property LicenseStatus -Descending | Export-CSV -path c:\work\licenses.csv -NoTypeInformation

#now let's see if we can go fix some of these. find the ones with licenses that are not activated, group them by OS (since keys are OS Flavor Specific, and then activate)
$missing = $licinfo | where {$_.LicenseStatus -gt 1} |
select -Property PSComputername, ProductKeyChannel, LicenseStatus, PartialProductKey, Name, KeyManagementServiceMachine, GenuineStatus, VLActivationType
# in case you want to visuall see the list of servers.. uncomment the line below
# $missing | ft
$key2012 = "11111-11111-11111-11111-11111"
$key2016 = "22222-22222-22222-22222-22222"
$kmsServer = "kms.ad.example.com:1688"
$s2012 = $missing | where {(Get-ADComputer -Identity $_.PSComputerName -Properties OperatingSystem, OperatingSystemVersion).OperatingSystem -match "Windows Server 2012 R2" }
$s2016 = $missing | where {(Get-ADComputer -Identity $_.PSComputerName -Properties OperatingSystem, OperatingSystemVersion).OperatingSystem -match "Windows Server 2016" }
#Fix the 2012s. On each computer update license key, manually set KMS server if necessary and run activation script
$s2012| % { $_.PScomputername; Invoke-Command -ComputerName $_.PScomputername -ScriptBlock `
{ & cscript /H:WScript C:\Windows\System32\slmgr.vbs -ipk $key2012; & cscript /H:WScript C:\Windows\System32\slmgr.vbs -skms $kmsServer; & cscript /H:WScript C:\Windows\System32\slmgr.vbs -ato }}

#Fix the 2016s. On each computer update license key, manually set KMS server if necessary and run activation script
$s2016| % { $_.PScomputername; Invoke-Command -ComputerName $_.PScomputername -ScriptBlock `
{ & cscript /H:WScript C:\Windows\System32\slmgr.vbs -ipk $key2016; & cscript /H:WScript C:\Windows\System32\slmgr.vbs -skms $kmsServer; & cscript /H:WScript C:\Windows\System32\slmgr.vbs -ato }}

#After running the script to fix, make sure that the status shows as good
$licinfoNEW = $s2012 | %{ Get-CimInstance -ComputerName $_.PSComputerName -ClassName SoftwareLicensingProduct -Filter "ApplicationID = '55c92734-d682-4d71-983e-d6ec3f16059f'" -OperationTimeoutSec 5 |
where {$_.PartialProductKey -ne $null} }

$licinfoNEW += $s2016 | %{ Get-CimInstance -ComputerName $_.PSComputerName -ClassName SoftwareLicensingProduct -Filter "ApplicationID = '55c92734-d682-4d71-983e-d6ec3f16059f'" -OperationTimeoutSec 5 |
where {$_.PartialProductKey -ne $null} }

#Display the information on the screen .. or export it to CSV if you choose
$licinfoNEW |
select -Property PSComputername, ProductKeyChannel, LicenseStatus, PartialProductKey, Name, KeyManagementServiceMachine, GenuineStatus, VLActivationType | ft

This is of course a script I had to whip up on the spot for a specific issue, I would love to hear any use cases that people might have that are related

Leave a comment