Win11Readiness.psm1
<#
.SYNOPSIS Gets Windows 11 Readiness status of devices enrolled in Intune. .DESCRIPTION This command will return whether or not devices will support Windows 11 based on their hardware. Status results include RAM Check Failed, Storage Check Failed, Processor Check Failed, TPM Check Failed, Secure Boot Check Failed, Any Checks Failed, Capable, Unknown. Results can be exported to CSV. .PARAMETER Status Return the devices that match the readiness status. Valid inputs are "ramCheckFailed", "storageCheckFailed", "processorCoreCountCheckFailed", "processorSpeedCheckFailed", "tpmCheckFailed", "secureBootCheckFailed", "processorFamilyCheckFailed", "processor64BitCheckFailed", "osCheckFailed", "notCapable", "capable", "unknown", "upgraded", and "all". .PARAMETER ExportCSV Exports the returned results to the path specified. Path and file name with extension .csv must be used. .PARAMETER addToGroup Adds devices from the returned results to the group specified. Will also remove devices from the group if not in the returned results. Must use the AAD group ID and devices must be in AAD. .EXAMPLE PS C:\>Get-Win11ReadinessStatus -Status "ramCheckFailed" -ExportCSV "C:\Win11Readiness\failedRAMResults.csv" -addToGroup "00000000-df80-47ac-a4e6-e01ec30d9f6f" Gets all devices that failed the readiness check due to failed RAM check, exports them to a CSV, and adds them to the specified group. #> function Get-Win11ReadinessStatus { param ( [Parameter(Mandatory = $false)] [ValidateSet( "ramCheckFailed", "storageCheckFailed", "processorCoreCountCheckFailed", "processorSpeedCheckFailed", "tpmCheckFailed", "secureBootCheckFailed", "processorFamilyCheckFailed", "processor64BitCheckFailed", "osCheckFailed", "notCapable", "capable", "unknown", "upgraded", "all" ) ] [string]$Status = "all", [Parameter(Mandatory = $false)] [AllowEmptyString()] [string]$ExportCSV, [Parameter(Mandatory = $false)] [guid]$addToGroup ) ## Initialize some variables $failedSplat = @( "ramCheckFailed", "storageCheckFailed", "processorCoreCountCheckFailed", "processorSpeedCheckFailed", "tpmCheckFailed", "secureBootCheckFailed", "processorFamilyCheckFailed", "processor64BitCheckFailed", "osCheckFailed" ) $statusSplat = @( "notCapable", "capable", "unknown", "upgraded" ) $groupId = [string]$addToGroup Select-MgProfile -Name 'beta' Connect-MgGraph -Scopes "Device.Read.All","DeviceManagementManagedDevices.Read.All","GroupMember.ReadWrite.All" > $null $date = Get-Date -Format yyyy-MM-dd-HHmm ## Gets devices and their Work from Anywhere metrics $requestParams = @{ Uri = "/beta/deviceManagement/userExperienceAnalyticsWorkFromAnywhereMetrics/allDevices/metricDevices" Method = "GET" } $getDevices = Invoke-MgGraphRequest @requestParams -OutputType PSObject $devices = @() $devices += @($getDevices.value) $nextLink = $getDevices.'@odata.nextLink' ## Pagination for Get Devices while ($getDevices.'@odata.nextLink') { $requestParams.Uri = $getDevices.'@odata.nextLink' $getDevices = Invoke-MgGraphRequest @requestParams -OutputType PSObject $devices += @($getDevices.value) } ## Filters devices based on -Status parameter if ($status -in $failedSplat) { $filteredDevices = $devices | where { $_.$status } } elseif ($status -in $statusSplat) { $filteredDevices = $devices | where { $_.upgradeEligibility -eq $status } } elseif ($status -eq "all") { $filteredDevices = $devices } ## Exports filtered devices to CSV if ($PSBoundParameters.ContainsKey('ExportCSV')) { $filteredDevices | Export-Csv -LiteralPath $ExportCSV -NoTypeInformation } ## Gets filtered devices' AAD object ID and adds them to the group selected with -addToGroup if ($PSBoundParameters.ContainsKey('addToGroup')) { $filteredDevices = $filteredDevices | where { $_.azureAdDeviceId -ne "00000000-0000-0000-0000-000000000000" } $filteredDevices | Add-Member -MemberType NoteProperty -Name aadObjectId -Value $null $filteredDevices | Add-Member -MemberType NoteProperty -Name requestParamsBody -Value $null $requestParams = @{ Method = "GET" Uri = "/beta/groups/$groupId`?select=displayName" } $groupName = (Invoke-MgGraphRequest @requestParams).displayName Write-Host "Are you sure you want to modify the membership of `"$groupName`"`? (y/n)" -ForegroundColor Yellow $confirmation = Read-Host if ($confirmation -ne "y") { return Write-Host "Canceling group modification." -ForegroundColor Red } ## Get Current Group Members $requestParams.Uri = "https://graph.microsoft.com/beta/groups/$groupId/members?select=id,displayName" $getGroupMembers = Invoke-MgGraphRequest @requestParams $nextLink = $getGroupMembers.'@odata.nextLink' $groupMembers = @($getGroupMembers.value) ## Pagination for Current Group Members while ($nextLink) { $requestParams.Uri = $nextLink $getGroupMembers = Invoke-MgGraphRequest @requestParams $nextLink = $getGroupMembers.'@odata.nextLink' $groupMembers += @($getGroupMembers.value) } ## Get AAD Object ID of Filtered Devices foreach ($filteredDevice in $filteredDevices) { $requestParams.Uri = "https://graph.microsoft.com/beta/devices?filter=deviceId eq '$($filteredDevice.azureAdDeviceId)'&select=id" $getAadDevice = Invoke-MgGraphRequest @requestParams $filteredDevice.aadObjectId = $getAadDevice.value.id ## Check if Filtered Device is in Group and if not add it. if ($filteredDevice.aadObjectId -in $groupMembers.id) { Write-Host "$($filteredDevice.deviceName) already a member." } else { Write-Host "Adding $($filteredDevice.deviceName)." $Uri = "https://graph.microsoft.com/beta/groups/$groupId/members/`$ref" $body = @{ "@odata.id" = "https://graph.microsoft.com/beta/directoryObjects/$($filteredDevice.aadObjectId)" } Invoke-MgGraphRequest -Method 'POST' -Uri $Uri -Headers $requestParams.Headers -ContentType "application/json" -Body ($body | ConvertTo-Json) } } ## If Group Member isn't in Filtered Devices, remove from group foreach ($groupMember in $groupMembers) { if ($groupMember.id -notin $filteredDevices.aadObjectId) { Write-Host "Removing $($groupMember.displayName) (`"id`":`"$($groupMember.id)`")" $Uri = "https://graph.microsoft.com/beta/groups/$groupId/members/$($groupMember.id)/`$ref" Invoke-MgGraphRequest -Method 'DELETE' -Uri $Uri -Headers $requestParams.Headers > $null } } } return $filteredDevices } Export-ModuleMember -Function Get-Win11ReadinessStatus |