Introduction
LDAP (Lightweight Directory Access Protocol) and Active Directory are the backbone of enterprise identity management. Ansible's community.general collection provides modules for managing LDAP entries directly — create OUs, provision users, manage groups, reset passwords, and configure Linux hosts to authenticate against LDAP/AD via SSSD. This guide covers both OpenLDAP and Active Directory operations.
Prerequisites
# Install collection
ansible-galaxy collection install community.general
# Python dependency on controller
pip install python-ldap
LDAP Module Reference
| Module | Purpose |
|---|---|
community.general.ldap_entry | Create/delete LDAP entries |
community.general.ldap_attrs | Modify attributes on existing entries |
community.general.ldap_search | Search LDAP directory |
community.general.ldap_passwd | Set LDAP passwords |
Connection Variables
# group_vars/all/ldap.yml
ldap_uri: ldaps://ldap.example.com
ldap_base: dc=example,dc=com
ldap_bind_dn: cn=admin,dc=example,dc=com
ldap_bind_pw: "{{ vault_ldap_admin_password }}"
Create Organizational Units
---
- name: Create LDAP OUs
hosts: localhost
gather_facts: false
tasks:
- name: Create OUs
community.general.ldap_entry:
dn: "ou={{ item }},{{ ldap_base }}"
objectClass:
- organizationalUnit
attributes:
description: "{{ item | capitalize }} OU"
server_uri: "{{ ldap_uri }}"
bind_dn: "{{ ldap_bind_dn }}"
bind_pw: "{{ ldap_bind_pw }}"
state: present
loop:
- people
- groups
- services
- departments
Provision Users
Single User
- name: Create LDAP user
community.general.ldap_entry:
dn: "uid=jdoe,ou=people,{{ ldap_base }}"
objectClass:
- inetOrgPerson
- posixAccount
- shadowAccount
attributes:
cn: John Doe
sn: Doe
givenName: John
mail: jdoe@example.com
uid: jdoe
uidNumber: "10001"
gidNumber: "10000"
homeDirectory: /home/jdoe
loginShell: /bin/bash
userPassword: "{{ vault_jdoe_password | password_hash('ldap_salted_sha1') }}"
server_uri: "{{ ldap_uri }}"
bind_dn: "{{ ldap_bind_dn }}"
bind_pw: "{{ ldap_bind_pw }}"
state: present
Bulk User Provisioning
- name: Provision users from list
community.general.ldap_entry:
dn: "uid={{ item.username }},ou=people,{{ ldap_base }}"
objectClass:
- inetOrgPerson
- posixAccount
- shadowAccount
attributes:
cn: "{{ item.first_name }} {{ item.last_name }}"
sn: "{{ item.last_name }}"
givenName: "{{ item.first_name }}"
mail: "{{ item.email }}"
uid: "{{ item.username }}"
uidNumber: "{{ item.uid }}"
gidNumber: "{{ item.gid | default('10000') }}"
homeDirectory: "/home/{{ item.username }}"
loginShell: "{{ item.shell | default('/bin/bash') }}"
departmentNumber: "{{ item.department | default('') }}"
title: "{{ item.title | default('') }}"
server_uri: "{{ ldap_uri }}"
bind_dn: "{{ ldap_bind_dn }}"
bind_pw: "{{ ldap_bind_pw }}"
state: present
loop: "{{ ldap_users }}"
loop_control:
label: "{{ item.username }}"
no_log: true
# vars/users.yml
ldap_users:
- username: jdoe
first_name: John
last_name: Doe
email: jdoe@example.com
uid: "10001"
department: Engineering
title: Senior Developer
- username: asmith
first_name: Alice
last_name: Smith
email: asmith@example.com
uid: "10002"
department: Operations
title: DevOps Engineer
Manage Groups
- name: Create LDAP group
community.general.ldap_entry:
dn: "cn=developers,ou=groups,{{ ldap_base }}"
objectClass:
- posixGroup
- groupOfNames
attributes:
gidNumber: "10100"
description: "Development team"
member: "uid=jdoe,ou=people,{{ ldap_base }}"
server_uri: "{{ ldap_uri }}"
bind_dn: "{{ ldap_bind_dn }}"
bind_pw: "{{ ldap_bind_pw }}"
state: present
- name: Add members to group
community.general.ldap_attrs:
dn: "cn=developers,ou=groups,{{ ldap_base }}"
attributes:
member: "uid={{ item }},ou=people,{{ ldap_base }}"
server_uri: "{{ ldap_uri }}"
bind_dn: "{{ ldap_bind_dn }}"
bind_pw: "{{ ldap_bind_pw }}"
state: present
loop:
- jdoe
- asmith
- bwilson
- name: Remove member from group
community.general.ldap_attrs:
dn: "cn=developers,ou=groups,{{ ldap_base }}"
attributes:
member: "uid=bwilson,ou=people,{{ ldap_base }}"
server_uri: "{{ ldap_uri }}"
bind_dn: "{{ ldap_bind_dn }}"
bind_pw: "{{ ldap_bind_pw }}"
state: absent
Password Management
- name: Reset user password
community.general.ldap_passwd:
dn: "uid={{ item.username }},ou=people,{{ ldap_base }}"
passwd: "{{ item.new_password }}"
server_uri: "{{ ldap_uri }}"
bind_dn: "{{ ldap_bind_dn }}"
bind_pw: "{{ ldap_bind_pw }}"
loop: "{{ password_resets }}"
no_log: true
- name: Set password expiry attributes
community.general.ldap_attrs:
dn: "uid={{ item }},ou=people,{{ ldap_base }}"
attributes:
shadowMax: "90"
shadowWarning: "14"
shadowLastChange: "{{ (ansible_date_time.epoch | int / 86400) | int }}"
server_uri: "{{ ldap_uri }}"
bind_dn: "{{ ldap_bind_dn }}"
bind_pw: "{{ ldap_bind_pw }}"
state: exact
loop: "{{ ldap_users | map(attribute='username') | list }}"
Search LDAP
- name: Find all users in Engineering
community.general.ldap_search:
dn: "ou=people,{{ ldap_base }}"
filter: "(&(objectClass=inetOrgPerson)(departmentNumber=Engineering))"
attrs:
- uid
- cn
- mail
server_uri: "{{ ldap_uri }}"
bind_dn: "{{ ldap_bind_dn }}"
bind_pw: "{{ ldap_bind_pw }}"
register: engineering_users
- name: Display users
ansible.builtin.debug:
msg: "{{ item.cn }}: {{ item.mail }}"
loop: "{{ engineering_users.results }}"
Configure SSSD (Linux → LDAP/AD Auth)
---
- name: Configure SSSD for LDAP authentication
hosts: linux_servers
become: true
tasks:
- name: Install SSSD packages
ansible.builtin.package:
name:
- sssd
- sssd-ldap
- sssd-tools
- oddjob-mkhomedir
state: present
- name: Deploy SSSD config
ansible.builtin.template:
src: sssd.conf.j2
dest: /etc/sssd/sssd.conf
mode: '0600'
owner: root
notify: restart sssd
- name: Enable SSSD and mkhomedir
ansible.builtin.command: >
authselect select sssd with-mkhomedir --force
changed_when: true
- name: Enable oddjobd for home directory creation
ansible.builtin.service:
name: oddjobd
state: started
enabled: true
- name: Start SSSD
ansible.builtin.service:
name: sssd
state: started
enabled: true
handlers:
- name: restart sssd
ansible.builtin.service:
name: sssd
state: restarted
# templates/sssd.conf.j2
[sssd]
domains = example.com
services = nss, pam, ssh
config_file_version = 2
[domain/example.com]
id_provider = ldap
auth_provider = ldap
chpass_provider = ldap
ldap_uri = {{ ldap_uri }}
ldap_search_base = {{ ldap_base }}
ldap_default_bind_dn = {{ ldap_bind_dn }}
ldap_default_authtok = {{ ldap_bind_pw }}
ldap_user_search_base = ou=people,{{ ldap_base }}
ldap_group_search_base = ou=groups,{{ ldap_base }}
ldap_tls_reqcert = demand
ldap_tls_cacert = /etc/ssl/certs/ca-certificates.crt
cache_credentials = true
enumerate = false
# Home directory
fallback_homedir = /home/%u
default_shell = /bin/bash
# Access control — only allow specific groups
access_provider = ldap
ldap_access_filter = (memberOf=cn=linux-users,ou=groups,{{ ldap_base }})
Active Directory Specific
# SSSD config for AD (templates/sssd-ad.conf.j2)
[domain/example.com]
id_provider = ad
auth_provider = ad
access_provider = ad
ad_domain = example.com
ad_server = dc01.example.com, dc02.example.com
krb5_realm = EXAMPLE.COM
ldap_id_mapping = true
fallback_homedir = /home/%u@%d
default_shell = /bin/bash
# Allow only specific AD groups
ad_access_filter = (memberOf=CN=Linux Users,OU=Groups,DC=example,DC=com)
- name: Join AD domain
ansible.builtin.command: >
realm join --user=admin@EXAMPLE.COM example.com
args:
creates: /etc/krb5.keytab
no_log: true
Deprovisioning
- name: Disable LDAP user
community.general.ldap_attrs:
dn: "uid={{ item }},ou=people,{{ ldap_base }}"
attributes:
loginShell: /usr/sbin/nologin
shadowExpire: "0"
server_uri: "{{ ldap_uri }}"
bind_dn: "{{ ldap_bind_dn }}"
bind_pw: "{{ ldap_bind_pw }}"
state: exact
loop: "{{ disabled_users }}"
- name: Delete LDAP user
community.general.ldap_entry:
dn: "uid={{ item }},ou=people,{{ ldap_base }}"
server_uri: "{{ ldap_uri }}"
bind_dn: "{{ ldap_bind_dn }}"
bind_pw: "{{ ldap_bind_pw }}"
state: absent
loop: "{{ deleted_users }}"
Troubleshooting
Test LDAP Connection
- name: Test LDAP search
community.general.ldap_search:
dn: "{{ ldap_base }}"
filter: "(objectClass=organization)"
server_uri: "{{ ldap_uri }}"
bind_dn: "{{ ldap_bind_dn }}"
bind_pw: "{{ ldap_bind_pw }}"
register: ldap_test
- ansible.builtin.debug:
var: ldap_test.results
SSSD Cache Issues
- name: Clear SSSD cache
ansible.builtin.command: sss_cache -E
changed_when: true
Related Articles
Conclusion
Ansible manages the full LDAP/AD lifecycle — create OUs, provision users in bulk, manage group membership, reset passwords, and configure Linux hosts for LDAP authentication via SSSD. Use community.general.ldap_entry for creating entries, ldap_attrs for modifying attributes, and ldap_search for querying. Store user lists in variables files for declarative user management where adding or removing a user is just editing a YAML list and running the playbook.