Skip to main content

How to restore a tree of objects from the Active Directory Recycle Bin

·710 words·4 mins
system-administration tech active-directory powershell sysadmin technology
James Pettigrove
Author
James Pettigrove
Cloud Engineer with a focus on Microsoft Azure
Active Directory Recycle Bin - This article is part of a series.
Part 2: This Article

In a previous post I detailed how to setup the Active Directory Recycle Bin and restore single deleted objects back in your Active Directory. Restoring a whole tree of objects though (i.e. Organisational Unit and the objects inside it) is no easy process.

Thankfully Microsoft via the community have provided a example script to do this:

Param (
 $lastKnownRDN,
 $lastKnownParent,
 $identity,
 $partition,
[switch] $includelivechildren,
[switch] $whatIf,
[switch] $verbose
)
 
###############################
##      Display Help         ##
###############################
 
function Display-Help {
""
"Usage:"
""
"Restore-ADTree  -lastKnownRDN "
"[-lastKnownParent ]"
"[-partition ]"
"[-includeLiveChildren]"
"[-whatif]"
"[-verbose]"
"OR"
""
"Restore-ADTree  -identity "
"[-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

Copy the above into a Powershell Script file named restoreadtree.ps1. There are a number of parameters we can use to perform the restore depending on your knowledge of the deleted object.

If you know the exact path of the Organisational Unit:

.\restoradtree.ps1 -identity ""OU=Admins,DC=DXPETTI,DC=COM""

This would restore the Organisational Unit “Admins” that sat at the top level of the DXPETTI.COM Active Directory domain.

Or if you couldn’t remember where an OU sat but still remember the name:

.\restoreadtree.ps1 -lastKnownRDN Admins

This would restore the Organisational Unit Admins despite not remembering the full path.

But what if you had multiple OUs with the same name? If you remember a part of the path then:

.\restoreadtree.ps1 -lastKnownRDN Admins -lastKnownParent ""OU=Users,DC=DXPETTI,DC=COM""

The above would restore the “Admins” OU that sat under (directly or otherwise) the User OU in the root level of the DXPETTI.com Active Directory domain.

Happy AD Recycle Bin restoring!

Active Directory Recycle Bin - This article is part of a series.
Part 2: This Article

Related

How to setup and restore a object from the Active Directory Recycle Bin
·310 words·2 mins
system-administration tech active-directory powershell sysadmin technology
CTRL+Z, the undo button, the recycle bin, shadow copies; The human element in the world of IT can some times be our undoing; This goes along way to explain the push to automate EVERY facet of the our IT systems.
Export users of one AD group and import to another
·196 words·1 min
system-administration tech active-directory powershell sysadmin technology windows
It’s quite common for members of one Active Directory security group to be replicate in another.
Utilizing Add-ADUser & Import-CSV Powershell Cmdlets to bulk create Active Directory accounts
·816 words·4 mins
system-administration tech active-directory powershell sysadmin technology
We’ve all been there…your company has just taken over or brought out another company and you have been given a list of new employees to receive network accounts or another example (and my reality) it is the start of a school year and you have a herd of new year 7 students that need network accounts.