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