Public/Search-IntunePolicy.ps1
|
function Search-IntunePolicy { [CmdletBinding()] param( [Parameter()] [string]$PolicySearchTerm, [Parameter()] [switch]$ExportToCSV, [Parameter()] [string]$ExportPath ) Write-Host "Policy Search / Reverse Lookup selected" -ForegroundColor Green if ($PolicySearchTerm) { $searchTerm = $PolicySearchTerm } else { Write-Host "Enter policy name or partial name to search for: " -ForegroundColor Cyan $searchTerm = Read-Host } if ([string]::IsNullOrWhiteSpace($searchTerm)) { Write-Host "No search term provided. Please try again." -ForegroundColor Red return } Write-Host "Searching for policies matching '$searchTerm'..." -ForegroundColor Yellow $groupNameCache = @{} # Cached group display name lookup shared by all row emitters function Get-SearchTargetGroupName { param([string]$GroupId) if (-not $groupNameCache.ContainsKey($GroupId)) { $groupInfo = Get-GroupInfo -GroupId $GroupId $groupNameCache[$GroupId] = if ($groupInfo.Success) { $groupInfo.DisplayName } else { "Unknown Group" } } return $groupNameCache[$GroupId] } # Helper function to resolve assignment targets for a matched policy function Resolve-SearchAssignments { param ( [object[]]$Assignments, [string]$CategoryLabel, [string]$PolicyName, [string]$PolicyId, [System.Collections.Generic.List[object]]$Results ) if ($null -eq $Assignments -or $Assignments.Count -eq 0) { $Results.Add([PSCustomObject]@{ Category = $CategoryLabel PolicyName = $PolicyName PolicyId = $PolicyId AssignmentType = "None" TargetName = "No assignments" TargetGroupId = "" FilterName = "" FilterType = "" }) return } foreach ($assignment in $Assignments) { $assignmentType = "Include" $targetName = "" $targetGroupId = "" if ($assignment.Reason -eq "Group Assignment") { $targetGroupId = $assignment.GroupId $targetName = Get-SearchTargetGroupName -GroupId $targetGroupId } elseif ($assignment.Reason -eq "Group Exclusion") { $assignmentType = "Exclude" $targetGroupId = $assignment.GroupId $targetName = Get-SearchTargetGroupName -GroupId $targetGroupId } elseif ($assignment.Reason -eq "All Users") { $targetName = "All Users" } elseif ($assignment.Reason -eq "All Devices") { $targetName = "All Devices" } else { continue } $filterName = '' $filterType = '' if ($assignment.FilterId -and $assignment.FilterType -and $assignment.FilterType -ne 'none') { if ($script:AssignmentFilterLookup -and $script:AssignmentFilterLookup.ContainsKey($assignment.FilterId)) { $filterName = $script:AssignmentFilterLookup[$assignment.FilterId].Name } else { $filterName = "Unknown Filter ($($assignment.FilterId))" } $filterType = switch ($assignment.FilterType) { 'include' { 'Include' } 'exclude' { 'Exclude' } default { $assignment.FilterType } } } $Results.Add([PSCustomObject]@{ Category = $CategoryLabel PolicyName = $PolicyName PolicyId = $PolicyId AssignmentType = $assignmentType TargetName = $targetName TargetGroupId = $targetGroupId FilterName = $filterName FilterType = $filterType }) } } # Applications keep their historical row emission: raw target walk with the # install intent appended to include targets (never to exclusions). function Add-SearchAppRows { param( [object]$App, [object[]]$RawAssignments, [System.Collections.Generic.List[object]]$Results ) $appName = if (-not [string]::IsNullOrWhiteSpace($App.displayName)) { $App.displayName } else { $App.name } if ($null -eq $RawAssignments -or $RawAssignments.Count -eq 0) { $Results.Add([PSCustomObject]@{ Category = "Application" PolicyName = $appName PolicyId = $App.id AssignmentType = "None" TargetName = "No assignments" TargetGroupId = "" }) return } foreach ($assignment in $RawAssignments) { $assignmentType = "Include" $targetName = "" $targetGroupId = "" $intentLabel = if ($assignment.intent) { " ($($assignment.intent))" } else { "" } switch ($assignment.target.'@odata.type') { '#microsoft.graph.allLicensedUsersAssignmentTarget' { $targetName = "All Users$intentLabel" } '#microsoft.graph.allDevicesAssignmentTarget' { $targetName = "All Devices$intentLabel" } '#microsoft.graph.groupAssignmentTarget' { $targetGroupId = $assignment.target.groupId $targetName = "$(Get-SearchTargetGroupName -GroupId $targetGroupId)$intentLabel" } '#microsoft.graph.exclusionGroupAssignmentTarget' { $assignmentType = "Exclude" $targetGroupId = $assignment.target.groupId $targetName = Get-SearchTargetGroupName -GroupId $targetGroupId } default { continue } } $Results.Add([PSCustomObject]@{ Category = "Application" PolicyName = $appName PolicyId = $App.id AssignmentType = $assignmentType TargetName = $targetName TargetGroupId = $targetGroupId }) } } $categories = Get-IntuneCategoryDefinition -Audience 'Search' # Name matching happens BEFORE any per-entity assignment fetch: only entities # passing this pre-filter ever trigger an assignments request. $entityPreFilter = { param($entity, $category) $entity.displayName -like "*$searchTerm*" -or $entity.name -like "*$searchTerm*" } $processEntity = { param($ctx) $results = $ctx.Buckets['SearchResults'] if ($ctx.Category.Id -eq 'Applications') { Add-SearchAppRows -App $ctx.Entity -RawAssignments $ctx.RawAssignments -Results $results return } $policyName = if (-not [string]::IsNullOrWhiteSpace($ctx.Entity.displayName)) { $ctx.Entity.displayName } else { $ctx.Entity.name } Resolve-SearchAssignments -Assignments $ctx.Assignments -CategoryLabel $ctx.Category.ExportCategory -PolicyName $policyName -PolicyId $ctx.Entity.id -Results $results } $scanResult = Invoke-IntuneCategoryScan -Categories $categories -ProcessEntity $processEntity -EntityPreFilter $entityPreFilter -ShowProgress -ProgressVerb 'Searching' $allSearchResults = $scanResult.Buckets['SearchResults'] # --- Display Results --- $uniquePolicies = $allSearchResults | Select-Object -Property PolicyId -Unique $totalMatches = $uniquePolicies.Count if ($totalMatches -eq 0) { Write-Host "`nNo policies found matching '$searchTerm'." -ForegroundColor Yellow } else { Write-Host "" Write-Host (Get-Separator -Character "=") -ForegroundColor Cyan Write-Host " POLICY SEARCH RESULTS" -ForegroundColor Cyan Write-Host " Search term: '$searchTerm'" -ForegroundColor White Write-Host " Found $totalMatches matching $(if ($totalMatches -eq 1) { 'policy' } else { 'policies' })" -ForegroundColor White Write-Host (Get-Separator -Character "=") -ForegroundColor Cyan $groupedResults = $allSearchResults | Group-Object -Property PolicyId foreach ($policyGroup in $groupedResults) { $first = $policyGroup.Group[0] $policyName = $first.PolicyName if (-not $policyName) { $policyName = "Unnamed Policy" } Write-Host "`n===== $policyName =====" -ForegroundColor White Write-Host "Category: $($first.Category) | Policy ID: $($first.PolicyId)" -ForegroundColor Gray $separator = Get-Separator Write-Host $separator -ForegroundColor Gray Write-Host "Assignment Targets:" -ForegroundColor Yellow foreach ($result in $policyGroup.Group) { $filterDisplay = '' if ($result.FilterName) { $filterDisplay = " (Filter: $($result.FilterName) [$($result.FilterType)])" } if ($result.AssignmentType -eq "None") { Write-Host " No assignments" -ForegroundColor DarkGray } elseif ($result.AssignmentType -eq "Exclude") { $target = if ($result.TargetGroupId) { "Group: $($result.TargetName) (ID: $($result.TargetGroupId))" } else { $result.TargetName } Write-Host " [EXCLUDE] $target$filterDisplay" -ForegroundColor Red } else { $target = if ($result.TargetGroupId) { "Group: $($result.TargetName) (ID: $($result.TargetGroupId))" } else { $result.TargetName } Write-Host " [INCLUDE] $target$filterDisplay" -ForegroundColor Green } } Write-Host $separator -ForegroundColor Gray } # Summary $totalTargets = ($allSearchResults | Where-Object { $_.AssignmentType -ne "None" }).Count Write-Host "`n=== Search Summary ===" -ForegroundColor Cyan Write-Host " Found $totalMatches $(if ($totalMatches -eq 1) { 'policy' } else { 'policies' }) matching '$searchTerm'" -ForegroundColor White Write-Host " Total assignment targets: $totalTargets" -ForegroundColor White } # --- Export --- $exportData = [System.Collections.ArrayList]::new() $null = $exportData.Add([PSCustomObject]@{ Category = "Search Info" Item = "Search term: $searchTerm" ScopeTags = "" AssignmentReason = "Found $totalMatches policies" FilterName = "" FilterType = "" }) foreach ($result in $allSearchResults) { $filterLabel = if ($result.FilterName) { " (Filter: $($result.FilterName) [$($result.FilterType)])" } else { "" } $null = $exportData.Add([PSCustomObject]@{ Category = $result.Category Item = "$($result.PolicyName) (ID: $($result.PolicyId))" ScopeTags = "" AssignmentReason = "[$($result.AssignmentType)] $($result.TargetName)$(if ($result.TargetGroupId) { " (ID: $($result.TargetGroupId))" })$filterLabel" FilterName = $result.FilterName FilterType = $result.FilterType }) } Export-ResultsIfRequested -ExportData $exportData -DefaultFileName "IntunePolicySearch.csv" -ForceExport:$ExportToCSV -CustomExportPath $ExportPath -ExportToCSV:$ExportToCSV -ParameterMode:$parameterMode } |