Back to script library
Entra / Microsoft 365 · Users & guests

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 groups
ForEach ($Item in $DeletedGroups) {
[string]$DeletedDateTime = $null
$TimeTillRemoval = $null
$PermanentRemovalDue = $null
If ($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.displayName
Id = $Item.id
Created = 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.Days
Type = "Group"
}
$DeletedObjects.Add($ReportLine)
}
# Deleted users
ForEach ($Item in $DeletedUsers) {
[string]$DeletedDateTime = $null
$TimeTillRemoval = $null
$PermanentRemovalDue = $null
If ($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.displayName
Id = $Item.id
Created = 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.Days
Type = "User"
}
$DeletedObjects.Add($ReportLine)
}
# Deleted service principals
ForEach ($Item in $DeletedSPs) {
[string]$DeletedDateTime = $null
$TimeTillRemoval = $null
$PermanentRemovalDue = $null
If ($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.displayName
Id = $Item.id
Created = 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.Days
Type = "Service Principal"
}
$DeletedObjects.Add($ReportLine)
}
# Deleted administrative units
ForEach ($Item in $DeletedAUs) {
[string]$DeletedDateTime = $null
$TimeTillRemoval = $null
$PermanentRemovalDue = $null
If ($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.displayName
Id = $Item.id
Created = 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.Days
Type = "Administrative Unit"
}
$DeletedObjects.Add($ReportLine)
}
# Deleted applications
ForEach ($Item in $DeletedApps) {
[string]$DeletedDateTime = $null
$TimeTillRemoval = $null
$PermanentRemovalDue = $null
If ($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.displayName
Id = $Item.id
Created = 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.Days
Type = "Application"
}
$DeletedObjects.Add($ReportLine)
}
# Deleted conditional access policies
ForEach ($Item in $DeletedCAPolicies) {
[string]$DeletedDateTime = $null
$TimeTillRemoval = $null
$PermanentRemovalDue = $null
If ($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.displayName
Id = $Item.id
Created = 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.Days
Type = "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
Attribution