Report deleted Entra ID objects
Report soft-deleted objects in the Entra ID recycle bin, including users, groups, apps, service principals, administrative units, and conditional access policies.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-MgGraph -NoWelcome -Scopes Directory.Read.All
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
Connect-MgGraph -NoWelcome -Scopes Directory.Read.All$CSVOutputFile = "C:\temp\softDeletedObjects.csv"$Now = Get-Date[array]$DeletedGroups = Get-MgDirectoryDeletedItemAsGroup -All[array]$DeletedSPs = Get-MgDirectoryDeletedItemAsServicePrincipal -All[array]$DeletedAUs = Get-MgDirectoryDeletedItemAsAdministrativeUnit -All[array]$DeletedApps = Get-MgDirectoryDeletedItemAsApplication -All[array]$DeletedUsers = Get-MgDirectoryDeletedItemAsUser -All `-Property Id, DisplayName, DeletedDateTime, UserPrincipalName[array]$DeletedCAPolicies = Get-MgBetaIdentityConditionalAccessDeletedItemPolicy -All# Devices don't work yet#[array]$DeletedDevices = Get-MgDirectoryDeletedItemAsDevice -All$DeletedObjects = [System.Collections.Generic.List[Object]]::new()# Process deleted groupsForEach ($Item in $DeletedGroups) {[string]$DeletedDateTime = $null$TimeTillRemoval = $null$PermanentRemovalDue = $nullIf ($Item.DeletedDateTime) {[datetime]$DeletedDateTime = Get-Date($Item.deletedDateTime)$PermanentRemovalDue = Get-Date($DeletedDateTime).AddDays(30)$TimeTillRemoval = $PermanentRemovalDue - $Now}If ($Item.CreatedDateTime) {[datetime]$CreatedDateTime = Get-Date ($Item.createdDatetime)}$ReportLine = [PSCustomObject]@{Group = $Item.displayNameId = $Item.idCreated = Get-Date($CreatedDateTime) -format 'dd-MMM-yyyy HH:mm'Deleted = Get-Date($DeletedDateTime) -format 'dd-MMM-yyyy HH:mm''Permanent Deletion due' = Get-Date($PermanentRemovalDue) -format 'dd-MMM-yyyy HH:mm'DaysRemaining = $TimeTillRemoval.DaysType = "Group"}$DeletedObjects.Add($ReportLine)}# Deleted usersForEach ($Item in $DeletedUsers) {[string]$DeletedDateTime = $null$TimeTillRemoval = $null$PermanentRemovalDue = $nullIf ($Item.DeletedDateTime) {[datetime]$DeletedDateTime = Get-Date($Item.deletedDateTime)$PermanentRemovalDue = Get-Date($DeletedDateTime).AddDays(30)$TimeTillRemoval = $PermanentRemovalDue - $Now}If ($Item.CreatedDateTime) {[datetime]$CreatedDateTime = Get-Date ($Item.createdDatetime)}$ReportLine = [PSCustomObject]@{Group = $Item.displayNameId = $Item.idCreated = Get-Date($CreatedDateTime) -format 'dd-MMM-yyyy HH:mm'Deleted = Get-Date($DeletedDateTime) -format 'dd-MMM-yyyy HH:mm''Permanent Deletion due' = Get-Date($PermanentRemovalDue) -format 'dd-MMM-yyyy HH:mm'DaysRemaining = $TimeTillRemoval.DaysType = "User"}$DeletedObjects.Add($ReportLine)}# Deleted service principalsForEach ($Item in $DeletedSPs) {[string]$DeletedDateTime = $null$TimeTillRemoval = $null$PermanentRemovalDue = $nullIf ($Item.DeletedDateTime) {[datetime]$DeletedDateTime = Get-Date($Item.deletedDateTime)$PermanentRemovalDue = Get-Date($DeletedDateTime).AddDays(30)$TimeTillRemoval = $PermanentRemovalDue - $Now}If ($Item.CreatedDateTime) {[datetime]$CreatedDateTime = Get-Date ($Item.createdDatetime)}$ReportLine = [PSCustomObject]@{Group = $Item.displayNameId = $Item.idCreated = Get-Date($CreatedDateTime) -format 'dd-MMM-yyyy HH:mm'Deleted = Get-Date($DeletedDateTime) -format 'dd-MMM-yyyy HH:mm''Permanent Deletion due' = Get-Date($PermanentRemovalDue) -format 'dd-MMM-yyyy HH:mm'DaysRemaining = $TimeTillRemoval.DaysType = "Service Principal"}$DeletedObjects.Add($ReportLine)}# Deleted administrative unitsForEach ($Item in $DeletedAUs) {[string]$DeletedDateTime = $null$TimeTillRemoval = $null$PermanentRemovalDue = $nullIf ($Item.DeletedDateTime) {[datetime]$DeletedDateTime = Get-Date($Item.deletedDateTime)$PermanentRemovalDue = Get-Date($DeletedDateTime).AddDays(30)$TimeTillRemoval = $PermanentRemovalDue - $Now}If ($Item.CreatedDateTime) {[datetime]$CreatedDateTime = Get-Date ($Item.createdDatetime)}$ReportLine = [PSCustomObject]@{Group = $Item.displayNameId = $Item.idCreated = Get-Date($CreatedDateTime) -format 'dd-MMM-yyyy HH:mm'Deleted = Get-Date($DeletedDateTime) -format 'dd-MMM-yyyy HH:mm''Permanent Deletion due' = Get-Date($PermanentRemovalDue) -format 'dd-MMM-yyyy HH:mm'DaysRemaining = $TimeTillRemoval.DaysType = "Administrative Unit"}$DeletedObjects.Add($ReportLine)}# Deleted applicationsForEach ($Item in $DeletedApps) {[string]$DeletedDateTime = $null$TimeTillRemoval = $null$PermanentRemovalDue = $nullIf ($Item.DeletedDateTime) {[datetime]$DeletedDateTime = Get-Date($Item.deletedDateTime)$PermanentRemovalDue = Get-Date($DeletedDateTime).AddDays(30)$TimeTillRemoval = $PermanentRemovalDue - $Now}If ($Item.CreatedDateTime) {[datetime]$CreatedDateTime = Get-Date ($Item.createdDatetime)}$ReportLine = [PSCustomObject]@{Group = $Item.displayNameId = $Item.idCreated = Get-Date($CreatedDateTime) -format 'dd-MMM-yyyy HH:mm'Deleted = Get-Date($DeletedDateTime) -format 'dd-MMM-yyyy HH:mm''Permanent Deletion due' = Get-Date($PermanentRemovalDue) -format 'dd-MMM-yyyy HH:mm'DaysRemaining = $TimeTillRemoval.DaysType = "Application"}$DeletedObjects.Add($ReportLine)}# Deleted conditional access policiesForEach ($Item in $DeletedCAPolicies) {[string]$DeletedDateTime = $null$TimeTillRemoval = $null$PermanentRemovalDue = $nullIf ($Item.DeletedDateTime) {[datetime]$DeletedDateTime = Get-Date($Item.deletedDateTime)$PermanentRemovalDue = Get-Date($DeletedDateTime).AddDays(30)$TimeTillRemoval = $PermanentRemovalDue - $Now}If ($Item.CreatedDateTime) {[datetime]$CreatedDateTime = Get-Date ($Item.createdDatetime)}$ReportLine = [PSCustomObject]@{Group = $Item.displayNameId = $Item.idCreated = Get-Date($CreatedDateTime) -format 'dd-MMM-yyyy HH:mm'Deleted = Get-Date($DeletedDateTime) -format 'dd-MMM-yyyy HH:mm''Permanent Deletion due' = Get-Date($PermanentRemovalDue) -format 'dd-MMM-yyyy HH:mm'DaysRemaining = $TimeTillRemoval.DaysType = "Conditional Access Policy"}$DeletedObjects.Add($ReportLine)}If ($DeletedObjects.count -eq 0) {Write-Host "No deleted items can be found - exiting"; break} Else {Write-Host ""Write-Host ("Count of deleted applications {0}" -f $DeletedApps.count)Write-Host ("Count of deleted administrative unuts {0}" -f $DeletedAUs.count)Write-Host ("Count of deleted groups {0}" -f $DeletedGroups.count)Write-Host ("Count of deleted service principals {0}" -f $DeletedSPs.count)Write-Host ("Count of deleted users {0}" -f $DeletedUsers.count)Write-Host ("Count of deleted conditional access policies {0}" -f $DeletedCAPolicies.count)Write-Host ""Write-Host ("Total count of deleted items {0}" -f $DeletedObjects.count)}$DeletedObjects | Sort-Object {$_.PermanentDeleteOn -as [datetime]} | Out-GridView$DeletedObjects | Export-Csv -Path $CSVOutputFile -Encoding UTF8 -NoTypeInformation