Public/Get-WindowsDeviceLink.ps1
|
function Get-WindowsDeviceLink { [CmdletBinding()] param( [ValidateNotNullOrEmpty()] [string]$WindowsManagementServicePath, [ValidateRange(5, 600)] [int]$TimeoutSeconds = 120, [ValidateNotNullOrEmpty()] [string]$OutputDirectory, [switch]$Online, [ValidateNotNullOrEmpty()] [string]$TenantId, [ValidateNotNullOrEmpty()] [string]$ClientId, [switch]$Interactive, [switch]$UseDeviceCode, [securestring]$AccessToken, [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate, [ValidateNotNullOrEmpty()] [string]$CertificateThumbprint, [ValidateNotNullOrEmpty()] [string]$CertificateSubjectName, [bool]$SendCertificateChain = $false, [securestring]$ClientSecret, [switch]$Identity, [switch]$EnvironmentVariable, [ValidateNotNullOrEmpty()] [string]$Environment = 'Global', [ValidateRange(1, 600)] [double]$ClientTimeout = 100 ) if (-not $Online) { $onlineOnlyParameters = @( 'TenantId','ClientId','Interactive','UseDeviceCode','AccessToken','Certificate', 'CertificateThumbprint','CertificateSubjectName','ClientSecret','Identity','EnvironmentVariable' ) $usedOnlineOnly = $onlineOnlyParameters | Where-Object { $PSBoundParameters.ContainsKey($_) } if ($usedOnlineOnly) { throw "Online authentication parameters require -Online. Parameters supplied: $($usedOnlineOnly -join ', ')." } } if ($PSBoundParameters.ContainsKey('WindowsManagementServicePath')) { $support = Test-WindowsDeviceLinkSupport -WindowsManagementServicePath $WindowsManagementServicePath } else { $support = Test-WindowsDeviceLinkSupport } if (-not $support.Supported) { throw "DeviceLink isn't supported: $($support.Reason)" } if ($support.ActivationMode -eq 'RegisteredWinRT') { $payload = [WinPEDeviceLink.Native.DeviceLinkClient]::GetDeviceLinkRegistered($TimeoutSeconds) } else { $payload = [WinPEDeviceLink.Native.DeviceLinkClient]::GetDeviceLink($support.DllPath, $TimeoutSeconds) } $decoded = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($payload)) | ConvertFrom-Json $deviceLink = [pscustomobject]@{ PSTypeName = 'Windows.DeviceLink.Information' Environment = $support.Environment SerialNumber = $decoded.DeviceInfo.SerialNumber Manufacturer = $decoded.DeviceInfo.Manufacturer Model = $decoded.DeviceInfo.ModelName SmbiosUuid = $decoded.DeviceInfo.SmbiosUuid LinkId = $decoded.DeviceInfo.LinkId CreationTimeUtc = $decoded.DeviceLinkCreationTimeUtc DeviceLink = $payload DllSource = $support.DllSource ActivationMode = $support.ActivationMode DllPath = $support.DllPath DllVersion = $support.DllVersion } $exportedFile = $null if ($PSBoundParameters.ContainsKey('OutputDirectory')) { $exportedFile = $deviceLink | Export-WindowsDeviceLinkCsv -DestinationPath $OutputDirectory Write-Information -InformationAction Continue -MessageData "DeviceLink CSV exported successfully: $($exportedFile.FullName)" } if (-not $Online) { if ($exportedFile) { return $exportedFile } return $deviceLink } $methods = @() if ($Interactive) { $methods += 'Interactive' } if ($UseDeviceCode) { $methods += 'DeviceCode' } if ($PSBoundParameters.ContainsKey('AccessToken')) { $methods += 'AccessToken' } if ($PSBoundParameters.ContainsKey('Certificate')) { $methods += 'Certificate' } if ($PSBoundParameters.ContainsKey('CertificateThumbprint')) { $methods += 'CertificateThumbprint' } if ($PSBoundParameters.ContainsKey('CertificateSubjectName')) { $methods += 'CertificateSubjectName' } if ($PSBoundParameters.ContainsKey('ClientSecret')) { $methods += 'ClientSecret' } if ($Identity) { $methods += 'ManagedIdentity' } if ($EnvironmentVariable) { $methods += 'EnvironmentVariable' } if ($methods.Count -eq 0) { throw 'When using -Online, specify an authentication method: -Interactive, -UseDeviceCode, -AccessToken, -Certificate, -CertificateThumbprint, -CertificateSubjectName, -ClientSecret, -Identity, or -EnvironmentVariable.' } if ($methods.Count -gt 1) { throw "Specify only one authentication method with -Online. Methods supplied: $($methods -join ', ')." } if ($methods[0] -eq 'DeviceCode') { if (-not $TenantId) { throw '-TenantId is required for -Online -UseDeviceCode.' } if ($Environment -ne 'Global') { throw 'Native device-code authentication currently supports the Global Microsoft cloud only.' } $tokenParameters = @{ TenantId = $TenantId } if ($ClientId) { $tokenParameters.ClientId = $ClientId } Write-Information -InformationAction Continue -MessageData 'Using native OAuth device-code authentication (Microsoft.Graph.Authentication is not required).' $token = Get-WindowsDeviceLinkDeviceCodeToken @tokenParameters return Invoke-WindowsDeviceLinkGraphRegistration -InputObject $deviceLink -AccessToken $token.AccessToken -TenantId $TenantId } if ($support.Environment -eq 'WindowsPE') { if ($Environment -ne 'Global') { throw 'Native WinPE authentication currently supports the Global Microsoft cloud only.' } switch ($methods[0]) { 'ClientSecret' { if (-not $TenantId -or -not $ClientId) { throw '-TenantId and -ClientId are required for client secret authentication.' } Write-Information -InformationAction Continue -MessageData 'Using native OAuth client-credentials authentication for WinPE (Microsoft.Graph.Authentication is not required).' $token = Get-WindowsDeviceLinkClientSecretToken -TenantId $TenantId -ClientId $ClientId -ClientSecret $ClientSecret return Invoke-WindowsDeviceLinkGraphRegistration -InputObject $deviceLink -AccessToken $token.AccessToken -TenantId $TenantId } 'AccessToken' { if (-not $TenantId) { throw '-TenantId is required with -AccessToken in WinPE so the registration result can identify the tenant.' } $credential = New-Object System.Management.Automation.PSCredential('token', $AccessToken) $plainToken = $credential.GetNetworkCredential().Password try { Write-Information -InformationAction Continue -MessageData 'Using supplied access token directly for WinPE Graph registration.' return Invoke-WindowsDeviceLinkGraphRegistration -InputObject $deviceLink -AccessToken $plainToken -TenantId $TenantId } finally { $plainToken = $null $credential = $null } } 'EnvironmentVariable' { if (-not $env:AZURE_TENANT_ID -or -not $env:AZURE_CLIENT_ID -or -not $env:AZURE_CLIENT_SECRET) { throw 'WinPE environment-variable authentication requires AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET.' } $environmentSecret = ConvertTo-SecureString $env:AZURE_CLIENT_SECRET -AsPlainText -Force Write-Information -InformationAction Continue -MessageData 'Using native OAuth client-credentials authentication from environment variables for WinPE.' $token = Get-WindowsDeviceLinkClientSecretToken ` -TenantId $env:AZURE_TENANT_ID ` -ClientId $env:AZURE_CLIENT_ID ` -ClientSecret $environmentSecret return Invoke-WindowsDeviceLinkGraphRegistration ` -InputObject $deviceLink ` -AccessToken $token.AccessToken ` -TenantId $env:AZURE_TENANT_ID } } } $connectParams = @{ Environment = $Environment ClientTimeout = $ClientTimeout } switch ($methods[0]) { 'Interactive' { if (-not $TenantId) { throw '-TenantId is required for -Online -Interactive.' } $connectParams.TenantId = $TenantId if ($ClientId) { $connectParams.ClientId = $ClientId } } 'AccessToken' { $connectParams.AccessToken = $AccessToken } 'Certificate' { if (-not $TenantId -or -not $ClientId) { throw '-TenantId and -ClientId are required for certificate authentication.' } $connectParams.TenantId = $TenantId $connectParams.ClientId = $ClientId $connectParams.Certificate = $Certificate $connectParams.SendCertificateChain = $SendCertificateChain } 'CertificateThumbprint' { if (-not $TenantId -or -not $ClientId) { throw '-TenantId and -ClientId are required for certificate thumbprint authentication.' } $connectParams.TenantId = $TenantId $connectParams.ClientId = $ClientId $connectParams.CertificateThumbprint = $CertificateThumbprint $connectParams.SendCertificateChain = $SendCertificateChain } 'CertificateSubjectName' { if (-not $TenantId -or -not $ClientId) { throw '-TenantId and -ClientId are required for certificate subject authentication.' } $connectParams.TenantId = $TenantId $connectParams.ClientId = $ClientId $connectParams.CertificateSubjectName = $CertificateSubjectName $connectParams.SendCertificateChain = $SendCertificateChain } 'ClientSecret' { if (-not $TenantId -or -not $ClientId) { throw '-TenantId and -ClientId are required for client secret authentication.' } $connectParams.TenantId = $TenantId $connectParams.ClientId = $ClientId $connectParams.ClientSecret = $ClientSecret } 'ManagedIdentity' { $connectParams.Identity = $true if ($ClientId) { $connectParams.ClientId = $ClientId } } 'EnvironmentVariable' { $connectParams.EnvironmentVariable = $true } } Connect-WindowsDeviceLink @connectParams | Out-Null $deviceLink | Register-WindowsDeviceLink } |