{"id":1520,"date":"2014-08-24T15:58:46","date_gmt":"2014-08-24T20:58:46","guid":{"rendered":"http:\/\/lisa.rushworth.us\/?p=1520"},"modified":"2026-05-15T14:16:51","modified_gmt":"2026-05-15T19:16:51","slug":"bulk-ldap-operations","status":"publish","type":"post","link":"https:\/\/www.rushworth.us\/lisa\/?p=1520","title":{"rendered":"Bulk LDAP Operations"},"content":{"rendered":"<p><strong>LDIF \u2013 Directory Import and Export using LDIFDE.EXE and LDAPMODIFY<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p>You can obtain ldifde.exe from any existing domain controller \u2013 copy\u00a0\\\\dcname\\c$\\winnt\\system32\\ldifde.exe\u00a0\u00a0to your SYSTEM32 folder. The ldapmodify command is part of the openldap-clients package on Linux. Windows builds of the openldap clients <a href=\"https:\/\/www.userbooster.de\/en\/download\/openldap-for-windows.aspx\" target=\"_blank\" rel=\"noopener\">are available<\/a>. The data being imported is essentially the same, just the command line to invoke the program differs.<\/p>\n<p>Using LDIF files to update LDAP data is facilitated if you know the directory schema attributes, especially those associated to the user object class.\u00a0\u00a0Active Directory schema is well documented on MSDN \u2013 base Active Directory schema can be found at\u00a0<a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/adschema\/adschema\/active_directory_schema_site.asp\">http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/adschema\/adschema\/active_directory_schema_site.asp<\/a>\u00a0and the extensions made by Exchange are documented at\u00a0<a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/wss\/wss\/wss_ldf_AD_Schema_intro.asp\">http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/wss\/wss\/wss_ldf_AD_Schema_intro.asp<\/a><\/p>\n<p>LDIFDE is a command line program which runs with currently logged on user\u2019s credentials \u2013 this means that your ID can write changes to AD using LDIFDE.\u00a0\u00a0<strong>Please do not play with this program in the production Active Directory domain<\/strong>\u00a0but rather test writing to a test domain.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>LDIF Export<\/strong><\/p>\n<p>Exporting directory information is fairly straight forward:<\/p>\n<p>&nbsp;<\/p>\n<p>ldifde \u2013f\u00a0<em>filename.txt<\/em>\u00a0\u2013d \u201c<em>ou<\/em><em>=base,DC<\/em>=example,DC=com\u201d \u2013p\u00a0subtree\u00a0\u2013r \u201c(&amp;(<em>attribute=value<\/em>)(<em>otherattribute<\/em><em>=othervalue<\/em>))\u201d \u2013s\u00a0<em>domaincontroller.example.com<strong>\u00a0<\/strong><\/em>\u2013l<strong>\u00a0<\/strong><em>\u201cattribute1, attribute2, attribute3 \u2026\u201d<\/em><\/p>\n<p><strong>\u00a0<\/strong><\/p>\n<table>\n<tbody>\n<tr>\n<td width=\"59\"><strong>-f<\/strong><\/td>\n<td width=\"528\">File to contain exported data<\/td>\n<\/tr>\n<tr>\n<td width=\"59\"><strong>-d<\/strong><\/td>\n<td width=\"528\">Search base<\/td>\n<\/tr>\n<tr>\n<td width=\"59\"><strong>-p<\/strong><\/td>\n<td width=\"528\">Search scope<\/td>\n<\/tr>\n<tr>\n<td width=\"59\"><strong>-r<\/strong><\/td>\n<td width=\"528\">RFC-2254 compliant filter<\/td>\n<\/tr>\n<tr>\n<td width=\"59\"><strong>-s<\/strong><\/td>\n<td width=\"528\">Domain controller from which to obtain data<\/td>\n<\/tr>\n<tr>\n<td width=\"59\"><strong>-l<\/strong><\/td>\n<td width=\"528\">Attributes to be returned (eliding this command will return values for all attributes)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>\u00a0<\/strong><\/p>\n<p>This will create a file named ljlexport.txt of all e####### users with email addresses whose accounts are located under EXAMPLE.COM\\EXAMPLE\\IT.\u00a0\u00a0The file will contain, for each user, their logon ID (sAMAccountName), email address (mail), account status (userAccountControl), display name, and telephone number.<\/p>\n<p>ldifde \u2013f ljlexport.txt \u2013d \u201cou=IT,ou=example,DC=example,DC=com\u201d \u2013r \u201c(&amp;(sAMAccountName=e*)(mail=*))\u201d \u2013s ad.example.com \u2013l \u201csAMAccountName, mail, userAccountControl, displayName, telephoneNumber\u201d<\/p>\n<p>&nbsp;<\/p>\n<p><strong>-r<\/strong>\u00a0specifies the search filter and can become a rather complex query depending on what you are looking for &#8212; &amp; is an AND filter, | is an OR filter.\u00a0\u00a0! can be used to find unmatched values and * works as a wildcard<\/p>\n<p>&#8220;(&amp;(mail=*)(!sAMAccountName=AB*))&#8221;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0find all mail enabled accounts where the ID does not start with AB.<\/p>\n<p>\u201c(&amp;(objectClass=user)(objectCategory=person))\u201d\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Real user accounts, objectClass=user alone will return a lot of things you don\u2019t believe are users\u00a0J<\/p>\n<p>&#8220;(&amp;(objectClass=user)(objectCategory=person)(telephoneNumber=813*))&#8221;\u00a0\u00a0\u00a0\u00a0\u00a0Real user accounts with phone numbers in the 813 area code<\/p>\n<p>&#8220;(&amp;(objectClass=user)(objectCategory=person)(msExchHomeServerName=*EXCHANGEHOST841))&#8221;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Real user with mailboxes on EXCHANGEHOST841<\/p>\n<p>&nbsp;<\/p>\n<p><strong>-d\u00a0<\/strong>specifies the search base (subtree\u00a0search by default) \u2013 you can use \u201cDC=example,DC=com\u201d to get the entire directory or something like \u201cou=Region1,ou=example,DC=example,DC=com\u201d to just get users within the Central OU.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>LDIF Import<\/strong><\/p>\n<p>Importing Directory Information is not so straight forward and\u00a0<strong>again<\/strong>\u00a0<strong>do not play with this program in the production Active Directory domain.\u00a0\u00a0<\/strong>You need to create an ldif import file to make changes to objects.\u00a0\u00a0A sample file content:<\/p>\n<pre>dn: cn=Landers\\,\u00a0Lisa,ou=GPOTest,ou=IT,ou=example,dc=example,dc=com\r\nchangetype: modify\r\nadd: proxyAddresses\r\nproxyAddresses: smtp:lisa@newtestdomain.example.com\r\n-\r\nreplace: telephoneNumber\r\ntelephoneNumber: 501-905-4305\r\n-\r\ndelete: mobile\r\nmobile: 501-607-3750\r\n-\r\ndelete: facsimileTelephoneNumber\r\n-\r\n\u00a0\r\ndn: cn=Ahrend\\,\u00a0Sam,ou=IT,ou=example,dc=example,dc=com\r\nchangetype: modify\r\nreplace: mDBUseDefaults\r\nmDBUseDefaults: FALSE\r\n-\r\nreplace: mDBStorageQuota\r\nmDBStorageQuota: 190000\r\n-\r\nreplace: mDBOverQuotaLimit\r\nmDBOverQuotaLimit: 200000\r\n-\r\n<\/pre>\n<p>Provided you have an import file, the syntax of the command is\u00a0<strong><em>ldifde \u2013i\u00a0\u2013v \u2013k \u2013y \u2013f filename.txt<\/em>\u00a0\u00a0<\/strong><\/p>\n<p><strong>\u00a0<\/strong><\/p>\n<table>\n<tbody>\n<tr>\n<td width=\"47\"><strong>-i<\/strong><\/td>\n<td width=\"408\">LDIFDE import operation<\/td>\n<\/tr>\n<tr>\n<td width=\"47\"><strong>-v<\/strong><\/td>\n<td width=\"408\">Produce verbose output<\/td>\n<\/tr>\n<tr>\n<td width=\"47\"><strong>-k<\/strong><\/td>\n<td width=\"408\">Ignore constraint violations (and entry exists errors on add)<\/td>\n<\/tr>\n<tr>\n<td width=\"47\"><strong>-y<\/strong><\/td>\n<td width=\"408\">Lazy commit<\/td>\n<\/tr>\n<tr>\n<td width=\"47\"><strong>-f<\/strong><\/td>\n<td width=\"408\">File name to be imported<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>\u00a0<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p><strong>Changetype Add<\/strong><\/p>\n<p>A changetype of add is used when the\u00a0<em>entire<\/em>\u00a0<em>object<\/em>\u00a0does not currently exist \u2013 this ldap operation creates a new object with the attributes specified in the stanza.<\/p>\n<p>&nbsp;<\/p>\n<pre>dn: cn=TestingGroup 10001,ou=testing,ou=it,DC=example,DC=com\r\nchangetype: add\r\ncn: TestingGroup 10001\r\ndistinguishedName: cn= TestingGroup 10001,ou=testing,ou=it,DC=example,DC=com\r\nname: TestingGroup10001\r\nsAMAccountName: TestingGroup10001\r\nobjectClass: group\r\nobjectCategory: CN=Group,CN=Schema,CN=Configuration,DC=example,DC=com\r\ngroupType: -2147483646\r\nmanagedBy: cn=Landers\\,\u00a0Lisa,OU=GPOTest,ou=IT,OU=example,DC=example,DC=com\r\nmember: cn=Landers\\,\u00a0Lisa,OU=GPOTest,ou=IT,OU=example,DC=example,DC=com\r\nmember: cn=Ahrend\\, Sam,\u00a0ou=IT,OU=example,DC=example,DC=com\r\nlegacyExchangeDN: \/o=MYEXCH\/ou=First Administrative Group\/cn=Recipients\/cn=TestingGroup10001\r\nmailNickname: TestingGroup10001\r\nreportToOriginator: TRUE\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>This example will create an e-mail enabled global security group named \u201cTestingGroup 10001\u201d under example.com \u2013 it \u2013 testing.\u00a0\u00a0Both Sam and I will be listed as members of\u00a0\u00a0the group and I will be listed as the group owner.\u00a0\u00a0Add operations can be chained together with just a blank line between them should you need to add multiple objects in batch.<\/p>\n<p>&nbsp;<\/p>\n<p>Any mandatory attributes for the schema classes need to be included (handled by ldifde works too) or the add operation will fail.\u00a0\u00a0Attributes not valid for the object classes will cause the operation to fail as well.\u00a0\u00a0If an object already exists, no change will be made even if some of the attributes specified differ from the values within AD.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>Changetype Delete<\/strong><\/p>\n<p>Delete is used to delete the\u00a0<em>entire object<\/em>\u00a0\u2013 so be extra careful here.\u00a0\u00a0The syntax is quite simple \u2013 the DN of the object to be removed and a line that says \u201cchangetype: delete\u201d.\u00a0\u00a0Again, multiple operations can be chained together with just a blank line.<\/p>\n<p>&nbsp;<\/p>\n<pre>dn: cn=TestingGroup 10002,ou=testing,ou=it,DC=example,DC=com\r\nchangetype: delete\r\n\u00a0\r\ndn: cn=TestingGroup 10003,ou=testing,ou=it,DC=example,DC=com\r\nchangetype: delete\r\n\u00a0\r\ndn: cn=TestingGroup 10004,ou=testing,ou=it,DC=example,DC=com\r\nchangetype: delete\r\n\u00a0\r\ndn: cn=TestingGroup 10005,ou=testing,ou=it,DC=example,DC=com\r\nchangetype: delete\r\n\u00a0\r\n<\/pre>\n<p><strong>Changetype Modify<\/strong><\/p>\n<p>Modify is used to change attributes on an existing object.\u00a0\u00a0Modify can be used to add, replace, or delete an attribute.\u00a0\u00a0The example above has two different stanza\u2019s (separated by a blank line).\u00a0\u00a0Within each stanza several operations are made:<\/p>\n<p>&nbsp;<\/p>\n<p>First to add another email address to the secondary email addresses.\u00a0\u00a0For a multi-value attribute (member, proxyAddresses \u2026) changetype: modify\\nadd:\u00a0<em>attribute<\/em>\u00a0adds another value to the attribute.\u00a0\u00a0For single-valued attributes modify\/add will fail if a value is present.<\/p>\n<pre>dn: cn=Landers\\,\u00a0Lisa,ou=GPOTest,ou=IT,ou=example,DC=example,DC=com\r\nchangetype: modify\r\nadd: proxyAddresses\r\nproxyAddresses: smtp:lisa@newtestdomain.example.com\r\n-\r\n<\/pre>\n<p>The next operation replaces the telephone number with the value specified \u2013 this will overwrite the existing value.\u00a0\u00a0Be careful not to replace multi-value attributes<\/p>\n<pre>replace: telephoneNumber\r\ntelephoneNumber: 501-555-4305\r\n-\r\n<\/pre>\n<p>The next operation deletes the mobile phone number with the value specified \u2013 if the value does not match, a change is not made.\u00a0\u00a0This can be used as a failsafe, in this case only delete my mobile telephone number if the value is what I expect it to be, or to remove entries from multi-value attributes.\u00a0\u00a0Delete the member of the group which is the specific member listed without changing the other group members, for instance.<\/p>\n<pre>delete: mobile\r\nmobile: 501-555-3750\r\n-\r\n<\/pre>\n<p>The next operation deletes the fax number \u2013 regardless of content the value is removed.<\/p>\n<pre>delete: facsimileTelephoneNumber\r\n-\r\n<\/pre>\n<p>A blank line separates the two stanzas and a new object is specified.\u00a0\u00a0Again the modify\/replace option is used which will change the attributes to the values specified.<\/p>\n<pre>dn: cn=Ahrend\\,\u00a0Sam,ou=IT,ou=example,DC=example,DC=com\r\nchangetype: modify\r\nreplace: mDBUseDefaults\r\nmDBUseDefaults: FALSE\r\n-\r\nreplace: mDBStorageQuota\r\nmDBStorageQuota: 190000\r\n-\r\nreplace: mDBOverQuotaLimit\r\nmDBOverQuotaLimit: 200000\r\n-\r\n\u00a0\r\ndn: cn=Landers\\,\u00a0Lisa,ou=IT,ou=example,DC=example,DC=com\r\nchangetype: modify\r\nreplace: mDBUseDefaults\r\nmDBUseDefaults: TRUE\r\n-\r\n<\/pre>\n<p><strong>Changetype ModDN<\/strong><br \/>\nModDN changes the object\u2019s distinguishedName.\u00a0\u00a0This is interesting as it can be used to move users \u2013 this example would move my account into the\u00a0Central OU\u00a0under ACI<\/p>\n<pre>dn: CN=Landers\\,\u00a0Lisa,OU=GPOTest,OU=IT,OU=example,,DC=example,DC=com\r\nchangetype: moddn\r\nnewrdn: CN= Landers \\,\u00a0Lisa,OU=Central,OU=example,DC=example,DC=com\r\ndeleteoldrdn: 1\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>ModDN can also be used to rename the object\u2019s display in administrative listings:<\/p>\n<pre>dn: CN=Landers\\,\u00a0Lisa,OU=GPOTest,OU=IT,OU=example,,DC=example,DC=com\r\nchangetype: moddn\r\nnewrdn: CN= Landers\\, Jane, OU=GPOTest,OU=IT,OU=example,,DC=example,DC=com\r\ndeleteoldrdn: 1\r\n<\/pre>\n<p>You would of course want to modify\/replace at least givenName and displayName on the object dn to avoid confusion \u2013 otherwise my middle name would appear in active directory users and computers but my first name in Outlook.\u00a0\u00a0I would modify the attributes first \u2013 if you modify the DN first, you need to remember to use the new DN for subsequent attribute value changes.<\/p>\n<pre>dn: CN=Landers\\,\u00a0Lisa,OU=GPOTest,OU=IT,OU=example,,DC=example,DC=com\r\nchangetype: modify\r\nreplace: givenName\r\ngivenName: Jane\r\n-\r\nreplace: displayName\r\ndisplayName: Landers, Jane\r\n-\r\n\u00a0\r\ndn: CN=Landers\\,\u00a0Lisa,OU=GPOTest,OU=IT,OU=example,,DC=example,DC=com\r\nchangetype: moddn\r\nnewrdn: CN= Landers\\, Jane, OU=GPOTest,OU=IT,OU=example,,DC=example,DC=com\r\ndeleteoldrdn: 1\r\n-\r\n\u00a0\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>LDIF \u2013 Directory Import and Export using LDIFDE.EXE and LDAPMODIFY &nbsp; You can obtain ldifde.exe from any existing domain controller \u2013 copy\u00a0\\\\dcname\\c$\\winnt\\system32\\ldifde.exe\u00a0\u00a0to your SYSTEM32 folder. The ldapmodify command is part of the openldap-clients package on Linux. Windows builds of the openldap clients are available. The data being imported is essentially the same, just the command &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[30],"tags":[304,303],"class_list":["post-1520","post","type-post","status-publish","format-standard","hentry","category-system-administration","tag-automation","tag-ldap"],"_links":{"self":[{"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/posts\/1520","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1520"}],"version-history":[{"count":3,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/posts\/1520\/revisions"}],"predecessor-version":[{"id":12237,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/posts\/1520\/revisions\/12237"}],"wp:attachment":[{"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1520"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1520"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1520"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}