Anavem
Languagefr
How to Delete Orphaned Groups in Azure AD Using Microsoft Graph PowerShell

How to Delete Orphaned Groups in Azure AD Using Microsoft Graph PowerShell

Learn to identify and remove orphaned Azure AD groups using the modern Microsoft Graph PowerShell SDK, replacing deprecated Azure AD PowerShell modules with current 2026 methods.

Evan MaelEvan Mael
March 27, 2026 15 min
mediumazure-ad 7 steps 15 min

Why Delete Orphaned Groups in Azure AD?

Orphaned groups in Azure Active Directory are remnants from discontinued synchronization processes, typically left behind after removing Azure AD Connect or other directory synchronization tools. These groups consume licenses, create security risks, and clutter your directory with obsolete objects that cannot be managed through the Azure Portal GUI.

What Changed in 2026 for Azure AD Group Management?

The traditional Azure AD PowerShell module was deprecated on March 30, 2024, and ceased functioning entirely on March 30, 2025. Any tutorial using Connect-AzureAD or Remove-AzureADGroup commands is now obsolete. Microsoft Graph PowerShell SDK is the current standard, offering enhanced security, better performance, and unified API access across all Microsoft 365 services.

How Do Microsoft Graph Commands Differ from Legacy Azure AD PowerShell?

The migration from Azure AD PowerShell to Microsoft Graph PowerShell brings significant changes in syntax and authentication. Where you previously used Connect-AzureAD, you now use Connect-MgGraph with specific scopes. Group management commands like Get-AzureADGroup become Get-MgGroup, providing more granular permissions and better integration with modern authentication protocols. This tutorial demonstrates the current 2026 approach using Microsoft Graph PowerShell SDK, ensuring your scripts remain functional and secure.

Implementation Guide

Full Procedure

01

Install Microsoft Graph PowerShell SDK

First, we need to install the Microsoft Graph PowerShell SDK, which replaced the deprecated Azure AD PowerShell module in 2024. Open PowerShell as Administrator and install the required modules.

# Check PowerShell version (must be 7.2 or higher)
$PSVersionTable.PSVersion

# Install Microsoft Graph PowerShell SDK
Install-Module Microsoft.Graph -Scope CurrentUser -Force

# Install specific modules for group management
Install-Module Microsoft.Graph.Groups -Scope CurrentUser -Force
Install-Module Microsoft.Graph.DirectoryObjects -Scope CurrentUser -Force

The installation may take several minutes as it downloads multiple sub-modules. You'll see progress indicators for each component being installed.

Pro tip: Use -Scope CurrentUser to avoid requiring Administrator privileges for installation.

Verify the installation by checking the module version:

Get-Module Microsoft.Graph -ListAvailable | Select-Object Name, Version
02

Connect to Microsoft Graph with Required Permissions

Connect to Microsoft Graph using the appropriate scopes for group management. This replaces the old Connect-AzureAD command from the deprecated module.

# Connect to Microsoft Graph with group management permissions
Connect-MgGraph -Scopes "Group.ReadWrite.All", "Directory.ReadWrite.All", "GroupMember.ReadWrite.All"

# Verify connection and check current context
Get-MgContext | Select-Object Account, Scopes, TenantId

A browser window will open for authentication. Sign in with your Global Administrator or Groups Administrator account. After successful authentication, you'll see confirmation in the PowerShell console.

Warning: The Directory.ReadWrite.All scope provides extensive permissions. Only use accounts with appropriate administrative rights.

Confirm you're connected to the correct tenant:

# Display tenant information
Get-MgOrganization | Select-Object DisplayName, Id, VerifiedDomains
03

Identify Orphaned Groups in Your Tenant

Now we'll identify orphaned groups that typically remain after removing Azure AD Connect or other synchronization tools. These groups often have specific characteristics that make them identifiable.

# Get all groups and filter for potential orphaned groups
$allGroups = Get-MgGroup -All -Property Id, DisplayName, Description, GroupTypes, SecurityEnabled, MailEnabled, OnPremisesSyncEnabled, CreatedDateTime

# Filter for groups that are likely orphaned (on-premises synced but no longer active)
$orphanedGroups = $allGroups | Where-Object {
    $_.OnPremisesSyncEnabled -eq $true -and
    $_.Description -like "*orphaned*" -or
    $_.DisplayName -like "*_*" -or
    $_.DisplayName -match "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
}

# Display potential orphaned groups
$orphanedGroups | Select-Object DisplayName, Id, Description, CreatedDateTime | Format-Table -AutoSize

This command identifies groups with common orphaned characteristics: on-premises sync enabled, GUID-like names, or descriptions containing "orphaned".

For a more comprehensive search, check for groups with no members:

# Check for empty groups that might be orphaned
$emptyGroups = @()
foreach ($group in $allGroups) {
    $memberCount = (Get-MgGroupMember -GroupId $group.Id -All).Count
    if ($memberCount -eq 0) {
        $emptyGroups += $group
    }
}

$emptyGroups | Select-Object DisplayName, Id, CreatedDateTime | Format-Table -AutoSize
Pro tip: Always review the list manually before deletion. Some empty groups might be intentionally created for future use.
04

Export Group Information for Backup

Before deleting any groups, create a comprehensive backup of their information. This is crucial for recovery if you accidentally delete the wrong groups.

# Create backup directory
$backupPath = "C:\AzureAD_GroupBackup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
New-Item -ItemType Directory -Path $backupPath -Force

# Export all group information to CSV
$allGroups | Export-Csv -Path "$backupPath\AllGroups_Backup.csv" -NoTypeInformation

# Export detailed information for each orphaned group
foreach ($group in $orphanedGroups) {
    $groupDetails = @{
        'GroupId' = $group.Id
        'DisplayName' = $group.DisplayName
        'Description' = $group.Description
        'GroupTypes' = $group.GroupTypes -join ','
        'SecurityEnabled' = $group.SecurityEnabled
        'MailEnabled' = $group.MailEnabled
        'CreatedDateTime' = $group.CreatedDateTime
        'OnPremisesSyncEnabled' = $group.OnPremisesSyncEnabled
    }
    
    # Get group members
    $members = Get-MgGroupMember -GroupId $group.Id -All
    $groupDetails['MemberCount'] = $members.Count
    $groupDetails['Members'] = ($members | ForEach-Object { $_.AdditionalProperties.displayName }) -join ';'
    
    # Export to individual JSON file
    $groupDetails | ConvertTo-Json | Out-File -FilePath "$backupPath\Group_$($group.DisplayName -replace '[^a-zA-Z0-9]', '_').json"
}

Write-Host "Backup completed at: $backupPath" -ForegroundColor Green

This creates a timestamped backup directory with comprehensive group information including members, properties, and metadata.

Warning: Store backups in a secure location. Group information may contain sensitive organizational data.

Verify the backup was created successfully:

Get-ChildItem $backupPath | Select-Object Name, Length, LastWriteTime
05

Validate Groups Before Deletion

Perform final validation to ensure you're only deleting truly orphaned groups. This step prevents accidental deletion of active groups.

# Create validation report
$validationReport = @()

foreach ($group in $orphanedGroups) {
    $validation = @{
        'GroupName' = $group.DisplayName
        'GroupId' = $group.Id
        'MemberCount' = (Get-MgGroupMember -GroupId $group.Id -All).Count
        'HasOwners' = (Get-MgGroupOwner -GroupId $group.Id -All).Count -gt 0
        'LastActivity' = 'Unknown' # Microsoft Graph doesn't provide last activity directly
        'IsMailEnabled' = $group.MailEnabled
        'IsSecurityGroup' = $group.SecurityEnabled
        'OnPremisesSync' = $group.OnPremisesSyncEnabled
        'RecommendDelete' = $false
    }
    
    # Determine if group should be deleted based on criteria
    if ($validation.MemberCount -eq 0 -and 
        -not $validation.HasOwners -and 
        $validation.OnPremisesSync -eq $true) {
        $validation.RecommendDelete = $true
    }
    
    $validationReport += New-Object PSObject -Property $validation
}

# Display validation report
$validationReport | Format-Table GroupName, MemberCount, HasOwners, IsMailEnabled, RecommendDelete -AutoSize

# Show only groups recommended for deletion
$groupsToDelete = $validationReport | Where-Object { $_.RecommendDelete -eq $true }
Write-Host "Groups recommended for deletion: $($groupsToDelete.Count)" -ForegroundColor Yellow

Review the validation report carefully. Groups marked with RecommendDelete = $true meet the criteria for safe deletion.

Pro tip: If you're unsure about a group, check with the group's original owners or your organization's IT team before deletion.

Double-check specific groups manually:

# Manually inspect a specific group
$groupId = "your-group-id-here"
Get-MgGroup -GroupId $groupId | Select-Object DisplayName, Description, CreatedDateTime, OnPremisesSyncEnabled
Get-MgGroupMember -GroupId $groupId -All | Select-Object AdditionalProperties
06

Delete Orphaned Groups Using Microsoft Graph

Now we'll delete the validated orphaned groups using the Microsoft Graph PowerShell SDK. This replaces the deprecated Remove-AzureADGroup cmdlet.

# Delete orphaned groups with confirmation
$deletionLog = @()

foreach ($group in $groupsToDelete) {
    try {
        Write-Host "Attempting to delete group: $($group.GroupName)" -ForegroundColor Yellow
        
        # Delete the group
        Remove-MgGroup -GroupId $group.GroupId -Confirm:$false
        
        $logEntry = @{
            'GroupName' = $group.GroupName
            'GroupId' = $group.GroupId
            'DeletionTime' = Get-Date
            'Status' = 'Success'
            'Error' = $null
        }
        
        Write-Host "Successfully deleted: $($group.GroupName)" -ForegroundColor Green
        
    } catch {
        $logEntry = @{
            'GroupName' = $group.GroupName
            'GroupId' = $group.GroupId
            'DeletionTime' = Get-Date
            'Status' = 'Failed'
            'Error' = $_.Exception.Message
        }
        
        Write-Host "Failed to delete $($group.GroupName): $($_.Exception.Message)" -ForegroundColor Red
    }
    
    $deletionLog += New-Object PSObject -Property $logEntry
    Start-Sleep -Seconds 2  # Avoid API throttling
}

# Export deletion log
$deletionLog | Export-Csv -Path "$backupPath\DeletionLog.csv" -NoTypeInformation

# Display summary
$successCount = ($deletionLog | Where-Object { $_.Status -eq 'Success' }).Count
$failedCount = ($deletionLog | Where-Object { $_.Status -eq 'Failed' }).Count

Write-Host "\nDeletion Summary:" -ForegroundColor Cyan
Write-Host "Successfully deleted: $successCount groups" -ForegroundColor Green
Write-Host "Failed deletions: $failedCount groups" -ForegroundColor Red
Warning: Group deletion is permanent and cannot be undone through PowerShell. Ensure you have proper backups before proceeding.

For safer deletion with individual confirmation:

# Alternative: Delete with individual confirmation
foreach ($group in $groupsToDelete) {
    $confirmation = Read-Host "Delete group '$($group.GroupName)'? (y/N)"
    if ($confirmation -eq 'y' -or $confirmation -eq 'Y') {
        Remove-MgGroup -GroupId $group.GroupId
        Write-Host "Deleted: $($group.GroupName)" -ForegroundColor Green
    }
}
07

Verify Deletion and Clean Up

Verify that the orphaned groups have been successfully deleted and perform cleanup tasks.

# Verify groups are deleted
Write-Host "Verifying group deletions..." -ForegroundColor Cyan

$verificationResults = @()
foreach ($deletedGroup in ($deletionLog | Where-Object { $_.Status -eq 'Success' })) {
    try {
        $group = Get-MgGroup -GroupId $deletedGroup.GroupId -ErrorAction Stop
        $verificationResults += @{
            'GroupName' = $deletedGroup.GroupName
            'Status' = 'Still Exists'
            'Note' = 'Group was not deleted successfully'
        }
    } catch {
        $verificationResults += @{
            'GroupName' = $deletedGroup.GroupName
            'Status' = 'Deleted Successfully'
            'Note' = 'Group no longer exists in Azure AD'
        }
    }
}

# Display verification results
$verificationResults | ForEach-Object { New-Object PSObject -Property $_ } | Format-Table -AutoSize

# Check for any remaining orphaned groups
Write-Host "\nChecking for remaining orphaned groups..." -ForegroundColor Cyan
$remainingGroups = Get-MgGroup -All -Property Id, DisplayName, OnPremisesSyncEnabled | Where-Object {
    $_.OnPremisesSyncEnabled -eq $true -and
    $_.DisplayName -like "*_*"
}

if ($remainingGroups.Count -gt 0) {
    Write-Host "Found $($remainingGroups.Count) potentially orphaned groups remaining:" -ForegroundColor Yellow
    $remainingGroups | Select-Object DisplayName, Id | Format-Table -AutoSize
} else {
    Write-Host "No additional orphaned groups found." -ForegroundColor Green
}

# Generate final report
$finalReport = @{
    'TotalGroupsProcessed' = $orphanedGroups.Count
    'SuccessfulDeletions' = $successCount
    'FailedDeletions' = $failedCount
    'BackupLocation' = $backupPath
    'ProcessingDate' = Get-Date
}

$finalReport | ConvertTo-Json | Out-File -FilePath "$backupPath\FinalReport.json"

Write-Host "\nCleanup completed. Final report saved to: $backupPath\FinalReport.json" -ForegroundColor Green
Pro tip: Keep the backup directory for at least 30 days in case you need to reference deleted group information.

Disconnect from Microsoft Graph when finished:

# Disconnect from Microsoft Graph
Disconnect-MgGraph
Write-Host "Disconnected from Microsoft Graph" -ForegroundColor Green

Frequently Asked Questions

Why can't I use Connect-AzureAD commands in 2026 for Azure AD group management?+
The Azure AD PowerShell module was deprecated on March 30, 2024, and stopped functioning entirely on March 30, 2025. Microsoft replaced it with the Microsoft Graph PowerShell SDK, which uses Connect-MgGraph and provides better security, performance, and unified API access. Any scripts using the old AzureAD module will fail in 2026.
What permissions do I need to delete orphaned groups using Microsoft Graph PowerShell?+
You need Global Administrator or Groups Administrator role in Azure AD, plus the following Microsoft Graph scopes: Group.ReadWrite.All for group management, Directory.ReadWrite.All for directory access, and GroupMember.ReadWrite.All for member management. These scopes are specified when connecting with Connect-MgGraph.
How can I identify which groups are truly orphaned in Azure AD?+
Orphaned groups typically have OnPremisesSyncEnabled set to true but no active synchronization source, often have GUID-like names or contain underscores, have zero members and no owners, and may have descriptions mentioning 'orphaned'. Use Get-MgGroup with filtering to identify these characteristics before deletion.
Can I recover Azure AD groups after deleting them with Microsoft Graph PowerShell?+
Groups deleted via PowerShell are permanently removed and cannot be recovered through PowerShell commands. However, Azure AD keeps deleted groups in a recycle bin for 30 days, accessible through the Azure Portal under 'Deleted groups'. Always create comprehensive backups before deletion as shown in this tutorial.
What's the difference between Remove-AzureADGroup and Remove-MgGroup commands?+
Remove-AzureADGroup was part of the deprecated Azure AD PowerShell module and no longer functions in 2026. Remove-MgGroup is the current Microsoft Graph PowerShell SDK command that provides the same functionality with better error handling, authentication through modern protocols, and integration with Microsoft Graph API permissions model.
Evan Mael
Written by

Evan Mael

Microsoft MCSA-certified Cloud Architect | Fortinet-focused. I modernize cloud, hybrid & on-prem infrastructure for reliability, security, performance and cost control - sharing field-tested ops & troubleshooting.

Discussion

Share your thoughts and insights

Sign in to join the discussion