Skip to content

Instantly share code, notes, and snippets.

@zbalkan
Last active February 8, 2025 16:08
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

4ba92656a3a8387e6b220bcf8fcd5fc6/d776cfa36544d1ff27162ae4daf746030e477462#file-start-windowsactivation-ps1

@zbalkan, I have a script which activates a Windows installation using a fake keyserver:

::!"%WinDir%\System32\Cmd.exe"

:: ~~~YAML
:: Shebang     : "[^3]"
:: Script-wide : "[^1]"
:: ~~~

:: 1. Miscellaneous
::    -------------

:: [^2]
@echo off

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

:: 1. Bibliography
::    ------------
:: 
::    [^1]: [`gist.github.com/zbalkan/4ba92656a3a8387e6b220bcf8fcd5fc6?permalink_comment_id=4795339#gistcomment-4795339`](https://gist.github.com/zbalkan/4ba92656a3a8387e6b220bcf8fcd5fc6?permalink_comment_id=4795339#gistcomment-4795339)
::    [^2]: [`stackoverflow.com/a/8486061`](https://stackoverflow.com/a/8486061/9731176)
::    [^3]: [`github.com/darkbatcher/picobat/issues/7`](https://github.com/darkbatcher/picobat/issues/7#issue-2821266297)

...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