-Start of Shift-
Ticket#000001
“Please Remove Matt Jacobs from the group: Super-Awesome-Admins”
- Technician Logs in to Entra
- Elevates using Privileged Identity Management (PIM)
- Searches for the group
- Clicks Delete
- Closes Ticket
What Just Happened?
Well…. someone got a little click happy and deleted the entire group instead of deleting a single member from the group. Now what do we do? It’s not an M365 group so we can’t recover it from Deleted Items. It’s not a synchronized group so we can’t recover it from Deleted Items / Tombstones in Active Directory.
What Do We Do?
You know me…. I AM an IAM guy…. (See what I did there?). I’ve been thinking about this a lot lately: “How would I restore or rebuild a Entra Group that got inadvertently deleted. More specifically, non synchronized / non M365, Entra Security Groups. If the group was assigned to an Enterprise Application, I could look at Sign-In Logs to repopulate. But unless I’ve got a Log Analytics Workspace, I can only go so far. The same would apply if I were to simply start looking at who’s been added to the group in Audit Logs. And that doesn’t even begin to cover Dynamic Groups. How do I get those queries back? If only we had a backup! We’ll good thing you stumbled across this blog post.
Enter Microsoft Graph PowerShell.
The code below will assist in creating a one time dump of all the “Cloud Only” Entra Security Groups both static and dynamic. The long term goal should be a daily repository stored in an Azure Blob or even AWS S3 Bucket if you are living in multiple Cloud Service Providers, so that you can quickly rebuild and repopulate your groups to restore functionality.
Notes:
- I’m making an assumption you have the needed privilege, security, and authorization to perform directory level reads.
The Code
<#
This script will pull all of the EntraID Cloud Security Groups within the Organization and export the Details.
For Dynamic Groups it will export the Membership Rules and Owners in to Separate Excel Files.
For Static Groups it will export the Members and Owners in to Separate Excel Files.
#>
#Main Script Execution
#Connect to Tenant
#Pull the Security Groups (Not M365 or Distribution) not synced from on prem
$CloudGroups = Get-MgGroup -Filter "mailEnabled eq false and securityEnabled eq true and OnPremisesSyncEnabled ne true" -All -ConsistencyLevel eventual -CountVariable CountVar
#Loop through and Display the Name and Owners
foreach ($group in $CloudGroups)
{
if ($Group.GroupTypes -eq "DynamicMembership")
{
#Gather The Group Details and Group Owners
$GroupDetails = Get-MgGroup -GroupId $group.Id | Select-Object ID,DisplayName,MembershipRule
$DynamicGroupOwners = Get-MgGroupOwner -GroupId $group.Id | Select-Object Id
#Export the Group Owners and Group Details to their respective CSV Files
$DynamicGroupOwners | Export-csv "$($Group.DisplayName)_Owners.csv" -Append -NoTypeInformation
$GroupDetails | Export-csv "$($Group.DisplayName)_MembershipRules.csv" -Append -NoTypeInformation
}
else
{
<#
Gather The Group Owners and Members.
Since Multiple types of identities can be members, we need to pull each type of member.
The idea behind pulling each type is that in the event we are having to restore / rebuild multiple objects,
the Id of that object may no longer be the same. We can use the DisplayName as a reference to manually re-add the
rebuilt Object.
#>
$GroupOwners = Get-MgGroupOwner -GroupId $group.Id -ConsistencyLevel eventual | Select-Object Id
$groupMembers = @()
$groupMembers += Get-MgGroupMemberAsUser -GroupId $group.ID -ConsistencyLevel eventual | Select-Object Id,DisplayName
$groupMembers += Get-MgGroupMemberAsGroup -GroupId $group.ID -ConsistencyLevel eventual | Select-Object Id,DisplayName
$groupMembers += Get-MgGroupMemberAsApplication -GroupId $group.ID -ConsistencyLevel eventual | Select-Object Id,DisplayName
$groupMembers += Get-MgGroupMemberAsDevice -GroupId $group.ID -ConsistencyLevel eventual | Select-Object Id,DisplayName
$groupMembers += Get-MgGroupMemberAsOrgContact -GroupId $group.ID -ConsistencyLevel eventual | Select-Object Id,DisplayName
$groupMembers += Get-MgGroupMemberAsServicePrincipal -GroupId $group.ID -ConsistencyLevel eventual | Select-Object Id,DisplayName
#Export the Group Owners and Members in to their respective CSV Files
$GroupOwners | Export-csv "$($Group.DisplayName)_Owners.csv" -Append -NoTypeInformation
$groupMembers | Export-csv "$($Group.DisplayName)_Members.csv" -Append -NoTypeInformation
}
}
#Disconnect from Tenant
#End of Script