Modifying Active Directory Passwords via ldapmodify
Question: I'm investigating the scripting of various LDAP operations. However, I've hit
a bit of a speed bump with Active Directory user creation.

The following LDIF fails when I load it in via the `ldapmodify` command:

    
    
    dn: CN=Frank,CN=Users,DC=domain,dc=local
    changeType: add
    objectClass: top
    objectClass: person
    objectClass: organizationalPerson
    objectClass: user
    cn: Frank
    userPrincipalName: frank@domain.local
    sAMAccountName: frank
    givenName: Frank
    sn: Stein
    displayName: Frank Stein
    description: Frankenstein's User
    userAccountControl: 512
    unicodePwd: "AnExamplePassword1!"
    

When attempting to add the user via LDIF, I used the following command:

    
    
    ldapmodify -H 'ldaps://<ip-of-server>:636' -D 'DOMAIN\Administrator' -x -W -f frank-add.ldif
    

This fails with the following error:

    
    
    ldap_add: Server is unwilling to perform (53)
            additional info: 0000001F: SvcErr: DSID-031A120C, problem 5003 (WILL_NOT_PERFORM), data 0
    

This is a problem with the password policy denying the user.

However, the following Python script works:

    
    
    #!/usr/bin/python
    
    import ldap
    import ldap.modlist as modlist
    
    AD_LDAP_URL='ldaps://<ip-of-server>:636'
    ADMIN_USER='DOMAIN\Administrator'
    # User must be authorized to create accounts, naturally.
    ADMIN_PASSWORD='password for ADMIN_USER'
    BASE_DN='dc=domain,dc=local'
    
    username='frank'
    firstname='Frank'
    surname='Stein'
    displayName = "Frank Stein"
    
    password='AnExamplePassword1!'
    # The value of password still needs to adhere to the domain's password policy.
    unicode_pass = unicode('\"' + password + '\"', 'iso-8859-1')
    password_value = unicode_pass.encode('utf-16-le')
    
    l = ldap.initialize(AD_LDAP_URL)
    l.simple_bind_s(ADMIN_USER, ADMIN_PASSWORD)
    
    dn=str('CN=%s,CN=Users,DC=domain,dc=local' % firstname)
    
    attrs = {}
    
    attrs['objectclass'] = ['top','person','organizationalPerson','user']
    attrs['cn'] = str(username)
    attrs['sAMAccountname'] = str(username)
    attrs['unicodePwd'] = str(password_value)
    attrs['givenName'] = str(firstname)
    attrs['sn'] = str(surname)
    attrs['displayName'] = str(displayName)
    attrs['description'] = str("Frankenstein's User")
    attrs['userPrincipalName'] = str("%s@domain.local" % username)
    attrs['userAccountControl'] = str(512)
    
    ldif = modlist.addModlist(attrs)
    l.add_s(dn,ldif)
    

Using the Python script, I am immediately able to sign in using the user's
password (minus the quotes that were escaped out). I can still trigger the
same "Unwilling to Perform" error by picking a password like 'password' that
is too simple. However, in this case the password being used is the same.

So far as I can see, the operations should be identical. The difference that
breaks the LDIF file is the way that I deal with the quotes that I need to
enclose the password in. Creation via LDIF succeeds if I make a disabled
account by setting the value of userAccountControl to 544 and not including a
password. However, this means that I would need to manually go and reset the
user's password.

So far, I've tried the following password formats via LDIF:

  * Without quotes.
  * Plain quotes.
  * Escaped quotes via \
  * Escaped quotes via ASCII: {\22}
  * Using Python to Base64-encode the password (With and without quotes, and with the format of the LDIF modified to `unicodePwd::`)

While I'm happy that I have a working method of adding users via the Python,
I'm still a bit confused about how to properly escape out password values when
using LDIF files and `ldapmodify`. Is there an alternate method that I'm not
considering?


Answer: Why not use ldifde and unicode base64 encode the password as described here:
<http://support.microsoft.com/kb/263991>

Your python script seems to be encoding the password as unicode / base64.
Perhaps your password needs to be encoded in your ldif file (with the quotes
when encoding) rather than plain text as you are doing in your example.

Eg:

    
    
    unicodePwd:: IgBBAG4ARQB4AGEAbQBwAGwAZQBQAGEAcwBzAHcAbwByAGQAMQAhACIA
    

For the example password you provided.

