title: Modifying Active Directory Passwords via ldapmodify
body: <p>I'm investigating the scripting of various LDAP operations. However, I've hit a bit of a speed bump with Active Directory user creation.</p>
  
  <p>The following LDIF fails when I load it in via the <code>ldapmodify</code> command:</p>
  
  <pre><code>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!"
  </code></pre>
  
  <p>When attempting to add the user via LDIF, I used the following command:</p>
  
  <pre><code>ldapmodify -H 'ldaps://&lt;ip-of-server&gt;:636' -D 'DOMAIN\Administrator' -x -W -f frank-add.ldif
  </code></pre>
  
  <p>This fails with the following error:</p>
  
  <pre><code>ldap_add: Server is unwilling to perform (53)
          additional info: 0000001F: SvcErr: DSID-031A120C, problem 5003 (WILL_NOT_PERFORM), data 0
  </code></pre>
  
  <p>This is a problem with the password policy denying the user.</p>
  
  <p>However, the following Python script works:</p>
  
  <pre><code>#!/usr/bin/python
  
  import ldap
  import ldap.modlist as modlist
  
  AD_LDAP_URL='ldaps://&lt;ip-of-server&gt;: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)
  </code></pre>
  
  <p>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.</p>
  
  <p>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.</p>
  
  <p>So far, I've tried the following password formats via LDIF:</p>
  
  <ul>
  <li>Without quotes.</li>
  <li>Plain quotes.</li>
  <li>Escaped quotes via \</li>
  <li>Escaped quotes via ASCII: {\22}</li>
  <li>Using Python to Base64-encode the password (With and without quotes, and with the format of the LDIF modified to    <code>unicodePwd::</code>)</li>
  </ul>
  
  <p>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 <code>ldapmodify</code>. Is there an alternate method that I'm not considering?</p>