Now that I’ve been using Ansible for joining Redhat and CentOS 8.x Linux machines to Active Directory domains in production for a while, I’m confident enough to share the playbook I’ve been using. This example uses password-based authentication and sudo elevation for the ansible user, so make sure to edit accordingly in case you are using key-based auth.
When testing, don’t forget to add your AD account to the newly created AD admin group, this has bitten me way more times than I’d like to admit and in case you’re wondering what that OID in ad_access_filter does: it allows for recursive group permissions to work, so while you can add users directly, you do not HAVE to.
join-ad-linux-rhel tree:
admin.account@server join-ad-linux-rhel]$ tree . ├── ansible.cfg ├── hosts ├── join-ad-linux-rhel.yaml └── templates ├── ADsudoers.j2 ├── krb5.j2 └── sssd.j2 1 directory, 6 files
join-ad-linux-rhel/ansible.cfg:
[defaults] inventory = hosts host_key_checking = False
join-ad-linux-rhel/hosts:
[all] examplehost ansible_host=192.168.1.100 ansible_ssh_user=ansibleuser
join-ad-linux-rhel/join-ad-linux-rhel.yaml:
- hosts: all user: ansibleuser become: true vars: pkgs: - sssd - sssd-tools - realmd - oddjob - oddjob-mkhomedir - adcli - samba-common - samba-common-tools - krb5-workstation - openldap-clients AD_Domain: MY.DOMAIN AD_Domain_alt: my.domain Join_OU: OU="member servers",OU=computers,DC=my,DC=domain SRV_ADM_GRP_OU: OU=groups,DC=my,DC=domain vars_prompt: - name: username prompt: "What is your AD administrator username?" private: no - name: password prompt: "What is your AD administrator password?" private: yes - name: adhostname prompt: "What is the desired hostname in a simple, non-fqdn format?" private: no tasks: - name: Checking if running RedHat/CentOS fail: msg: The system is not running RedHat/CentOS, aborting when: ansible_facts['os_family'] != 'RedHat' - name: Checking if packages required to join AD realm are present yum: name={{ pkgs }} state=present update_cache=yes - name: Settings up hostname shell: hostnamectl set-hostname {{ adhostname }}.{{ AD_Domain_alt }} - name: Joinining the AD realm (creating AD computer account and updating /etc/krb5.keytab) shell: echo '{{ password }}' | adcli join --stdin-password {{ AD_Domain }} -U {{ username }} --domain-ou={{ Join_OU }} - name: Creating AD server admin group shell: echo '{{ password }}' | adcli create-group ADM_{{ adhostname }} --stdin-password --domain={{ AD_Domain }} --description="Admin group for {{ adhostname }} server" --domain-ou={{ SRV_ADM_GRP_OU }} -U {{ username }} - name: Configuring sssd.conf template: src: sssd.j2 dest: /etc/sssd/sssd.conf owner: root group: root mode: 0600 - name: Configuring krb5.conf template: src: krb5.j2 dest: /etc/krb5.conf owner: root group: root mode: 0644 - name: Configuring sudoers template: src: ADsudoers.j2 dest: /etc/sudoers.d/ADsudoers owner: root group: root mode: 0440 - name: Configuring PAM/SSHD to use SSSD shell: authselect select sssd with-mkhomedir --force - name: Enabling oddjobd service systemd: name: oddjobd.service enabled: yes state: started - name: Restarting SSSD systemd: name: sssd enabled: yes state: restarted
join-ad-linux-rhel/templates/ADsudoers.j2:
%ADM_{{ adhostname }}@{{ AD_Domain_alt }} ALL=(ALL:ALL) ALL
join-ad-linux-rhel/templates/sssd.j2:
[sssd] domains = {{ AD_Domain_alt }} config_file_version = 2 services = nss, pam [domain/{{ AD_Domain_alt }}] krb5_realm = {{ AD_Domain }} realmd_tags = manages-system joined-with-adcli cache_credentials = True id_provider = ad default_shell = /bin/bash ldap_id_mapping = True use_fully_qualified_names = False fallback_homedir = /home/%u access_provider = ad ad_maximum_machine_account_password_age = 30 ad_access_filter = DOM:{{ AD_Domain_alt }}:(memberOf:1.2.840.113556.1.4.1941:=cn=ADM_{{ adhostname }},{{ SRV_ADM_GRP_OU }}) dyndns_update = false dyndns_update_ptr = false #debug_level = 9
join-ad-linux-rhel/templates/krb5.j2
includedir /etc/krb5.conf.d/ [logging] default = FILE:/var/log/krb5libs.log kdc = FILE:/var/log/krb5kdc.log admin_server = FILE:/var/log/kadmind.log [libdefaults] dns_lookup_realm = true dns_lookup_kdc = true ticket_lifetime = 24h renew_lifetime = 7d forwardable = true rdns = false pkinit_anchors = FILE:/etc/pki/tls/certs/ca-bundle.crt spake_preauth_groups = edwards25519 default_realm = {{ AD_Domain }} default_ccache_name = KEYRING:persistent:%{uid} [realms] {{ AD_Domain }} = { } [domain_realm] .{{ AD_Domain_alt }} = {{ AD_Domain }} {{ AD_Domain_alt }} = {{ AD_Domain }}
When copy/pasting these into actual files on your system, take care to ensure indentation isn’t lost as YAML is very particular about these things. Run the playbook with:
ansible-playbook -k -K join-ad-linux-rhel.yaml