Active Directory – Recycle Bin Feature


To totally unlock this section you need to Log-in

If an object has been deleted in your Active Directory, and you want it recovered, there are a number of things you can do.

What is the Recycle Bin Feature?

Simply put, ADRB allows you to recover objects immediately, without the need to use your System State backups, latent sites, or 3rd party add-ons. It does this by implementing two new attributes, and using two existing attributes:

isDeleted

  • Has existed since Windows 2000
  • Exists on every object
  • Describes if an object is deleted but restorable

isRecycled

  • New to Windows Server 2008 R2
  • Exists on every object once it is recycled
  • Describes if an object is deleted but not restorable

msDS-deletedObjectLifetime

  • New to Windows Server 2008 R2
  • Is set on the “CN=Directory Service,CN=Windows NT, CN=Services, CN=Configuration, DC=COMPANY,DC=COM” container
  • Describes how long a deleted object will be restorable

tombstoneLifetime

  • Has existed since Windows 2000
  • Is set on the “CN=Directory Service,CN=Windows NT, CN=Services, CN=Configuration, DC=COMPANY,DC=COM” container
  • Describes how long a deleted object will not be restorable

Requirements and Enabling

In order to turn AD Recycle Bin you will need to have implemented a Windows Server 2008 R2 Forest Functional Level. Wait, come back! Despite the terror it seems to inspire in our customers, increasing functional levels is not a big deal. In order to do it, you must:

  • Have extended your schema to Windows Server 2008 R2.
  • Have only Windows Server 2008 R2 DC’s in your forest.
  • Raise your domain(s) functional level.
  • Raise your forest’s functional level.

Note: Did you know that in Windows Server 2008 R2, you can actually lower the functional level back to 2008? As long as you have not turned the Recycle Bin feature on, the domain and forest functional levels that are at 2008 R2 can be reverted to 2008 with a simple PowerShell command. Here is an example of lowering it back to 2008 when it was already at 2008 R2:

Set-AdForestMode -identity contoso.com -server dc1.contoso.com -forestmode Windows2008Forest
Set-AdDomainMode -identity contoso.com -server dc2.child.contoso.com -forestmode Windows2008Domain

This means you can go to the R2 functional level, make sure your environment is not having any issues, then if you are satisfied you can enable the Recycle Bin. At that point you can no longer revert.

Cycle of Deleted Objects

Take a look at the following images of the cycle of a deleted object in the Active Directory before and after enabling “Active Directory Recycle Bin”:

Active Directory - Recycle Bin Feature

Active Directory - Recycle Bin Feature

What happens to a Deleted Active Directory Object?

Before

  1. Deleted object enters a “tombstone” state.
  2. Attribute “IsDeleted” is changed to TRUE value.
  3. The object is moved to “Deleted Objects” container (CN=Deleted Objects).
  4. The object is in the “tombstone” state for is 180 days for Windows Server 2003 SP1/ 2008 and 60 days in Windows Server 2000/2003.
  5. In tombstone state, most of the link-valued and non-linked value attributes are stripped off.
  6. A process called “Garbage collector” removes the object from the database after the tombstone state expires.
  7. The object is completely erased.
  8. The object cannot be recovered.

Here the administrator has to use authoritative restoration to restore the deleted objects.

After

  1. The deleted object enters a “logically deleted” state.
  2. Attribute “IsDeleted” is changed to TRUE value.
  3. Value of “WhenDeleted” is changed to “Time Changed”.
  4. A unique value is assigned to Windows security descriptor.
  5. RDN is changed to an impossible value.
  6. The object is moved to “Deleted Objects” container (CN=Deleted Objects).v
  7. The object remains in the “logically deleted” state for a period of 60 to 180 days in Windows Server 2008 R2.
  8. As soon as an object enters “logically deleted” state, all the object’s link-valued and non-linked value attributes are preserved by the system. Following attributes are not stripped off: Object-GUID, Object-SID, Object-Dist-Name, USN.
  9. The object moves to “Recycle” state. It remains here for another 60 to 180 days.
  10. Most of the attributes are erased.
  11. After the expiry of recycled state, the garbage collection process starts, and it removes the object from the database.
  12. The object cannot be recovered.

The administrator can use Powershell commands, LDP.exe, and AD Administrative Center to restore deleted objects.

Enabling Active Directory Recycle Bin

Active Directory Recycle Bin can be activated only where all domain controllers are running Windows Server 2016, Windows Server 2012 R2, Windows Server 2012 or Windows Server 2008 R2.

Note: Enabling Active Directory Recycle Bin is irreversible.

Execute the following command, in Powershell, to enable Active Directory Recycle Bin:

Enable-ADOptionalFeature –Identity 'CN=Recycle Bin Feature,CN=Optional Features,CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,DC=www,DC=domain,DC=com' –Scope ForestOrConfigurationSet –Target 'www.domain.com'

If you are using Windows Server 2016, Windows Server 2012 R2, or Windows Server 2012, you can use the Active Directory Administrative Center to enable the Recycle Bin.

The following is another step by step into Powershell to enable this feature (and not using the "one-liner" above).

Logon to your “Domain Naming Master” DC as an Enterprise Administrator and start Powershell; it’s that big blue icon on your taskbar and then load the AD PowerShell module:

Import-module ActiveDirectory

Run the following cmdlet to turn on the Recycle Bin:

Enable-ADOptionalFeature 'Recycle Bin Feature' -Scope ForestOrConfigurationSet -Target  

The command will prompt you for a last chance. Enter Y to turn the Recycle Bin on.

Active Directory - Recycle Bin Feature

That’s it, you’re done.

Note: If you just can’t be bothered to logon to your domain naming master or type in forest names, you can use the following PowerShell command to do everything for you (and learn a useful technique for using object properties):

Enable-ADOptionalFeature "Recycle Bin Feature" -server ((Get-ADForest -Current LocalComputer).DomainNamingMaster) -Scope ForestOrConfigurationSet -Target (Get-ADForest -Current LocalComputer)

Controlling the Lifetime of Deleted Objects

To control the length of a time that deleted objects will be recoverable, you will need to modify the msDS-deletedObjectLifetime attribute that lives on the Directory Service container. Microsoft really hopes you won’t mess with it but here is how to do it correctly in PowerShell. Remember that you are setting this value in days:

Set-ADObject -Identity "CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,DC=" -Partition "CN=Configuration,DC=" -Replace:@{"msDS-DeletedObjectLifetime" = }

For example, in my Contoso.com forest we will set Deleted Object Lifetime to 365 days:

Active Directory - Recycle Bin Feature

To see the current Deleted Object Lifetime, we use Get-AdObject:

Active Directory - Recycle Bin Feature

Using the Recycle Bin

We’ve got everything setup, so let’s start with a simple restore scenario. Again, we will use PowerShell to do all the work.

The Sales OU contains a few users, including SaraDavis:

Active Directory - Recycle Bin Feature

For example, last night one of the administrators was told that an employee named Sarah Davis had left the company and her account needed to be deleted. Unfortunately, the email from HR misspelled her name, and so the Administrator deleted Sara Davis. Let’s look at a few ways to examine the deleted user information with PowerShell:

Get-ADObject -filter 'isdeleted -eq $true -and name -ne "Deleted Objects"' -includeDeletedObjects -property *

The command above will list out all deleted objects and all the attribute data on those objects. So we run it:

Active Directory - Recycle Bin Feature

Note how all the attribute data has been preserved, including group memberships – SaraDavis was a member of the Sales VPs group. Ouch, deleting an executive is never good for a career.

There is a ton of data being returned here, and while interesting We are not sure we care all that much.

When restoring users we just need the bare minimum information to make sure that this is the right object we need to get back; we don’t want this whole Sarah/Sara debacle again. So we will narrow the output with some pipelining:

Get-ADObject -filter 'isdeleted -eq $true -and name -ne "Deleted Objects"' -includeDeletedObjects -property * | Format-List samAccountName,displayName,lastknownParent

Active Directory - Recycle Bin Feature

By adding the pipeline like above, we are feeding the query results to the Format-List cmdlet, and return the user’s logon ID, display name, and last known parent location when it was deleted; much more concise.

We can use a couple methods to restore the user, such as querying for the user and feeding it through a pipeline to Restore-ADObject:

Get-ADObject -Filter 'samaccountname -eq "SaraDavis"' -IncludeDeletedObjects | Restore-ADObject

We can also simply call Restore-ADObject directly as long as we have dumped out the user’s distinguished name or GUID:

Active Directory - Recycle Bin Feature

Restoring multiple objects

In this next scenario it turns out that someone dropped an apple on their keyboard and deleted all the users in the Sales OU.

Until we get those users back they can’t do their work, and our company can’t sell its products. Knowing what we know now from the first scenario, this starts getting easier and more familiar.

Since there have been a bunch of users deleted, returning the data in a list format isn’t ideal. This time we will use a table format:

Get-ADObject -filter 'isdeleted -eq $true -and name -ne "Deleted Objects"' -includeDeletedObjects -property * | Format-Table msds-lastKnownRdn,lastknownParent -auto -wrap

To retore the actual users, we will simply base my query on the last known parent OU. Since all the users in Sales were deleted, my query simply finds all objects in that OU and pipelines them to Restore-ADobject:

Get-ADObject -filter 'lastKnownParent -eq "OU=Sales,DC=Contoso,DC=com"' -includeDeletedObjects | restore-adobject

Active Directory - Recycle Bin Feature

Restoring all objects based on time and date

In a large complex environment we may not simply want to restore all users deleted from an OU. For example, if we had all my users stored in the default Users container, simply restoring every object could bring back users that were supposed to be deleted.

In that case we can use a date and time rule to simply restore all the objects that were deleted by a provisioning script went berserk at 1:40AM. To get these users back, we will first populate a variable that describes the time criteria:

$changedate = New-Object Datetime(2009, 8, 22, 1, 40, 00)

This variable stores the output of the Datetime conversion for 1:40:00 AM, August 22, 2009.

Now we can search for any objects deleted after that time using a variation on our usual syntax. Part of my filter will now include 'whenChanged is greater than ':

Get-ADObject -filter 'whenChanged -gt $changedate -and isDeleted -eq $true' -includeDeletedObjects -property * | ft samaccountname,lastknownparent -auto -wrap

Active Directory - Recycle Bin Feature

Having examined what we could be restoring, we then restore those objects:

Active Directory - Recycle Bin Feature

This is one of the powers of AD Recycle Bin over the older authoritative restore system. You never had this much control before, and were stuck restoring extraneous objects even if they were intentionally deleted. Not to mention they were usually from yesterday, not 30 minutes ago.

Restoring all objects in a deleted OU

A key point to understand and remember with AD Recycle Bin is that you must restore hierarchically; a parent object must be restored before a child object. So if we was to delete an entire OU and all its contents, we must first restore the OU before we can restore its contents.

This time someone managed to delete the entire Sales OU and its five users. In order to demonstrate how objects are stored in the Deleted Objects container we will be doing a few extra steps here.

First, we will dump out all deleted objects with their last known parents and its last known relative distinguished name.

Get-ADObject -filter 'isDeleted -eq $true' -and name -ne "Deleted Objects"' -includeDeletedObjects -property * | ft msds-lastKnownRdn,lastKnownParent -auto -wrap

Active Directory - Recycle Bin Feature

We can see all the deleted users including the Sales OU. Note how lastKnownParent attribute for the deleted users is actually the deleted Sales OU distinguished name.

If we tried to restore those child objects right now nothing would happen, as their parent is deleted. So it is better restore the Sales OU first; to do that we will specify the OU’s last known RDN and its last known parent:

Get-ADObject -filter 'msds-lastKnownRdn -eq "Sales" -and lastKnownParent -eq "DC=contoso,DC=com"' -includeDeletedObjects | Restore-ADObject

Before we restore the user objects we will take a look at the same query we ran originally. Notice how the last known parent has magically changed! Now if we run the restore, pointing the last known parent to the Sales OU, all the child objects are restored.

Active Directory - Recycle Bin Feature

Restoring multiple objects and nested OU’s

Consider this complex restore scenario:

Active Directory - Recycle Bin Feature

We have multiple child OU’s, each with child user objects. And now someone has gone and deleted the parent Sales OU. Restoring all those parent-child relationships is going to be a bit of a drag, right?

Not if you keep this script handy:

Param (
 $lastKnownRDN,
 $lastKnownParent,
 $identity,
 $partition,
[switch] $includelivechildren,
[switch] $whatIf,
[switch] $verbose
)

###############################
##      Display Help         ##
###############################

function Display-Help {
""
"Usage:"
""
"Restore-ADTree  -lastKnownRDN <lastknown RDN of Deleted object>"
"[-lastKnownParent <distinguishedname Of LastKnownParent>]"
"[-partition <name of partition>]"
"[-includeLiveChildren]"
"[-whatif]"
"[-verbose]"
"OR"
""
"Restore-ADTree  -identity <distinguised Name or GUID of Live or Deleted Object>"
"[-partition <name of partition>]"
"[-includeLiveChildren]"
"[-whatif]"
"[-verbose]"
""
"Examples:"
""
"Restore-ADTree -lastknownRDN Accounting"
""
"Restore-ADTree -lastKnownRDN Accounting -lastknownParent ""DC=CONTOSO,DC=COM"" "
""
"Restore-ADTree -identity b48290aa-e14f-4417-9c03-560a546d18b9"
""
"Restore-ADTree -identity ""OU=Accounting,DC=CONTOSO,DC=COM"" "
""
}

###############################
##     Validate Parameters   ##
###############################

if (!(($identity) -or ($lastKnownRDN))){
display-help
break
}

if (($identity) -and (($lastKnownRDN) -or ($lastKnownParent))){
display-help
break
}

####################################################
##     INCOMPLETE Get RDNType given objClass      ##
####################################################

Function Get-RDNType {
Param($objClass)
switch ($objClass){
"container"{Return "CN"}
"OrganizationalUnit" {Return "OU"}
default {Return "CN"}
}
}

############################################
##     Restore-Tree Recursive Function    ##
############################################

function Restore-Tree($strObjectGUID ,$strNamingContext, $bIncludeLiveChildren, $bWhatIf ,$strRestoredPrevParentDN)
{
$objRestoredParent = $null
$objRestoredParent = get-adobject -identity $strObjectGUID -partition $strNamingContext
if ($objRestoredParent){ 
if (!($bWhatIf)){
Write-Host ""Not restoring live object $objRestoredParent.distinguishedName .""  -ForeGroundColor Yellow
} else {
Write-Host ""Will not restore live object $objRestoredParent.distinguishedName .""  -ForeGroundColor Yellow
}
$strLiveSearchBase = $objRestoredParent.distinguishedName
$RestoredDN = $objRestoredParent.distinguishedName
} else {
if (!($bWhatIf)){

Restore-ADobject -identity $strObjectGUID -partition $strNamingContext -errorVariable errRestore 

if  (($errRestore)){
$objRestoredParent = get-adobject -identity $strObjectGUID -partition $strNamingContext -includeDeletedObjects
Write-Host ""Restore of object $objRestoredParent.distinguishedName failed.`n Error: $errRestore""  -ForeGroundColor Red
Exit Function
} else {
$objRestoredParent = get-adobject -identity $strObjectGUID -partition $strNamingContext
$RestoredDN = $objRestoredParent.distinguishedName
Write-Host ""Successfully restored object $objRestoredParent.DistinguishedName"" -ForeGroundColor Green
}
} else {
$objRestoredParent = get-adobject -identity $strObjectGUID -partition $strNamingContext -properties msds-lastknownRDN,lastKnownParent,whenChanged  -includeDeletedObjects

$RestoredDN = $(Get-RDNType($objRestoredParent.ObjectClass)) + "=" + $objRestoredParent.("msds-lastKnownRDN") + "," + $strRestoredPrevParentDN
Write-Host ""Will restore deleted object $RestoredDN""  -ForeGroundColor Green
if ($verbose){
Write-Host ""Deleted DN: $objRestoredParent.distinguishedName `n whenDeleted: $objRestoredParent.("whenChanged") "" 
}
}
$strLiveSearchBase = $null
}

if (($strLiveSearchBase) -and ($bIncludeLiveChildren)) {
$strFilter = "(objectClass=*)"
$objChildren = get-adobject -SearchScope onelevel -SearchBase $strLiveSearchBase -ldapFilter $strFilter  -ResultPageSize 300 -ResultSetSize 10000
        if ($objChildren -ne $null){
        foreach ($objChild in $objChildren)
{
Restore-Tree $objChild.objectGUID $strNamingContext $bIncludeLiveChildren $bWhatIf $RestoredDN
}
}
}

$strSearchBase = "CN=Deleted Objects,"+$strNamingContext
$strFilter = "(lastknownParent=" + $objRestoredParent.distinguishedName.Replace("\0","\\0") + ")"
$objChildren = get-adobject -SearchScope subtree -SearchBase $strSearchBase -includedeletedobjects -ldapFilter $strFilter -ResultPageSize 300 -ResultSetSize 10000

if ($objChildren -ne $null){
Write-Host ""Restoring deleted children of $RestoredDN""
foreach ($objChild in $objChildren)
{
Restore-Tree $objChild.objectGUID $strNamingContext $bIncludeLiveChildren $bWhatIf $RestoredDN
}
}
}

######################
##  Main Function   ##
######################

$ErrorActionPreference = "SilentlyContinue"
if (!($partition)){
$strNamingContext = [string] (get-adrootDSE).defaultNamingContext
} else {
$strNamingContext = $partition
}

$strDelObjContainer = "CN=Deleted Objects,"+$strNamingContext

if ($identity){
$objSearchResult = get-adobject -identity $identity -partition $strNamingContext -includeDeletedObjects
} else {
$strFilter = "(msds-lastknownRDN=" + $lastKnownRDN + ")" 
$objSearchResult = get-adobject -SearchScope subtree -SearchBase $strDelObjContainer -includedeletedobjects -ldapFilter $strFilter -properties lastknownparent,whenChanged,isDeleted  
}

If (!($objSearchResult))  {
Write-Host "Search for tree root returned 0 objects.Exiting without making changes.";Exit
} Else {
$objMeasure = $objSearchResult | Measure-Object
If ($objMeasure.Count -gt 1) {
Write-Host "Search for tree root returned more than one object.Rerun command and select one of below objects" -ForeGroundColor Yellow
foreach ($objRoot in $objSearchResult) { $objRoot}
break
}
}

if ($objSearchResult.isDeleted) {
$PrevParent = $objSearchResult.("lastKnownParent")
$bRootIsDeleted = $true
} else {
$PrevParent = $objSearchResult.distinguishedName
$bRootIsDeleted = $false
}

Restore-Tree $objSearchResult.objectGUID $strNamingContext $includelivechildren $whatIf $PrevParent -errorVariable errRestore

By running this script and specifying a last known RDN and perhaps last known parent, it will walk all child objects and do the work for you. It even has pretty colors. Slick and simple.

Active Directory - Recycle Bin Feature

Best Practices

Here are some of the best practices MS Support have come up with.

  • Keep taking real System State backups - The AD recycle Bin makes object restores quick and easy. But it is not going to help you if your only four DC’s are ruined by the same leaky plumbing in your data center. And you won’t be restoring the deleted contents of SYSVOL with the recycle bin. And you won’t be using the Recycle Bin to undo the damage from script that replaces every user’s mailbox attribute with the wrong server name.
  • Lowering deleted object lifetime not recommended - There may be a temptation to drop the msds-DeletedObjectLifetime down to a few days in order to save space in your AD database. The longer you keep deleted objects around the better. And when it gets down to it - hard drive disk space is ridiculously cheap. A massive AD database would be 40GB.
  • Practice and train - Don’t just turn on the Recycle Bin. Run through disaster recovery exercises periodically to make sure you and your staff know what they are doing. You don’t want to be figuring out how to recover your CEO’s user account; it should simply be a routine restore operation. Hyper-V makes test labs cheap and easy.
  • Turn on DS change auditing for deletions - The Recycle Bin will not tell you who deleted your users, groups, and computers. Principal deletions are rare operations in AD and should definitely be audited. I can tell you from many, many CritSits here that after the objects are back, the next thing your management will want to know is “what happened?” Make sure you have an answer, or your own account may be deleted.

Active Directory - Recycle Bin Feature

  • Use PowerShell, not LDP.exe – There are a number of limitations within LDP.EXE, such as its clunky interface and need for cryptic optional settings. The biggest one I see though is that when displaying deleted objects, it cannot display all the attribute data, such as the critical memberOf that shows you group memberships.

Troubleshooting common issues

Here are a few errors people tend to run into the first time they start using the Recycle Bin. Don’t worry, they happen to everyone and have straightforward solutions:

Error: An attempt was made to add an object to the directory with a name that is already in use

Cause: Someone has created an object with the same distinguished name. This may be because someone jumped the gun and tried to fix the deleted user by recreating it. Just move it elsewhere temporarily, make sure it has no other attribute duplications as well, finish the restore of the real object, then go figure out what happened.

Error: The operation could not be performed because the object’s parent is either uninstantiated or deleted

Cause: The object’s parent was also deleted and hasn’t been restored. Usually it’s an OU. Restore that parent first.

Error: Illegal modify operation. Some aspect of the modification is not permitted

Cause: Often this is caused by trying to restore an object without having the Recycle Bin enabled. You may see this error in other scenarios though.

Active Directory Administrative Center

To use the Active Directory Recycle Bin, you can also use the ADAC (Active Directory Administrative Center) console (Windows Server 2016 and above).

In the Server Manager management console, go to Tools > Active Directory Administrative Center:

Active Directory - Recycle Bin Feature

Click the Deleted Objects folder:

Active Directory - Recycle Bin Feature

Search the list of deleted objects for the object that needs to be restored.

Active Directory - Recycle Bin Feature

Right-click the selected object and select Restore from the shortcut menu.

Active Directory - Recycle Bin Feature

Depending on your business practices and environment, object lifetime and the tombstone lifetime can be increased or decreased. Tombstone lifetime is determined via the value of the tombstoneLifetime attribute and the deleted object lifetime is determined by the value of the msDS-deletedObjectLifetime attribute.

By default, tombstoneLifetime is set to null, which expires the tombstone lifetime defaults after 60 days. By default, msDS-deletedObjectLifetime is also set to null and changes to the value of the tombstone lifetime should it ever be changed.