Public/Start-WindowsActivation.ps1
|
#Requires -Version 5 <# .Synopsis Installs product keys or activates Windows .DESCRIPTION A drop in replacement for slmgr script. By default attempts activation using the product key already installed on the machine. Use -UseKmsClientKey to also install the KMS client setup key (GVLK) for the detected OS edition before activating. This is a material licensing change and is therefore opt-in. Use -ProductKey to install an explicit product key before activating it in the same operation. .INPUTS string[]. You can pass the computer names .OUTPUTS None if successful. Throws on error. .EXAMPLE Start-WindowsActivation -Verbose # Activates the local computer using its existing product key .EXAMPLE Start-WindowsActivation -UseKmsClientKey -Verbose # Installs the GVLK for the detected OS edition then activates .EXAMPLE Start-WindowsActivation -ProductKey XXXXX-XXXXX-XXXXX-XXXXX-XXXXX # Installs an explicit product key then activates .EXAMPLE Start-WindowsActivation -ActivationId aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee # Activates one licensing product .EXAMPLE Start-WindowsActivation -Computer WS01 -Credentials (Get-Credential) # Activates WS01 over WinRM .EXAMPLE Start-WindowsActivation -Computer WS01, WS02 -CacheDisabled # Disables the KMS cache on WS01 and WS02 .EXAMPLE Start-WindowsActivation -Computer WS01 -KMSServerFQDN server.domain.net -KMSServerPort 2500 # Activates against a specific KMS server .EXAMPLE Start-WindowsActivation -ReArm # ReArm the trial period (guard clauses apply but cannot guarantee 100% safety) .EXAMPLE Start-WindowsActivation -ReArm -ApplicationId 11111111-2222-3333-4444-555555555555 # ReArm one application .EXAMPLE Start-WindowsActivation -ReArm -ActivationId aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee # ReArm one licensing product .EXAMPLE Start-WindowsActivation -Offline -ConfirmationID 123456-123456-123456-123456-123456-123456-123456-123456-123456 # Phone activation .LINK https://github.com/zbalkan/slmgr-ps #> function Start-WindowsActivation { [CmdletBinding(SupportsShouldProcess = $true, PositionalBinding = $false, ConfirmImpact = 'High', DefaultParameterSetName = 'ActivateWithKMS')] Param ( # Type localhost or . for local computer or do not use the parameter [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $false)] [Parameter(ParameterSetName = 'ActivateWithKMS')] [Parameter(ParameterSetName = 'Rearm')] [Parameter(ParameterSetName = 'Offline')] [AllowNull()] [string[]] $Computer = @('localhost'), # Define credentials other than current user if needed [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false)] [Parameter(ParameterSetName = 'ActivateWithKMS')] [Parameter(ParameterSetName = 'Rearm')] [Parameter(ParameterSetName = 'Offline')] [AllowNull()] [PSCredential] $Credentials, [Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false, ParameterSetName = 'ActivateWithKMS')] [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 = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false, ParameterSetName = 'ActivateWithKMS')] [ValidateRange(1, 65535)] [int] $KMSServerPort = 1688, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false, ParameterSetName = 'Rearm')] [switch] $Rearm, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false, ParameterSetName = 'Rearm')] [Guid] $ApplicationId, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false, ParameterSetName = 'ActivateWithKMS')] [switch] $CacheDisabled, # Installs the KMS client setup key (GVLK) for the detected OS edition before # attempting activation. Only needed when the machine currently has a MAK or retail # key and must be switched to volume/KMS licensing. This is a material licensing change. [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false, ParameterSetName = 'ActivateWithKMS')] [switch] $UseKmsClientKey, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false, ParameterSetName = 'ActivateWithKMS')] [string] $ProductKey, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false, ParameterSetName = 'ActivateWithKMS')] [Parameter(ParameterSetName = 'Offline')] [Parameter(ParameterSetName = 'Rearm')] [Guid] $ActivationId, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false, ParameterSetName = 'Offline')] [switch]$Offline, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false, ParameterSetName = 'Offline')] [ValidateScript( { # Accept 54 digits (9 groups × 6), optionally separated by dashes or spaces $stripped = $_ -replace '[\s\-]', '' if ($stripped -match '^\d{54}$') { $true } else { throw "$_ is not a valid Confirmation ID. Expected 54 digits (9 groups of 6), optionally separated by dashes or spaces." } })] [ValidateNotNullOrEmpty()] [string] $ConfirmationId ) Begin { $hasProductKey = $PSBoundParameters.ContainsKey('ProductKey') $hasActivationId = $PSBoundParameters.ContainsKey('ActivationId') $hasApplicationId = $PSBoundParameters.ContainsKey('ApplicationId') if ($PSCmdlet.ParameterSetName -eq 'Rearm' -and -not $Rearm.IsPresent) { throw 'ApplicationId and ActivationId require the Rearm switch in the rearm parameter set.' } if ($hasApplicationId -and $hasActivationId) { throw 'ApplicationId and ActivationId cannot be used together for rearm.' } if ($UseKmsClientKey.IsPresent -and $hasProductKey) { throw 'UseKmsClientKey and ProductKey cannot be used together.' } if ($hasActivationId -and ($UseKmsClientKey.IsPresent -or $hasProductKey)) { throw 'ActivationId cannot be combined with product-key installation because InstallProductKey is service-scoped.' } $hasInvalidProductKey = $ProductKey -notmatch '^[A-Za-z0-9]{5}(?:-[A-Za-z0-9]{5}){4}$' if ($hasProductKey -and $hasInvalidProductKey) { throw 'ProductKey must contain five groups of five alphanumeric characters separated by dashes.' } } Process { $activationFailures = [System.Collections.Generic.List[System.Management.Automation.ErrorRecord]]::new() Write-Verbose "Enumerating computers: $($Computer.Count) computer(s)." foreach ($c in $Computer) { $action = switch ($PSCmdlet.ParameterSetName) { 'Offline' { 'Apply an offline Windows confirmation ID' } 'Rearm' { if ($hasApplicationId) { "Rearm application $ApplicationId" } elseif ($hasActivationId) { "Rearm licensing product $ActivationId" } else { 'Rearm Windows' } } default { 'Activate Windows' } } if (-not $pscmdlet.ShouldProcess($c, $action)) { continue } Write-Verbose "Creating new CimSession for computer $c" $session = $null try { $session = Get-Session -Computer $c -Credentials $Credentials -ErrorAction Stop Write-Verbose 'Connecting to SoftwareLicensingService...' $service = Get-CimInstance -CimSession $session -ClassName SoftwareLicensingService -ErrorAction Stop switch ($PSCmdlet.ParameterSetName) { 'Offline' { Write-Verbose 'Initiating offline activation operation' $offlineParams = @{ CimSession = $session Service = $service ConfirmationId = $ConfirmationId } if ($PSBoundParameters.ContainsKey('ActivationId')) { $offlineParams['ActivationId'] = $ActivationId } Invoke-OfflineActivation @offlineParams } 'Rearm' { Write-Verbose 'Initiating ReArm operation' $rearmParams = @{ CimSession = $session; Service = $service } if ($hasApplicationId) { $rearmParams['ApplicationId'] = $ApplicationId } if ($hasActivationId) { $rearmParams['ActivationId'] = $ActivationId } Invoke-Rearm @rearmParams } 'ActivateWithKMS' { if ($CacheDisabled.IsPresent) { Write-Verbose 'Disabling KMS host caching' $service | Invoke-SppCimMethod -MethodName DisableKeyManagementServiceHostCaching } Write-Verbose 'Initiating Windows activation operation' $kmsParams = @{ CimSession = $session; Service = $service } if ($PSBoundParameters.ContainsKey('KMSServerFQDN')) { $kmsParams['KMSServerFQDN'] = $KMSServerFQDN } if ($PSBoundParameters.ContainsKey('KMSServerPort')) { $kmsParams['KMSServerPort'] = $KMSServerPort } if ($UseKmsClientKey.IsPresent) { $kmsParams['InstallKmsClientKey'] = $true } if ($PSBoundParameters.ContainsKey('ProductKey')) { $kmsParams['ProductKey'] = $ProductKey } if ($PSBoundParameters.ContainsKey('ActivationId')) { $kmsParams['ActivationId'] = $ActivationId } Invoke-KMSActivation @kmsParams } default { throw 'Unknown parameter combination' } } } catch { $activationFailures.Add($_) } finally { if ($null -ne $session) { Remove-CimSession -CimSession $session -ErrorAction Ignore | Out-Null } } } if ($activationFailures.Count -gt 0) { foreach ($failure in $activationFailures) { Write-Error -ErrorRecord $failure } $PSCmdlet.ThrowTerminatingError($activationFailures[0]) } } } |