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.

How

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) {
[pscustomobject]@{
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 comment