Appendix B: Restore Multiple, Deleted Active Directory Objects (Sample Script)

Applies To: Windows Server 2008 R2

This appendix contains a sample Windows PowerShell script that you can use to recover a deleted tree of Active Directory objects. To recover a deleted tree of Active Directory objects by using this sample script, save the following code as a .ps1 file and then run it at the command prompt in the Active Directory module for Windows PowerShell.

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