Private/Get-IntuneAccessApplicationEvidence.ps1
|
function Get-IntuneAccessApplicationEvidence { [CmdletBinding()] param( [Parameter(Mandatory)] [AllowEmptyCollection()] [object[]] $Workload, [Parameter(Mandatory)] [AllowEmptyCollection()] [object[]] $Assignment, [Parameter(Mandatory)] [AllowEmptyCollection()] [object[]] $ManagedDevice, [AllowEmptyCollection()] [object[]] $DeploymentOutcome = @(), [ValidateRange(1, 5000)] [int] $MaximumRelationshipQueries = 500 ) $null = Assert-IntuneAccessConnection -RequiredScope @('DeviceManagementApps.Read.All', 'DeviceManagementManagedDevices.Read.All') $warnings = [System.Collections.Generic.List[string]]::new() $statuses = [System.Collections.Generic.List[object]]::new() $detectedApps = @() try { $detectedApps = @(Invoke-IntuneAccessGraphRequest -Uri 'deviceManagement/detectedApps?$select=id,displayName,version,sizeInByte,deviceCount,publisher,platform' -ApiVersion v1.0) $statuses.Add([PSCustomObject] @{ Source = 'Detected applications'; State = 'Available'; RecordCount = $detectedApps.Count; ApiVersion = 'v1.0'; Reason = '' }) } catch { $warnings.Add("Detected applications could not be collected. $($_.Exception.Message)") $statuses.Add([PSCustomObject] @{ Source = 'Detected applications'; State = 'Unavailable'; RecordCount = 0; ApiVersion = 'v1.0'; Reason = $_.Exception.Message }) } $detectedRecords = [System.Collections.Generic.List[object]]::new() $queryCandidates = @($detectedApps | Where-Object { [int64] (Get-IntuneAccessProperty $_ 'deviceCount' 0) -gt 0 }) $queryCount = 0 foreach ($detected in $queryCandidates) { $deviceLinks = @() $state = 'Available' if ($queryCount -ge $MaximumRelationshipQueries) { $state = 'NotCollected' } else { try { $queryCount++ $deviceLinks = @(Invoke-IntuneAccessGraphRequest -Uri "deviceManagement/detectedApps/$([uri]::EscapeDataString([string] (Get-IntuneAccessProperty $detected 'id')))/managedDevices?`$select=id,deviceName,lastSyncDateTime" -ApiVersion v1.0) } catch { $state = 'Unavailable' $warnings.Add("Device relationships for detected application '$([string] (Get-IntuneAccessProperty $detected 'displayName'))' could not be collected. $($_.Exception.Message)") } } $detectedRecords.Add([PSCustomObject] @{ PSTypeName = 'IntuneAccess.DetectedApplication' Id = [string] (Get-IntuneAccessProperty $detected 'id') DisplayName = [string] (Get-IntuneAccessProperty $detected 'displayName') Publisher = [string] (Get-IntuneAccessProperty $detected 'publisher') Version = [string] (Get-IntuneAccessProperty $detected 'version') Platform = [string] (Get-IntuneAccessProperty $detected 'platform') SizeInBytes = Get-IntuneAccessProperty $detected 'sizeInByte' ReportedDeviceCount = Get-IntuneAccessProperty $detected 'deviceCount' 0 ManagedDevices = @($deviceLinks | ForEach-Object { [PSCustomObject] @{ Id = [string] (Get-IntuneAccessProperty $_ 'id'); DeviceName = [string] (Get-IntuneAccessProperty $_ 'deviceName'); LastSyncDateTime = Get-IntuneAccessProperty $_ 'lastSyncDateTime' } }) RelationshipState = $state SourceApiVersion = 'v1.0' }) } if ($queryCandidates.Count -gt $MaximumRelationshipQueries) { $warnings.Add("Detected-application device relationships were limited to $MaximumRelationshipQueries queries. Remaining records are labelled NotCollected.") } $appDefinitions = @() try { $rawApps = @(Invoke-IntuneAccessGraphRequest -Uri 'deviceAppManagement/mobileApps' -ApiVersion beta) $appDefinitions = @($rawApps | ForEach-Object { $app = $_ $appId = [string] (Get-IntuneAccessProperty $app 'id') $relationships = @() $relationshipState = 'Available' try { $relationships = @(Invoke-IntuneAccessGraphRequest -Uri "deviceAppManagement/mobileApps/$([uri]::EscapeDataString($appId))/relationships" -ApiVersion beta) } catch { $relationshipState = 'Unavailable' } [PSCustomObject] @{ PSTypeName = 'IntuneAccess.ApplicationDefinitionEvidence' Id = $appId DisplayName = [string] (Get-IntuneAccessProperty $app 'displayName') Publisher = [string] (Get-IntuneAccessProperty $app 'publisher') ODataType = [string] (Get-IntuneAccessProperty $app '@odata.type') InstallCommandLine = [string] (Get-IntuneAccessProperty $app 'installCommandLine') UninstallCommandLine = [string] (Get-IntuneAccessProperty $app 'uninstallCommandLine') DetectionRules = @(Get-IntuneAccessProperty $app 'detectionRules' @()) RequirementRules = @(Get-IntuneAccessProperty $app 'requirementRules' @()) MinimumSupportedOperatingSystem = Get-IntuneAccessProperty $app 'minimumSupportedOperatingSystem' Relationships = @($relationships | ForEach-Object { [PSCustomObject] @{ Id = [string] (Get-IntuneAccessProperty $_ 'id'); TargetId = [string] (Get-IntuneAccessProperty $_ 'targetId'); RelationshipType = [string] (Get-IntuneAccessProperty $_ '@odata.type') } }) RelationshipState = $relationshipState SourceApiVersion = 'beta' } }) $statuses.Add([PSCustomObject] @{ Source = 'Application definitions'; State = 'Available'; RecordCount = $appDefinitions.Count; ApiVersion = 'beta'; Reason = '' }) } catch { $warnings.Add("Application definitions could not be enriched. $($_.Exception.Message)") $statuses.Add([PSCustomObject] @{ Source = 'Application definitions'; State = 'Unavailable'; RecordCount = 0; ApiVersion = 'beta'; Reason = $_.Exception.Message }) } $applicationWorkloads = @($Workload | Where-Object WorkloadType -EQ 'Applications') $deviceEvidence = [System.Collections.Generic.List[object]]::new() foreach ($app in $applicationWorkloads) { $appAssignments = @($Assignment | Where-Object WorkloadId -EQ $app.Id) $appOutcomes = @($DeploymentOutcome | Where-Object WorkloadId -EQ $app.Id) $detectedByName = @($detectedRecords | Where-Object DisplayName -EQ $app.Name) $definition = $appDefinitions | Where-Object Id -EQ $app.Id | Select-Object -First 1 $detectedDeviceIds = @($detectedByName | ForEach-Object { @($_.ManagedDevices) | ForEach-Object { [string] $_.Id } }) $deviceIds = @(@($appOutcomes | ForEach-Object { [string] $_.DeviceId }) + $detectedDeviceIds | Where-Object { $_ } | Select-Object -Unique) foreach ($deviceId in $deviceIds) { $device = $ManagedDevice | Where-Object Id -EQ $deviceId | Select-Object -First 1 $outcomes = @($appOutcomes | Where-Object DeviceId -EQ $deviceId) $detections = @($detectedByName | Where-Object { $candidateIds = @($_.ManagedDevices | ForEach-Object { [string] $_.Id }); $deviceId -in $candidateIds }) $deviceEvidence.Add([PSCustomObject] @{ PSTypeName = 'IntuneAccess.DeviceApplicationEvidence' ApplicationId = [string] $app.Id ApplicationName = [string] $app.Name DeviceId = [string] $deviceId DeviceName = if ($null -eq $device) { [string] (Get-IntuneAccessProperty ($outcomes | Select-Object -First 1) 'DeviceName') } else { [string] (Get-IntuneAccessProperty $device 'DeviceName') } ConfiguredIntents = @($appAssignments.Intent | Select-Object -Unique) AssignmentEvidence = $appAssignments InstallResults = $outcomes DetectedSoftware = $detections DetectionState = if ($detections.Count -gt 0) { 'DetectedByName' } elseif ($detectedRecords.Count -eq 0) { 'NotEvaluated' } else { 'NotDetectedByExactName' } Requirements = if ($null -eq $definition) { @() } else { @($definition.RequirementRules) } DetectionRules = if ($null -eq $definition) { @() } else { @($definition.DetectionRules) } Relationships = if ($null -eq $definition) { @() } else { @($definition.Relationships) } EvidenceBoundary = 'Assignment intent, reported installation state and detected software are separate. Exact-name detection is not proof that an Intune detection rule passed. Client logs can be required for root-cause analysis.' }) } } [PSCustomObject] @{ PSTypeName = 'IntuneAccess.ApplicationEvidence' ApplicationDefinitions = $appDefinitions DetectedApplications = $detectedRecords.ToArray() DeviceApplicationEvidence = $deviceEvidence.ToArray() CollectionStatus = $statuses.ToArray() Warnings = $warnings.ToArray() GraphPermissionsUsed = @('DeviceManagementApps.Read.All', 'DeviceManagementManagedDevices.Read.All') GeneratedAt = [DateTimeOffset]::Now ToolVersion = $script:IntuneAccessVersion } } |