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
Advertisement
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.