Clean Azure AD Groups for Disabled Users

Root Cause

Imagine you have on-premises Active Directory synced to AzureAD through ADConnect. You do not delete users during leave process. Those account are disabled cause of various reasons (Jira, I’m looking at you). Now you need to clean up any Office Online groups user belongs to, like Exchange Groups, Teams etc.


This is rather simple task.

  1. First get all disabled users from given OUs
  2. Export them into CSV for further processing/change management/ whatever reason
  3. Connect to Azure AD
  4. Loop through each user, get their group membership and return a custom object with data – also for further processing/change management/whatever reason
  5. Loop through the data and remove user membership from any online groups.

We could remove user’s membership in step 4 in one loop but reporting and executing are two different tasks, so I prefer to keep it that way.

Things to remember

  • Online account needs to have proper permissions!
  • We require AzureAD module to perform online operations!
  • We will be enumerating only online groups. That is why we’re looking at DirSyncEnabled attribute being empty (not present).
  • We also want to process only if the user really does belong to any online group.
  • We will create a custom object to export the data. We will export into two different files – json and csv. CSV is a flat file, meaning we cannot store arrays in one column. Those arrays (of groups) will be flattened into one string with coma separator. To allow further processing we will use JSON file which handles nested arrays perfectly!

The Code

First let’s get all disabled Users from an array of OUs:

$userOUs = @('OU=Leavers,OU=Site1,OU=Contoso Users,DC=Contoso,DC=com','OU=Leavers,OU=Site2,OU=Contoso Users,DC=Contoso,DC=com')
$disabledUsers = foreach ($ou in $userOUs) {
get-aduser filter {Enabled -eq $false} SearchBase $ou
$disabledUsers | Export-Csv Path C:\AdminTools\disabled_users.csv NoTypeInformation

Now let’s connect to AzureAD and get each user’s group membership. To do that we will use Get-AzureADUserMembership cmdlet which requires ObjectID of a user. We want to proceed only if any online group was found (-not {$_.DirSyncEnabled})

Then we will create custom object with all the information we require.

#Get Credentials to O365 tenant
$credential = Get-Credential
Connect-AzureAD Credential $credential
$disabled_OnlineMembership = foreach ($user in $disabledUsers) {
if ($user.UserPrincipalName) {
$userObjectId = Get-Msoluser UserPrincipalName $user.UserPrincipalName | Select-Object ExpandProperty ObjectId
$group = Get-AzureADUserMembership ObjectId $userObjectId | Select-Object * | Where-Object {-not ($_.DirSyncEnabled)}
if ($group) {
UserName = $user.SamAccountName
DisplayName = $user.Name
Enabled = $user.Enabled
UserDN = $user.DistinguishedName
AADUserObjecID = $userObjectId
GroupName = $group.DisplayName
GroupMail = $group.Mail
AADGroupID = $group.ObjectID

Export time.

JSON is easy. Depth parameter will make sure nothing gets lost in translation.

Before exporting to excel we need to flatten arrays of group (name and Mail).

$disabled_OnlineMembership | ConvertTo-Json Depth 99| Out-File c:\AdminTools\disabled_onlinemembership.json
#Export For further 'human' processing through excel:
$disabled_OnlineMembership | Select-Object UserName,DisplayName,Enabled,UserDN,AADUserObjectID,
@{n='Groups';e={$_.GroupName -join ','}},
@{n='GroupMail';e={$_.GroupMail -join ','}} |
Export-Csv Path c:\AdminTools\disabled_onlinemembership.csv NoTypeInformation

And finally let’s clean up the mess:

$UsersToClean = Get-Content Path 'c:\AdminTools\disabled_onlinemembership.json ' RAW | ConvertFrom-Json
#Remove from groups
foreach ($onlineUser in $UsersToClean) {
Write-Host "Processing user {$($onlineUser.DisplayName)}"
foreach ($group in $onlineUser.AADGroupID) {
Write-Host " Processing group {$($group)}"
Remove-AzureADGroupMember ObjectId $group MemberId $onlineUser.AADUserObjectID

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s