Blog Post

Exchange Team Blog
5 MIN READ

So, I have to guarantee you that I can recover the contacts in this OU...

The_Exchange_Team's avatar
May 18, 2005

In this post, I wanted to exploit our script capabilities to ease an administrative burden.  Manually changing addresses for 100 users is burdensome.  Attempting the same for 40,000 users is impractical. 

The practical side of ldifde; let the tool do the work. Ldifde can help you if you need to:

   - delete the contacts from an organizational unit and guarantee the customer that you can recover the contents
   - make a global change to contacts; remove e-mail addresses, change mail domains
   - or just make sure that you could restore the contacts to your directory, here are a couple of tools that will make the job simple. 

 The first is integral to Windows and the second is a custom script; each has it's place.  Users in foreign systems can be added to Active Directory as contacts or disabled Windows User accounts by several Microsoft tools.  The Active directory connector, Connector for Lotus Notes and Connector for Novell GroupWise to mention a few. 

 When these objects are added to Active Directory the information required to identify the object, e-mail routing and the like, is not the only information added to the object.  Much of the information added to the object is to facilitate management of the object by Active Directory.  In turn, much of this information is controlled by the system and can not be imported into Active Directory. 

 As a result, an unfiltered ldifde export file cannot be used as an import file.  However, ldifde provides filtering capabilities that allow you to customize exactly which objects and which attributes of the objects are exported.  If you know which attributes are required to properly add the object to active directory, you can construct an export filter to accomplish the task. 

 The ldifde command below exports all the contacts in an organizational unit with all the attributes necessary to restore the contacts to Active Directory.   The resultant file can be further modified to remove unwanted proxyAddresses, change mail domain, or any other valid global change.

ldifde -f export.ldf -d "ou=domino users,dc=contoso,dc=com" -r
("objectClass=contact") -l objectClass,sn,givenName,displayName,
proxyaddresses,importedFrom,targetAddress,mailNickname,name,mail

(IMPORTANT: the ldifde command line is an unbroken string; the line wrap is due to document formatting.)

I ran this command in my test Exchange organization and it produced a file with the content below; and some others that I have deleted for brevity.  I deleted the objects from my directory and ran ldifde -i -f export.ldf.  Voila, the contacts were restored to my directory as if they had never been deleted.

dn: CN=Arvin Sloan,OU=Domino Users,DC=contoso,DC=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: contact
sn: Sloan
givenName: Arvin
displayName: Arvin Sloan
proxyAddresses: GWISE:exchange.ag1.Arvin Sloan
proxyAddresses: NOTES:Arvin
Sloan/LDO@LDOM
proxyAddresses: notes:UID=37e72d53-b8deac46-85256fb8-797537
proxyAddresses: notes:Arvin
Sloan@LDOM
proxyAddresses: X400:c=US;a= ;p=CONTOSO;o=Exchange;s=Sloan;g=Arvin;
proxyAddresses: SMTP:ASloan@contoso.com
importedFrom: {2E63C79B-E9D9-4528-90FD-CA3B9A55E661}
targetAddress: NOTES:Arvin
Sloan/LDO@LDOM
mailNickname: ASloan
name: Arvin Sloan
mail:
ASloan@contoso.com

 Then again, there are times when all we have is a raw directory export of an organizational unit and we need to recover the contents or make global changes.  Enter Windows Script Host and the script language of choice.  The vbscript below provides virtually the same output as the ldifde filter export. 

 The user is prompted for both input and output file.  Oddly enough, the input file is a raw ldifde export and the export of the script is a properly formatted import file.  Hence, the script takes export.ldf as an input file and produces import.ldf.
 
'===========================================================
' NAME: Export2Import.vbs
' AUTHOR: Ed Thornburg , Microsoft
' DATE  : 5/5/2005
' COMMENT: This script will parse an unfiltered ldifde export of a container
' and produce a file that will recover the contents of the container
' '===========================================================
' You have a royalty-free right to use, reproduce, modify and distribute
' this sample file or any modified version in any way you find useful,
' provided you agree Microsoft has no liability, obligations, warranty, or
' responsibility regarding any result produced by use of this file.
' Copyright (C) 1996-2005 Microsoft Corporation
'===========================================================

Const ForReading = 1
Const ForWriting = 2

export = InputBox("Enter the fully qualified path to the export file","Export2Import","c:\export.ldf")
import = InputBox("Enter the fully qualified path for the import file","Export2Import","c:\import.ldf")

Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oExport = oFSO.OpenTextFile(export, ForReading)

Set oFS1 = CreateObject("Scripting.FileSystemObject")
Set oImport = oFS1.CreateTextFile(import, ForWriting)

Do While oExport.AtEndOfStream <> True

line = oExport.ReadLine
If InStr(Line,LCase("dn:")) Then
   oImport.WriteLine(line)
   oImport.WriteLine("objectClass: contact")
Elseif InStr(Line,"sn:") Then
   oImport.WriteLine(line)
Elseif InStr(Line,"givenName:") Then
    oImport.WriteLine(line)
Elseif InStr(Line,"displayName:") Then
    oImport.WriteLine(line)
Elseif InStr(Line,"SMTP:") Then
    oImport.WriteLine(line)
Elseif InStr(Line,"NOTES:") Then
    oImport.WriteLine(line)
Elseif InStr(Line,"notes:") Then
    oImport.WriteLine(line)
Elseif InStr(Line,"X400:") Then
    oImport.WriteLine(line)
Elseif InStr(Line,"importedFrom:") Then
    oImport.WriteLine(line)
Elseif InStr(Line,"targetAddress:") Then
    oImport.WriteLine(line)
Elseif InStr(Line,"mailNickname:") Then
    oImport.WriteLine(line)
Elseif InStr(Line,LCase("name:")) Then
    oImport.WriteLine(line)
Elseif InStr(Line,"mail:") Then
    oImport.WriteLine(line)
    oImport.WriteBlankLines(1)
End If

Loop

set oFSO = Nothing
set oFS1 = Nothing
set oExport = Nothing
set oImport = Nothing

WScript.Echo("Export file transformed to " & import )

Performing a raw ldifde dump, export.ldf, or the same organizational unit and parsing the export file with the script above produced an import file, import.ldf, that restored the contacts to the organizational unit just as the filtered ldifde export had done.  Note that there is a slight difference in the number of objectClass definitions between the two files, but this is of no consequence.

dn: CN=Arvin Sloan,OU=Domino Users,DC=contoso,DC=com
objectClass: contact
sn: Sloan
givenName: Arvin
displayName: Arvin Sloan
proxyAddresses: SMTP:ASloan@contoso.com
proxyAddresses: X400:c=US;a= ;p=CONTOSO;o=Exchange;s=Sloan;g=Arvin;
proxyAddresses: notes:Arvin
Sloan@LDOM
proxyAddresses: notes:UID=37e72d53-b8deac46-85256fb8-797537
proxyAddresses: NOTES:Arvin
Sloan/LDO@LDOM
importedFrom: {2E63C79B-E9D9-4528-90FD-CA3B9A55E661}
targetAddress: NOTES:Arvin
Sloan/LDO@LDOM
mailNickname: ASloan
name: Arvin Sloan
mail:
ASloan@contoso.com

Remember, proper planning prevents poor performance.  Test these tools in your lab.  I you need a change here or there and can't figure it out, mail me.  If I can help, I will.

- Ed Thornburg

Updated Jul 01, 2019
Version 2.0
  • Thanks for the informative article. I have used LDIFDE/CSVDE for creating AD object backups and to copy them into test lab domains. One thing that has troubled me with LDIFDE is that there is considerable find/repacing needed to make object updates (versus adds). Would you happen to have or know of a similar script to modify an LDIF export to alter the 'changetype:' and to add the '-' at the record's end?
  • Here are two Perl one-line scripts that illustrate how to manipulate an LDIFDE export file, converting it to an import file.

    Each of the export files massaged by the scripts below contained only one or more records consisting of the dn: and changetype: fields, separated by a blank line. The ldifde command that created the export files specified a nonexistent property with the -l parameter so that no properties would be output.

    The first script adds the msExchMasterAccountSid property with the SELF SID.

    perl -pe "s/: add/: modifynadd: msExchMasterAccountSidnmsExchMasterAccountSid:: AQEAAAAAAAUKAAAA/;s/^$/-n/;" export.tmp > import.tmp

    The second scripts deletes the msExchMasterAccountSid property.

    perl -pe "s/: add/: modifyndelete: msExchMasterAccountSid/;s/^$/-n/;" export.tmp > import.tmp

    The '-' at the end of each record is added with the s/^$/-n/ substitution.
  • LDIFDE is a cool tool. But the CSVDE output would be easier to handle in excel - the pitty is that csvde isn't able to modify objects... I always asked myself why this limitation exists... :-(
  • My users have the description field populated with "(07878)" each user has their own employee number. The code is setup to ignore the first "(".
    Create a file called DNmapping.bat and drop this into it;

    echo off
    @for /F %%I in (%1) DO (
    @ldifde -f tempout.txt -r "(description=*%%I*)" -l dn
    @type tempout.txt >> outfile.txt
    )
    echo on

    Create a text file with the employeeIDs like this;
    07878
    12345
    87456

    From Cmd line run "DNmapping.bat employeeid.txt"
    LDIFDE will search AD match the number and return the results to the Outfile.txt

    Thank Dan (Shady) Winter for the simple but useful code!
  • I've seen issues with creating contacts using LDIFDE where you don't set the legacyExchangeDN setting (this is in Exchange mixed mode). Any comment on that?
  • I have been trying to use LDIFDE to export all the attributes of a specific object in AD, I know that by using the –L switch, you can specify which attributes you want to dump and that omitting the switch, LDIFDE should dump all the attributes, but it does not seem to be doing that. If I compare the LDIFDE dump to the attributes of the same object using ADSIEdit, I see a lot of other attributes that don’t show-up in the LDIFDE dump. If I use the –L switch with LDIFDE and specify one of the attributes the originally did not show-up in the dump, LDIFDE dumps it just fine. Can you guys know why LDIFDE will not dump all the attributes of an object?