Src/Private/Get-AbrEntraIDGroups.ps1
|
function Get-AbrEntraIDGroups { <# .SYNOPSIS Documents Entra ID group inventory including dynamic groups and membership counts. .DESCRIPTION Collects and reports on: - Group summary statistics (totals, types, dynamic vs assigned) - Group inventory (name, type, membership rule, mail-enabled, owner count) - Dynamic group detail (membership rules) at InfoLevel 2 .NOTES Version: 0.1.20 Author: Pai Wei Sing #> [CmdletBinding()] param ( [Parameter(Position = 0, Mandatory)] [string]$TenantId ) begin { Write-PScriboMessage -Message "Collecting Entra ID Groups for tenant $TenantId." Show-AbrDebugExecutionTime -Start -TitleMessage 'Groups' } process { #region Groups # Section{} created unconditionally so catch{} always writes inside it Section -Style Heading2 'Groups' { Paragraph "The following section documents the groups configured in tenant $TenantId." BlankLine try { Write-Host " - Retrieving groups..." $Groups = Get-MgGroup -All ` -Property Id,DisplayName,GroupTypes,MailEnabled,SecurityEnabled,MembershipRule,MembershipRuleProcessingState,CreatedDateTime,Description,Mail,OnPremisesSyncEnabled ` -ErrorAction Stop if ($Groups) { #region Group Summary $SecurityGroups = @($Groups | Where-Object { $_.SecurityEnabled -and $_.GroupTypes -notcontains 'Unified' }).Count $M365Groups = @($Groups | Where-Object { $_.GroupTypes -contains 'Unified' }).Count $DynamicGroups = @($Groups | Where-Object { $_.GroupTypes -contains 'DynamicMembership' }).Count $AssignedGroups = @($Groups | Where-Object { $_.GroupTypes -notcontains 'DynamicMembership' }).Count $MailEnabled = @($Groups | Where-Object { $_.MailEnabled }).Count $SyncedGroups = @($Groups | Where-Object { $_.OnPremisesSyncEnabled -eq $true }).Count $GrpSumObj = [System.Collections.ArrayList]::new() $grpSumInObj = [ordered] @{ 'Total Groups' = @($Groups).Count 'Security Groups' = $SecurityGroups 'Microsoft 365 Groups' = $M365Groups 'Dynamic Membership Groups' = $DynamicGroups 'Assigned Membership Groups' = $AssignedGroups 'Mail-Enabled Groups' = $MailEnabled 'Hybrid (On-Prem Synced)' = $SyncedGroups } $GrpSumObj.Add([pscustomobject]$grpSumInObj) | Out-Null $GrpSumTableParams = @{ Name = "Group Summary - $TenantId"; List = $true; ColumnWidths = 55, 45 } if ($Report.ShowTableCaptions) { $GrpSumTableParams['Caption'] = "- $($GrpSumTableParams.Name)" } $GrpSumObj | Table @GrpSumTableParams #endregion #region Group Inventory Table $GrpObj = [System.Collections.ArrayList]::new() foreach ($Group in ($Groups | Sort-Object DisplayName)) { try { $GroupType = if ($Group.GroupTypes -contains 'Unified') { 'Microsoft 365' } elseif ($Group.SecurityEnabled -and $Group.MailEnabled) { 'Mail-Enabled Security' } elseif ($Group.SecurityEnabled) { 'Security' } elseif ($Group.MailEnabled) { 'Distribution' } else { 'Other' } $MembershipType = if ($Group.GroupTypes -contains 'DynamicMembership') { 'Dynamic' } else { 'Assigned' } $grpInObj = [ordered] @{ 'Group Name' = $Group.DisplayName 'Type' = $GroupType 'Membership' = $MembershipType 'Mail-Enabled' = if ($Group.MailEnabled) { 'Yes' } else { 'No' } 'Security-Enabled' = if ($Group.SecurityEnabled) { 'Yes' } else { 'No' } 'Hybrid Synced' = if ($Group.OnPremisesSyncEnabled) { 'Yes' } else { 'No' } 'Created' = if ($Group.CreatedDateTime) { ($Group.CreatedDateTime).ToString('yyyy-MM-dd') } else { '--' } } $GrpObj.Add([pscustomobject](ConvertTo-HashToYN $grpInObj)) | Out-Null } catch { Write-PScriboMessage -IsWarning -Message "Group '$($Group.DisplayName)': $($_.Exception.Message)" } } $GrpTableParams = @{ Name = "Group Inventory - $TenantId"; List = $false; ColumnWidths = 28, 16, 12, 10, 12, 12, 10 } if ($Report.ShowTableCaptions) { $GrpTableParams['Caption'] = "- $($GrpTableParams.Name)" } $GrpObj | Table @GrpTableParams $null = ($script:ExcelSheets['Groups'] = $GrpObj) #endregion #region Dynamic Group Detail (InfoLevel 2) if ($InfoLevel.Groups -ge 2) { $DynamicGroupList = $Groups | Where-Object { $_.GroupTypes -contains 'DynamicMembership' } if ($DynamicGroupList) { Section -Style Heading3 'Dynamic Group Membership Rules' { Paragraph "The following $(@($DynamicGroupList).Count) dynamic group(s) are configured in tenant $TenantId. Review membership rules regularly to ensure they target the intended user/device population." BlankLine $DynObj = [System.Collections.ArrayList]::new() foreach ($DynGroup in ($DynamicGroupList | Sort-Object DisplayName)) { $ProcessingState = switch ($DynGroup.MembershipRuleProcessingState) { 'On' { 'Active' } 'Paused' { 'Paused' } default { if ($DynGroup.MembershipRuleProcessingState) { $DynGroup.MembershipRuleProcessingState } else { '--' } } } $dynInObj = [ordered] @{ 'Group Name' = $DynGroup.DisplayName 'Processing State' = $ProcessingState 'Membership Rule' = if ($DynGroup.MembershipRule) { $DynGroup.MembershipRule } else { 'No rule defined' } } $DynObj.Add([pscustomobject]$dynInObj) | Out-Null } $null = (& { if ($HealthCheck.EntraID.Groups) { $null = ($DynObj | Where-Object { $_.'Processing State' -eq 'Paused' } | Set-Style -Style Warning | Out-Null) $null = ($DynObj | Where-Object { $_.'Membership Rule' -eq 'No rule defined' } | Set-Style -Style Critical | Out-Null) } }) $DynTableParams = @{ Name = "Dynamic Group Rules - $TenantId"; List = $false; ColumnWidths = 28, 16, 56 } if ($Report.ShowTableCaptions) { $DynTableParams['Caption'] = "- $($DynTableParams.Name)" } $DynObj | Table @DynTableParams $null = ($script:ExcelSheets['Dynamic Groups'] = $DynObj) } } } #endregion } else { Paragraph "No groups were found in tenant $TenantId." } } catch { Write-AbrSectionError -Section 'Groups section' -Message "$($_.Exception.Message)" } } # end Section Groups #endregion } end { Show-AbrDebugExecutionTime -End -TitleMessage 'Groups' } } |