Add Ansible deployment and systemd-resolved DNS support

This commit is contained in:
2026-01-09 19:19:11 +01:00
parent 605c112d8b
commit 36cf28ce46
6 changed files with 284 additions and 25 deletions
+47
View File
@@ -0,0 +1,47 @@
# Ansible deployment
This repo includes an Ansible role that deploys and runs `ad-join-script.sh` in a non-interactive way by passing all inputs as variables.
## Files
- `ansible/site.yml`: example playbook using the role
- `ansible/roles/ad_join`: role that copies and runs the script
## Variables
Required:
- `ad_join_hostname`: short hostname (without domain)
- `ad_join_domain_name`: AD domain (e.g. `corp.example.com`)
- `ad_join_admin_user`: account used to join the domain
- `ad_join_admin_password`: password for `ad_join_admin_user`
- `ad_join_dns_servers`: DNS server IPs used to validate domain resolution (list preferred; also accepts a comma-separated string)
- `ad_join_dns_server`: single DNS server IP (backward compatible)
- `ad_join_ad_group`: AD group to grant sudo access (written to `/etc/sudoers`)
Optional:
- `ad_join_force` (default: `false`): run even if `realm list` already shows the domain
- `ad_join_run` (default: `true`): set to `false` to only deploy the script
## Semaphore setup notes
- Point Semaphore to the playbook at `ansible/site.yml`.
- Define the variables above in the task template (use a secret variable for `ad_join_admin_password`).
## Run (example)
```bash
ansible-playbook -i inventory.ini ansible/site.yml \
-e ad_join_hostname=ubuntuhost01 \
-e ad_join_domain_name=corp.example.com \
-e ad_join_admin_user=JoinUser \
-e ad_join_admin_password='***' \
-e 'ad_join_dns_servers=["192.0.2.53","192.0.2.54"]' \
-e ad_join_ad_group='LinuxAdmins'
```
## Important behavior
- The script attempts to configure DNS via `systemd-resolved` first, then falls back to writing `/etc/resolv.conf`. If DNS is managed elsewhere, you may need to adapt DNS configuration for your environment.
- The role runs the join step only when the host is not already joined (unless `ad_join_force: true`).
+26
View File
@@ -0,0 +1,26 @@
---
ad_join_script_src: "{{ playbook_dir }}/../ad-join-script.sh"
ad_join_script_dest: /usr/local/sbin/ubuntu-ad-join.sh
ad_join_install_packages:
- realmd
- sssd
- sssd-tools
- libnss-sss
- libpam-sss
- adcli
- samba-common-bin
- oddjob
- oddjob-mkhomedir
- packagekit
ad_join_hostname: ""
ad_join_admin_user: ""
ad_join_admin_password: ""
ad_join_domain_name: ""
ad_join_ad_group: ""
ad_join_dns_server: ""
ad_join_dns_servers: []
ad_join_force: false
ad_join_run: true
+70
View File
@@ -0,0 +1,70 @@
---
- name: Validate required variables
ansible.builtin.assert:
that:
- ad_join_hostname | length > 0
- ad_join_domain_name | length > 0
- ad_join_admin_user | length > 0
- ad_join_admin_password | length > 0
- ad_join_ad_group | length > 0
fail_msg: >-
Missing required variables. Pass them as extra vars (Semaphore) or via inventory/group vars.
- name: Normalize DNS servers list
ansible.builtin.set_fact:
ad_join_dns_servers_effective: >-
{{
(
ad_join_dns_servers.split(',') | map('trim') | reject('equalto', '') | list
)
if (ad_join_dns_servers is string)
else (ad_join_dns_servers | default([]))
}}
- name: Back-compat for single DNS variable
ansible.builtin.set_fact:
ad_join_dns_servers_effective: "{{ [ad_join_dns_server] }}"
when:
- ad_join_dns_servers_effective | length == 0
- ad_join_dns_server | length > 0
- name: Validate DNS server(s) provided
ansible.builtin.assert:
that:
- ad_join_dns_servers_effective | length > 0
fail_msg: >-
Missing DNS server(s). Set ad_join_dns_servers (preferred) or ad_join_dns_server.
- name: Install required packages
ansible.builtin.apt:
name: "{{ ad_join_install_packages }}"
state: present
update_cache: true
- name: Check current domain join status
ansible.builtin.command: realm list
register: ad_join_realm_list
changed_when: false
failed_when: false
- name: Deploy AD join script
ansible.builtin.copy:
src: "{{ ad_join_script_src }}"
dest: "{{ ad_join_script_dest }}"
mode: "0750"
owner: root
group: root
- name: Join domain using script
ansible.builtin.command: "{{ ad_join_script_dest }}"
environment:
ADJOIN_HOSTNAME: "{{ ad_join_hostname }}"
ADJOIN_ADMIN_USER: "{{ ad_join_admin_user }}"
ADJOIN_ADMIN_PASSWORD: "{{ ad_join_admin_password }}"
ADJOIN_DOMAIN_NAME: "{{ ad_join_domain_name }}"
ADJOIN_AD_GROUP: "{{ ad_join_ad_group }}"
ADJOIN_DNS_SERVERS: "{{ ad_join_dns_servers_effective | join(' ') }}"
no_log: true
when:
- ad_join_run | bool
- ad_join_force | bool or (ad_join_realm_list.stdout is not regex_search('realm-name:\\s*' ~ (ad_join_domain_name | regex_escape) ~ '\\b'))
+9
View File
@@ -0,0 +1,9 @@
---
- name: Join Ubuntu hosts to Active Directory
hosts: all
become: true
gather_facts: true
roles:
- role: ad_join