Public/Get-WindowsDeviceLinkLocalAssociation.ps1
|
function Get-WindowsDeviceLinkLocalAssociation { <# .SYNOPSIS Reads and correlates local DeviceLink tenant-association metadata. .DESCRIPTION Performs a read-only correlation of the current DeviceLinkId with two known local tenant-identification sources: - HKLM:\SOFTWARE\Microsoft\Provisioning\AutopilotSettings\<LinkId>_TenantIdHint - the tenantId claim in DeviceLinkJwtCompressed, when the Association JWT is present Historical registry hints are counted but never selected. Only registry values whose prefix exactly matches the current UEFI DeviceLinkId are considered. When both local sources are present and agree, TrustLevel is CorrelatedLocalSources. When they disagree, the cmdlet fails closed by returning no TenantId and setting ConflictDetected to True. The raw Association JWT, signature, DeviceLinkId firmware bytes, TPM material, and unrelated JWT claim values are never returned. No Microsoft Graph or other cloud request is performed. Association JWT signature validation is not currently performed because a supported signing-key discovery mechanism for the observed DeviceTag token has not been established. .OUTPUTS PSCustomObject with local DeviceLink association and tenant-correlation metadata. .EXAMPLE Get-WindowsDeviceLinkLocalAssociation | Format-List * Reads local DeviceLink association metadata without cloud authentication. .NOTES This command reports local evidence only. It does not prove that a tenant-side Device Association record currently exists. #> [CmdletBinding()] param() $firmware = @(Get-WindowsDeviceLinkFirmwareState) $presentCount = @($firmware | Where-Object Present).Count $firmwareState = ('{0}/4' -f $presentCount) $localState = switch ($presentCount) { 0 { 'NoFirmwareState' } 2 { 'BaseIdentity' } 4 { 'CompleteAssociationFirmwareState' } default { 'IncompleteFirmwareState' } } $registryPath = 'HKLM:\SOFTWARE\Microsoft\Provisioning\AutopilotSettings' $historicalHintCount = 0 if (Test-Path -LiteralPath $registryPath) { try { $registryBaseline = Get-ItemProperty -LiteralPath $registryPath -ErrorAction Stop $historicalHintCount = @( $registryBaseline.PSObject.Properties | Where-Object { $_.Name -match '(?i)_TenantIdHint$' } ).Count } catch {} } $linkRaw = Get-WindowsDeviceLinkFirmwareVariableBytes -Name DeviceLinkId if (-not $linkRaw.Present -or -not $linkRaw.Bytes) { return [pscustomobject]@{ PSTypeName = 'Windows.DeviceLink.LocalAssociation' LocalAssociationState = $localState FirmwareState = $firmwareState LinkId = $null TenantId = $null DiscoveryUrl = $null RegistryHintPresent = $false RegistryTenantIdHint = $null RegistryDiscoveryUrl = $null HistoricalTenantHintCount = $historicalHintCount JwtPresent = $false JwtParseState = 'Absent' JwtEncoding = $null JwtTenantId = $null JwtTenantClaim = $null JwtDiscoveryUrl = $null TenantSourcesMatch = $null Source = 'Unavailable' TrustLevel = 'Unavailable' SignatureValidation = 'NotPerformed' ConflictDetected = $false CloudChecked = $false Conclusion = 'The current DeviceLinkId is unavailable, so local tenant correlation cannot be performed.' } } try { $linkId = ConvertFrom-WindowsDeviceLinkIdBytes -Bytes $linkRaw.Bytes if (-not $linkId) { throw 'The current DeviceLinkId firmware value could not be safely decoded as a GUID.' } $registryTenant = $null $registryDiscoveryUrl = $null $registryHintPresent = $false if (Test-Path -LiteralPath $registryPath) { $registry = Get-ItemProperty -LiteralPath $registryPath -ErrorAction Stop $tenantPropertyName = "$($linkId)_TenantIdHint" $discoveryPropertyName = "$($linkId)_DiscoveryUrl" if ($registry.PSObject.Properties.Name -contains $tenantPropertyName) { $value = [string]$registry.$tenantPropertyName if (-not [string]::IsNullOrWhiteSpace($value)) { $registryTenant = $value.Trim().Trim('{','}') $registryHintPresent = $true } } if ($registry.PSObject.Properties.Name -contains $discoveryPropertyName) { $value = [string]$registry.$discoveryPropertyName if (-not [string]::IsNullOrWhiteSpace($value)) { $registryDiscoveryUrl = $value } } } $jwt = Get-WindowsDeviceLinkLocalAssociationJwtMetadata $correlation = Resolve-WindowsDeviceLinkLocalTenantCorrelation -RegistryTenantId $registryTenant -JwtTenantId $jwt.TenantId $discoveryUrl = if (-not [string]::IsNullOrWhiteSpace($jwt.DiscoveryUrl)) { $jwt.DiscoveryUrl } else { $registryDiscoveryUrl } [pscustomobject]@{ PSTypeName = 'Windows.DeviceLink.LocalAssociation' LocalAssociationState = $localState FirmwareState = $firmwareState LinkId = $linkId TenantId = $correlation.TenantId DiscoveryUrl = $discoveryUrl RegistryHintPresent = $registryHintPresent RegistryTenantIdHint = $registryTenant RegistryDiscoveryUrl = $registryDiscoveryUrl HistoricalTenantHintCount = $historicalHintCount JwtPresent = [bool]$jwt.Present JwtParseState = $jwt.ParseState JwtEncoding = $jwt.Encoding JwtTenantId = $jwt.TenantId JwtTenantClaim = $jwt.TenantClaim JwtDiscoveryUrl = $jwt.DiscoveryUrl TenantSourcesMatch = $correlation.SourcesMatch Source = $correlation.Source TrustLevel = $correlation.TrustLevel SignatureValidation = $jwt.SignatureValidation ConflictDetected = $correlation.Conflict CloudChecked = $false Conclusion = $correlation.Conclusion } } finally { if ($linkRaw -and $linkRaw.Bytes) { [Array]::Clear($linkRaw.Bytes, 0, $linkRaw.Bytes.Length) } $linkRaw = $null } } |