Monday, 5 August 2013

Restore a deleted Active Directory object using C#

  Restoring deleted Active Directory objects is contains multiple tasks, Here I have written complete functions to recover deleted objects in C#. Before proceed, you need to add two reference assemblies System.DirectoryServices and System.DirectoryServices.Protocols.



static void Main()
  {
     string domainDN = "DC=Work2008,DC=local";
     string deletedObjectGuid = "AC56F9F3-C11C-456F-92B1-5FAFFB493A6D";
     SearchResult searchResult = SearchAndGetDeletedObject(domainDN, deletedObjectGuid);

     if (searchResult != null)
     {
       string deletedObjectDN = searchResult.Properties["distinguishedName"][0].ToString();
       string newDN = GetNewDNToRestore(searchResult);
       string serverName = "DevDC";
       NetworkCredential netCredential = new NetworkCredential("Administrator", "password","work2008.local");
       RestoreTombstone(deletedObjectDN, newDN, serverName, netCredential);
     }    
  }


private static SearchResult SearchAndGetDeletedObject(string domainDN, string deletedObjectGuid)
 {  
    DirectoryEntry dirEntry = new DirectoryEntry("LDAP://CN=Deleted Objects,"+domainDN,"Administrator", "password");
    dirEntry.AuthenticationType = AuthenticationTypes.FastBind | AuthenticationTypes.Secure;
    DirectorySearcher directorySearcher = new DirectorySearcher(dirEntry, string.Format("(&(isDeleted=TRUE)(objectguid={0}))", GetBinaryStringFromGuid(deletedObjectGuid)));
    directorySearcher.CacheResults = false;
    directorySearcher.SearchScope = System.DirectoryServices.SearchScope.OneLevel;
    directorySearcher.Tombstone = true;
    directorySearcher.PageSize = 10000;
    SearchResult searchResult = directorySearcher.FindOne();
    return searchResult;
 }


private static string GetBinaryStringFromGuid(string guidstring)
 {
    Guid guid = new Guid(guidstring);
    byte[] bytes = guid.ToByteArray();
    StringBuilder sb = new StringBuilder();
    foreach (byte b in bytes)
    {
      sb.Append(string.Format(@"\{0}", b.ToString("X")));
    }
    return sb.ToString();
 }


private static string GetNewDNToRestore(SearchResult searchResult)
 {
   string newDN = string.Empty;
   string cn = string.Format("cn={0}", searchResult.Properties["cn"][0].ToString().Split(new char[] {'\n' })[0]);
    newDN = string.Format("{0},{1}", cn, searchResult.Properties["lastKnownParen t"][0]);
    newDN = newDN.Replace(@"\", @"\\");
    return newDN;
 }



private static void RestoreTombstone(string deletedObjectDN, string newDN, string domainControllerName, NetworkCredential credential)
 {
    LdapConnection connection = new LdapConnection(new LdapDirectoryIdentifier(domainControllerName), credential, AuthType.Negotiate);
    using (connection)
    {
      string cn = string.Empty;
      connection.Bind();
      connection.SessionOptions.ProtocolVersion = 3;
      DirectoryAttributeModification isDeleteAttributeMod = new DirectoryAttributeModification();
      isDeleteAttributeMod.Name = "isDeleted";
      isDeleteAttributeMod.Operation = DirectoryAttributeOperation.Delete;
      DirectoryAttributeModification dnAttributeMod = new DirectoryAttributeModification();
      dnAttributeMod.Name = "distinguishedName";
      dnAttributeMod.Operation = DirectoryAttributeOperation.Replace; 
      dnAttributeMod.Add(newDN);
      ModifyRequest request = new ModifyRequest(deletedObjectDN,new DirectoryAttributeModification[] { isDeleteAttributeMod, dnAttributeMod });
      request.Controls.Add(new ShowDeletedControl());

      try
      {
         ModifyResponse response = (ModifyResponse)connection.SendRequest(request);
         if (response.ResultCode != ResultCode.Success)
         {
         }
      }
      catch (Exception exception)
      {
      }
 }
 

Thanks,
Morgan
Software Developer

Advertisements
Advertisements

1 comment:

  1. This doesn't actually work. If you remove "CN=Deleted Objects," and just create your directoryEntry with domainDM, you can get deleted results. However, if you attempt to access "CN=Deleted Objects" everytime directorySearcher.FindOne() is called, you'll get an "There is no such object on the server" exception. The only way to do it would be to use the lower-level controls:

    System.DirectoryServices.Protocols.LdapConnection connection = new System.DirectoryServices.Protocols.LdapConnection(server);
    System.DirectoryServices.Protocols.SearchRequest request = new System.DirectoryServices.Protocols.SearchRequest("LDAP://DC=Test,DC=Domain", "(isDeleted=TRUE)", System.DirectoryServices.Protocols.SearchScope.OneLevel, properties);
    System.DirectoryServices.Protocols.ShowDeletedControl control = new System.DirectoryServices.Protocols.ShowDeletedControl();
    request.Controls.Add(control);
    System.DirectoryServices.Protocols.SearchResponse response = (System.DirectoryServices.Protocols.SearchResponse)connection.SendRequest(request);

    The code you posted above simply doesn't work, unfortunately.

    Charles.

    ReplyDelete