Public/Test-WindowsDeviceLinkPreflight.ps1
|
function Test-WindowsDeviceLinkPreflight { <# .SYNOPSIS Performs a read-only DeviceLink prerequisites assessment. .DESCRIPTION Combines runtime support, firmware readability, TPM and Secure Boot observations into a machine-readable preflight result. Unknown observations are preserved as warnings rather than treated as success. No DeviceLink, firmware or cloud state is changed. #> [CmdletBinding()] param( [ValidateNotNullOrEmpty()][string]$WindowsManagementServicePath ) $checks = New-Object System.Collections.Generic.List[object] function Add-Check { param([string]$Name,[string]$State,[string]$Summary,[object]$ObservedValue) $checks.Add([pscustomobject]@{Name=$Name;State=$State;Summary=$Summary;ObservedValue=$ObservedValue}) | Out-Null } $supportParams = @{} if ($PSBoundParameters.ContainsKey('WindowsManagementServicePath')) { $supportParams.WindowsManagementServicePath=$WindowsManagementServicePath } $support = Test-WindowsDeviceLinkSupport @supportParams Add-Check -Name 'Runtime' -State $(if($support.Supported){'Ready'}else{'Blocked'}) -Summary $(if($support.Supported){'DeviceLink runtime/native probe is available.'}else{$support.Reason}) -ObservedValue $support.ActivationMode $isAdmin = $false try { $identity=[Security.Principal.WindowsIdentity]::GetCurrent() $principal=New-Object Security.Principal.WindowsPrincipal($identity) $isAdmin=$principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } catch {} Add-Check -Name 'Elevation' -State $(if($isAdmin){'Ready'}else{'Warning'}) -Summary $(if($isAdmin){'Current process is elevated.'}else{'Current process does not appear elevated; firmware operations may fail.'}) -ObservedValue $isAdmin try { $fw=@(Get-WindowsDeviceLinkFirmwareState) $readErrors=@($fw|Where-Object{$_.LastError -and $_.LastError -notin @(2,203)}) if($readErrors.Count -eq 0){Add-Check -Name 'FirmwareAccess' -State 'Ready' -Summary 'Known DeviceLink firmware variables were queried successfully.' -ObservedValue (($fw|Where-Object Present).Count.ToString()+'/4')} else{Add-Check -Name 'FirmwareAccess' -State 'Blocked' -Summary 'One or more firmware variables could not be queried reliably.' -ObservedValue (($readErrors|Select-Object -ExpandProperty LastError -Unique)-join ',')} } catch { Add-Check -Name 'FirmwareAccess' -State 'Blocked' -Summary 'DeviceLink firmware state could not be read.' -ObservedValue $null } try { $tpm=Get-CimInstance -Namespace 'root\CIMV2\Security\MicrosoftTpm' -ClassName Win32_Tpm -ErrorAction Stop | Select-Object -First 1 if($tpm){ $spec=[string]$tpm.SpecVersion $isTpm2=($spec -match '(^|,)2\.0(,|$)' -or $spec -match '^2\.0') Add-Check -Name 'TPM' -State $(if($isTpm2){'Ready'}else{'Warning'}) -Summary $(if($isTpm2){'TPM 2.0 is reported.'}else{'A TPM is present, but TPM 2.0 could not be confirmed.'}) -ObservedValue $spec } else { Add-Check -Name 'TPM' -State 'Warning' -Summary 'TPM presence could not be confirmed.' -ObservedValue $null } } catch { Add-Check -Name 'TPM' -State 'Warning' -Summary 'TPM state is unavailable in the current environment.' -ObservedValue $null } try { $secureBoot=Confirm-SecureBootUEFI -ErrorAction Stop Add-Check -Name 'SecureBoot' -State $(if($secureBoot){'Ready'}else{'Warning'}) -Summary $(if($secureBoot){'Secure Boot is enabled.'}else{'Secure Boot is disabled.'}) -ObservedValue ([bool]$secureBoot) } catch { Add-Check -Name 'SecureBoot' -State 'Warning' -Summary 'Secure Boot state is unavailable in the current environment.' -ObservedValue $null } try { $dlParams=@{TimeoutSeconds=30} if($PSBoundParameters.ContainsKey('WindowsManagementServicePath')){$dlParams.WindowsManagementServicePath=$WindowsManagementServicePath} $identity=Get-WindowsDeviceLink @dlParams Add-Check -Name 'LocalIdentity' -State 'Ready' -Summary 'A local DeviceLink identity can be obtained.' -ObservedValue ([string]$identity.LinkId) } catch { Add-Check -Name 'LocalIdentity' -State 'Blocked' -Summary 'A local DeviceLink identity could not be obtained.' -ObservedValue $null } # Windows PowerShell 5.1 can throw "Argument types do not match" when @(...) # is used directly against a generic List[object]. Materialize through the pipeline # so the helper and returned object receive a normal PowerShell Object[]. $checkArray = @($checks | ForEach-Object { $_ }) $resolved = Resolve-WindowsDeviceLinkPreflightState -Checks $checkArray [pscustomobject]@{ PSTypeName='Windows.DeviceLink.Preflight' State=$resolved.State Ready=$resolved.Ready BlockingCount=$resolved.BlockingCount WarningCount=$resolved.WarningCount Environment=$support.Environment Architecture=$support.Architecture ActivationMode=$support.ActivationMode DllVersion=$support.DllVersion Checks=$checkArray } } |