使用Python在Microsoft AD中创建有效用户遇阻求助
Python 创建 Microsoft Active Directory 用户失败问题排查与解决
问题描述
尝试使用python-ldap模块创建AD用户,执行conn.add_s()时出现错误:
"errorMessage": "{'msgtype': 105, 'msgid': 2, 'result': 53, 'desc': 'Server is unwilling to perform', 'ctrls': [], 'info': '00000529: SvcErr: DSID-031A124C, problem 5003 (WILL_NOT_PERFORM), data 0\n'}", "errorType": "UNWILLING_TO_PERFORM"
原代码如下:
conn = ldap.initialize('ldap://' + ldap_hostname) conn.protocol_version = 3 conn.set_option(ldap.OPT_REFERRALS, 0) conn.simple_bind_s(ldap_admin_dn, ldap_admin_pw) attrs = {} attrs['objectClass'] = ['top'.encode('utf-8'), 'person'.encode('utf-8'), 'organizationalPerson'.encode('utf-8'), 'user'.encode('utf-8')] attrs['cn'] = "Test User".encode("utf-8") attrs['userPrincipalName'] = "testuser@domain.com".encode("utf-8") attrs['displayName'] = "Test User".encode("utf-8") attrs['givenName'] = "Test".encode("utf-8") attrs['sn'] = "User".encode("utf-8") attrs['sAMAccountName'] = "testuser".encode("utf-8") attrs['mail'] = "testuser@domain.com".encode("utf-8") attrs['primaryGroupID'] = "513".encode("utf-8") # Convert our dict to nice syntax for the add-function using ldif = modlist.addModlist(attrs) # Set up user dn user_cn = "Test User" user_dn = "CN={},{}".format(user_cn, ldap_users_ou_dn) # Create user conn.add_s(user_dn, ldif) # Set initial password password_value = "LaLaLaLaLa123123123!".encode('utf-16-le') add_pass = [(ldap.MOD_REPLACE, 'unicodePwd', [password_value])] conn.modify_s(user_dn, add_pass) # Set user account control mod_acct = [(ldap.MOD_REPLACE, 'userAccountControl', '66048')] conn.modify_s(user_dn, mod_acct)
问题根源
- 连接方式错误:Microsoft AD要求敏感操作(如设置密码、写入用户数据)必须通过LDAPS(
ldaps://)连接,非LDAPS连接会被服务器拒绝。 - 属性编码冗余:
python-ldap会自动处理字符串的UTF-8编码,手动调用.encode('utf-8')会导致属性值格式异常,触发服务器校验失败。 - 密码格式错误:设置
unicodePwd时,密码必须用双引号包裹后再编码为UTF-16-LE,原代码缺少双引号,后续修改密码也会失败。 - 冗余属性设置:
primaryGroupID默认值就是513(Domain Users),无需手动指定;userAccountControl可以在创建用户时直接设置,无需后续修改。
修复后的可运行代码
import ldap from ldap import modlist # 配置参数 ldap_hostname = "your-ad-server.domain.com" ldap_admin_dn = "CN=Admin User,OU=Admins,DC=domain,DC=com" ldap_admin_pw = "AdminPassword123!" ldap_users_ou_dn = "OU=Users,DC=domain,DC=com" # 初始化LDAPS连接 conn = ldap.initialize(f'ldaps://{ldap_hostname}:636') conn.protocol_version = ldap.VERSION3 conn.set_option(ldap.OPT_REFERRALS, 0) # 测试环境临时禁用证书验证,生产环境请配置可信SSL证书 conn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) conn.start_tls_s() # 绑定管理员账号 conn.simple_bind_s(ldap_admin_dn, ldap_admin_pw) # 构造用户属性(无需手动编码) user_attrs = { 'objectClass': ['top', 'person', 'organizationalPerson', 'user'], 'cn': 'Test User', 'userPrincipalName': 'testuser@domain.com', 'displayName': 'Test User', 'givenName': 'Test', 'sn': 'User', 'sAMAccountName': 'testuser', 'mail': 'testuser@domain.com', # 创建时直接设置用户为启用状态(66048 = NORMAL_ACCOUNT + DONT_EXPIRE_PASSWORD) 'userAccountControl': '66048' } # 转换为LDAP添加格式 ldif = modlist.addModlist(user_attrs) # 构造用户DN user_dn = f'CN=Test User,{ldap_users_ou_dn}' # 创建用户 conn.add_s(user_dn, ldif) # 设置用户密码:必须用双引号包裹,再转UTF-16-LE password = '"LaLaLaLaLa123123123!"' unicode_pwd = password.encode('utf-16-le') mod_pwd = [(ldap.MOD_REPLACE, 'unicodePwd', unicode_pwd)] conn.modify_s(user_dn, mod_pwd) # 解绑连接 conn.unbind_s()
关键修改说明
- 改用LDAPS连接:使用
ldaps://并指定默认端口636,确保安全连接;生产环境需配置合法SSL证书,移除证书禁用选项。 - 移除手动编码:所有属性值直接传入字符串,由
python-ldap自动处理编码逻辑。 - 修正密码格式:密码用双引号包裹后再编码为UTF-16-LE,完全符合AD的
unicodePwd格式要求。 - 优化属性设置:创建用户时直接指定
userAccountControl为启用状态,省略冗余的primaryGroupID配置。
内容的提问来源于stack exchange,提问作者Marius Mitrofan
相关产品推荐
相关产品推荐

