-
-
Notifications
You must be signed in to change notification settings - Fork 57
Expand file tree
/
Copy pathFind-EmptyAssignmentGroups.ps1
More file actions
118 lines (108 loc) · 4.23 KB
/
Copy pathFind-EmptyAssignmentGroups.ps1
File metadata and controls
118 lines (108 loc) · 4.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<#
.SYNOPSIS
Find Entra ID groups used in Intune assignments that have zero members.
.DESCRIPTION
Empty assignment groups are a common source of "ghost" assignments that look
targeted but never reach a device. This script enumerates every group ID
referenced by an Intune assignment (configuration profiles, compliance
policies, scripts, apps, app config, autopilot profiles, filters) and
reports those whose membership count is zero.
.PARAMETER OutputPath
CSV output path. Default: .\empty-assignment-groups.csv
.EXAMPLE
.\Find-EmptyAssignmentGroups.ps1
.NOTES
Author : Jannik Reinhard
Version: 1.0
#>
#Requires -Modules Microsoft.Graph.Authentication
[CmdletBinding()]
Param(
[string]$OutputPath = ".\empty-assignment-groups.csv"
)
$assignmentSources = @(
'deviceManagement/deviceConfigurations',
'deviceManagement/configurationPolicies',
'deviceManagement/deviceCompliancePolicies',
'deviceManagement/deviceManagementScripts',
'deviceManagement/deviceShellScripts',
'deviceManagement/deviceHealthScripts',
'deviceManagement/windowsAutopilotDeploymentProfiles',
'deviceAppManagement/mobileApps',
'deviceAppManagement/managedAppPolicies',
'deviceAppManagement/mobileAppConfigurations'
)
function Connect-MgGraphIfNeeded {
if (-not (Get-MgContext)) {
Connect-MgGraph -Scopes @(
"DeviceManagementConfiguration.Read.All",
"DeviceManagementApps.Read.All",
"Group.Read.All"
) -NoWelcome
}
}
function Get-AllPages {
Param([Parameter(Mandatory)][string]$Uri)
$items = [System.Collections.Generic.List[object]]::new()
while ($Uri) {
$page = Invoke-MgGraphRequest -Method GET -Uri $Uri
foreach ($v in $page.value) { $items.Add($v) }
$Uri = $page.'@odata.nextLink'
}
return $items
}
try {
Connect-MgGraphIfNeeded
$groupIds = [System.Collections.Generic.HashSet[string]]::new()
$usage = @{}
foreach ($source in $assignmentSources) {
Write-Host "Collecting assignments from $source ..." -ForegroundColor Cyan
try {
$items = Get-AllPages -Uri "https://graph.microsoft.com/beta/$source?`$expand=assignments"
} catch {
Write-Warning "Skipping $source -> $_"
continue
}
foreach ($item in $items) {
foreach ($a in @($item.assignments)) {
$targetType = $a.target.'@odata.type'
if ($targetType -eq '#microsoft.graph.groupAssignmentTarget' -or
$targetType -eq '#microsoft.graph.exclusionGroupAssignmentTarget') {
$gid = $a.target.groupId
[void]$groupIds.Add($gid)
if (-not $usage.ContainsKey($gid)) { $usage[$gid] = [System.Collections.Generic.List[string]]::new() }
$name = if ($item.displayName) { $item.displayName } else { $item.name }
$usage[$gid].Add("$source/$name")
}
}
}
}
Write-Host "Unique groups referenced: $($groupIds.Count)" -ForegroundColor Cyan
$empty = [System.Collections.Generic.List[object]]::new()
foreach ($gid in $groupIds) {
try {
$count = Invoke-MgGraphRequest -Method GET `
-Uri "https://graph.microsoft.com/beta/groups/$gid/members/`$count" `
-Headers @{ ConsistencyLevel = 'eventual' }
if ([int]$count -eq 0) {
$g = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/groups/$gid"
$empty.Add([pscustomobject]@{
groupId = $gid
groupName = $g.displayName
usageCount = $usage[$gid].Count
usedBy = ($usage[$gid] -join '; ')
})
}
} catch {
Write-Verbose "Group $gid lookup failed: $_"
}
}
Write-Host "Empty assignment groups: $($empty.Count)" -ForegroundColor Yellow
$empty | Sort-Object usageCount -Descending |
Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "Report written to $OutputPath" -ForegroundColor Green
exit 0
} catch {
Write-Error "Find-EmptyAssignmentGroups failed: $_"
exit 1
}