LDAPGuru | BLOG | ldapsearch | adding a new entry | modifying an existing entry | references |
ldapmodify is a command-line tool that can be used to modify directory server entries via the LDAP protocol. ldapmodify is
distributed with most directory server software. Examples are given first in the legacy OpenLDAP syntax and second in the more
modern syntax. Users should use the correct syntax for their local installation. The examples use the root DN, which in the case
of this server is "cn=directory manager". Your friendly neighborhood LDAP administrator most likely will not allow the use of
the root DN; contact the administrator to obtain access rights that can be used to perform the examples here, which include:
The following discussion refers to a directory server running on the localhost and listening to port 1389. Directory Servers use naming contexts to list the prefixes of the data that the server masters or shadows. If the directory server permits, the naming contexts can be discovered by querying the Root DSE for the namingContexts attribute (to query the root DSE it is necessary to specify the base DN as the zero-length string and the search scope as base):
#
# The legacy OpenLDAP syntax
#
/usr/bin/ldapsearch -LLL -x -h localhost -p 1389 -b '' -s base '(objectClass=*)' namingContexts
dn:
namingContexts: dc=example,dc=com
#
# The more modern syntax:
#
ldapsearch --hostname localhost --port 1389 --baseDn '' --searchScope base '(objectClass=*)' namingContexts
dn:
namingContexts: dc=example,dc=com
As shown in the example, the prefix (naming context) of this directory server is 'dc=example,dc=com': this naming context will
be used through the examples. Most modern, professional-quality directory servers have the ability to support multiple naming
contexts.
Create an LDIF file containing the data to use for the new entry. This example adds an entry for a user named Wolfie Mozart. The LDIF:
$ cat /tmp/mozart.LDIF
dn: uid=wam,ou=people,dc=example,dc=com
changetype: add
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: Johannes Chrysostomus Wolfgangus Theophilus Mozart
cn: Wolfgang Amadeus Mozart
cn: Wolfie Mozart
displayName: Mozart
givenName: Wolfgang
givenName: Wolfie
initials: wam
sn: Mozart
uid: wam
userPassword: the-password-to-use-for-mozart
Add the entry using ldapmodify:
#
# The legacy ldapmodify syntax
#
/usr/bin/ldapmodify -x -h localhost -p 1389 \
-D 'cn=directory manager' -w password -c -f /tmp/mozart.LDIF
adding new entry "uid=wam,ou=people,dc=example,dc=com"
There is an insidious error that could go un-noticed when using the legacy OpenLDAP ldapmodify tool. In the example LDIF, the
line givenName: Wolfgang has a trailing space, which is not allowed by the LDAP standards. Leading or trailing spaces must
be escaped. Note that the older ldapmodify tool fails to note this fact and adds the entry anyway. The result is that the
directory server base–64 encodes the givenName which ended in a space. Look for the '::' (double colon) in the LDIF output of
the ldapsearch tool:
$ /usr/bin/ldapsearch -LLL -x -h localhost -p 1389 \
-b uid=wam,ou=people,dc=example,dc=com -s base '(objectClass=*)'
dn: uid=wam,ou=people,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
initials: wam
givenName:: V29sZmdhbmcg
givenName: Wolfie
uid: wam
cn: Johannes Chrysostomus Wolfgangus Theophilus Mozart
cn: Wolfgang Amadeus Mozart
cn: Wolfie Mozart
sn: Mozart
displayName: Mozart
The more modern syntax (in this case also using the StartTLS extended operation request to promote the initial non-secure connection to TLS):
ldapsearch --hostname localhost --port 1389 \
--useStartTls --trustAll \
--baseDn uid=wam,ou=people,dc=example,dc=com \
--searchScope base '(objectClass=*)'
dn: uid=wam,ou=people,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
initials: wam
givenName:: V29sZmdhbmcg
givenName: Wolfie
uid: wam
cn: Johannes Chrysostomus Wolfgangus Theophilus Mozart
cn: Wolfgang Amadeus Mozart
cn: Wolfie Mozart
sn: Mozart
displayName: Mozart
The use of the modern ldapmodify tool correctly notes the error – which is most likely a typographical error – giving the operator an opportunity to consider whether the entry is as intended before it is added:
ldapmodify --hostname localhost --port 1389 \
--useStartTls --trustAll \
--bindDn 'cn=directory manager' --bindPassword password \
-c -f /tmp/mozart.LDIF
# Error at or near line 1 in LDIF file Console:
# com.unboundid.directory.server.util.LDIFException: The LDIF record for entry
# `uid=wam,ou=people,dc=example,dc=com' contains line 'givenName: Wolfgang ' which ends with
# an illegal trailing space. The '--stripTrailingSpaces' argument may be used to indicate
# that illegal trailing spaces should be stripped out of records instead of causing them to be
# rejected
Change Mozart’s displayName attribute value. Create the following LDIF using the keyword
replace and the changetype designator "modify":
dn: uid=wam,ou=people,dc=example,dc=com
changetype: modify
replace: displayName
displayName: Wolfgang Mozart
Modify the entry:
/usr/bin/ldapmodify -x -h localhost -p 1389 \
-D 'cn=directory manager' -w password \
-c -f /tmp/modify-mozart-displayname.LDIF
modifying entry "uid=wam,ou=people,dc=example,dc=com"
Verify the change:
ldapsearch --hostname localhost --port 1389 \
--useStartTls --trustAll \
--baseDN uid=wam,ou=people,dc=example,dc=com \
--searchScope base '(objectClass=*)' displayName
dn: uid=wam,ou=people,dc=example,dc=com
displayName: Wolfgang Mozart
Note that modern directory servers should support the pre-read control, which will result in the attributes being read and presented before they are modified.
ldapmodify --preReadAttributes displayName \
--hostname localhost --port 1389\
--useStartTls --trustAll \
--bindDn 'cn=directory manager' --bindPassword password \
-c -f /tmp/modify-mozart-displayname.LDIF
# Processing MODIFY request for uid=wam,ou=people,dc=example,dc=com
# MODIFY operation successful for DN uid=wam,ou=people,dc=example,dc=com
# Target entry before the operation:
# dn: uid=wam,ou=people,dc=example,dc=com
# displayName: Wolfgang Mozart
In this example, change multiple attributes in one connection:
ldapmodify --hostname localhost --port 1389 \
--useStartTls --trustAll \
--bindDn uid=user.0,ou=people,dc=example,dc=com
Password for user 'uid=user.0,ou=people,dc=example,dc=com':
dn: uid=user.0,ou=people,dc=example,dc=com
changetype: modify
replace: street
street: 91327-A Madison Avenue
-
replace: homePhone
homePhone: +1 457 878 9948
# Processing MODIFY request for uid=user.0,ou=people,dc=example,dc=com
# MODIFY operation successful for DN uid=user.0,ou=people,dc=example,dc=com
Add a description attribute to Mozart’s user entry. First create the necessary LDIF, this time
using the keyword add and the changetype designator "modify". The add keyword is used
for adding attributes, the modify designator is used to indicate that the operator desires to
change an attribute in the entry. Simply put, indicate which attribute with add:
attributeName, then specify the attribute itself with the value:
dn: uid=wam,ou=people,dc=example,dc=com
changetype: modify
add: description
description: This is W. Mozart's user entry.
Modify the entry:
#
# The legacy ldapmodify syntax
#
/usr/bin/ldapmodify -x -h localhost -p 1389 \
-D 'cn=directory manager' -w password -c -f /tmp/modify-mozart.LDIF
modifying entry "uid=wam,ou=people,dc=example,dc=com"
#
# The more modern syntax - in this case promoting the non-secure
# connection to a secure connection with StartTLS
#
ldapmodify --hostname localhost --port 1389 \
--useStartTls --trustAll \
--bindDn 'cn=directory manager' --bindPassword password \
-c -f /tmp/modify-mozart.LDIF
# Processing MODIFY request for uid=wam,ou=people,dc=example,dc=com
# MODIFY operation successful for DN uid=wam,ou=people,dc=example,dc=com
Once an multi-valued attribute (in this case. description) has a value, that same value cannot
be added to the entry, in other words, multi-valued attributes may not have duplicated
values. Here is the result of trying to add a description attribute with the same value as one
that already exists:
/usr/bin/ldapmodify -x -h localhost -p 1389 \
-D 'cn=directory manager' -w password \
-c -f /tmp/modify-mozart.LDIF
modifying entry "uid=wam,ou=people,dc=example,dc=com"
ldap_modify: Type or value exists (20)
additional info: Entry uid=wam,ou=people,dc=example,dc=com cannot be modified \
because it would have resulted in one or more duplicate values for attribute \
description: This is W. Mozart's user entry.
Not all attributes are multi-valued, but description is. Interestingly, the userPassword is
multi-valued, though modern professional-quality directory servers can force users to have only
one userPassword attribute value. PAM can also be configured to use the
first password found in the entry, which can be confusing and misleading since attributes and
entries are not ordered:
auth sufficient pam_ldap.so use_first_pass
Better to use a professional directory server which allows the operators and administrators to specify that an entry can only contain one password per policy than rely on LDAP ordering.
To delete an attribute from an entry, use the keyword delete and the changetype designator
modify. If the attribute is multi-valued, the client must specify the value of the attribute to
delete. Create the following LDIF:
# cat /tmp/remove-description-mozart.LDIF
dn: uid=wam,ou=people,dc=example,dc=com
changetype: modify
delete: description
description: This is W. Mozart's user entry.
Remove the value:
#
# The older ldapmodify syntax
#
/usr/bin/ldapmodify -x -h localhost -p 1389 \
-D 'cn=directory manager' -w password \
-c -f /tmp/remove-description-mozart.LDIF
modifying entry "uid=wam,ou=people,dc=example,dc=com"
#
# The more modern syntax (in this case also using the Start TLS extended operation
# for added security):
#
ldapmodify --hostname localhost --port 1389 \
--useStartTls --trustAll \
--bindDn 'cn=directory manager' --bindPassword password \
-c -f /tmp/remove-description-mozart.LDIF
# Processing MODIFY request for uid=wam,ou=people,dc=example,dc=com
# MODIFY operation successful for DN uid=wam,ou=people,dc=example,dc=com
Verify that the description attribute with the designated value has been deleted:
#
# The legacy ldapsearch syntax
#
/usr/bin/ldapsearch -LLL -x -h localhost -p 1389 \
-b uid=wam,ou=people,dc=example,dc=com \
-s base '(objectClass=*)'
dn: uid=wam,ou=people,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
initials: wam
givenName: Wolfgang
givenName: Wolfie
uid: wam
cn: Johannes Chrysostomus Wolfgangus Theophilus Mozart
cn: Wolfgang Amadeus Mozart
cn: Wolfie Mozart
sn: Mozart
displayName: Mozart
#
# The more modern syntax
#
ldapsearch --hostname localhost --port 1389 \
--useStartTls --trustAll \
--baseDN uid=wam,ou=people,dc=example,dc=com \
--searchScope base '(objectClass=*)'
dn: uid=wam,ou=people,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
initials: wam
givenName: Wolfgang
givenName: Wolfie
uid: wam
cn: Johannes Chrysostomus Wolfgangus Theophilus Mozart
cn: Wolfgang Amadeus Mozart
cn: Wolfie Mozart
sn: Mozart
displayName: Mozart
An entry is deleted by specifying the keyword delete for the changetype operation designator in
the LDIF:
dn: uid=wam,ou=people,dc=example,dc=com
changetype: delete
Delete the entry:
#
# The legacy ldapmodify syntax
#
/usr/bin/ldapmodify -x -h localhost -p 1389 \
-D 'cn=directory manager' -w password -c -f /tmp/delete-mozart.LDIF
deleting entry "uid=wam,ou=people,dc=example,dc=com"
#
# The more modern syntax (in this case also using the Start TLS extended operation
# for added security and the pre-read control to show the value of
# the entry's attributes before deletion):
#
ldapmodify --preReadAttributes '*' \
--hostname localhost --port 1389 \
--useStartTls --trustAll \
--bindDn 'cn=directory manager' --bindPassword password \
-c -f /tmp/delete-mozart.LDIF
# Processing DELETE request for uid=wam,ou=people,dc=example,dc=com
# DELETE operation successful for DN uid=wam,ou=people,dc=example,dc=com
# Target entry before the operation:
# dn: uid=wam,ou=people,dc=example,dc=com
# objectClass: top
# objectClass: person
# objectClass: organizationalPerson
# objectClass: inetOrgPerson
# initials: wam
# givenName: Wolfgang
# givenName: Wolfie
# uid: wam
# cn: Johannes Chrysostomus Wolfgangus Theophilus Mozart
# cn: Wolfgang Amadeus Mozart
# cn: Wolfie Mozart
# sn: Mozart
# userPassword: {SSHA}bJdy+0w4YTkWD9wNLnZVqhved5r4QPa33l3X/Q==
# displayName: Mozart
The use of the --preReadAttributes in the more modern syntax examples should not be construed to
mean that the pre-read control is not supported by the older ldapmodify client: the command-line
option to include the pre-read control with the older tool is -e preread=attribute-list. For
example, the following sequence replaces a password and includes the pre-read control:
$ /usr/bin/ldapmodify -x -h localhost -p 1389 \
-D 'cn=directory manager' -W \
-e preread=userPassword
Enter LDAP Password:
dn: uid=user.1,ou=people,dc=example,dc=com
changetype: modify
replace: userPassword
userPassword: a-new-password
modifying entry "uid=user.1,ou=people,dc=example,dc=com"
control: 1.3.6.1.1.13.1 false ZGwEJnVpZD11c2VyLjEsb3U9UGVvcGxlLGRjPWV4YW1wbGUs
ZGM9Y29tMEIwQAQMdXNlclBhc3N3b3JkMTAELntTU0hBfS9Vd1ZlSzh0cDlVaXhRVUxwTjZnR2Qrd
HJ3dHNkRTVhb2FXTHZBPT0=
# ==> preread
dn: uid=user.1,ou=People,dc=example,dc=com
userPassword:: e1NTSEF9L1V3VmVLOHRwOVVpeFFVTHBONmdHZCt0cnd0c2RFNWFvYVdMdkE9PQ=
=
# <== preread
The ldapmodify command also supports deleting an entry and all of the entry’s subordinates, and moving (renaming) an entry. The commands ldapdelete, ldapadd, and so forth may also exist, but ldapmodify should be preferred to these shortcuts to ensure the operator is comfortable with the LDIF syntax in its purest form.
© 2012 Terry Gardner