kms-website: make activation scripts idempotent + harden Office detection
- Replace locale-dependent "License Status: Licensed" regex with a locale-independent WMI probe (SoftwareLicensingProduct.LicenseStatus==1). Fixes false "not licensed yet" reports on non-English Windows and on re-runs. - Idempotent: always pin the KMS host, but skip /ato (Windows) and /act (Office) when already licensed — report days remaining instead of re-contacting the public KMS server. - Find-Ospp now also checks Click-to-Run \root\Office16\ (+ \root\Office15\) layouts, not just the MSI Office16 path. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
efa1353e6b
commit
d11dc8c0ce
2 changed files with 66 additions and 31 deletions
|
|
@ -72,27 +72,40 @@ if (-not ($doWin -or $doOfficeAct -or $doProjAct -or $doVisioAct)) {
|
|||
}
|
||||
|
||||
# --- Windows -------------------------------------------------------------
|
||||
# Locale-independent license probe (slmgr /dlv text is localized; the WMI
|
||||
# LicenseStatus integer is not). 1 = Licensed. $null = no KMS-client SKU.
|
||||
function Get-WindowsLicense {
|
||||
$q = "SELECT LicenseStatus, GracePeriodRemaining FROM SoftwareLicensingProduct WHERE Name LIKE 'Windows%' AND PartialProductKey IS NOT NULL"
|
||||
$p = $null
|
||||
try { $p = Get-CimInstance -Query $q -ErrorAction Stop | Select-Object -First 1 }
|
||||
catch { try { $p = Get-WmiObject -Query $q -ErrorAction Stop | Select-Object -First 1 } catch {} }
|
||||
if (-not $p) { return $null }
|
||||
[pscustomobject]@{ Licensed = ($p.LicenseStatus -eq 1); DaysLeft = [int]([math]::Round($p.GracePeriodRemaining / 1440)) }
|
||||
}
|
||||
|
||||
function Activate-Windows {
|
||||
Step "Windows activation"
|
||||
$slmgr = "$env:WINDIR\System32\slmgr.vbs"
|
||||
& cscript //Nologo $slmgr /skms "$KmsHost`:$KmsPort" | Out-Host
|
||||
if ($LASTEXITCODE -ne 0) { Bad "slmgr /skms failed"; return }
|
||||
$lic = Get-WindowsLicense
|
||||
if ($null -eq $lic) { Bad "No Volume License Windows SKU — install a GVLK first (slmgr /ipk <GVLK>)."; return }
|
||||
if ($lic.Licensed) { OK "Windows already licensed ($($lic.DaysLeft) days) — host pinned, skipping /ato"; return }
|
||||
& cscript //Nologo $slmgr /ato | Out-Host
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Bad "slmgr /ato failed. Most likely cause: not a Volume License edition (Home/retail/OEM cannot KMS-activate)."
|
||||
Write-Host " See https://kms.viktorbarzin.me/#faq"
|
||||
return
|
||||
}
|
||||
$dlv = & cscript //Nologo $slmgr /dlv 2>&1 | Out-String
|
||||
if ($dlv -match 'License Status:\s*Licensed') { OK "Windows licensed" } else { Warn "Status not 'Licensed' yet — try 'slmgr /dlv' in a minute" }
|
||||
$lic = Get-WindowsLicense
|
||||
if ($lic.Licensed) { OK "Windows licensed ($($lic.DaysLeft) days)" }
|
||||
else { Bad "Windows not licensed — likely not a VL edition (Home/retail/OEM reject KMS). See https://kms.viktorbarzin.me/#faq" }
|
||||
}
|
||||
if ($doWin) { Activate-Windows }
|
||||
|
||||
# --- Office / Project / Visio: activate already-installed ----------------
|
||||
function Find-Ospp {
|
||||
# Covers MSI (Office16/15) and Click-to-Run (\root\Office16) layouts, 64- and 32-bit.
|
||||
$candidates = @(
|
||||
"${env:ProgramFiles}\Microsoft Office\Office16\ospp.vbs",
|
||||
"${env:ProgramFiles(x86)}\Microsoft Office\Office16\ospp.vbs",
|
||||
"${env:ProgramFiles}\Microsoft Office\root\Office16\ospp.vbs",
|
||||
"${env:ProgramFiles(x86)}\Microsoft Office\root\Office16\ospp.vbs",
|
||||
"${env:ProgramFiles}\Microsoft Office\Office15\ospp.vbs",
|
||||
"${env:ProgramFiles(x86)}\Microsoft Office\Office15\ospp.vbs"
|
||||
)
|
||||
|
|
@ -109,9 +122,13 @@ function Activate-Ospp([string]$label) {
|
|||
Step "$label activation via $ospp"
|
||||
& cscript //Nologo $ospp /sethst:$KmsHost | Out-Host
|
||||
& cscript //Nologo $ospp /setprt:$KmsPort | Out-Host
|
||||
& cscript //Nologo $ospp /act | Out-Host
|
||||
# Idempotent: skip /act when already licensed (the '---LICENSED---' marker
|
||||
# in ospp output is a fixed literal, not localized).
|
||||
$st = & cscript //Nologo $ospp /dstatus 2>&1 | Out-String
|
||||
if ($st -match '---LICENSED---') { OK "$label licensed" } else { Warn "$label status not LICENSED yet" }
|
||||
if ($st -match '---LICENSED---') { OK "$label already licensed — host set, skipping /act"; return }
|
||||
& cscript //Nologo $ospp /act | Out-Host
|
||||
$st = & cscript //Nologo $ospp /dstatus 2>&1 | Out-String
|
||||
if ($st -match '---LICENSED---') { OK "$label licensed" } else { Warn "$label status not LICENSED yet (no VL Office SKU? See https://kms.viktorbarzin.me/#office)" }
|
||||
}
|
||||
if ($doOfficeAct) { Activate-Ospp 'Office' }
|
||||
if ($doProjAct) { Activate-Ospp 'Project' }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
# setup-kms.ps1
|
||||
#
|
||||
# Minimal KMS-host wiring for an already-installed Volume License Windows.
|
||||
# Runs `slmgr /skms <host>:<port>` + `slmgr /ato` and prints the licence status.
|
||||
# Pins the KMS host, then activates only if not already licensed.
|
||||
# Idempotent — safe to re-run: if Windows is already licensed it reports the
|
||||
# remaining days and exits without re-contacting the KMS server.
|
||||
# Does NOT install Office. Does NOT change DNS suffix. Pin only.
|
||||
#
|
||||
# Usage:
|
||||
|
|
@ -30,38 +32,54 @@ if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdenti
|
|||
return
|
||||
}
|
||||
|
||||
Step "KMS host = $KmsHost`:$KmsPort"
|
||||
# Locale-independent license probe (slmgr /dlv text is localized; the WMI
|
||||
# LicenseStatus integer is not). 1 = Licensed. Returns $null if no KMS-client
|
||||
# Windows SKU is installed. GracePeriodRemaining is in minutes.
|
||||
function Get-WindowsLicense {
|
||||
$q = "SELECT LicenseStatus, GracePeriodRemaining FROM SoftwareLicensingProduct WHERE Name LIKE 'Windows%' AND PartialProductKey IS NOT NULL"
|
||||
$p = $null
|
||||
try { $p = Get-CimInstance -Query $q -ErrorAction Stop | Select-Object -First 1 }
|
||||
catch { try { $p = Get-WmiObject -Query $q -ErrorAction Stop | Select-Object -First 1 } catch {} }
|
||||
if (-not $p) { return $null }
|
||||
[pscustomobject]@{ Licensed = ($p.LicenseStatus -eq 1); DaysLeft = [int]([math]::Round($p.GracePeriodRemaining / 1440)) }
|
||||
}
|
||||
|
||||
$slmgr = "$env:WINDIR\System32\slmgr.vbs"
|
||||
|
||||
Step "slmgr /skms $KmsHost`:$KmsPort"
|
||||
# Pin the KMS host. Idempotent: re-running just re-sets the same value.
|
||||
Step "Pinning KMS host -> $KmsHost`:$KmsPort"
|
||||
$out = & cscript //Nologo $slmgr /skms "$KmsHost`:$KmsPort" 2>&1
|
||||
Write-Host $out
|
||||
if ($LASTEXITCODE -ne 0) { Bad "slmgr /skms failed (exit $LASTEXITCODE)"; return }
|
||||
if ($LASTEXITCODE -ne 0) { Bad "slmgr /skms failed (exit $LASTEXITCODE): $out"; return }
|
||||
OK "KMS host pinned"
|
||||
|
||||
Step "slmgr /ato (activate)"
|
||||
$out = & cscript //Nologo $slmgr /ato 2>&1
|
||||
Write-Host $out
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Bad "slmgr /ato failed (exit $LASTEXITCODE)"
|
||||
Write-Host ""
|
||||
Write-Host "Most common cause: this Windows is not a Volume License edition."
|
||||
Write-Host "KMS activates only VL SKUs (Pro, Enterprise, Education, LTSC, Server)."
|
||||
Write-Host "Home / retail / OEM keys reject KMS responses. See https://kms.viktorbarzin.me/#faq"
|
||||
$lic = Get-WindowsLicense
|
||||
if ($null -eq $lic) {
|
||||
Bad "No KMS-client (Volume License) Windows SKU detected."
|
||||
Write-Host " Install a GVLK first: slmgr /ipk <GVLK> (see https://kms.viktorbarzin.me/#windows)"
|
||||
return
|
||||
}
|
||||
OK "Activation request sent"
|
||||
|
||||
Step "slmgr /dlv (status)"
|
||||
$out = & cscript //Nologo $slmgr /dlv 2>&1
|
||||
# Idempotent: already activated -> don't re-hit the KMS server. The pinned host
|
||||
# above means Windows keeps auto-renewing every 7 days on its own.
|
||||
if ($lic.Licensed) {
|
||||
Write-Host ""
|
||||
Write-Host "==> Already licensed via KMS ($($lic.DaysLeft) days remaining). Nothing to do." -ForegroundColor Green
|
||||
Write-Host " Host is pinned to $KmsHost; Windows auto-renews every 7 days."
|
||||
return
|
||||
}
|
||||
|
||||
Step "Activating (slmgr /ato)"
|
||||
$out = & cscript //Nologo $slmgr /ato 2>&1
|
||||
Write-Host $out
|
||||
|
||||
if ($out -match 'License Status:\s*Licensed') {
|
||||
$lic = Get-WindowsLicense
|
||||
if ($lic.Licensed) {
|
||||
Write-Host ""
|
||||
Write-Host "==> SUCCESS: Windows is now licensed via KMS." -ForegroundColor Green
|
||||
Write-Host " Licence renews automatically every 7 days; lasts 180 days per renewal."
|
||||
Write-Host "==> SUCCESS: Windows is now licensed via KMS ($($lic.DaysLeft) days)." -ForegroundColor Green
|
||||
Write-Host " Renews automatically every 7 days; lasts 180 days per renewal."
|
||||
} else {
|
||||
Write-Host ""
|
||||
Write-Host "==> Activation request sent but status is not 'Licensed' yet." -ForegroundColor Yellow
|
||||
Write-Host " Re-run 'slmgr /dlv' in a minute, or check https://kms.viktorbarzin.me/#faq"
|
||||
Write-Host "==> Activation did not stick." -ForegroundColor Yellow
|
||||
Write-Host " Most common cause: this Windows is not a Volume License edition"
|
||||
Write-Host " (Home / retail / OEM reject KMS). See https://kms.viktorbarzin.me/#faq"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue