Computer Recovery from Active Directory Deleted Objects

Computer Recovery from Active Directory Deleted Objects

First, some background as to how we got to this point. At the company I work for, we had a problem. Our Active Directory environment was full of computer accounts that belonged to PCs that had been retired/recycled. This was because the arrangement we had with our sub-companies made it so that they handled the implementation and removal of computer hardware themselves, we just provided them the hardware and operating system image. As a result, there was no real deprovisioning process and the sites dumped the hard drives into a shredder without ever removing them from the domain, leaving thousands of orphaned computer accounts to clutter our AD. As a solution, it was decided to run a script daily that checked to see if each computer had been logged into for the last 30 days, and if it wasn't the PC would be disabled. If it hadn't been logged into for 90 then the PC account would just be deleted from our AD. Problem solved, right?

Well, not quite. See, we run healthcare facilities, and as a result they cannot be with the downtime losing a PC results in, so to hedge against that they purchased two PCs for every one they needed. Therefore, they had a good stock of computers in a closet in case one died for a quick swap out. Issue was, our script deleted the PC objects related to those computers, and as a result nobody could log in as the PC no longer had a trust relationship without it's computer account. IT techies couldn't get in either, as there was no way to get the local admin password as the LAPS attribute wasn't saved anywhere (in retrospect, the script should have saved the LAPS password somewhere before deleting it). So the only option for saving these was a reimage, which (due to a large amount of complexities I won't get into) is a very time consuming and expensive process.

End result? Lots of tickets, close to 100, about PCs pulled from closets that couldn't be logged into. And yea, that's a lot of UPS labels. So I decided we needed to make use of the AD Recycle Bin. Luckily, it was enabled on our domain and just wasn't configured correctly for most admins. When this feature is enabled, every time someone deletes a computer object from Active Directory it isn't deleted, just moved to a new container called "Deleted Objects" in a process called "tombstoning". The resulting "tombstone" object contains most of the properties of the original, and as such can be brought back by a domain administrator. Issue is, it's very unlikely that the person dealing with the calls about these computers is a domain admin, so we need to allow other people to look into the folder.  

Very fancy container

Doing this isn't super hard, just it requires a bit of set up in regards to the permissions people are given over this object. Essentially, in order to interact with it as a normal admin, you need to have the same perms as a domain admin would over "Deleted Objects". Also, you need the "reanimate tombstones" permission on the root Active Directory domain in order to bring them back to life. Microsoft has a guide on how to do this, but I find that it's missing a couple of permissions and through trial and error and some old forum threads found the correct perms needed.

Here are the permissions you need to grant to use this tool completely (Can be done on an individual basis, but I would recommend assigning to a security group):

dsacls "dc=fmc,dc=inc" /g "fmcinc\groupname:ca;Reanimate Tombstones"
dsacls "cn=deleted objects,dc=fmc,dc=inc" /g fmcinc\groupname:SDRPWOCCDCLCWSWPRC

This must naturally be run on the DC by a domain admin. Do note that, while maybe not the most sensitive perms in the world, this could result in admins gaining access to sensitive deleted computer accounts. I'm not sure how much of a threat that is in the real world, but in my research I found at least one CTF that required it's abuse. Don't run random commands on your DC because some random in a blog told you it might work. Make sure you know what you're giving out (and in this case it's almost everything in this OU).

Once these permissions were granted, a new problem arose. I will not keep my love of Active Directory Administrative Center (ADAC) a secret, but in this case my trust appears to have been misplaced. See, we use Azure Active Directory Connect to sync our domain to Entra ID (or whatever they call it now). This results in a load of junk "deleted objects" being created for azure-enabled security groups and mailboxes every time the sync runs. This on top of PCs recreating their printer maps (which leads to junk entries being sent to deleted objects). ADAC can only display up to 100,000 entries at once, and in an org as large as ours that just isn't enough. After a struggle, you will just get an error and it will give up.

The default shown here is 20,000, but even at the max value it still isn't enough for our enviroment

This is a slight problem, but not an insurmountable one. We could use the LDAP UI, but that's not super fun to work with. The better option is to just use the AD PowerShell module to do the heavy lifting for us.

The command that I'm going to use is this:

Get-ADObject -Filter {((ObjectClass -eq "Computer") -and (isDeleted -eq "TRUE")) -and (Name -like "*pcname*")} -IncludeDeletedObjects -Properties * | Format-List Name, LastKnownParent, Modified, objectSID, ms-Mcs-AdmPwd

Lets go into it how it works for the PowerShell-uninitiated (so if you want to customize it you can). Get-ADObject does exactly what it sounds like. We are just grabbing something from AD. Next, we need to filter it down to what we need.

First, I check to make sure it's a computer (I don't need users although it's unlikely they will appear anyways), and also that it's actually deleted (I don't want to run anything against a normal AD account). As such, (ObjectClass -eq "Computer") -and (isDeleted -eq "TRUE") will check that for us. If both of those are true, I also only want the PC with the name I want back. As such, we'll add another filter (Name -like "*pcname*"). Note the asterisks, I don't actually know what the PC name is a lot of the time but our provisioning script appends the serial number of the PC to the name, so I'm just telling it to look for a PC with that number included somewhere in the name but I don't require it to be exact. At a minimum you will need the right-side asterisk because all deleted object names are appended with `nDEL:<id> and there is absolutely zero way for us to know what that ID is beforehand (not that we need to).

Finally, we need to tell it to look for deleted things, so add -IncludeDeletedObjects and also pull every property, add -Properties *. I then pipe that output into a formatted list to give me the PC name, what OU it used to live in, when it was deleted, what it's SID was, and what the LAPS password was (just in case I can't restore it for some reason). Result looks something like this:

Very deleted

That's great, but how do we play necromancer and bring it back to life? Well, we can just repeat that search again but instead of piping Format-List in we will instead pipe in Restore-ADObject. Also, for good measure lets add the -TargetPath parameter and tell it where it's supposed to go as our wayward script moved everything into a "disabled" OU before it deleted it.  

Let's see it:

Get-ADObject -Filter {((ObjectClass -eq "Computer") -and (isDeleted -eq "TRUE")) -and (Name -like "*pcname*")} -IncludeDeletedObjects | Restore-ADObject -TargetPath "OU=Computers,OU=Your,OU=Site,OU=Here,DC=Contoso,DC=INC"

And just like that, our PC is back from the grave! That being said, if you remember from my background info the PC was disabled first before being deleted, and as our restored object shares all the same properties as before it was deleted, it's still going to be disabled. We could try and run our search again and pipe something else in, but we can't as the tombstone object we were working with has dissapeared! It was deleted when we restored it, so we need to run another search to find it again.

This one is easy tho, same principal as last time, just with only the name as a filter.

Get-ADObject -Filter {Name -Like "stb-5cg032810j"} | Enable-ADAccount

And just like that (and after 30 or so minutes for it to replicate to all DCs), we are back! Downside tho, we have dozens of these left to do and running three commands forever is gonna get old fast. Let's take all these concepts and make a script.

param ($TargetCN, $pcName)
$pcSearchTerm = "*" + $pcName + "*"  
#Checked deleted objects folder for deleted computer objects matching search term  
$deletedPC = Get-ADObject -Filter {((ObjectClass -eq "Computer") -and (isDeleted -eq "TRUE")) -and (Name -like $pcSearchTerm)} -IncludeDeletedObjects -Properties *  
#Lists some info about PC  
$deletedPC | Format-List Name, LastKnownParent, Modified, objectSID, ms-Mcs-AdmPwd  
#Attempts to restore object  
try { $deletedPC | Restore-ADObject -TargetPath $TargetCN}
catch{"PC couldn't be reanimated"}

$revivedPC = Get-ADObject -Filter {Name -like $pcSearchTerm}
try{$revivedPC | Enable-ADAccount}
catch{"Could not re-enable AD Computer Account."}

This is pretty quick and dirty (I don't need every property lol), and the error handling is really not where it needs to be, but this will let me plug and chug a name and bring it back.

Hope this was helpful in some way to you and/or saved you a little more Google searching. Thanks for sticking with me!

~Zeke