Private/Resolve-IACEffectiveAssignment.ps1
|
function Resolve-IACEffectiveAssignment { [CmdletBinding()] param( [Parameter(Mandatory)]$Category, [Parameter(Mandatory)]$Entity, [AllowEmptyCollection()][object[]]$Assignments = @(), [Parameter(Mandatory)][hashtable]$MembershipSources, [switch]$HasUser, [switch]$HasDevice, [bool]$UserMembershipKnown = $true, [bool]$DeviceMembershipKnown = $true, [AllowNull()]$ManagedDevice, [Parameter(Mandatory)][string]$SubjectType, [AllowEmptyString()][string]$SubjectId, [Parameter(Mandatory)][string]$SubjectName ) $reasonChain = [System.Collections.Generic.List[object]]::new() $candidates = [System.Collections.Generic.List[object]]::new() $sequence = 0 foreach ($assignment in @($Assignments)) { $sequence++ $targetResult = 'Unknown' $membershipSourceNames = @() $targetDimensions = [System.Collections.Generic.List[string]]::new() $targetCode = 'Target.Unsupported' $targetMessage = "Unsupported assignment target type '$($assignment.TargetType)'." switch ($assignment.TargetType) { 'AllUsers' { $targetResult = if ($HasUser) { 'Match' } else { 'NotMatch' } if ($HasUser) { [void]$targetDimensions.Add('User') } $targetCode = 'Target.AllUsers' $targetMessage = if ($HasUser) { 'A user subject is present.' } else { 'No user subject was supplied.' } } 'AllDevices' { $targetResult = if ($HasDevice) { 'Match' } else { 'NotMatch' } if ($HasDevice) { [void]$targetDimensions.Add('Device') } $targetCode = 'Target.AllDevices' $targetMessage = if ($HasDevice) { 'A managed-device subject is present.' } else { 'No device subject was supplied.' } } 'Group' { if ($assignment.TargetId -and $MembershipSources.ContainsKey("$($assignment.TargetId)")) { $targetResult = 'Match' $membershipSourceNames = @($MembershipSources["$($assignment.TargetId)"].Sources) foreach ($source in $membershipSourceNames) { if ($source -in @('User', 'Device') -and -not $targetDimensions.Contains($source)) { [void]$targetDimensions.Add($source) } } $targetCode = 'Target.TransitiveGroupMembership' $targetMessage = "The subject is a transitive member through: $($membershipSourceNames -join ', ')." } elseif (($HasUser -and -not $UserMembershipKnown) -or ($HasDevice -and -not $DeviceMembershipKnown)) { $targetResult = 'Unknown' if ($HasUser -and -not $UserMembershipKnown) { [void]$targetDimensions.Add('User') } if ($HasDevice -and -not $DeviceMembershipKnown) { [void]$targetDimensions.Add('Device') } $targetCode = 'Target.GroupMembershipUnknown' $targetMessage = 'At least one supplied subject has an incomplete transitive group-membership result.' } else { $targetResult = 'NotMatch' $targetCode = 'Target.GroupNotMember' $targetMessage = 'Neither supplied subject is a transitive member of the target group.' } } } $filterResult = 'NotEvaluated' $filterCode = 'Filter.NotEvaluated' $filterReason = 'The target did not match, so its assignment filter was not evaluated.' if ($targetResult -eq 'Match') { $filterId = "$($assignment.FilterId)" $filterMode = "$($assignment.FilterType)" $hasFilterId = -not [string]::IsNullOrWhiteSpace($filterId) -and $filterId -ne '00000000-0000-0000-0000-000000000000' if (-not $hasFilterId -or $filterMode -ieq 'none') { $filterResult = 'Match' $filterCode = 'Filter.None' $filterReason = 'No assignment filter is applied.' } elseif ([string]::IsNullOrWhiteSpace($filterMode) -or $filterMode -notin @('include', 'exclude')) { $filterResult = 'Unknown' $filterCode = 'Filter.UnsupportedMode' $filterReason = "Unsupported assignment filter mode '$filterMode'." } elseif ($null -eq $ManagedDevice) { $filterResult = 'Unknown' $filterCode = 'Filter.NoDevice' $filterReason = 'A managed device is required to evaluate this assignment filter.' } elseif (-not $script:AssignmentFilterLookup -or -not $script:AssignmentFilterLookup.ContainsKey($filterId)) { $filterResult = 'Unknown' $filterCode = 'Filter.NotInCache' $filterReason = "Assignment filter '$filterId' is not present in the tenant filter cache." } else { $filterEvaluation = Test-IACAssignmentFilter ` -Filter $script:AssignmentFilterLookup[$filterId] ` -Device $ManagedDevice -FilterMode $filterMode $filterResult = $filterEvaluation.Result $filterCode = "Filter.Evaluated.$filterResult" $filterReason = $filterEvaluation.Reason } } elseif ($targetResult -eq 'Unknown') { $filterResult = 'Unknown' $filterCode = 'Filter.TargetUnknown' $filterReason = 'Filter evaluation is blocked because target membership is unknown.' } $candidateOutcome = if ($targetResult -eq 'Unknown' -or $filterResult -eq 'Unknown') { 'Unknown' } elseif ($targetResult -ne 'Match' -or $filterResult -ne 'Match') { 'Inactive' } elseif ($assignment.AssignmentMode -eq 'Exclude') { 'Excluded' } elseif ($assignment.AssignmentMode -eq 'Include') { 'Included' } else { 'Unknown' } [void]$reasonChain.Add([PSCustomObject][ordered]@{ Sequence = $sequence Code = $targetCode FilterCode = $filterCode Outcome = $candidateOutcome AssignmentId = $assignment.AssignmentId AssignmentMode = $assignment.AssignmentMode TargetType = $assignment.TargetType TargetId = $assignment.TargetId MembershipSources = @($membershipSourceNames) TargetResult = $targetResult FilterId = $assignment.FilterId FilterMode = $assignment.FilterType FilterResult = $filterResult Message = "$targetMessage $filterReason" }) [void]$candidates.Add([PSCustomObject]@{ Assignment = $assignment Outcome = $candidateOutcome Dimensions = @($targetDimensions) }) } $activeExclusions = @($candidates | Where-Object Outcome -eq Excluded) $unknownExclusions = @($candidates | Where-Object { $_.Outcome -eq 'Unknown' -and $_.Assignment.AssignmentMode -eq 'Exclude' }) $activeInclusions = @($candidates | Where-Object Outcome -eq Included) $unknownInclusions = @($candidates | Where-Object { $_.Outcome -eq 'Unknown' -and $_.Assignment.AssignmentMode -eq 'Include' }) $unknownOther = @($candidates | Where-Object { $_.Outcome -eq 'Unknown' -and $_.Assignment.AssignmentMode -notin @('Include', 'Exclude') }) $compatibleExclusion = $null if ($activeInclusions.Count -gt 0 -and $activeExclusions.Count -gt 0) { foreach ($exclusion in $activeExclusions) { foreach ($inclusion in $activeInclusions) { if (@($exclusion.Dimensions | Where-Object { $_ -in $inclusion.Dimensions }).Count -gt 0) { $compatibleExclusion = $exclusion break } } if ($compatibleExclusion) { break } } } if ($activeInclusions.Count -gt 0) { if ($compatibleExclusion) { $effectiveState = 'Excluded'; $representative = $compatibleExclusion.Assignment $decisionCode = 'Decision.Excluded' $decisionMessage = 'A matching exclusion in the same user/device targeting dimension takes precedence over an inclusion.' } elseif ($activeExclusions.Count -gt 0) { $effectiveState = 'Unknown'; $representative = $activeExclusions[0].Assignment $decisionCode = 'Decision.CrossDimensionUnknown' $decisionMessage = 'Inclusion and exclusion match different user/device targeting dimensions; Intune behavior cannot be inferred safely.' } elseif ($unknownExclusions.Count -gt 0) { $effectiveState = 'Unknown'; $representative = $unknownExclusions[0].Assignment $decisionCode = 'Decision.UnresolvedExclusion' $decisionMessage = 'A possible exclusion prevents a definitive inclusion result.' } elseif ($unknownOther.Count -gt 0) { $effectiveState = 'Unknown'; $representative = $unknownOther[0].Assignment $decisionCode = 'Decision.UnknownAssignmentMode' $decisionMessage = 'An assignment uses an unsupported include/exclude mode.' } else { $effectiveState = 'Included'; $representative = $activeInclusions[0].Assignment $decisionCode = 'Decision.Included' $decisionMessage = 'At least one inclusion is active and no exclusion can override it.' } } elseif ($unknownInclusions.Count -gt 0) { $effectiveState = 'Unknown'; $representative = $unknownInclusions[0].Assignment $decisionCode = 'Decision.UnresolvedInclusion' $decisionMessage = 'A possible inclusion could not be evaluated definitively.' } elseif ($unknownOther.Count -gt 0) { $effectiveState = 'Unknown'; $representative = $unknownOther[0].Assignment $decisionCode = 'Decision.UnknownAssignmentMode' $decisionMessage = 'An assignment uses an unsupported include/exclude mode.' } else { $effectiveState = 'NotTargeted' $representative = if ($Assignments.Count -eq 0) { Get-IACNoAssignmentPlaceholder } else { Get-IACNoAssignmentPlaceholder -Reason 'No Matching Assignment' } $decisionCode = 'Decision.NotTargeted' $decisionMessage = if ($Assignments.Count -eq 0) { 'The policy has no assignments.' } else { 'No inclusion targets either supplied subject.' } } $sequence++ [void]$reasonChain.Add([PSCustomObject][ordered]@{ Sequence = $sequence Code = $decisionCode FilterCode = $null Outcome = $effectiveState AssignmentId = $representative.AssignmentId AssignmentMode = $representative.AssignmentMode TargetType = $representative.TargetType TargetId = $representative.TargetId MembershipSources = @() TargetResult = $null FilterId = $representative.FilterId FilterMode = $representative.FilterType FilterResult = $null Message = $decisionMessage }) $record = ConvertTo-IACAssignmentRecord -Category $Category -Entity $Entity -Assignment $representative ` -SubjectType $SubjectType -SubjectId $SubjectId -SubjectName $SubjectName -Source 'Get-IntuneEffectiveAssignment' if ($representative.TargetType -eq 'Group' -and $representative.TargetId -and $MembershipSources.ContainsKey("$($representative.TargetId)")) { $record.TargetName = $MembershipSources["$($representative.TargetId)"].Name } $record.EffectiveState = $effectiveState $record.ReasonChain = @($reasonChain) return $record } |