Loading…

Managing the Active Directory Recycle Bin Without PowerShell

The Active Directory Recycle Bin is a feature of recent versions of Windows Server that allows you to restore deleted objects to full fidelity without having to reanimate a tombstone. The biggest problem with the recycle bin is that it’s disabled by default. To enable it you need to use the Enable-ADOptionalFeature cmdlet from the ActiveDirectory PowerShell module. Although this is easy enough, it requires that you have the PowerShell module installed. The module itself uses the Active Directory Web Services to issue requests. After digging through the webservice a bit, I found that commands need to manage the recycle bin without PowerShell.

Query the Status of the Recycle Bin

public bool IsEnabled()
{
   var dcString = _domain.Split('.').Aggregate((x, y) => "DC=" + x + ",DC=" + y);

   using (var directoryEntry = new DirectoryEntry($"LDAP://{_computerName}/CN=Recycle Bin Feature,CN=Optional Features,CN=Directory     Service,CN=Windows NT,CN=Services,CN=Configuration,{dcString}", _username, _password))
   {
     var enabledScopes = directoryEntry.Properties["msDS-EnabledFeatureBL"].Value as object[];
     return enabledScopes != null && enabledScopes.Length > 0;
   }
 }

Enable the Recycle Bin

public void Enable()
{
   var cnString = _domain.Split('.').Aggregate((x, y) => "DC=" + x + ",DC=" + y);
   var featureId = "766ddcd8-acd0-445e-f3b9-a7f9b6744f2a";
   var featureDn = $"CN=Partitions,CN=Configuration,{cnString}";
   var connectionObject = new LdapConnection(new LdapDirectoryIdentifier(_computerName), new NetworkCredential(_username, _password));
   string value = featureDn + ":" + featureId;
   var directoryAttributeModification = new DirectoryAttributeModification();
   directoryAttributeModification.Name = "enableOptionalFeature";
   directoryAttributeModification.Operation = DirectoryAttributeOperation.Add;
   directoryAttributeModification.Add(value);
   var dirResponse = (ModifyResponse)connectionObject.SendRequest(new ModifyRequest
   {
       DistinguishedName = "",
       Modifications =
       {
            directoryAttributeModification
       }
   });
}

Restore Object From Recycle Bin

public void RestoreDirectoryEntry(string objectGuid)
        {
            using (var root = new DirectoryEntry($"LDAP://{_computerName}", _username, _password))
            {
                using (var searcher = new DirectorySearcher(root))
                {
                    searcher.SearchScope = SearchScope.Subtree;
                    searcher.Tombstone = true;
                    searcher.Filter = $"(objectguid={ConvertGuidToOctetString(new Guid(objectGuid))}*)";
                    var result = searcher.FindOne();

                    var newDn = string.Format(CultureInfo.InvariantCulture, "CN={0},{1}",
                                    result.Properties["cn"][0].ToString().Split(new[] { '\n' })[0],
                                    result.Properties["lastKnownParent"][0]);

                    var dam = new DirectoryAttributeModification
                    {
                        Name = "isDeleted",
                        Operation = DirectoryAttributeOperation.Delete
                    };

                    var dam2 = new DirectoryAttributeModification
                    {
                        Name = "distinguishedName",
                        Operation = DirectoryAttributeOperation.Replace
                    };
                    dam2.Add(newDn);

                    var mr = new ModifyRequest(
                        result.Properties["distinguishedName"][0].ToString(),
                        new[] { dam, dam2 });

                    mr.Controls.Add(new ShowDeletedControl());

                    // Get LdapConnection
                    LdapConnection ldapConnection = null;
                    try
                    {
                        ldapConnection = new LdapConnection(new LdapDirectoryIdentifier(_computerName), new NetworkCredential(_username + "@" + _domain, _password));
                        var response = (ModifyResponse)ldapConnection.SendRequest(mr);
                    }
                    finally
                    {
                        if (ldapConnection != null) ldapConnection.Dispose();
                    }
                }
            }
        }

        internal string ConvertGuidToOctetString(Guid objectGuid)
        {
            byte[] byteGuid = objectGuid.ToByteArray();

            string queryGuid = "";

            foreach (byte b in byteGuid)
            {
                queryGuid += @"\" + b.ToString("x2");
            }

            return queryGuid;
        }

Leave a Reply