Windows OEM activation script
During an in-place upgrade from Windows 10 to Windows 11, the automatic license transfer doesn’t always seem to work reliably. In my tests, the activation simply didn’t carry over after the upgrade — which, of course, causes unnecessary hassle in day-to-day work.
To save you from doing this manually, I’ve provided a small PowerShell script that applies the license information correctly and activates Windows 11 without any extra effort. Just run it, lean back, and you’re done.
Below is a drop‑in PowerShell script that:
- Detects if Windows is already licensed — if yes, it exits cleanly.
- Reads the embedded OEM key from the ACPI MSDM table via WMI (
OA3xOriginalProductKey). - Installs that key with
slmgr /ipk, then activates withslmgr /ato. - Writes a simple log to
C:\Windows\activate.log. - Returns exit code 0 on success; 1 on failure; 2 if no embedded key is found.
Requirements: Run as Administrator. Internet access is required for online activation when using Retail/OEM keys (KMS not used here by design).
<#
.SYNOPSIS
Activates Windows locally using the embedded OEM product key (ACPI MSDM).
.DESCRIPTION
- Checks current activation status.
- Retrieves embedded OEM key from WMI (SoftwareLicensingService.OA3xOriginalProductKey).
- Installs the key with slmgr.vbs and activates online.
- Logs to C:\Windows\TWS\activate.log.
- Exit codes:
0 = Already licensed or activation succeeded
1 = Error during activation
2 = No embedded key found
.NOTES
Run elevated (as Administrator).
#>
[CmdletBinding()]
param(
[switch]$VerboseOutput
)
# --- Helpers ---------------------------------------------------------------
function Write-Log {
param([string]$Message, [string]$Level = 'INFO')
$logDir = 'C:\Windows\TWS'
$logPath = Join-Path $logDir 'activate.log'
if (-not (Test-Path $logDir)) { New-Item -Path $logDir -ItemType Directory -Force | Out-Null }
$timestamp = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
$line = "[$timestamp] [$Level] $Message"
Add-Content -Path $logPath -Value $line
if ($VerboseOutput) { Write-Host $line }
}
function Test-Admin {
$currentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($currentIdentity)
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
function Get-ActivationStatus {
# Windows OS ApplicationID
$winAppId = '55c92734-d682-4d71-983e-d6ec3f16059f'
$statusMap = @{
0 = 'Unlicensed'
1 = 'Licensed'
2 = 'OOBGrace'
3 = 'OOTGrace'
4 = 'NonGenuineGrace'
5 = 'Notification'
6 = 'ExtendedGrace'
}
$prod = Get-CimInstance -Namespace root\cimv2 -ClassName SoftwareLicensingProduct `
-ErrorAction SilentlyContinue |
Where-Object { $_.ApplicationID -eq $winAppId -and $_.PartialProductKey } |
Select-Object -First 1
$status = if ($null -ne $prod) { $statusMap[[int]$prod.LicenseStatus] } else { 'Unknown' }
[pscustomobject]@{
StatusName = $status
Licensed = ($status -eq 'Licensed')
ProductName = $prod.Name
Description = $prod.Description
PartialKey = $prod.PartialProductKey
}
}
function Install-And-ActivateKey {
param([Parameter(Mandatory)][string]$ProductKey)
$slmgrPath = Join-Path $env:windir 'System32\slmgr.vbs'
if (-not (Test-Path $slmgrPath)) {
throw "slmgr.vbs not found at $slmgrPath"
}
$cscript = Join-Path $env:windir 'System32\cscript.exe'
$run = {
param($argsLine)
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = $cscript
$psi.Arguments = "//NoLogo `"$slmgrPath`" $argsLine"
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.UseShellExecute = $false
$proc = [System.Diagnostics.Process]::Start($psi)
$stdout = $proc.StandardOutput.ReadToEnd()
$stderr = $proc.StandardError.ReadToEnd()
$proc.WaitForExit()
[pscustomobject]@{
Args = $argsLine
Code = $proc.ExitCode
StdOut = $stdout.Trim()
StdErr = $stderr.Trim()
}
}
Write-Log "Installing product key (masked) ..." "INFO"
$masked = ($ProductKey -replace '(.{5})(?=.)','\$1-') -replace '-$',''
Write-Log "Key: $masked" "INFO"
$ipk = & $run "/ipk $ProductKey"
if ($ipk.StdErr) { Write-Log "slmgr /ipk stderr: $($ipk.StdErr)" "WARN" }
if ($ipk.StdOut) { Write-Log "slmgr /ipk stdout: $($ipk.StdOut)" "INFO" }
# Proceed to activation regardless of exit code; we'll verify via WMI
Write-Log "Attempting online activation ..." "INFO"
$ato = & $run "/ato"
if ($ato.StdErr) { Write-Log "slmgr /ato stderr: $($ato.StdErr)" "WARN" }
if ($ato.StdOut) { Write-Log "slmgr /ato stdout: $($ato.StdOut)" "INFO" }
}
# --- Main -----------------------------------------------------------------
try {
if (-not (Test-Admin)) {
Write-Log "Script must be run as Administrator." "ERROR"
exit 1
}
$pre = Get-ActivationStatus
Write-Log "Pre-check status: $($pre.StatusName); Product: $($pre.ProductName); PartialKey: $($pre.PartialKey)" "INFO"
if ($pre.Licensed) {
Write-Log "Windows is already licensed. Nothing to do." "INFO"
exit 0
}
# Read embedded OEM key from WMI
$svc = Get-CimInstance -Namespace root\cimv2 -ClassName SoftwareLicensingService -ErrorAction SilentlyContinue
$embeddedKey = $svc.OA3xOriginalProductKey
if ([string]::IsNullOrWhiteSpace($embeddedKey)) {
# Optional fallback: BackupProductKeyDefault (not always present/valid)
try {
$bk = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SoftwareProtectionPlatform' `
-Name 'BackupProductKeyDefault' -ErrorAction Stop
if ($bk.BackupProductKeyDefault) {
Write-Log "No embedded key found; found BackupProductKeyDefault in registry. Using it as a fallback." "WARN"
$embeddedKey = $bk.BackupProductKeyDefault
}
} catch {
# No fallback. Continue
}
}
if ([string]::IsNullOrWhiteSpace($embeddedKey)) {
Write-Log "No embedded OEM product key found. Cannot proceed." "ERROR"
exit 2
}
Install-And-ActivateKey -ProductKey $embeddedKey
Start-Sleep -Seconds 3
$post = Get-ActivationStatus
Write-Log "Post-check status: $($post.StatusName); Product: $($post.ProductName); PartialKey: $($post.PartialKey)" "INFO"
if ($post.Licensed) {
Write-Log "Activation successful." "INFO"
exit 0
} else {
Write-Log "Activation did not complete successfully." "ERROR"
exit 1
}
}
catch {
Write-Log "Unhandled error: $($_.Exception.Message)" "ERROR"
exit 1
}