Entra / Microsoft 365 · Groups
Report entra group insights
A script to show how to extract the Entra Groups insights from the Graph to report them in a readable format.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-MgGraph -Scopes "Group.Read.All", "GroupMember.Read.All", "User.Read.All", "Organization.Read.All", "Reports.Read.All" -NoWelcome
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
param([int] $LookbackDays = 30)Connect-MgGraph -Scopes "Group.Read.All", "GroupMember.Read.All", "User.Read.All", "Organization.Read.All", "Reports.Read.All" -NoWelcomeWrite-Host "Starting up by finding groups and insights..." -ForegroundColor Green# Can we get insights?$Uri = "https://graph.microsoft.com/beta/reports/identityAnalytics/groups"[array]$GroupInsights = Invoke-MgGraphRequest -Method Get -Uri $Uri -OutputType PSObject | Select-Object -ExpandProperty valueIf ($GroupInsights) {Write-Host "Group insights retrieved successfully." -ForegroundColor Green} Else {Write-Host "Failed to retrieve group insights." -ForegroundColor RedBreak}$InsightsDate = Get-Date $GroupInsights[0].CalculatedDateTime -format "dd-MMM-yyyy"[array]$Groups = Get-MgGroup -All -Property "id,displayName,description,createdDateTime,securityEnabled,mailEnabled,groupTypes,owners" -PageSize 500 -ExpandProperty OwnersIf ($Groups) {Write-Host ("{0} groups retrieved successfully." -f $Groups.count) -ForegroundColor Green} Else {Write-Host "Failed to retrieve groups." -ForegroundColor RedBreak}$Report = [System.Collections.Generic.List[Object]]::new()ForEach ($Group in $Groups) {$Insight = $null$Insight = $GroupInsights | Where-Object { $_.id -eq $Group.id }If ($Insight) {$GroupSoftDeletionDateTime = $null; $GroupRestorationDateTime = $null; $GroupExpirationDateTime = $nullIf ($Insight.groupExpirationDateTime) {$GroupExpirationDateTime = Get-Date($Insight.groupExpirationDateTime) -format 'dd-MMM-yyyy HH:mm'}If ($insight.softDeletionDateTime) {$GroupSoftDeletionDateTime = Get-Date($Insight.softDeletionDateTime) -format 'dd-MMM-yyyy HH:mm'}If ($INsight.GroupRestorationDateTime) {$GroupRestorationDateTime = Get-Date($Insight.GroupRestorationDateTime) -format 'dd-MMM-yyyy HH:mm'}If ($Group.GroupTypes -contains "DynamicMembership") {$DynamicGroup = $true} Else {$DynamicGroup = $false}If ($Group.GroupTypes -contains "Unified") {$M365Group = $true} Else {$M365Group = $false}$ReportLine = [PSCustomObject]@{Group = $Group.displayNameId = $Group.idCreated = Get-Date($Group.createdDateTime) -format 'dd-MMM-yyyy HH:mm'expirationDateTime = $GroupExpirationDateTimesoftDeletionDateTime = $GroupSoftDeletionDateTimelastRestorationDateTime = $GroupRestorationDateTimeSecurityEnabled = $Group.securityEnabledMailEnabled = $Group.mailEnabledGroupTypes = ($Group.groupTypes -join ",")TotalMembers = $Insight.memberTransitiveUserCountTotalOwners = $Group.owners.CountOwners = $Group.owners.additionalProperties.displayName -join ", "TotalGuests = $Insight.guestTransitiveUserCountGuestOwnerCount = $Insight.guestOwnerCountMemberOwnerCount = $Insight.memberOwnerCountTotalMembership = $Insight.transitiveUserCountservicePrincipalOwnerCount = $Insight.servicePrincipalOwnerCounttransitiveServicePrincipalCount = $Insight.transitiveServicePrincipalCountDynamicMembership = $DynamicGroup'Microsoft 365 Group' = $M365GroupMembershipRuleProcessingState = $Insight.membershipRuleProcessingStateMembershipRuleExpressionCount = $Insight.membershipRuleExpressionCountmembershipRuleContainsCount = $Insight.membershipRuleContainsCountmembershipRuleMatchCount = $Insight.membershipRuleMatchCountsensitivityLabelCount = $Insight.sensitivityLabelCountGrouptype = $Insight.groupTypeVisibility = $Group.visibilityCloudDL = $Insight.isCloudDistributionListGroupOnPremisesDL = $Insight.isOnPremisesDistributionListGroupCloudSecurityGroup = $Insight.isCloudSecurityGroupOnPremisesSecurityGroup = $Insight.isOnPremisesSecurityGroupCloudMailEnabledSecurityGroup = $Insight.isCloudMailEnabledSecurityGroupOnPremisesMailEnabledSecurityGroup = $Insight.isOnPremisesMailEnabledSecurityGroup}$Report.Add($ReportLine)} Else {$ReportLine = [PSCustomObject]@{Group = $Group.displayNameId = $Group.idCreated = Get-Date($Group.createdDateTime) -format 'dd-MMM-yyyy HH:mm'SecurityEnabled = $Group.securityEnabledMailEnabled = $Group.mailEnabledGroupTypes = ($Group.groupTypes -join ",")TotalMembers = "unknown"TotalOwners = $Group.owners.CountOwners = $Group.owners.additionalProperties.displayName -join ", "}$Report.Add($ReportLine)}}$Report = $Report | Sort-Object -Property Group# Now we can generate some analytics from the insights[datetime]$30DaysAgo = (Get-Date).AddDays(-$LookbackDays)[datetime]$30DaysFromNow = (Get-Date).AddDays(30)# Graph API# https://graph.microsoft.com/beta/reports/identityAnalytics/groups?$filter=guestOwnerCount eq 0 and memberOwnerCount eq 0 and servicePrincipalOwnerCount eq 0[array]$GroupsWithNoOwners = $Report | Where-Object { $_.GuestOwnerCount -eq 0 -and $_.MemberOwnerCount -eq 0 -and $_.servicePrincipalOwnerCount -eq 0 }Write-Host ("Groups with no owners: {0}" -f $GroupsWithNoOwners.count) -ForegroundColor Yellow[array]$GroupsWithServicePrincipalOwners = $Report | Where-Object { $_.servicePrincipalOwnerCount -gt 0 }Write-Host ("Groups with service principals as owners: {0}" -f $GroupsWithServicePrincipalOwners.count) -ForegroundColor Yellow[array]$GroupsWithServicePrincipalsAsOwnersOrMembers = $Report | Where-Object { $_.servicePrincipalOwnerCount -gt 0 -or $_.transitiveServicePrincipalCount -gt 0 }Write-Host ("Groups with service principals as owners or members: {0}" -f $GroupsWithServicePrincipalsAsOwnersOrMembers.count) -ForegroundColor Yellow[array]$GroupsWithGuestsAsOwners = $Report | Where-Object { $_.GuestOwnerCount -gt 0 }Write-Host ("Groups with guest owners: {0}" -f $GroupsWithGuestsAsOwners.count) -ForegroundColor Yellow[int]$ServicePrincipalsAsMembers = $GroupsWithServicePrincipalsAsOwnersOrMembers.count - $GroupsWithServicePrincipalOwners.countWrite-Host ("Groups with service principals as members but not owners: {0}" -f $ServicePrincipalsAsMembers) -ForegroundColor Yellow[array]$GroupsWithDynamicMembership = $Report | Where-Object { $_.DynamicMembership -eq $true }Write-Host ("Groups with dynamic membership: {0}" -f $GroupsWithDynamicMembership.count) -ForegroundColor Yellow[array]$GroupsWithPendingExpiration = $Report | Where-Object {$_.expirationDateTime -ne $null -and $_.expirationDateTime -ne "01-Jan-0001 00:00" -and $_.expirationDateTime -as [datetime] -lt $30DaysFromNow}Write-Host ("Groups with pending expiration in the next 30 days: {0}" -f $GroupsWithPendingExpiration.count) -ForegroundColor Yellow[array]$GroupsInSoftDeletion = $Report | Where-Object { $_.softDeletionDateTime -ne $null -and $_.softDeletionDateTime -as [datetime] -gt $30DaysAgo }Write-Host ("Soft-deleted groups: {0}" -f $GroupsInSoftDeletion.count) -ForegroundColor Yellow[array]$GroupsRestored = $Report | Where-Object { $_.lastRestorationDateTime -as [datetime] -gt $30DaysAgo }Write-Host ("Groups restored in the last 30 days: {0}" -f $GroupsRestored.count) -ForegroundColor Yellow[array]$NewlyCreatedGroups = $Report | Where-Object { $_.Created -as [datetime] -gt (Get-Date).AddDays(-$LookbackDays) }Write-Host ("Newly created groups in the last 30 days: {0}" -f $NewlyCreatedGroups.count) -ForegroundColor Yellow[array]$GroupsWithNoSensitivityLabel = $Report | Where-Object { $_.sensitivityLabelCount -eq 0 }Write-Host ("Groups with no sensitivity label: {0}" -f $GroupsWithNoSensitivityLabel.count) -ForegroundColor Yellow$M365GroupCount = ($Report | Where-Object { $_.'Microsoft 365 Group' -eq $true }).CountWrite-Host ("Number of Microsoft 365 Groups: {0}" -f $M365GroupCount) -ForegroundColor Yellow$DynamicMicrosoft365GroupCount = ($Report | Where-Object { $_.'Microsoft 365 Group' -eq $true -and $_.DynamicMembership -eq $true }).CountWrite-Host ("Number of Dynamic Microsoft 365 Groups: {0}" -f $DynamicMicrosoft365GroupCount) -ForegroundColor Yellow# Largest group$LargestGroup = $Report | Sort-Object -Property TotalMembership -Descending | Select-Object -First 1Write-Host ("The largest group is {0} with {1} members." -f $LargestGroup.Group, $LargestGroup.TotalMembership) -ForegroundColor Yellow# Group with largest amount of guest accounts$LargestGroupWithGuests = $Report | Where-Object { $_.TotalGuests -gt 0 } | Sort-Object -Property TotalMembership -Descending | Select-Object -First 1Write-Host ("Group with largest number of guest accounts is {0} with {1} members." -f $LargestGroupWithGuests.Group, $LargestGroupWithGuests.TotalMembership) -ForegroundColor Yellow# Group with largest amount of member accounts$LargestGroupWithMembers = $Report | Where-Object { $_.TotalMembers -gt 0 } | Sort-Object -Property TotalMembers -Descending | Select-Object -First 1Write-Host ("Group with largest number of member accounts is {0} with {1} members." -f $LargestGroupWithMembers.Group, $LargestGroupWithMembers.TotalMembership) -ForegroundColor Yellow# Groups with complicated rules$GroupsComplicatedRules = $Report | Where-Object { $_.MembershipRuleExpressionCount -gt 10 } | Sort-Object -Property MembershipRuleExpressionCount -DescendingWrite-Host ("Dynamic groups with complicated membership rules: {0}" -f $GroupsComplicatedRules.count) -ForegroundColor Yellow# Dynamic groups using ‘contains’ or ‘match’ operators in their membership rules, which can impact performance.$GroupInefficientRules = $Report | Where-Object { $_.MembershipRuleMatchCount -gt 0 -or $_.MembershipRuleContainsCount -gt 0 } | Sort-Object -Property MembershipRuleExpressionCount -DescendingWrite-Host ("Dynamic groups with inefficient membership rules: {0}" -f $GroupInefficientRules.count) -ForegroundColor Yellow$CloudDLCount = ($Report | Where-Object { $_.CloudDL -eq $true }).CountWrite-Host ("Cloud distribution lists: {0}" -f $CloudDLCount) -ForegroundColor Yellow$OnPremisesDLCount = ($Report | Where-Object { $_.OnPremisesDL -eq $true }).CountWrite-Host ("On-premises distribution lists : {0}" -f $OnPremisesDLCount) -ForegroundColor Yellow$CloudSecurityGroupCount = ($Report | Where-Object { $_.CloudSecurityGroup -eq $true }).CountWrite-Host ("Cloud security groups: {0}" -f $CloudSecurityGroupCount) -ForegroundColor Yellow$OnPremisesSecurityGroupCount = ($Report | Where-Object { $_.OnPremisesSecurityGroup -eq $true }).CountWrite-Host ("On-premises security groups: {0}" -f $OnPremisesSecurityGroupCount) -ForegroundColor Yellow$CloudMailEnabledSecurityGroupCount = ($Report | Where-Object { $_.CloudMailEnabledSecurityGroup -eq $true }).CountWrite-Host ("Cloud mail-enabled security groups: {0}" -f $CloudMailEnabledSecurityGroupCount) -ForegroundColor Yellow$OnPremisesMailEnabledSecurityGroupCount = ($Report | Where-Object { $_.OnPremisesMailEnabledSecurityGroup -eq $true }).CountWrite-Host ("On-premises mail-enabled security groups: {0}" -f $OnPremisesMailEnabledSecurityGroupCount) -ForegroundColor Yellow# OK. Output the report filesWrite-Host "Generating HTML report..." -ForegroundColor Green$CreationDate = Get-Date -format 'dd-MMM-yyyy HH:mm:ss'$Version = "1.0"$Organization = (Get-MgOrganization).DisplayName# Create the HTML report$HTMLHead="<html><style>BODY{font-family: Arial; font-size: 8pt;}H1{font-size: 36px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}H2{font-size: 24px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}H3{font-size: 16px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}TABLE{border: 1px solid black; border-collapse: collapse; font-size: 8pt;}TH{border: 1px solid #969595; background: #dddddd; padding: 5px; color: #000000;}TD{border: 1px solid #969595; padding: 5px; }td.info{background: #85D4FF;}</style><body><div align=center><p><h1>Entra ID Groups Insights</h1></p><p><h2><b>For the " + $Organization + " organization</b></h2></p><p><h3>Generated: " + (Get-Date -format "dd-MMM-yyyy") + " (Insights from " + $InsightsDate + ")</h3></p></div>"# Add a section for groups with no ownersIf ($GroupsWithNoOwners.count -gt 0) {$HTMLGroupsWithNoOwnersSection = "<p><h2>Groups with no owners" + " (" + $GroupsWithNoOwners.count + ")</h2></p>" + ($GroupsWithNoOwners | ConvertTo-Html -Property Group, Id, Created, SecurityEnabled, MailEnabled, GroupTypes, TotalMembers, TotalOwners, Owners -Fragment )} Else {$HTMLGroupsWithNoOwnersSection = "<p><h2>Groups with no owners</h2></p><p>No groups without owners found.</p>"}$HTMLBody = $HTMLHead + $HTMLGroupsWithNoOwnersSection# Groups with service principals as ownersIf ($GroupsWithServicePrincipalOwners.count -gt 0) {$HTMLGroupsWithServicePrincipalOwnersSection = "<p><h2>Groups with service principals as owners" + " (" + $GroupsWithServicePrincipalOwners.count + ")</h2></p>" + ($GroupsWithServicePrincipalOwners | ConvertTo-Html -Property Group, Id, Created, SecurityEnabled, MailEnabled, GroupTypes, TotalMembers, TotalOwners, Owners -Fragment )} Else {$HTMLGroupsWithServicePrincipalOwnersSection = "<p><h2>Groups with service principals as owners</h2></p><p>No groups with service principals as owners found.</p>"}$HTMLBody = $HTMLBody + $HTMLGroupsWithServicePrincipalOwnersSection# Groups with service principals as owners or membersIf ($GroupsWithServicePrincipalsAsOwnersOrMembers.count -gt 0) {$HTMLGroupsWithServicePrincipalsAsOwnersOrMembersSection = "<p><h2>Groups with service principals as owners or members" + " (" + $GroupsWithServicePrincipalsAsOwnersOrMembers.count + ")</h2></p>" + ($GroupsWithServicePrincipalsAsOwnersOrMembers | ConvertTo-Html -Property Group, Id, Created, SecurityEnabled, MailEnabled, GroupTypes, TotalMembers, TotalOwners, Owners -Fragment )} Else {$HTMLGroupsWithServicePrincipalsAsOwnersOrMembersSection = "<p><h2>Groups with service principals as owners or members</h2></p><p>No groups with service principals as owners or members found.</p>"}$HTMLBody = $HTMLBody + $HTMLGroupsWithServicePrincipalsAsOwnersOrMembersSection# Groups with service principals as members but not ownersIf ($ServicePrincipalsAsMembers -gt 0) {$HTMLGroupsWithServicePrincipalsAsMembersSection = "<p><h2>Groups with service principals as members but not owners" + " (" + $ServicePrincipalsAsMembers + ")</h2></p>" + ($Report | Where-Object { $_.transitiveServicePrincipalCount -gt 0 -and $_.servicePrincipalOwnerCount -eq 0 } | ConvertTo-Html -Property Group, Id, Created, SecurityEnabled, MailEnabled, GroupTypes, TotalMembers, TotalOwners, Owners -Fragment )} Else {$HTMLGroupsWithServicePrincipalsAsMembersSection = "<p><h2>Groups with service principals as members but not owners</h2></p><p>No groups with service principals as members but not owners found.</p>"}$HTMLBody = $HTMLBody + $HTMLGroupsWithServicePrincipalsAsMembersSection# Groups with guest ownersIf ($GroupsWithGuestsAsOwners.count -gt 0) {$HTMLGroupsWithGuestsAsOwnersSection = "<p><h2>Groups with guest owners" + " (" + $GroupsWithGuestsAsOwners.count + ")</h2></p>" + ($GroupsWithGuestsAsOwners | ConvertTo-Html -Property Group, Id, Created, SecurityEnabled, MailEnabled, GroupTypes, TotalMembers, TotalOwners, Owners -Fragment )} Else {$HTMLGroupsWithGuestsAsOwnersSection = "<p><h2>Groups with guest owners</h2></p><p>No groups with guest owners found.</p>"}$HTMLBody = $HTMLBody + $HTMLGroupsWithGuestsAsOwnersSection# Groups with dynamic membershipIf ($GroupsWithDynamicMembership.count -gt 0) {$HTMLGroupsWithDynamicMembershipSection = "<p><h2>Groups with dynamic membership" + " (" + $GroupsWithDynamicMembership.count + ")</h2></p>" + ($GroupsWithDynamicMembership | ConvertTo-Html -Property Group, Id, Created, SecurityEnabled, MailEnabled, GroupTypes, TotalMembers, TotalOwners, Owners -Fragment )} Else {$HTMLGroupsWithDynamicMembershipSection = "<p><h2>Groups with dynamic membership</h2></p><p>No groups with dynamic membership found.</p>"}$HTMLBody = $HTMLBody + $HTMLGroupsWithDynamicMembershipSection# Dynamic groups with complicated rulesIf ($GroupsComplicatedRules.count -gt 0) {$HTMLGroupsWithComplicatedRulesSection = "<p><h2>Dynamic groups with complicated membership rules" + " (" + $GroupsComplicatedRules.count + ")</h2></p>" + ($GroupsComplicatedRules | ConvertTo-Html -Property Group, Id, Created, MembershipRuleExpressionCount, SecurityEnabled, MailEnabled, GroupTypes, TotalMembers, TotalOwners, Owners -Fragment )} Else {$HTMLGroupsWithComplicatedRulesSection = "<p><h2>Dynamic groups with complicated membership rules</h2></p><p>No dynamic groups with complicated membership rules found.</p>"}$HTMLBody = $HTMLBody + $HTMLGroupsWithComplicatedRulesSection# Dynamic groups with inefficient rulesIf ($GroupInefficientRules.count -gt 0) {$HTMLGroupsWithInefficientRulesSection = "<p><h2>Dynamic groups with inefficient membership rules" + " (" + $GroupInefficientRules.count + ")</h2></p>" + ($GroupInefficientRules | ConvertTo-Html -Property Group, Id, Created, MembershipRuleMatchCount, MembershipRuleContainsCount, SecurityEnabled, MailEnabled, GroupTypes, TotalMembers, TotalOwners, Owners -Fragment )} Else {$HTMLGroupsWithInefficientRulesSection = "<p><h2>Dynamic groups with inefficient membership rules</h2></p><p>No dynamic groups with inefficient membership rules found.</p>"}$HTMLBody = $HTMLBody + $HTMLGroupsWithInefficientRulesSection# Groups with pending expiration in the next 30 daysIf ($GroupsWithPendingExpiration.count -gt 0) {$HTMLGroupsWithPendingExpirationSection = "<p><h2>Groups with pending expiration in the next 30 days" + " (" + $GroupsWithPendingExpiration.count + ")</h2></p>" + ($GroupsWithPendingExpiration | ConvertTo-Html -Property Group, Id, Created, expirationDateTime, SecurityEnabled, MailEnabled, GroupTypes, TotalMembers, TotalOwners, Owners -Fragment )} Else {$HTMLGroupsWithPendingExpirationSection = "<p><h2>Groups with pending expiration in the next 30 days</h2></p><p>No groups with pending expiration in the next 30 days found.</p>"}$HTMLBody = $HTMLBody + $HTMLGroupsWithPendingExpirationSection# Groups in soft deletion statusIf ($GroupsInSoftDeletion.count -gt 0) {$HTMLGroupsInSoftDeletionSection = "<p><h2>Groups in soft deletion" + " (" + $GroupsInSoftDeletion.count + ")</h2></p>" + ($GroupsInSoftDeletion | ConvertTo-Html -Property Group, Id, Created, softDeletionDateTime, SecurityEnabled, MailEnabled, GroupTypes, TotalMembers, TotalOwners, Owners -Fragment )} Else {$HTMLGroupsInSoftDeletionSection = "<p><h2>Groups in soft deletion</h2></p><p>No groups in soft deletion found.</p>"}$HTMLBody = $HTMLBody + $HTMLGroupsInSoftDeletionSection# Groups restored in the last 30 daysIf ($GroupsRestored.count -gt 0) {$HTMLGroupsRestoredSection = "<p><h2>Groups restored in the last 30 days" + " (" + $GroupsRestored.count + ")</h2></p>" + ($GroupsRestored | ConvertTo-Html -Property Group, Id, Created, lastRestorationDateTime, SecurityEnabled, MailEnabled, GroupTypes, TotalMembers, TotalOwners, Owners -Fragment )} Else {$HTMLGroupsRestoredSection = "<p><h2>Groups restored in the last 30 days</h2></p><p>No groups restored in the last 30 days found.</p>"}$HTMLBody = $HTMLBody + $HTMLGroupsRestoredSection# Newly created groups in the last 30 daysIf ($NewlyCreatedGroups.count -gt 0) {$HTMLNewlyCreatedGroupsSection = "<p><h2>Newly created groups in the last 30 days" + " (" + $NewlyCreatedGroups.count + ")</h2></p>" + ($NewlyCreatedGroups | ConvertTo-Html -Property Group, Id, Created, SecurityEnabled, MailEnabled, GroupTypes, TotalMembers, TotalOwners, Owners -Fragment )} Else {$HTMLNewlyCreatedGroupsSection = "<p><h2>Newly created groups in the last 30 days</h2></p><p>No newly created groups in the last 30 days found.</p>"}$HTMLBody = $HTMLBody + $HTMLNewlyCreatedGroupsSectionIf ($GroupsWithNoSensitivityLabel.count -gt 0) {$HTMLGroupsWithNoSensitivityLabelSection = "<p><h2>Groups with no sensitivity label" + " (" + $GroupsWithNoSensitivityLabel.count + ")</h2></p>" + ($GroupsWithNoSensitivityLabel | ConvertTo-Html -Property Group, Id, Created, sensitivityLabelCount, SecurityEnabled, MailEnabled, GroupTypes, TotalMembers, TotalOwners, Owners -Fragment )} Else {$HTMLGroupsWithNoSensitivityLabelSection = "<p><h2>Groups with no sensitivity label</h2></p><p>No groups with no sensitivity label found.</p>"}$HTMLBody = $HTMLBody + $HTMLGroupsWithNoSensitivityLabelSection$HTMLTail = "<p><h2>Summary of Entra ID Groups Insights created for: " + $Organization + "</h2></p>" +"<p>Created: " + $CreationDate + " Entra insights data from " + $InsightsDate + "</p>" +"<p>-----------------------------------------------------------------------------------------------------------------------------</p>"+"<p>Total groups found: " + $Report.Count + "</p>" +"<p>Number of groups without a manager: " + $GroupsWithNoOwners.count+ "</p>" +"<p>Number of groups with no members: " + ($Report | Where-Object { $_.TotalMembers -eq 0 }).Count + "</p>" +"<p>NUmber of groups with service principals as owners: " + $GroupsWithServicePrincipalOwners.count + "</p>" +"<p>Number of groups with service principals as owners or members: " + $GroupsWithServicePrincipalsAsOwnersOrMembers.count + "</p>" +"<p>Number of groups with service principals as members but not owners: " + $ServicePrincipalsAsMembers + "</p>" +"<p>Number of groups with guest owners: " + $GroupsWithGuestsAsOwners.count + "</p>" +"<p>Number of Microsoft 365 Groups: " + $M365GroupCount + "</p>" +"<p>Number of groups with dynamic membership: " + $GroupsWithDynamicMembership.count + "</p>" +"<p>Number of dynamic Microsoft 365 Groups: " + $DynamicMicrosoft365GroupCount + "</p>" +"<p>Number of groups pending expiration in next 30 days: " + $GroupsWithPendingExpiration.Count + "</p>" +"<p>Number of soft-deleted groupss: " + $GroupsInSoftDeletion.Count + "</p>" +"<p>Number of groups restored in the last 30 days: " + $GroupsRestored.Count + "</p>" +"<p>Number of groups created in the last 30 days: " + $NewlyCreatedGroups.Count + "</p>" +"<p>Number of groups with no sensitivity label: " + $GroupsWithNoSensitivityLabel.Count + "</p>" +"<p>Number of dynamic groups with complicated membership rules: " + $GroupsComplicatedRules.Count + "</p>" +"<p>Number of dynamic groups with inefficient membership rules: " + $GroupInefficientRules.Count + "</p>" +"<p>Number of cloud distribution lists: " + $CloudDLCount + "</p>" +"<p>Number of on-premises distribution lists: " + $OnPremisesDLCount + "</p>" +"<p>Number of cloud security groups: " + $CloudSecurityGroupCount + "</p>" +"<p>Number of on-premises security groups: " + $OnPremisesSecurityGroupCount + "</p>" +"<p>Number of cloud mail-enabled security groups: " + $CloudMailEnabledSecurityGroupCount + "</p>" +"<p>Number of on-premises mail-enabled security groups: " + $OnPremisesMailEnabledSecurityGroupCount + "</p>" +"<p>The largest group is <b>" + $LargestGroup.Group + "</b> with <b>" + $LargestGroup.TotalMembership + "</b> members.</p>" +"<p>The group with the largest number of guest accounts is <b>" + $LargestGroupWithGuests.Group + "</b> with <b>" + $LargestGroupWithGuests.TotalMembership + "</b> members.</p>" +"<p>The group with the largest number of member accounts is <b>" + $LargestGroupWithMembers.Group + "</b> with <b>" + $LargestGroupWithMembers.TotalMembership + "</b> members.</p>" +"<p>-----------------------------------------------------------------------------------------------------------------------------</p>"+"<p>Entra ID Group Insights<b> " + $Version + "</b>"$HTMLReport = $HTMLBody + $HTMLTail$HTMLReportFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + "\Entra ID Groups Insights.html"$HTMLReport | Out-File $HTMLReportFile -Encoding UTF8# Generate the report in either Excel worksheet or CSV format, d epending on if the ImportExcel module is availableIf (Get-Module ImportExcel -ListAvailable) {$ExcelGenerated = $TrueImport-Module ImportExcel -ErrorAction SilentlyContinue$ExcelOutputFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + "\Entra Group Insights.xlsx"$Report | Export-Excel -Path $ExcelOutputFile -WorksheetName "Entra Group Insights" -Title ("Entra Group Insights {0}" -f (Get-Date -format 'dd-MMM-yyyy')) -TitleBold -TableName "EntraGroupInsights" -AutoSize -AutoFilter} Else {$CSVOutputFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + "\Entra Group Insights.CSV"$Report | Export-Csv -Path $CSVOutputFile -NoTypeInformation -Encoding Utf8}If ($ExcelGenerated -eq $true) {Write-Host ("Entra Group Insights report is available in Excel workbook {0}" -f $ExcelOutputFile) -ForegroundColor Green} Else {Write-Host ("Entra Group Insights report is available in CSV file {0}" -f $CSVOutputFile) -ForegroundColor Green}Write-Host ("HTML report is available in file {0}" -f $HTMLReportFile) -ForegroundColor Green
Parameters
ParameterDefaultNotes
-LookbackDays30Number of days of group insights activity to include in the report.Attribution
Author
Office365itpros