Core/IntunePolicyAssignment.ps1
|
function Get-GroupInfo { param([Parameter(Mandatory)][string]$GroupId) if ($script:AllGroups.Count -gt 0) { $group = $script:AllGroups[$GroupId] if ($group) { return @{ Id = $group.id; DisplayName = $group.displayName; Success = $true } } } try { $group = Invoke-RKCoreGraphRequest -Uri "v1.0/groups/$GroupId" return @{ Id = $group.id; DisplayName = $group.displayName; Success = $true } } catch { return @{ Id = $GroupId; DisplayName = "Unknown Group"; Success = $false } } } function Get-DetailedPolicyAssignments { param([Parameter(Mandatory)][string]$EntityType, [string]$EntityId, [string]$PolicyName, [switch]$DebugMode, [switch]$ThrowOnError) $assignmentsUri = $null if ($EntityType -eq "deviceAppManagement/managedAppPolicies") { try { $r = Invoke-RKCoreGraphRequest -Uri "beta/deviceAppManagement/managedAppPolicies/$EntityId" $path = switch ($r.'@odata.type') { "#microsoft.graph.androidManagedAppProtection" { "androidManagedAppProtections" } "#microsoft.graph.iosManagedAppProtection" { "iosManagedAppProtections" } "#microsoft.graph.windowsManagedAppProtection" { "windowsManagedAppProtections" } default { $null } } if ($path) { $assignmentsUri = "beta/deviceAppManagement/$path('$EntityId')/assignments" } } catch { if ($ThrowOnError -and (Get-RKCoreGraphErrorStatus -ErrorRecord $_) -ne 404) { throw } return @() } } elseif ($EntityType -eq "mobileAppConfigurations") { $assignmentsUri = "beta/deviceAppManagement/mobileAppConfigurations('$EntityId')/assignments" } elseif ($EntityType -eq "deviceAppManagement/mobileApps") { $assignmentsUri = "beta/deviceAppManagement/mobileApps('$EntityId')/assignments" } elseif ($EntityType -like "deviceAppManagement/*ManagedAppProtections") { $assignmentsUri = "beta/$EntityType('$EntityId')/assignments" } elseif ($EntityType -eq "deviceManagement/intents") { $assignmentsUri = "beta/deviceManagement/intents/$EntityId/assignments" } elseif ($EntityType -eq "deviceManagement/templates") { $assignmentsUri = "beta/deviceManagement/templates/$EntityId/assignments" } elseif ($EntityType -like "virtualEndpoint/*") { $assignmentsUri = "beta/deviceManagement/$EntityType/$EntityId/assignments" } elseif ($EntityType -eq "configurationPolicies") { $assignmentsUri = "beta/deviceManagement/configurationPolicies/$EntityId/assignments" } else { $assignmentsUri = "beta/deviceManagement/$EntityType('$EntityId')/assignments" } if (-not $assignmentsUri) { return @() } try { Write-Verbose " Querying assignments for: $PolicyName ($EntityType, $EntityId)" Write-Verbose " Assignments URI: $assignmentsUri" $assignments = @(Invoke-RKCoreGraphRequest -Uri $assignmentsUri -All) Write-Verbose " Assignments result: $($assignments.Count) assignments returned" $detailedAssignments = @() foreach ($assignment in $assignments) { $targetType = $null $targetName = $null $targetId = $null $groupId = $null if ($assignment.target) { $targetType = $assignment.target.'@odata.type' $groupId = $assignment.target.groupId $targetId = $assignment.target.deviceAndAppManagementAssignmentFilterId $targetFilterType = $assignment.target.deviceAndAppManagementAssignmentFilterType if ($targetType -eq '#microsoft.graph.allLicensedUsersAssignmentTarget') { $targetName = "All Users" } elseif ($targetType -eq '#microsoft.graph.allDevicesAssignmentTarget') { $targetName = "All Devices" } elseif ($targetType -eq '#microsoft.graph.groupAssignmentTarget' -and $groupId) { $groupInfo = Get-GroupInfo -GroupId $groupId $targetName = $groupInfo.DisplayName } elseif ($targetType -eq '#microsoft.graph.exclusionGroupAssignmentTarget' -and $groupId) { $groupInfo = Get-GroupInfo -GroupId $groupId $targetName = $groupInfo.DisplayName } } $assignmentIntent = "Apply" if ($assignment.intent) { switch ($assignment.intent) { "required" { $assignmentIntent = "Required" } "available" { $assignmentIntent = "Available" } "uninstall" { $assignmentIntent = "Uninstall" } "availableWithoutEnrollment" { $assignmentIntent = "Available (No Enrollment)" } default { $assignmentIntent = $assignment.intent } } } # Filter enrichment $filterId = $null $filterName = "No Filter" $filterType = "None" $filterRule = $null $filterPlatform = $null if ($targetId) { $filter = $script:AllFilters[$targetId] if ($filter) { $filterId = $filter.id $filterName = $filter.displayName # Use filterType from assignment target, not from filter object if ($targetFilterType) { $filterType = $targetFilterType } else { $filterType = $filter.assignmentFilterManagementType } $filterRule = $filter.rule $filterPlatform = $filter.platform } } # Convert raw OData type to friendly AssignmentType for categorization $friendlyAssignmentType = if ($script:ODataToFriendly.ContainsKey($targetType)) { $script:ODataToFriendly[$targetType] } else { $targetType } $detailedAssignments += [PSCustomObject]@{ PolicyName = $PolicyName PolicyId = $EntityId PolicyType = $EntityType AssignmentType = $friendlyAssignmentType AssignmentIntent = $assignmentIntent TargetName = $targetName TargetId = $targetId GroupId = $groupId FilterId = $filterId FilterName = $filterName FilterType = $filterType FilterRule = $filterRule FilterPlatform = $filterPlatform AssignmentId = $assignment.id } } return $detailedAssignments } catch { # The anomaly core passes -ThrowOnError: an unread assignment would make devices look unprotected. # A 404 means the policy was deleted while we read it, which is the same as no assignments. if ($ThrowOnError -and (Get-RKCoreGraphErrorStatus -ErrorRecord $_) -ne 404) { throw } return @() } } function Test-IntuneFilter { [CmdletBinding()] param([Parameter(Mandatory)][string]$FilterRule, [Parameter(Mandatory)][PSCustomObject]$DeviceProperties) $propertyMapping = @{ 'cpuArchitecture' = 'ProcessorArchitecture'; 'deviceCategory' = 'DeviceCategoryDisplayName'; 'deviceName' = 'DeviceName'; 'deviceOwnership' = 'OwnerType'; 'deviceTrustType' = 'JoinType' 'enrollmentProfileName' = 'EnrollmentProfileName'; 'isRooted' = 'JailBroken'; 'manufacturer' = 'Manufacturer'; 'model' = 'Model'; 'operatingSystemVersion' = 'OSVersion'; 'osVersion' = 'OSVersion' 'deviceType' = 'DeviceType'; 'operatingSystem' = 'OperatingSystem'; 'complianceState' = 'ComplianceState'; 'managementAgent' = 'ManagementAgent'; 'autopilotEnrolled' = 'AutopilotEnrolled' 'deviceEnrollmentType' = 'DeviceEnrollmentType'; 'chassisType' = 'ChassisType'; 'managementState' = 'ManagementState'; 'deviceRegistrationState' = 'DeviceRegistrationState' 'azureAdRegistered' = 'AzureAdRegistered'; 'isEncrypted' = 'IsEncrypted'; 'isSupervised' = 'IsSupervised'; 'jailBroken' = 'JailBroken'; 'serialNumber' = 'SerialNumber'; 'processorArchitecture' = 'ProcessorArchitecture' } function Test-Condition { param([string]$PropertyPath, [string]$Operator, [string]$Value) $property = $PropertyPath.Trim(); if ($property.StartsWith("device.")) { $property = $property.Substring(7) } $actualPropertyName = if ($propertyMapping.ContainsKey($property.ToLower())) { $propertyMapping[$property.ToLower()] } else { $property } $actualValue = $null; if ($DeviceProperties.PSObject.Properties.Name -contains $actualPropertyName) { $actualValue = $DeviceProperties.$actualPropertyName } if ($null -eq $actualValue) { return $false } $actualValueStr = $actualValue.ToString(); $targetValueStr = $Value.ToString() switch -Regex ($Operator.Trim()) { "^-?eq$" { return $actualValueStr -eq $targetValueStr } "^-?ne$" { return $actualValueStr -ne $targetValueStr } "^-?contains$" { return $actualValueStr.Contains($targetValueStr) } "^-?notContains$" { return -not $actualValueStr.Contains($targetValueStr) } "^-?startsWith$" { return $actualValueStr.StartsWith($targetValueStr) } "^-?notStartsWith$" { return -not $actualValueStr.StartsWith($targetValueStr) } "^-?endsWith$" { return $actualValueStr.EndsWith($targetValueStr) } "^-?notEndsWith$" { return -not $actualValueStr.EndsWith($targetValueStr) } "^-?match$" { return $actualValueStr -match $targetValueStr } "^-?notMatch$" { return $actualValueStr -notmatch $targetValueStr } "^-?in$" { $cleanValue = $targetValueStr -replace '^\[|\]$', ''; $valueArray = $cleanValue -split ',' | ForEach-Object { $_.Trim().Trim('"').Trim("'") }; return $valueArray -contains $actualValueStr } "^-?notIn$" { $cleanValue = $targetValueStr -replace '^\[|\]$', ''; $valueArray = $cleanValue -split ',' | ForEach-Object { $_.Trim().Trim('"').Trim("'") }; return $valueArray -notcontains $actualValueStr } "^-?gt$" { try { return [System.Version]$actualValueStr -gt [System.Version]$targetValueStr } catch { return $actualValueStr -gt $targetValueStr } } "^-?ge$" { try { return [System.Version]$actualValueStr -ge [System.Version]$targetValueStr } catch { return $actualValueStr -ge $targetValueStr } } "^-?lt$" { try { return [System.Version]$actualValueStr -lt [System.Version]$targetValueStr } catch { return $actualValueStr -lt $targetValueStr } } "^-?le$" { try { return [System.Version]$actualValueStr -le [System.Version]$targetValueStr } catch { return $actualValueStr -le $targetValueStr } } default { return $false } } } $filterRule = $FilterRule -replace '\s+', ' ' $orConditions = @(); $sections = $filterRule -split '\s+or\s+', 0, 'IgnoreCase' foreach ($section in $sections) { $andConditions = @(); $andSections = $section -split '\s+and\s+', 0, 'IgnoreCase' foreach ($andSection in $andSections) { $cleanSection = $andSection.Trim() if ($cleanSection.StartsWith("(") -and $cleanSection.EndsWith(")")) { $cleanSection = $cleanSection.Substring(1, $cleanSection.Length - 2).Trim() } if ($cleanSection -match '^(.+?)\s+(-?\w+)\s+(.+)$') { $propertyPath = $matches[1].Trim(); if ($propertyPath.StartsWith("device.")) { $propertyPath = $propertyPath.Substring(7) } $operator = $matches[2].Trim(); $value = $matches[3].Trim() $cleanValue = $value if (-not ($value.StartsWith('[') -and $value.EndsWith(']'))) { $cleanValue = $value.Trim().Trim('"').Trim("'") } $andConditions += @{ PropertyPath = $propertyPath; Operator = $operator; Value = $cleanValue } } } $orConditions += @{ AndConditions = $andConditions } } foreach ($orCondition in $orConditions) { $allTrue = $true foreach ($andCondition in $orCondition.AndConditions) { if (-not (Test-Condition -PropertyPath $andCondition.PropertyPath -Operator $andCondition.Operator -Value $andCondition.Value)) { $allTrue = $false; break } } if ($allTrue) { return $true } } return $false } |