Public/Complete-WindowsDeviceLinkAssociation.ps1
|
function Complete-WindowsDeviceLinkAssociation { <# .SYNOPSIS Completes device-side DeviceLink association after tenant preassociation. .DESCRIPTION Performs a guarded native DeviceLink Configure operation on full Windows. The cmdlet requires a healthy preflight and a clean local preassociation firmware state (DeviceLinkId plus DeviceLinkCreationTimeUtc only), invokes ConfigureDeviceLinkAsync exactly once, and then verifies that all four known DeviceLink firmware variables are present and the resulting association JWT is structurally/temporally valid and bound to the current device identity. No retry, firmware reset, cloud deletion or reboot is performed automatically. #> [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='High')] param( [ValidateRange(5,600)] [int]$TimeoutSeconds = 180 ) $support = Test-WindowsDeviceLinkSupport if (-not $support.Supported) { throw "DeviceLink isn't supported: $($support.Reason)" } if ($support.Environment -ne 'Windows' -or $support.ActivationMode -ne 'RegisteredWinRT') { throw 'DeviceLink association completion currently requires full Windows with the registered DeviceLink WinRT runtime.' } $preflight = Test-WindowsDeviceLinkPreflight if ($preflight.BlockingCount -gt 0) { $blocked=@($preflight.Checks|Where-Object State -eq 'Blocked'|ForEach-Object{"$($_.Name): $($_.Summary)"}) throw "DeviceLink preflight is blocked: $($blocked -join '; ')" } $beforeFirmware=@(Get-WindowsDeviceLinkFirmwareState) $beforePresent=@($beforeFirmware|Where-Object Present|Select-Object -ExpandProperty Name) $expectedBefore=@('DeviceLinkId','DeviceLinkCreationTimeUtc') $alreadyComplete=(@($beforePresent).Count -eq 4 -and 'DeviceLinkId' -in $beforePresent -and 'DeviceLinkJwtCompressed' -in $beforePresent -and 'DeviceLinkJwtLastWrite' -in $beforePresent -and 'DeviceLinkCreationTimeUtc' -in $beforePresent) if ($alreadyComplete) { $existingJwt=Test-WindowsDeviceLinkAssociationJwt return [pscustomobject]@{ PSTypeName='Windows.DeviceLink.AssociationCompletion' Changed=$false AlreadyComplete=$true Result='AlreadyComplete' ConfigureOperationResult=$null ConfigureAsyncStatus=$null ConfigureAsyncError=$null FirmwareVariablesPresent='4/4' JwtState=$existingJwt.State IdentityMatch=$existingJwt.IdentityMatch RetryCount=0 CleanupInvoked=$false RebootInvoked=$false } } $cleanPreassociation=(@($beforePresent).Count -eq 2 -and ($expectedBefore|Where-Object{$_ -notin $beforePresent}).Count -eq 0) if (-not $cleanPreassociation) { throw "Refusing DeviceLink association completion because local firmware state is not the expected clean preassociation state. Present: $($beforePresent -join ', ')." } $identity = Get-WindowsDeviceLink -TimeoutSeconds $TimeoutSeconds $target = if($identity.LinkId){$identity.LinkId}else{'current device'} if(-not $PSCmdlet.ShouldProcess($target,'Complete DeviceLink association using native ConfigureDeviceLinkAsync')){ return } $native=[WinPEDeviceLink.Native.DeviceLinkManagerClient]::ConfigureRegistered($identity.DeviceLink,$TimeoutSeconds) $afterFirmware=@(Get-WindowsDeviceLinkFirmwareState) $afterPresent=@($afterFirmware|Where-Object Present|Select-Object -ExpandProperty Name) $firmwareComplete=(@($afterPresent).Count -eq 4 -and 'DeviceLinkId' -in $afterPresent -and 'DeviceLinkJwtCompressed' -in $afterPresent -and 'DeviceLinkJwtLastWrite' -in $afterPresent -and 'DeviceLinkCreationTimeUtc' -in $afterPresent) $jwt=Test-WindowsDeviceLinkAssociationJwt $jwtValid=($jwt.State -eq 'Valid' -and $jwt.Present -and $jwt.IdentityMatch -ne $false) $nativeSucceeded=($native.ConfigureAsyncStatus -eq 1 -and $native.ConfigureAsyncError -eq 0 -and $native.ConfigureOperationResult -eq 1) if(-not $nativeSucceeded -or -not $firmwareComplete -or -not $jwtValid){ $hr='0x{0:X8}' -f ([BitConverter]::ToUInt32([BitConverter]::GetBytes([int]$native.ConfigureAsyncError),0)) throw "DeviceLink Configure completed without the expected verified post-state. NativeSuccess=$nativeSucceeded; AsyncStatus=$($native.ConfigureAsyncStatus); AsyncError=$hr; OperationResult=$($native.ConfigureOperationResult); FirmwareComplete=$firmwareComplete; JwtState=$($jwt.State); IdentityMatch=$($jwt.IdentityMatch). No cleanup or retry was performed." } $errorHex='0x{0:X8}' -f ([BitConverter]::ToUInt32([BitConverter]::GetBytes([int]$native.ConfigureAsyncError),0)) [pscustomobject]@{ PSTypeName='Windows.DeviceLink.AssociationCompletion' Changed=$true AlreadyComplete=$false Result='AssociatedLocally' LinkId=$identity.LinkId TenantId=$native.TenantId DiscoveryResult=$native.DiscoveryResult DiscoveryUrl=$native.DiscoveryUrl ConfigureResultBefore=$native.ConfigureResultBefore ConfigureResultAfter=$native.ConfigureResultAfter ConfigureOperationResult=$native.ConfigureOperationResult ConfigureAsyncStatus=$native.ConfigureAsyncStatus ConfigureAsyncError=$errorHex FirmwareVariablesPresent='4/4' JwtState=$jwt.State IdentityMatch=$jwt.IdentityMatch SignatureValidation=$jwt.SignatureValidation RetryCount=0 CleanupInvoked=$false RebootInvoked=$false } } |