kms-website: auto-fetch + auto-install GVLKs (no manual key lookup)
Scripts now detect the running edition and fetch the matching GVLK from a published key list instead of requiring the user to copy one from the table. - data/products.yaml: add editionid to every Windows/Server entry, plus build numbers where an EditionID spans releases (LTSC, Server). Azure Edition left unmapped on purpose (collides with Datacenter; KMS may fail there anyway). - /keys.json: Hugo KEYS output format renders products.yaml as JSON (single source of truth). layouts/index.keys.json. - setup-kms.ps1: when no VL key is installed, read registry EditionID (+build/ProductType for server) -> fetch /keys.json -> slmgr /ipk the match -> activate. Only acts when not already licensed (never clobbers retail). - kms-bootstrap.ps1: same for Windows; for Office/Project/Visio, read Click-to-Run ProductReleaseIds -> ospp /inpkey the matching GVLK -> /act. - $env:KMS_KEYS_URL overrides the key-list URL. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
c27077549c
commit
9059dbc85b
5 changed files with 170 additions and 21 deletions
|
|
@ -1,10 +1,11 @@
|
|||
# setup-kms.ps1
|
||||
#
|
||||
# Minimal KMS-host wiring for an already-installed Volume License Windows.
|
||||
# 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.
|
||||
# Minimal KMS wiring for Volume License Windows. Pins the KMS host and activates
|
||||
# only if not already licensed. If no VL key is installed it auto-detects the
|
||||
# Windows edition and fetches the matching GVLK from the published key list
|
||||
# (no manual key lookup). Idempotent - safe to re-run: an already-licensed
|
||||
# machine reports the remaining days and exits without re-contacting KMS.
|
||||
# Does NOT install Office. Does NOT change DNS suffix.
|
||||
#
|
||||
# Usage:
|
||||
# iwr -UseBasicParsing https://kms.viktorbarzin.me/scripts/setup-kms.ps1 | iex
|
||||
|
|
@ -18,7 +19,8 @@
|
|||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$KmsHost = $(if ($env:KMS_HOST) { $env:KMS_HOST } else { 'vlmcs.viktorbarzin.me' }),
|
||||
[int] $KmsPort = $(if ($env:KMS_PORT) { [int]$env:KMS_PORT } else { 1688 })
|
||||
[int] $KmsPort = $(if ($env:KMS_PORT) { [int]$env:KMS_PORT } else { 1688 }),
|
||||
[string]$KeysUrl = $(if ($env:KMS_KEYS_URL) { $env:KMS_KEYS_URL } else { 'https://kms.viktorbarzin.me/keys.json' })
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
|
@ -44,6 +46,27 @@ function Get-WindowsLicense {
|
|||
[pscustomobject]@{ Licensed = ($p.LicenseStatus -eq 1); DaysLeft = [int]([math]::Round($p.GracePeriodRemaining / 1440)) }
|
||||
}
|
||||
|
||||
# Auto-select the GVLK for THIS machine's edition from the published key list,
|
||||
# so the user never has to look one up. Matches on the registry EditionID
|
||||
# (locale-independent); for editions that share an EditionID across releases
|
||||
# (LTSC, Server) it also narrows by the OS build number.
|
||||
function Resolve-WindowsGvlk {
|
||||
$cv = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -ErrorAction SilentlyContinue
|
||||
if (-not $cv) { return $null }
|
||||
$editionId = $cv.EditionID
|
||||
$build = "$($cv.CurrentBuildNumber)"
|
||||
$isServer = $false
|
||||
try { $isServer = ((Get-CimInstance Win32_OperatingSystem -ErrorAction Stop).ProductType -ne 1) } catch {}
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
try { $keys = (Invoke-WebRequest -UseBasicParsing -Uri $KeysUrl -TimeoutSec 20).Content | ConvertFrom-Json }
|
||||
catch { Bad "Could not fetch the key list from $KeysUrl"; return $null }
|
||||
$pool = if ($isServer) { $keys.windows_server } else { $keys.windows }
|
||||
$m = $pool | Where-Object { $_.editionid -eq $editionId -and ( -not $_.builds -or ($_.builds -contains $build) ) } | Select-Object -First 1
|
||||
if ($m) { Write-Host " detected $editionId (build $build) -> $($m.edition)"; return $m.gvlk }
|
||||
Write-Host " no published GVLK matches EditionID '$editionId' (build $build)"
|
||||
return $null
|
||||
}
|
||||
|
||||
$slmgr = "$env:WINDIR\System32\slmgr.vbs"
|
||||
|
||||
# Pin the KMS host. Idempotent: re-running just re-sets the same value.
|
||||
|
|
@ -53,19 +76,29 @@ if ($LASTEXITCODE -ne 0) { Bad "slmgr /skms failed (exit $LASTEXITCODE): $out";
|
|||
OK "KMS host pinned"
|
||||
|
||||
$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)"
|
||||
|
||||
# Idempotent: already activated (retail OR KMS) -> don't re-hit the KMS server and
|
||||
# never clobber a working key. The pinned host keeps Windows auto-renewing.
|
||||
if ($lic -and $lic.Licensed) {
|
||||
Write-Host ""
|
||||
Write-Host "==> Already licensed ($($lic.DaysLeft) days remaining). Nothing to do." -ForegroundColor Green
|
||||
Write-Host " Host is pinned to $KmsHost; Windows auto-renews every 7 days."
|
||||
return
|
||||
}
|
||||
|
||||
# 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
|
||||
# No Volume License key installed -> fetch + install the GVLK for this edition.
|
||||
if ($null -eq $lic) {
|
||||
Step "No Volume License key installed - detecting edition and fetching its GVLK"
|
||||
$gvlk = Resolve-WindowsGvlk
|
||||
if (-not $gvlk) {
|
||||
Bad "Could not auto-select a GVLK for this edition."
|
||||
Write-Host " Pick one from https://kms.viktorbarzin.me/#windows and run: slmgr /ipk <GVLK>"
|
||||
return
|
||||
}
|
||||
Step "Installing GVLK $gvlk"
|
||||
$out = & cscript //Nologo $slmgr /ipk $gvlk 2>&1
|
||||
if ($LASTEXITCODE -ne 0) { Bad "slmgr /ipk failed (exit $LASTEXITCODE): $out"; return }
|
||||
OK "GVLK installed"
|
||||
}
|
||||
|
||||
Step "Activating (slmgr /ato)"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue