Public/Start-WindowsActivation.ps1

#Requires -RunAsAdministrator
#Requires -Version 5

<#
.Synopsis
Activates Windows via KMS
.DESCRIPTION
A drop in replacement for slmgr script
.INPUTS
string[]. You can pass the computer names
.OUTPUTS
None if successful. Error message if there is an error.
.EXAMPLE
Start-WindowsActivation -Verbose # Activates the local computer
.EXAMPLE
Start-WindowsActivation -Computer WS01 -Credentials (Get-Credential) # Activates the computer named WS01 using different credentials
.EXAMPLE
Start-WindowsActivation -Computer WS01, WS02 -CacheDisabled $false # Disabled the KMS cache for the computers named WS01 and WS02. Cache is enabled by default.
.EXAMPLE
Start-WindowsActivation -Computer WS01 -KMSServerFQDN server.domain.net -KMSServerPort 2500 # Activates the computer named WS01 against server.domain.net:2500
.EXAMPLE
Start-WindowsActivation -ReArm # ReArm the trial period. ReArming already licensed devices can break current license issues. Guard clauses wil protect 99% but cannot guarantee 100%.
.EXAMPLE
Start-WindowsActivation -Offline -ConfirmationID <confirmation ID> # Used for offline -aka 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 = $true,
            ValueFromPipelineByPropertyName = $true,
            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 = $true,
            ValueFromPipelineByPropertyName = $true,
            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 = 'ActivateWithKMS')]
        [switch]
        $CacheDisabled,

        [Parameter(Mandatory = $false,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $false,
            ValueFromRemainingArguments = $false,
            ParameterSetName = 'Offline')]
        [switch]$Offline,

        [Parameter(Mandatory = $true,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $false,
            ValueFromRemainingArguments = $false,
            ParameterSetName = 'Offline')]
        [ValidateLength(64, 64)]
        [ValidateScript(
            {
                $pattern = [Regex]::new('^[0-9]{64}$')
                if ($pattern.Matches($_).Count -gt 0)
                {
                    $true
                }
                else
                {
                    throw "$_ is invalid. Please provide a valid Confirmation Id"
                }
            })]
        [ValidateNotNullOrEmpty()]
        [string]
        $ConfirmationId

    )
    Begin
    {
        $PreviousPreference = $ErrorActionPreference
        $ErrorActionPreference = 'Stop'
        Write-Verbose 'ErrorActionPreference: Stop'
    }
    Process
    {
        if ($pscmdlet.ShouldProcess($Computer -join ', ', 'Activate license via KMS'))
        {
            Write-Verbose "Enumerating computers: $($Computer.Count) computer(s)."
            foreach ($c in $Computer)
            {
                Write-Verbose "Creating new CimSession for computer $c"
                $session = Get-Session -Computer $c -Credentials $Credentials

                Write-Verbose 'Connecting to SoftwareLicensingService..'
                $service = Get-CimInstance -CimSession $session -ClassName SoftwareLicensingService

                try
                {
                    switch ($PSCmdlet.ParameterSetName)
                    {
                        'Offline'
                        {
                            Write-Verbose 'Initiating offline activation operation'
                            Invoke-OfflineActivation -CimSession $session -Service $service -$ConfirmationId
                            exit 0
                        }

                        'Rearm'
                        {
                            Write-Verbose 'Initiating ReArm operation'
                            Invoke-Rearm -CimSession $session -Service $service
                            exit 0
                        }

                        'ActivateWithKMS'
                        {
                            Write-Verbose "Changing KMS cache setting as: $($CacheDisabled.IsPresent -eq $false)"
                            if ($CacheDisabled.IsPresent)
                            {
                                $arguments = @{ DisableCaching = 1 }
                                $Service | Invoke-CimMethod -MethodName DisableKeyManagementServiceHostCaching -Arguments $arguments | Out-Null # Disable caching
                            }

                            Write-Verbose 'Initiating KMS activation operation'
                            Invoke-KMSActivation $PSBoundParameters -CimSession $session -Service $service
                            exit 0
                        }

                        default
                        {
                            throw 'Unknown parameter combination' # We do not expect this to be triggered at all but it is here to prevent human errors
                        }
                    }
                    if ($null -ne $session)
                    {
                        Remove-CimSession -CimSession $session -ErrorAction Ignore | Out-Null
                    }
                }
                catch
                {
                    if ($null -ne $session)
                    {
                        Remove-CimSession -CimSession $session -ErrorAction Ignore | Out-Null
                    }
                    exit 1
                }
            }
        }
    }
    End
    {
        $ErrorActionPreference = $PreviousPreference
    }
}