Public/Install-WindowsLicense.ps1

#Requires -Version 5

<#
.SYNOPSIS
Installs one or more Windows license files.
.DESCRIPTION
Reads .xrm-ms license files from the computer running PowerShell and installs their
contents on each target through SoftwareLicensingService. Paths are resolved and read
before any CIM session is opened. The Software Protection Platform performs license
validation at runtime.
.INPUTS
String[]. You can pass computer names through the pipeline.
.OUTPUTS
slmgr-ps.LicensingOperationResult. One result is emitted for each attempted computer.
Throws one aggregate error after processing all targets when any operation fails.
.EXAMPLE
Install-WindowsLicense -Path C:\Licenses\example.xrm-ms
.EXAMPLE
Install-WindowsLicense -Computer WS01, WS02 -Credentials (Get-Credential) -Path C:\Licenses\base.xrm-ms
.LINK
https://github.com/zbalkan/slmgr-ps
#>

function Install-WindowsLicense
{
    [OutputType([PSCustomObject])]
    [CmdletBinding(SupportsShouldProcess = $true,
        PositionalBinding = $false,
        ConfirmImpact = 'High')]
    param(
        [Parameter(Position = 0,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true)]
        [AllowNull()]
        [string[]]$Computer = @('localhost'),

        [Parameter()]
        [AllowNull()]
        [PSCredential]$Credentials,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string[]]$Path
    )

    begin
    {
        $licenses = @(Get-LicenseFileContent -Path $Path -ErrorAction Stop)
        $installFailures = [System.Collections.Generic.List[System.Management.Automation.ErrorRecord]]::new()
    }
    process
    {
        foreach ($targetComputer in $Computer)
        {
            $action = "Install $($licenses.Count) Windows license file(s)"
            if (-not $PSCmdlet.ShouldProcess($targetComputer, $action))
            {
                continue
            }

            $session = $null
            $targetFailures = [System.Collections.Generic.List[System.Management.Automation.ErrorRecord]]::new()
            try
            {
                $session = Get-Session -Computer $targetComputer -Credentials $Credentials -ErrorAction Stop
                $service = Get-CimInstance -CimSession $session -ClassName SoftwareLicensingService -ErrorAction Stop
                $installedAny = $false

                foreach ($license in $licenses)
                {
                    try
                    {
                        $service | Invoke-SppCimMethod -MethodName InstallLicense -Arguments @{ License = $license.Content }
                        $installedAny = $true
                    }
                    catch
                    {
                        $message = "Failed to install license file '$($license.Path)' on '$targetComputer': $($_.Exception.Message)"
                        $exception = [System.InvalidOperationException]::new($message, $_.Exception)
                        $target = [PSCustomObject]@{ ComputerName = $targetComputer; Path = $license.Path }
                        $errorRecord = [System.Management.Automation.ErrorRecord]::new(
                            $exception,
                            'WindowsLicenseInstallFailed',
                            [System.Management.Automation.ErrorCategory]::InvalidOperation,
                            $target)
                        $targetFailures.Add($errorRecord)
                    }
                }

                if ($installedAny)
                {
                    try
                    {
                        $service | Invoke-SppCimMethod -MethodName RefreshLicenseStatus
                    }
                    catch
                    {
                        $message = "License files were installed on '$targetComputer', but licensing status could not be refreshed: $($_.Exception.Message)"
                        $exception = [System.InvalidOperationException]::new($message, $_.Exception)
                        $errorRecord = [System.Management.Automation.ErrorRecord]::new(
                            $exception,
                            'WindowsLicenseRefreshFailed',
                            [System.Management.Automation.ErrorCategory]::InvalidOperation,
                            $targetComputer)
                        $targetFailures.Add($errorRecord)
                    }
                }
            }
            catch
            {
                $message = "License installation could not start on '$targetComputer': $($_.Exception.Message)"
                $exception = [System.InvalidOperationException]::new($message, $_.Exception)
                $errorRecord = [System.Management.Automation.ErrorRecord]::new(
                    $exception,
                    'WindowsLicenseTargetFailed',
                    [System.Management.Automation.ErrorCategory]::ConnectionError,
                    $targetComputer)
                $targetFailures.Add($errorRecord)
            }
            finally
            {
                if ($null -ne $session)
                {
                    Remove-CimSession -CimSession $session -ErrorAction Ignore | Out-Null
                }
            }

            if ($targetFailures.Count -eq 0)
            {
                New-LicensingOperationResult `
                    -ComputerName $targetComputer `
                    -Operation InstallLicense `
                    -Success $true `
                    -VerificationState ProviderAccepted
            }
            else
            {
                $innerExceptions = [System.Collections.Generic.List[System.Exception]]::new()
                foreach ($failure in $targetFailures) { $innerExceptions.Add($failure.Exception) }
                $targetException = [System.AggregateException]::new(
                    "$($targetFailures.Count) license installation failure(s) on '$targetComputer'. First failure: $($targetFailures[0].Exception.Message)",
                    $innerExceptions.ToArray())
                $targetException.Data['Failures'] = $targetFailures.ToArray()
                $targetError = [System.Management.Automation.ErrorRecord]::new(
                    $targetException,
                    'WindowsLicenseTargetFailed',
                    [System.Management.Automation.ErrorCategory]::InvalidOperation,
                    $targetComputer)
                $structured = New-LicensingOperationError `
                    -ErrorRecord $targetError `
                    -ComputerName $targetComputer `
                    -Operation InstallLicense
                $installFailures.Add($structured.ErrorRecord)
                Write-Output $structured.Result
            }
        }
    }
    end
    {
        Complete-LicensingOperationBatch -Failures $installFailures
    }
}