Skip to content

Instantly share code, notes, and snippets.

@zbalkan
Last active December 16, 2023 17:22
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save zbalkan/4ba92656a3a8387e6b220bcf8fcd5fc6 to your computer and use it in GitHub Desktop.
Save zbalkan/4ba92656a3a8387e6b220bcf8fcd5fc6 to your computer and use it in GitHub Desktop.
It's a drop-in replacement for slmgr.vbs script
#Requires -RunAsAdministrator
#Requires -Version 5
<#
.Synopsis
Activates Windows via KMS
.DESCRIPTION
It's a drop in replacement for slmgr scripts
.EXAMPLE
Start-WindowsActivation -Verbose # Activates the local computer
.EXAMPLE
Start-WindowsActivation -Computer WS01 # Activates the computer named WS01
.EXAMPLE
Start-WindowsActivation -Computer WS01 -KMSServerFQDN server.domain.net -KMSServerPort 2500 # Activates the computer named WS01 against server.domain.net:2500
#>
function global:Start-WindowsActivation
{
[CmdletBinding(SupportsShouldProcess = $true,
PositionalBinding = $false,
ConfirmImpact = 'Medium',
DefaultParameterSetName = 'default')]
Param
(
# Type localhost or . for local computer or do not use the parameter
[Parameter(Mandatory = $false,
Position = 0,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false)]
[string[]]
$Computers = @('localhost'),
[Parameter(Mandatory = $false,
Position = 1,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false,
ParameterSetName = 'SpecifyKMSServer')]
[ValidateLength(6, 253)]
[ValidateScript(
{
$pattern = [Regex]::new('(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)')
if ($pattern.Matches($_).Count -gt 0)
{
$true
}
else
{
throw "$_ is invalid. Please provide a valid FQDN"
}
})]
[ValidateNotNullOrEmpty()]
[string]
$KMSServerFQDN,
[Parameter(Mandatory = $false,
Position = 2,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false,
ParameterSetName = 'SpecifyKMSServer')]
[ValidateRange(1, 65535)]
[int]
$KMSServerPort = 1688
)
Begin
{
# Enum for a meaningful check. Reference: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/sppwmi/softwarelicensingproduct
enum LicenseStatusCode
{
Unlicensed
Licensed
OOBGrace
OOTGrace
NonGenuineGrace
Notification
ExtendedGrace
}
function getLicenseStatus
{
param(
[string]$Computer
)
if ($Computer -eq 'localhost')
{
$product = Get-CimInstance -Query 'SELECT * FROM SoftwareLicensingProduct' | Where-Object { $_.PartialProductKey }
}
else
{
$product = Get-CimInstance -Query 'SELECT * FROM SoftwareLicensingProduct' -ComputerName $Computer | Where-Object { $_.PartialProductKey }
}
$status = [LicenseStatusCode]( $product | Select-Object LicenseStatus).LicenseStatus
$activated = $status -eq [LicenseStatusCode]::Licensed
$result = [PSCustomObject]@{
LicenseStatus = $status
Activated = $activated
}
return $result
}
function sanitizeComputerName
{
param(
[string]$Computer
)
if ($Computer -eq '.' -or $Computer -eq '127.0.0.1' -or $null -eq $Computer)
{
return 'localhost'
}
else
{
return $Computer
}
}
# KMS Client License Keys - https://docs.microsoft.com/en-us/windows-server/get-started/kmsclientkeys
# Add as you wish
function getProductKey
{
param(
[string]$Computer
)
if ($Computer -eq 'localhost')
{
$OsVersion = ((Get-CimInstance -Class Win32_OperatingSystem).Caption)
}
else
{
$OsVersion = ((Get-CimInstance -Class Win32_OperatingSystem -ComputerName $Computer).Caption)
}
$productKey = switch -Wildcard ($OsVersion)
{
'Microsoft Windows Server 2022 Standard*' { 'VDYBN-27WPP-V4HQT-9VMD4-VMK7H' } # End of support: Oct 13, 2026
'Microsoft Windows Server 2022 Datacenter*' { 'WX4NM-KYWYW-QJJR4-XV3QB-6VM33' } # End of support: Oct 13, 2026
'Microsoft Windows Server 2019 Standard*' { 'N69G4-B89J2-4G8F4-WWYCC-J464C' } # End of support: Jan 9, 2024
'Microsoft Windows Server 2019 Datacenter*' { 'WMDGN-G9PQG-XVVXX-R3X43-63DFG' } # End of support: Jan 9, 2024
'Microsoft Windows Server 2019 Essentials*' { 'WVDHN-86M7X-466P6-VHXV7-YY726' } # End of support: Jan 9, 2024
# "Microsoft Windows Server 2016 Standard*" { "WC2BQ-8NRM3-FDDYY-2BFGV-KHKQY" } # End of support: Jan 11, 2022
# "Microsoft Windows Server 2016 Datacenter*" { "CB7KF-BWN84-R7R2Y-793K2-8XDDG" } # End of support: Jan 11, 2022
# "Microsoft Windows Server 2016 Essentials*" { "JCKRF-N37P4-C2D82-9YXRT-4M63B" } # End of support: Jan 11, 2022
# "Microsoft Windows Server 2012 R2 Datacenter*" { "W3GGN-FT8W3-Y4M27-J84CP-Q3VJ9" } # End of support: Oct 9, 2018
# "Microsoft Windows Server 2012 R2 Standard*" { "D2N9P-3P6X9-2R39C-7RTCD-MDVJX" } # End of support: Oct 9, 2018
# "Microsoft Windows Server 2012 Standard*" { "D2N9P-3P6X9-2R39C-7RTCD-MDVJX" } # End of support: October 9, 2018
# "Microsoft Windows Server 2008 R2 Enterprise*" { "489J6-VHDMP-X63PK-3K798-CPX3Y" } # End of support: Jan 14, 2020
'Microsoft Windows 10 Enterprise*' { 'NPPR9-FWDCX-D2C8J-H872K-2YT43' } # End of support: Oct 14, 2025
# 'Windows 10 Enterprise N*' { 'DPH2V-TTNVB-4X9Q3-TJR4H-KHJW4' } # End of support: Oct 14, 2025
'Microsoft Windows 10 Professional*' { 'W269N-WFGWX-YVC9B-4J6C9-T83GX' } # End of support: Oct 14, 2025
# 'Windows 10 Professional N' { 'MH37W-N47XK-V7XM9-C7227-GCQG9' } # End of support: Oct 14, 2025
# "Microsoft Windows 7 Enterprise*" { "33PXH-7Y6KF-2VJC9-XBBR8-HVTHH" } # End of support: Jan 14, 2020
# "Microsoft Windows 7 Professional*" { "FJ82H-XT6CR-J8D7P-XQJJ2-GPDD4" } # End of support: Jan 14, 2020
default { 'Unknown' }
}
return $productKey
}
function activateWithDNS
{
param(
[string]$Computer,
[string]$ProductKey
)
if ($Computer -eq 'localhost')
{
$service = Get-CimInstance -Query 'SELECT * FROM SoftwareLicensingService'
}
else
{
$service = Get-CimInstance -Query 'SELECT * FROM SoftwareLicensingService' -ComputerName $Computer
}
$service.InstallProductKey($ProductKey) > $null
Start-Sleep -Seconds 10 # Installing product key takes time.
$service.RefreshLicenseStatus() > $null
Start-Sleep -Seconds 2 # It also takes time.
}
function activateWithParams
{
param(
[string]$Computer,
[string]$ProductKey,
[string]$KeyServerName,
[int]$KeyServerPort
)
if ($Computer -eq 'localhost')
{
$service = Get-CimInstance -Query 'SELECT * FROM SoftwareLicensingService'
}
else
{
$service = Get-CimInstance -Query 'SELECT * FROM SoftwareLicensingService' -ComputerName $Computer
}
$service.SetKeyManagementServiceMachine($KeyServerName)
$service.SetKeyManagementServicePort($KeyServerPort)
$service.InstallProductKey($ProductKey) > $null
Start-Sleep -Seconds 10 # Installing product key takes time.
$service.RefreshLicenseStatus() > $null
Start-Sleep -Seconds 2 # It also takes time.
}
}
Process
{
if ($pscmdlet.ShouldProcess('Computer', 'Activate license via KMS'))
{
$ErrorActionPreference = 'Stop'
Write-Verbose 'ErrorActionPreference: Stop'
Write-Verbose "Enumerating computers: $($Computers.Count) computer(s)."
foreach ($Computer in $Computers)
{
# Sanitize Computer name
$Computer = sanitizeComputerName -Computer $Computer
Write-Verbose "Computer name: $Computer"
# Check Windows Activation Status
$product = getLicenseStatus -Computer $Computer
Write-Verbose "License Status: $($product.Status)"
if ($product.Activated) { Write-Warning 'The product is already activated.'; continue; }
# Get product key
$productKey = getProductKey -Computer $Computer
Write-Verbose "Product Key (for KMS): $productKey"
# Activate Windows
if ($productKey -eq 'Unknown')
{
Write-Error 'Unknown OS.'
}
else
{
if ($PSCmdlet.ParameterSetName -eq 'SpecifyKMSServer')
{
activateWithParams -Computer $Computer -ProductKey $productKey -KeyServerName $KMSServerFQDN -KeyServerPort $KMSServerPort
}
else
{
activateWithDNS -Computer $Computer -ProductKey $productKey
}
$product = getLicenseStatus -Computer $Computer
if ($product.Activated)
{
Write-Verbose "The computer activated succesfully. Current status: $($product.LicenseStatus)"
}
else
{
Write-Error "Activation failed. Current status: $($product.LicenseStatus)"
}
}
}
}
}
}
@zbalkan
Copy link
Author

zbalkan commented Aug 26, 2020

TODO:

[x] $valid is useless and can be removed. Checking if product key is null would be enough.
[x] Accept multiple computer names.
[ ] Other KMS keys should be added.
[ ] Add edge cases for other LicenseStatusCodes that would only require cleaning existing keys.
[ ] Add exception handling where applicable.

@zbalkan
Copy link
Author

zbalkan commented Aug 27, 2020

Converted if statements to switch thanks to u/Yevrag35's comment.

@zbalkan
Copy link
Author

zbalkan commented Sep 21, 2020

Create a new one for Office using OSPP.vbs.

@zbalkan
Copy link
Author

zbalkan commented Dec 22, 2020

[ ] Add conversion from Evaluation to Full edition.

@zbalkan
Copy link
Author

zbalkan commented Oct 26, 2022

Removed DISM based Evaluation-to-Full conversion. Added EOL comments. Commented out EOL versions.

@zbalkan
Copy link
Author

zbalkan commented Oct 26, 2022

Major refactor:

  • Used functions for readability.
  • Used Get-CimInstance instead of Get-WmiObject to future-proof the cmdlet. It works with PowerShell 7. But it is relatively slower.

@zbalkan
Copy link
Author

zbalkan commented Feb 26, 2023

Added capability to define the KMS server name (FQDN) and port based on feedback of r/Zulgrib.

@RokeJulianLockhart
Copy link

RokeJulianLockhart commented Dec 16, 2023

I have a script which activates a windows installation using a fake keyserver

@echo off
:: https://stackoverflow.com/a/8486061/9731176

title Activate Windows 11 (ALL versions) for FREE &cls&echo =====================================================================================&echo #Project: Activating Microsoft software products for FREE without additional software&echo =====================================================================================&echo.&echo #Supported products:&echo - Windows 11 Home&echo - Windows 11 Professional&echo - Windows 11 Education&echo - Windows 11 Enterprise&echo.&echo.&echo ============================================================================&echo Activating your Windows...&cscript //nologo slmgr.vbs /ckms >nul&cscript //nologo slmgr.vbs /upk >nul&cscript //nologo slmgr.vbs /cpky >nul&set i=1&wmic os | findstr /I "enterprise" >nul
if %errorlevel% EQU 0 (cscript //nologo slmgr.vbs /ipk NPPR9-FWDCX-D2C8J-H872K-2YT43 >nul||cscript //nologo slmgr.vbs /ipk DPH2V-TTNVB-4X9Q3-TJR4H-KHJW4 >nul||cscript //nologo slmgr.vbs /ipk YYVX9-NTFWV-6MDM3-9PT4T-4M68B >nul||cscript //nologo slmgr.vbs /ipk 44RPN-FTY23-9VTTB-MP9BX-T84FV >nul||cscript //nologo slmgr.vbs /ipk WNMTR-4C88C-JK8YV-HQ7T2-76DF9 >nul||cscript //nologo slmgr.vbs /ipk 2F77B-TNFGY-69QQF-B8YKP-D69TJ >nul||cscript //nologo slmgr.vbs /ipk DCPHK-NFMTC-H88MJ-PFHPY-QJ4BJ >nul||cscript //nologo slmgr.vbs /ipk QFFDN-GRT3P-VKWWX-X7T3R-8B639 >nul||cscript //nologo slmgr.vbs /ipk M7XTQ-FN8P6-TTKYV-9D4CC-J462D >nul||cscript //nologo slmgr.vbs /ipk 92NFX-8DJQP-P6BBQ-THF9C-7CG2H >nul&goto skms) else wmic os | findstr /I "home" >nul
if %errorlevel% EQU 0 (cscript //nologo slmgr.vbs /ipk TX9XD-98N7V-6WMQ6-BX7FG-H8Q99 >nul||cscript //nologo slmgr.vbs /ipk 3KHY7-WNT83-DGQKR-F7HPR-844BM >nul||cscript //nologo slmgr.vbs /ipk 7HNRX-D7KGG-3K4RQ-4WPJ4-YTDFH >nul||cscript //nologo slmgr.vbs /ipk PVMJN-6DFY6-9CCP6-7BKTT-D3WVR >nul&goto skms) else wmic os | findstr /I "education" >nul
if %errorlevel% EQU 0 (cscript //nologo slmgr.vbs /ipk NW6C2-QMPVW-D7KKK-3GKT6-VCFB2 >nul||cscript //nologo slmgr.vbs /ipk 2WH4N-8QGBV-H22JP-CT43Q-MDWWJ >nul&goto skms) else wmic os | findstr /I "11 pro" >nul
if %errorlevel% EQU 0 (cscript //nologo slmgr.vbs /ipk W269N-WFGWX-YVC9B-4J6C9-T83GX >nul||cscript //nologo slmgr.vbs /ipk MH37W-N47XK-V7XM9-C7227-GCQG9 >nul||cscript //nologo slmgr.vbs /ipk NRG8B-VKK3Q-CXVCJ-9G2XF-6Q84J >nul||cscript //nologo slmgr.vbs /ipk 9FNHH-K3HBT-3W4TD-6383H-6XYWF >nul||cscript //nologo slmgr.vbs /ipk 6TP4R-GNPTD-KYYHQ-7B7DP-J447Y >nul||cscript //nologo slmgr.vbs /ipk YVWGF-BXNMC-HTQYQ-CPQ99-66QFC >nul&goto skms) else (goto notsupported)
:skms
if %i% GTR 10 goto busy
if %i% EQU 1 set KMS=kms7.MSGuides.com
if %i% EQU 2 set KMS=s8.now.im
if %i% EQU 3 set KMS=s9.now.im
if %i% GTR 3 goto ato
cscript //nologo slmgr.vbs /skms %KMS%:1688 >nul
:ato
echo ============================================================================&echo.&echo.&cscript //nologo slmgr.vbs /ato | find /i "successfully" && (echo.&echo ============================================================================&echo.&echo #My official blog: devsjournal.com&echo.&echo #How it works: bit.ly/kms-server&echo.&echo #Please feel free to contact me at msguides.com@gmail.com if you have any questions or concerns.&echo.&echo #Please consider supporting this project: donate.msguides.com&echo #Your support is helping me keep my servers running 24/7!&echo.&echo ============================================================================&choice /n /c YN /m "Would you like to visit my blog [Y,N]?" & if errorlevel 2 exit) || (echo The connection to my KMS server failed! Trying to connect to another one... & echo Please wait... & echo. & echo. & set /a i+=1 & goto skms)
explorer "http://MSGuides.com"&goto halt
:notsupported
echo ============================================================================&echo.&echo Sorry, your version is not supported.&echo.&goto halt
:busy
echo ============================================================================&echo.&echo Sorry, the server is busy and can't respond to your request. Please try again.&echo.
:halt
pause >nul

which I'd really like to convert to PowerShell. It seems to me like your script does or at least can do the same, but do you think you can confirm, @zbalkan?

@zbalkan
Copy link
Author

zbalkan commented Dec 16, 2023

Hi,

I improved the script to a Powershell module. It supports more than that: https://github.com/zbalkan/slmgr-ps

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment