Private/Get-IntuneAccessOperationalEvidence.ps1
|
function Get-IntuneAccessOperationalEvidence { [CmdletBinding()] param( [Parameter(Mandatory)] [AllowEmptyCollection()] [object[]] $Workload ) $context = Assert-IntuneAccessConnection $warnings = [System.Collections.Generic.List[string]]::new() $collectionStatus = [System.Collections.Generic.List[object]]::new() $outcomes = [System.Collections.Generic.List[object]]::new() if ('DeviceManagementManagedDevices.Read.All' -notin @($context.Scopes)) { throw 'Operational evidence requires DeviceManagementManagedDevices.Read.All. Reconnect with the OperationalEvidence feature.' } # managedDevice uses managedDeviceOwnerType and deviceRegistrationState in the # documented v1.0 contract. Older internal fixtures used ownerType and # managementState, so the normalisation below keeps those names as fallbacks. $rawDevices = @(Invoke-IntuneAccessGraphRequest -Uri 'deviceManagement/managedDevices?$select=id,deviceName,userId,userPrincipalName,userDisplayName,azureADDeviceId,operatingSystem,osVersion,complianceState,managementAgent,deviceEnrollmentType,lastSyncDateTime,model,manufacturer,serialNumber,managedDeviceOwnerType,deviceRegistrationState,enrolledDateTime' -ApiVersion v1.0) $devices = @($rawDevices | ForEach-Object { [PSCustomObject] @{ PSTypeName = 'IntuneAccess.ManagedDevice' Id = [string] (Get-IntuneAccessProperty $_ 'id') DeviceName = [string] (Get-IntuneAccessProperty $_ 'deviceName') UserId = [string] (Get-IntuneAccessProperty $_ 'userId') UserPrincipalName = [string] (Get-IntuneAccessProperty $_ 'userPrincipalName') UserDisplayName = [string] (Get-IntuneAccessProperty $_ 'userDisplayName') EntraDeviceId = [string] (Get-IntuneAccessProperty $_ 'azureADDeviceId') OperatingSystem = [string] (Get-IntuneAccessProperty $_ 'operatingSystem') OsVersion = [string] (Get-IntuneAccessProperty $_ 'osVersion') ComplianceState = [string] (Get-IntuneAccessProperty $_ 'complianceState') ManagementAgent = [string] (Get-IntuneAccessProperty $_ 'managementAgent') EnrollmentType = [string] (Get-IntuneAccessProperty $_ 'deviceEnrollmentType') LastSyncDateTime = Get-IntuneAccessProperty $_ 'lastSyncDateTime' EnrolledDateTime = Get-IntuneAccessProperty $_ 'enrolledDateTime' Manufacturer = [string] (Get-IntuneAccessProperty $_ 'manufacturer') Model = [string] (Get-IntuneAccessProperty $_ 'model') SerialNumber = [string] (Get-IntuneAccessProperty $_ 'serialNumber') Ownership = [string] (Get-IntuneAccessProperty $_ 'managedDeviceOwnerType' (Get-IntuneAccessProperty $_ 'ownerType' '')) ManagementState = [string] (Get-IntuneAccessProperty $_ 'deviceRegistrationState' (Get-IntuneAccessProperty $_ 'managementState' '')) SourceApiVersion = 'v1.0' } }) $users = @($devices | Where-Object { -not [string]::IsNullOrWhiteSpace($_.UserPrincipalName) } | Group-Object { $_.UserPrincipalName.ToLowerInvariant() } | ForEach-Object { $first = $_.Group[0] [PSCustomObject] @{ PSTypeName = 'IntuneAccess.ManagedUser' Id = [string] $first.UserId DisplayName = [string] $first.UserDisplayName UserPrincipalName = [string] $first.UserPrincipalName ManagedDeviceIds = @($_.Group.Id) ManagedDeviceCount = $_.Count } }) foreach ($workloadObject in $Workload) { $statusEndpoint = '' $apiVersion = 'v1.0' $reason = '' switch ([string] $workloadObject.SourceCollection) { 'Device configuration' { $statusEndpoint = "$($workloadObject.SourceEndpoint)/$([uri]::EscapeDataString([string] $workloadObject.Id))/deviceStatuses" } 'Compliance policies' { $statusEndpoint = "$($workloadObject.SourceEndpoint)/$([uri]::EscapeDataString([string] $workloadObject.Id))/deviceStatuses" } 'Applications' { $statusEndpoint = "$($workloadObject.SourceEndpoint)/$([uri]::EscapeDataString([string] $workloadObject.Id))/deviceStatuses"; $apiVersion = 'beta' } 'PowerShell scripts' { $statusEndpoint = "$($workloadObject.SourceEndpoint)/$([uri]::EscapeDataString([string] $workloadObject.Id))/deviceRunStates?`$expand=managedDevice(`$select=id,deviceName,userPrincipalName)"; $apiVersion = 'beta' } 'Remediations' { $statusEndpoint = "$($workloadObject.SourceEndpoint)/$([uri]::EscapeDataString([string] $workloadObject.Id))/deviceRunStates"; $apiVersion = 'beta' } default { $reason = 'No documented GET outcome contract is adopted for this workload family.' } } if ([string]::IsNullOrWhiteSpace($statusEndpoint)) { $collectionStatus.Add([PSCustomObject] @{ WorkloadId = $workloadObject.Id; WorkloadName = $workloadObject.Name; State = 'NotSupported'; RecordCount = 0; ApiVersion = ''; Reason = $reason }) continue } try { $rawOutcomes = @(Invoke-IntuneAccessGraphRequest -Uri $statusEndpoint -ApiVersion $apiVersion) foreach ($rawOutcome in $rawOutcomes) { $outcomes.Add((ConvertTo-IntuneAccessDeploymentOutcome -InputObject $rawOutcome -Workload $workloadObject -ApiVersion $apiVersion -ManagedDevice $devices)) } $collectionStatus.Add([PSCustomObject] @{ WorkloadId = $workloadObject.Id; WorkloadName = $workloadObject.Name; State = 'Available'; RecordCount = $rawOutcomes.Count; ApiVersion = $apiVersion; Reason = '' }) } catch { $reason = $_.Exception.Message $collectionStatus.Add([PSCustomObject] @{ WorkloadId = $workloadObject.Id; WorkloadName = $workloadObject.Name; State = 'Unavailable'; RecordCount = 0; ApiVersion = $apiVersion; Reason = $reason }) $warnings.Add("Deployment outcomes for '$($workloadObject.Name)' could not be collected. $reason") } } foreach ($user in $users) { $userOutcomes = @($outcomes | Where-Object UserPrincipalName -EQ $user.UserPrincipalName) $user | Add-Member -NotePropertyName OutcomeCount -NotePropertyValue $userOutcomes.Count -Force $user | Add-Member -NotePropertyName ErrorCount -NotePropertyValue @($userOutcomes | Where-Object Category -EQ 'Error').Count -Force } [PSCustomObject] @{ PSTypeName = 'IntuneAccess.OperationalEvidence' ManagedDevices = $devices ManagedUsers = $users DeploymentOutcomes = $outcomes.ToArray() CollectionStatus = $collectionStatus.ToArray() Warnings = $warnings.ToArray() GraphPermissionsUsed = @('DeviceManagementManagedDevices.Read.All', 'DeviceManagementConfiguration.Read.All', 'DeviceManagementApps.Read.All', 'DeviceManagementScripts.Read.All') GeneratedAt = [DateTimeOffset]::Now ToolVersion = $script:IntuneAccessVersion } } |