Compare commits

..

11 Commits

Author SHA1 Message Date
497cb6cbf5 style 💎: Update samba provision settings to show password
Updated the `no_log` setting in the `tasks/provision.yml` file to display passwords, allowing for easier debugging and troubleshooting.
2025-11-03 18:26:30 +01:00
7e7c8a4b2d style 💎: Update samba provision task to remove log option
Updated the `no_log` option in the `samba_provision` task to always display passwords, making it easier to debug and understand the provisioning process.
2025-11-03 18:24:17 +01:00
8406e1eab6 feat : Added optional command to show cached Kerberos ticket
Updated verify.yml to include an optional command to display the cached Kerberos ticket, utilizing the 'klist' command and registering its output. This change enhances the flexibility of the verification process.
2025-11-02 19:50:44 +01:00
50dcdfdfbe docs 📝: Update defaults/main.yml documentation
Updated the default values for authentication domain, admin password and IP settings in the main.yml file to ensure consistency with the latest Ansible host configuration.
2025-11-02 19:28:13 +01:00
36a4ac99ed fix 🐛: Update Samba AD DC role for Debian-based systems
Updated the Ansible role to support Debian-based systems (Debian, Ubuntu, etc.) and added new features such as static /etc/resolv.conf, per-host backup of Samba config files, and Molecule tests for both present and absent states.
2025-11-02 19:22:39 +01:00
e6e7f22592 refactor ♻️: Update Samba domain info and provision process
Updated the `samba_domain_info` section to use the new authentication domain and netbios domain. Also updated the `tasks/provision.yml` file to reflect these changes, including the correct realm, domain, server role, and interfaces for the Samba domain.
2025-11-02 18:16:05 +01:00
570500c886 feat : Check domain before provisioning Samba AD DC
Added a check to ensure the domain is not already provisioned before running the samba-tool domain provision command. This prevents duplicate provisions and ensures consistency in the domain setup.
2025-11-02 17:42:35 +01:00
843ebd52a8 feat : Add Samba configuration to defaults/main.yml
Added Samba domain information and package installation to the defaults/main.yml template, allowing for easier setup of a Samba Active Directory Domain Controller. This change enables the use of Samba as an alternative to OpenLDAP for authentication purposes.
2025-11-02 17:37:21 +01:00
6078e8d9b9 feat : Add new feature to configure Samba AD DC DNS settings
This commit adds a new feature to the playbook that configures DNS settings for a Samba AD DC. It includes changes to the `defaults/main.yml` file, `tasks/install.yml`, `tasks/preparing.yml`, `tasks/verify.yml`, and `templates/resolv.conf.j2` files.
2025-10-21 21:10:30 +02:00
6c75e2910b patch undefined: Updated the DNS configuration in the resolv.conf.j2 template to include both the local host and the Ansible-managed DNS server.
Added a new line to the `resolv.conf.j2` template to specify the Ansible-managed DNS server (`{{ addc_ansible_host }}`). This ensures that the system uses both the local host and the managed DNS server for DNS resolution.
2025-10-19 22:25:19 +02:00
0104e69124 feat : Add main.yml file for Samba AD DC setup
This commit adds a new configuration file `main.yml` to the `meta` directory, which sets up an Active Directory Domain Controller (AD DC) using Samba. The file includes details such as author information, description, and dependencies.
2025-10-11 09:00:31 +02:00
20 changed files with 688 additions and 290 deletions

161
README.md
View File

@@ -1,86 +1,145 @@
# Ansible Role: samba_ad_dc
Ansible role to **install**, **provision**, and optionally **remove** a Samba Active Directory Domain Controller (AD DC) on **Debian-based systems** (e.g., Debian, Ubuntu).
Ansible role to **install**, **provision**, and optionally **remove** a Samba Active Directory Domain Controller (ADDC) on **Debianbased systems** (Debian, Ubuntu, etc.).
---
## ✅ Features
- Installs and configures Samba as an AD Domain Controller
- Uses `samba-tool` to provision the domain
- Idempotent: Won't re-provision if already set up
- Reversible: Set `state: absent` to cleanly remove Samba AD DC
- Sets up `/etc/hosts` and DNS resolver
- Separate Kerberos configuration
- Logging of provisioning
- Molecule tests included for both `present` and `absent` states
* Installs & configures Samba as an ADDC
* Provisions the domain with `samba-tool`
* Idempotent skips provisioning if the domain is already present
* Reversible `state: absent` cleans up the DC
* Sets up `/etc/hosts` + a **static** `/etc/resolv.conf`
* Disables services that would otherwise overwrite `/etc/resolv.conf`
(systemdresolved, `resolvconf`, `NetworkManager` DNS, `dhclient`)
* Configures a local NTP daemon (`ntp.conf` + `ntp_signd`) for timesync
* Keeps a **perhost backup** of the Samba config files
* Molecule tests cover both `present` and `absent` states
---
## 📦 Role Variables
### Main Variables
### Core Samba / Kerberos variables
| Variable | Description | Default |
|--------------------------|----------------------------------------------|----------------------|
| `samba_ad_dc_state` | `present` to install, `absent` to remove | `present` |
| `samba_realm` | Kerberos Realm (e.g., `EXAMPLE.COM`) | `EXAMPLE.COM` |
| `samba_domain` | NetBIOS domain name (e.g., `EXAMPLE`) | `EXAMPLE` |
| `samba_admin_password` | Admin password for the domain | `StrongAdminPassword123!` |
| `samba_dns_backend` | DNS backend (`SAMBA_INTERNAL`, `BIND9_DLZ`) | `SAMBA_INTERNAL` |
| `samba_hostname` | Hostname for the server | `inventory_hostname` |
| Variable | Description | Default value |
|-------------------------|---------------------------------------------|-----------------------------|
| `samba_ad_dc_state` | `present` → install, `absent` → remove | `present` |
| `samba_realm` | Kerberos realm (e.g., `CORP.EXAMPLE.COM`) | `EXAMPLE.COM` |
| `samba_domain` | NetBIOS domain name (e.g., `CORP`) | `EXAMPLE` |
| `samba_admin_password` | Password for the domain administrator | `StrongAdminPassword123!` |
| `samba_dns_backend` | DNS backend (`SAMBA_INTERNAL`/`BIND9_DLZ`) | `SAMBA_INTERNAL` |
| `samba_hostname` | Hostname that will appear in `smb.conf` | `inventory_hostname` |
### Extended configuration variables (see `defaults/main.yml`)
| Variable | Description | Default value |
|-----------------------|-------------------------------------------------|---------------|
| `samba_packages` | Packages to install when `state: present`. Includes the full list needed for a Debian/Ubuntu ADDC. | *See the YAML block below* |
| `location_internal_dns` | Internal DNS server used in `/etc/resolv.conf` | `192.168.1.1` |
| `location_external_dns` | External (fallback) DNS server | `8.8.8.8` |
| `backup_path` | Directory where the role will store a backup of
`/etc/samba`, `/etc/krb5.conf`, and other files. | `"/path/to/your/backup/directory"` |
> **NOTE** the `samba_packages` block is kept for clarity, but it is *not* an
> **Ansible variable** you set on the host; it simply holds the list that
> `tasks/install.yml` uses.
### NTP configuration
The role also deploys an NTP daemon (file: `tasks/ntpd.yml`):
* **`/etc/ntp.conf`** is rendered from `templates/ntp.conf.j2`
* The NTP service is enabled & started automatically
* The handler `Restart ntp service` is called whenever the config changes
### DNSprep
`tasks/preparing.yml` performs the following important housekeeping steps
before any Samba configuration:
1. Stops and disables `systemd-resolved` (if present).
2. Removes `/etc/resolv.conf` when it is a symlink to the resolver.
3. Creates a **static** `/etc/resolv.conf` that contains both your internal
and external nameservers (`location_internal_dns`, `location_external_dns`).
4. Uninstalls the `resolvconf` package (if it is installed).
5. Prevents NetworkManager from writing DNS entries by editing
`/etc/NetworkManager/NetworkManager.conf`.
6. Prevents `dhclient` from touching `/etc/resolv.conf` (implementation left
to the user you can add a `blockinfile` task or a similar filelock).
> **Tip** if you ever run into “no DNS resolution” on the host *after* the role has finished, doublecheck that
> `systemd-resolved` is truly disabled and that `/etc/resolv.conf` is **not** a symlink.
---
## 🧰 Example Playbook
## 📁 Included Task Files
```yaml
- hosts: samba
become: true
roles:
- role: samba_ad_dc
vars:
samba_realm: "CORP.EXAMPLE.COM"
samba_domain: "CORP"
samba_admin_password: "SuperSecretPassw0rd!"
| File | Purpose |
|--------------------------|---------|
| `tasks/install.yml` | Installs packages, configures NTP & DNS, then provisions Samba |
| `tasks/remove.yml` | Stops & removes Samba packages + cleans up files |
| `tasks/kerberos.yml` | Copies `krb5.conf.j2``/etc/krb5.conf` |
| `tasks/verify.yml` | Runs `samba-tool` checks + `kinit` tests |
| `tasks/dns_hosts.yml` | Ensures `/etc/hosts` contains the DC record |
| `tasks/logging.yml` | Stores provisioning output to a log file |
| `tasks/preparing.yml` | Disables `systemd-resolved`, `resolvconf`, `NetworkManager`, and creates a static `/etc/resolv.conf` |
| `tasks/ntpd.yml` | Configures NTP (template `ntp.conf.j2`) and ensures it is running |
❌ Remove Samba AD DC
- hosts: samba
become: true
roles:
- role: samba_ad_dc
vars:
samba_ad_dc_state: absent
---
📁 Included Tasks
## 🧩 Compatibility
install.yml: Installs and provisions Samba AD
* **Operating systems**: Debian10/11/12+ , Ubuntu20.04/22.04+
* **Ansible**: 2.9+ (the role uses only core modules)
remove.yml: Stops and removes Samba AD
---
kerberos.yml: Configures Kerberos (/etc/krb5.conf)
## 🔒 Security Notes
verify.yml: Validates the installation (samba-tool, kinit)
* Store `samba_admin_password` in Ansible Vault in production.
* The role backs up the Samba configuration to the directory defined by
`backup_path` see `tasks/logging.yml` for how the backup is created.
dns_hosts.yml: Ensures /etc/hosts and DNS resolvers are set
---
logging.yml: Logs provisioning output
## 📄 Templates
📄 Templates
| Template | Description |
|----------|-------------|
| `templates/smb.conf.j2` | Samba configuration for the AD DC |
| `templates/krb5.conf.j2` | Kerberos client config |
| `templates/resolv.conf.j2` | Static `/etc/resolv.conf` (search domain + nameserver) |
| `templates/ntp.conf.j2` | NTP configuration used by `tasks/ntpd.yml` |
smb.conf.j2: Samba configuration
---
krb5.conf.j2: Kerberos configuration
## 🛠️ Troubleshooting
🔒 Security Notes
| Issue | What to check |
|-------|---------------|
| `samba-tool domain provision` fails with a password error | Verify `samba_admin_password` is correct and **nolog** (`no_log: true`) is set. |
| DNS resolution fails on the DC itself | Ensure `samba_iface` contains `lo` **and** the NIC (e.g., `lo eth0`) and that `/etc/resolv.conf` is not a symlink. |
| Time skew on the DC | Verify NTP service is running (`systemctl status ntp`). |
| Backup directory missing | Check that `backup_path` is writable by the user running the playbook. |
Passwords should be stored in Ansible Vault for production.
---
DNS and Kerberos configuration assumes internal AD DNS — adjust for external resolvers if needed.
## 📥 Install via Ansible Galaxy
🧩 Compatibility
```bash
ansible-galaxy init samba_ad_dc
```
OS: Debian 10/11/12+, Ubuntu 20.04/22.04+
After adding this file, run:
Ansible: 2.9+
```bash
ansible-galaxy collection install samba_ad_dc
```
---
## 🤝 Contributing
Feel free to submit PRs just keep the variable documentation in sync with the files you modify. Happy automating!

View File

@@ -1,25 +1,51 @@
samba_ad_dc_state: present # or 'absent'
# AD Provisioning details
samba_realm: "EXAMPLE.COM"
samba_domain: "EXAMPLE"
samba_admin_password: "StrongAdminPassword123!"
samba_dns_backend: "SAMBA_INTERNAL"
samba_hostname: "{{ inventory_hostname }}"
# Hostname = DC1
addc_hostname: "DC1"
# DC local IP Address = 10.99.0.1
addc_ansible_host: "10.99.0.1"
# NetBIOS domain name (Workgroup).
addc_netbios_domain: "SAMDOM"
# Top level Domain = EXAMPLE.COM
addc_tld: "EXAMPLE.COM"
# Authentication Domain = SAMDOM.EXAMPLE.COM
addc_auth_domain: "{{ addc_netbios_domain | upper }}.{{ addc_tld | upper }}"
samba_log_dir: /var/log/samba
samba_provision_log_file: "{{ samba_log_dir }}/ad_provision.log"
addc_admin_password: "Passw0rd"
addc_dns_backend: "SAMBA_INTERNAL"
addc_server_role: "dc"
# allows skipping verification when needed
samba_verify: true
addc_ip_network_prefix: "{{ addc_ansible_host.split('.')[:3] | join('.') }}"
addc_ip_last_octet: "{{ addc_ansible_host.split('.')[-1] }}"
addc_reverse_zone_name: "{{ addc_ip_network_prefix.split('.') | reverse | join('.') }}.in-addr.arpa"
samba_domain_info:
realm: "{{ addc_auth_domain | upper }}"
domain: "{{ addc_netbios_domain | upper }}"
server_role: "{{ addc_server_role | default('dc') }}"
dns_backend: "{{ addc_dns_backend | default('SAMBA_INTERNAL') }}"
adminpass: "{{ addc_admin_password }}"
interfaces: "{{ samba_iface_list | join(' ') }}"
bind_interfaces_only: "yes"
samba_iface_list:
- lo
- eth0
samba_packages:
- acl
- attr
- samba
- winbind
- libpam-winbind
- libnss-winbind
- krb5-config
- krb5-user
- dnsutils
- python3-setproctitle
# - smbclient
# template for /etc/resolv.conf
samba_dns_nameservers:
- 127.0.0.1
- 8.8.8.8
samba_resolv_conf_backup_path: /etc/resolv.conf.ansible.bak
# Internal state tracking
samba_samdb_path: "/var/lib/samba/private/sam.ldb"
samba_conf_path: "/etc/samba/smb.conf"
location_internal_dns: 192.168.1.1
location_external_dns: 8.8.8.8
backup_path: "/path/to/your/backup/directory"

View File

@@ -1,5 +1,15 @@
---
- name: Restart NetworkManager
ansible.builtin.systemd:
name: NetworkManager
state: restarted
- name: Restart Samba AD DC
service:
ansible.builtin.service:
name: samba-ad-dc
state: restarted
- name: Restart ntp service
ansible.builtin.service:
name: ntp
state: restarted

12
meta/main.yml Normal file
View File

@@ -0,0 +1,12 @@
---
galaxy_info:
author: Jose
description: Setup Samba AD DC
# license: MIT
# min_ansible_version: 2.9
# platforms:
# - name: Debian
# versions:
# - buster
# - bullseye
dependencies: []

56
tasks/0backupcheck.yml Normal file
View File

@@ -0,0 +1,56 @@
---
- name: Check if backup directory exists
stat:
path: "{{ backup_path }}"
register: backup_dir_stat
- name: Check if backup directory is not empty
find:
paths: "{{ backup_path }}"
file_type: any
recurse: false
when: backup_dir_stat.stat.exists and backup_dir_stat.stat.isdir
register: backup_dir_contents
- name: Check if each required file exists
stat:
path: "{{ dir_path }}/{{ item }}"
loop: "{{ backup_required_files }}"
register: required_file_stats
- name: Determine missing files
set_fact:
missing_files: >-
{{ required_file_stats.results
| selectattr('stat.exists', 'equalto', false)
| map(attribute='item')
| list }}
- name: Set fact if all required files are present
set_fact:
all_required_files_present: true
when: missing_files | length == 0
- name: Debug - Show status
debug:
msg: >-
{% if all_required_files_present | default(false) %}
All required files are present.
{% else %}
Missing required files: {{ missing_files }}
{% endif %}
- name: Set fact if backup directory exists and is not empty
set_fact:
backup_dir_valid: true
when:
- backup_dir_stat.stat.exists
- backup_dir_stat.stat.isdir
- backup_dir_contents.matched | int > 0
- all_required_files_present
- name: Debug - Show final result
debug:
msg: "Backup directory exists and is not empty."
when: backup_dir_valid | default(false)

View File

@@ -1,29 +0,0 @@
---
- name: Backup original /etc/resolv.conf if not already backed up
copy:
src: /etc/resolv.conf
dest: "{{ samba_resolv_conf_backup_path }}"
remote_src: yes
force: no
when:
- samba_ad_dc_state == "present"
- ansible_virtualization_type != "docker"
- name: Template /etc/resolv.conf with custom DNS nameservers
template:
src: resolv.conf.j2
dest: /etc/resolv.conf
owner: root
group: root
mode: '0644'
when:
- samba_ad_dc_state == "present"
- ansible_virtualization_type != "docker"
- name: Set /etc/hosts entry for Samba AD DC
lineinfile:
path: /etc/hosts
line: "{{ ansible_default_ipv4.address }} {{ samba_hostname }}.{{ samba_realm | lower }} {{ samba_hostname }}"
state: present
create: yes

View File

@@ -1,11 +0,0 @@
---
- name: Restore original /etc/resolv.conf from backup
copy:
src: "{{ samba_resolv_conf_backup_path }}"
dest: /etc/resolv.conf
remote_src: yes
force: yes
when:
- samba_ad_dc_state == "absent"
- ansible_virtualization_type != "docker"
- samba_resolv_conf_backup_path is defined

View File

@@ -1,47 +1,50 @@
---
- name: Install required packages
apt:
name:
- samba
- krb5-config
- krb5-user
- winbind
- smbclient
- dnsutils
state: present
update_cache: yes
ansible.builtin.package:
name: "{{ samba_packages }}"
state: latest
- name: Stop samba-ad-dc before provisioning (if running)
service:
ansible.builtin.service:
name: samba-ad-dc
state: stopped
enabled: no
ignore_errors: yes
- name: Provision AD domain
include_tasks: provision.yml
- name: Deploy smb.conf
template:
src: smb.conf.j2
dest: "{{ samba_conf_path }}"
owner: root
group: root
mode: '0644'
notify: Restart Samba AD DC
- name: Enable and start samba-ad-dc service
service:
name: samba-ad-dc
state: started
enabled: yes
- name: Configure Kerberos
include_tasks: kerberos.yml
- name: Set DNS resolver and hosts entry
include_tasks: dns_hosts.yml
- name: Run verification checks
include_tasks: verify.yml
when: samba_verify | bool
# - name: Check if backup exist
# - name: Provision AD domain
# include_tasks: provision.yml
# - name: Deploy smb.conf
# ansible.builtin.template:
# src: smb.conf.j2
# dest: "{{ samba_conf_path }}"
# owner: root
# group: root
# mode: '0644'
# notify: Restart Samba AD DC
# - name: Enable and start samba-ad-dc service
# ansible.builtin.service:
# name: samba-ad-dc
# state: started
# enabled: yes
# - name: Configure Kerberos
# include_tasks: kerberos.yml
# - name: Set DNS resolver and hosts entry
# include_tasks: dns_hosts.yml
# - name: Run verification checks
# include_tasks: verify.yml
# when: samba_verify | bool

View File

@@ -1,8 +1,17 @@
---
- name: Configure Kerberos (krb5.conf)
template:
src: krb5.conf.j2
- name: Extract krb5.conf path from provision output
# The samba-tool output usually contains the path on a specific line.
# We extract the path using regex and the 'search' filter.
ansible.builtin.set_fact:
krb5_conf_path: "{{ samba_provision_output.stdout | regex_search('krb5.conf file is located at (.*)', '\\1') | first }}"
when: samba_provision_output.stdout is defined
- name: Copy krb5.conf to /etc/krb5.conf
ansible.builtin.copy:
src: "{{ krb5_conf_path }}"
dest: /etc/krb5.conf
owner: root
group: root
mode: '0644'
# Only run this if the provision was successful (changed)
when: samba_provision_output.changed

View File

@@ -1,9 +1,23 @@
---
- name: Install or remove Samba AD DC
include_tasks: install.yml
when: samba_ad_dc_state == 'present'
- name: Prepare for Samba AD DC
include_tasks: preparing.yml
- name: Install
include_tasks: install.yml
- name: Provision
include_tasks: provision.yml
- name: Setup DNS resolver
include_tasks: setupresolver.yml
- name: Configure Kerberos
include_tasks: kerberos.yml
- name: Testing Samba AD DC
include_tasks: verify.yml
- name: Configure Time Synchronization
include_tasks: ntpd.yml
- name: Remove Samba AD DC
include_tasks: remove.yml
when: samba_ad_dc_state == 'absent'

54
tasks/ntpd.yml Normal file
View File

@@ -0,0 +1,54 @@
---
- name: Ensure the ntp package is installed
ansible.builtin.package:
name: ntp
state: present
- name: Search common Samba locations for the 'ntp_signd' directory
ansible.builtin.find:
paths:
# Common paths for Samba installations
- /var/lib/samba/
- /usr/local/samba/
- /etc/samba/
pattern: ntp_signd
file_type: directory
register: find_ntp_signd
- name: Set the path variable, failing if not found
ansible.builtin.set_fact:
ntp_signd_path: "{{ find_ntp_signd.files[0].path }}"
# This conditional logic ensures the playbook stops if the directory is missing,
# or if more than one directory named 'ntp_signd' is found (which is unlikely/undesirable).
when: find_ntp_signd.matched == 1
failed_when: find_ntp_signd.matched != 1
- name: Verify permissions on the detected 'ntp_signd' directory
ansible.builtin.stat:
path: "{{ ntp_signd_path }}"
register: ntp_signd_stats
- name: Assert that the permissions allow read access
ansible.builtin.assert:
that:
# Check if the directory exists and has permissions that grant read/execute to 'other' (r-x)
- ntp_signd_stats.stat.exists
- ntp_signd_stats.stat.mode is search('[rwx-]{2}[rwx-]{2}[4-7]')
fail_msg: "FATAL: The detected ntp_signd directory ({{ ntp_signd_path }}) does not have necessary read permissions (mode: {{ ntp_signd_stats.stat.mode }})."
success_msg: "SUCCESS: Permissions on {{ ntp_signd_path }} are correctly configured."
- name: Configure ntp.conf for Active Directory Domain Controller (AD DC)
ansible.builtin.template:
src: templates/ntp.conf.j2 # Path to your NTP template file
dest: /etc/ntp.conf
owner: root
group: root
mode: '0644'
notify:
- Restart ntp service
- name: Enable and start the ntp service
ansible.builtin.service:
name: ntp
state: started
enabled: true

112
tasks/preparing.yml Normal file
View File

@@ -0,0 +1,112 @@
---
# Disable tools, such as resolvconf, that automatically update your /etc/resolv.conf DNS resolver configuration file
- name: Stop and disable systemd-resolved if present
ansible.builtin.systemd:
name: systemd-resolved
enabled: false
state: stopped
when: ansible_facts.services['systemd-resolved.service'] is defined
- name: Remove /etc/resolv.conf if it's a symlink to systemd-resolved
ansible.builtin.file:
path: /etc/resolv.conf
state: absent
when: "'/run/systemd/resolve' in ansible_facts.lsb.description | default('')"
- name: Create static /etc/resolv.conf
ansible.builtin.copy:
dest: /etc/resolv.conf
content: |
nameserver {{ location_internal_dns }}
nameserver {{ location_external_dns }}
owner: root
group: root
mode: '0644'
- name: Disable resolvconf package (if installed)
ansible.builtin.package:
name: resolvconf
state: absent
- name: Disable DNS updates from NetworkManager (if present)
ansible.builtin.blockinfile:
path: /etc/NetworkManager/NetworkManager.conf
block: |
[main]
dns=none
notify: Restart NetworkManager
when: ansible_facts.services['NetworkManager.service'] is defined
- name: Prevent dhclient from modifying resolv.conf (if present)
ansible.builtin.lineinfile:
path: /etc/dhcp/dhclient.conf
regexp: '^#?supersede domain-name-servers'
line: 'supersede domain-name-servers {{ location_internal_dns }}, {{ location_external_dns }};'
create: yes
# Verify that the /etc/hosts file on the DC correctly resolves the fully-qualified domain name (FQDN) and short host name to the LAN IP address of the DC
- name: Set /etc/hosts entry for Samba AD DC
ansible.builtin.lineinfile:
path: /etc/hosts
line: "{{ addc_ansible_host }} {{ addc_hostname | upper }}.{{ addc_tld | lower }} {{ addc_hostname | upper }}"
state: present
create: yes
- name: Ensure '127.0.0.1 localhost' is present and nothing else on that line
ansible.builtin.lineinfile:
path: /etc/hosts
regexp: '^127\.0\.0\.1\s+'
line: '127.0.0.1 localhost'
state: present
# Remove any existing smb.conf file
- name: Get compiled default smb.conf path from smbd
ansible.builtin.shell: smbd -b | grep CONFIGFILE | awk '{print $2}'
register: smb_conf_path
changed_when: false
failed_when: smb_conf_path.rc != 0
- name: Remove smb.conf using discovered path
ansible.builtin.file:
path: "{{ smb_conf_path.stdout }}"
state: absent
# Remove all Samba database files, such as *.tdb and *.ldb files
- name: Get Samba directories from smbd -b
ansible.builtin.shell: smbd -b | egrep "LOCKDIR|STATEDIR|CACHEDIR|PRIVATE_DIR" | awk '{print $2}'
register: samba_dirs
changed_when: false
failed_when: samba_dirs.rc != 0
- name: Filter existing directories
ansible.builtin.find:
paths: "{{ item }}"
file_type: directory
recurse: no
loop: "{{ samba_dirs.stdout_lines }}"
register: existing_dirs
- name: Collect existing directories
ansible.builtin.set_fact:
valid_dirs: "{{ existing_dirs.results | selectattr('matched', '>', 0) | map(attribute='files') | sum(start=[]) | map(attribute='path') | list }}"
- name: Find *.tdb and *.ldb files
ansible.builtin.find:
paths: "{{ item }}"
patterns: "*.tdb,*.ldb"
recurse: yes
use_regex: false
loop: "{{ valid_dirs }}"
register: db_files
- name: Remove found tdb/ldb files
ansible.builtin.file:
path: "{{ item.path }}"
state: absent
loop: "{{ db_files.results | map(attribute='files') | sum(start=[]) }}"
when: item.path is defined
- name: Report removed files
ansible.builtin.debug:
msg: "Removed: {{ item.path }}"
loop: "{{ db_files.results | map(attribute='files') | sum(start=[]) }}"

View File

@@ -1,37 +1,23 @@
---
---
- name: Ensure Samba log directory exists
file:
path: "{{ samba_log_dir }}"
state: directory
owner: root
group: root
mode: '0755'
- name: check if domain already provisioned
ansible.builtin.stat:
path: /var/lib/samba/private/adsync.conf
register: samba_provisioned
- name: Provision the Samba AD DC (with logging)
command: >
- name: Provision the Samba AD DC
ansible.builtin.command: >
samba-tool domain provision
--use-rfc2307
--realm={{ samba_realm }}
--domain={{ samba_domain }}
--server-role=dc
--dns-backend={{ samba_dns_backend }}
--adminpass={{ samba_admin_password }}
args:
creates: "{{ samba_samdb_path }}"
--realm={{ samba_domain_info.realm }}
--domain={{ samba_domain_info.domain }}
--server-role={{ samba_domain_info.server_role }}
--dns-backend={{ samba_domain_info.dns_backend }}
--adminpass={{ samba_domain_info.adminpass }}
--option="interfaces={{ samba_domain_info.interfaces }}"
--option="bind interfaces only={{ samba_domain_info.bind_interfaces_only }}"
when: not samba_provisioned.stat.exists
register: samba_provision_output
changed_when: samba_provision_output.rc == 0
no_log: false # You may toggle this if password should be hidden
- name: Write provisioning output to log
copy:
content: "{{ samba_provision_output.stdout }}"
dest: "{{ samba_provision_log_file }}"
owner: root
group: root
mode: '0644'
- name: Redact passwords in provisioning log (optional)
replace:
path: "{{ samba_provision_log_file }}"
regexp: "--adminpass=.*"
replace: "--adminpass=********"

View File

@@ -1,39 +0,0 @@
---
- name: Stop Samba AD DC
service:
name: samba-ad-dc
state: stopped
enabled: no
ignore_errors: true
- name: Remove Samba configuration
file:
path: "{{ samba_conf_path }}"
state: absent
- name: Remove Samba DB and related files
file:
path: "{{ item }}"
state: absent
loop:
- /var/lib/samba
- /etc/krb5.conf
- /etc/samba
- /var/cache/samba
- /var/log/samba
- name: Remove Samba-related packages
apt:
name:
- samba
- krb5-config
- krb5-user
- winbind
- smbclient
- dnsutils
state: absent
purge: yes
autoremove: yes
- name: Restore DNS config
include_tasks: dns_hosts_restore.yml

8
tasks/setupresolver.yml Normal file
View File

@@ -0,0 +1,8 @@
---
- name: Template /etc/resolv.conf with custom DNS nameservers
template:
src: resolv.conf.j2
dest: /etc/resolv.conf
owner: root
group: root
mode: '0644'

View File

@@ -1,40 +1,165 @@
---
- name: Verify Samba AD DC setup
when: samba_verify | bool
block:
- name: Start the samba service
ansible.builtin.service:
name: samba
state: started
enabled: true
- name: Run 'samba-tool domain info'
command: samba-tool domain info 127.0.0.1
register: domain_info
- name: Create the reverse DNS zone {{ addc_reverse_zone_name }}
community.general.expect:
# Note: The 'expect' module is in the 'community.general' collection
command: "samba-tool dns zonecreate {{ addc_ansible_host }} {{ addc_reverse_zone_name }} -U Administrator"
responses:
# Use the '(?i)' flag for case-insensitive matching of the prompt.
'(?i)password for.*:': "{{ addc_admin_password }}"
no_log: true # Highly recommended to prevent the password from appearing in logs
- name: Create the PTR (reverse) DNS record
community.general.expect:
# Command syntax: samba-tool dns add <server> <zone> <record_name> PTR <target_fqdn>
command: >
samba-tool dns add {{ addc_ansible_host }}
{{ addc_reverse_zone_name }}
{{ addc_ip_last_octet }} PTR
{{ addc_hostname | lower }}.{{ addc_auth_domain | lower }}
-U Administrator
responses:
# Expects the standard Samba password prompt
'(?i)password for.*:': "{{ addc_admin_password }}"
no_log: true # Hide sensitive data from logs
- name: Verify Samba file server by listing local shares
ansible.builtin.command: smbclient -L localhost -N
register: smbclient_output
changed_when: false # This is a verification step, it doesn't change the host state
- name: Report the results of the smbclient verification
ansible.builtin.debug:
msg: "Samba Shares found: {{ smbclient_output.stdout }}"
- name: Verify Samba AD authentication by accessing the netlogon share
community.general.expect:
# Command to run: smbclient //localhost/netlogon -UAdministrator -c 'ls'
# The -c 'ls' command lists files on the share.
command: smbclient //localhost/netlogon -UAdministrator -c 'ls'
responses:
# Use the (?i) flag for case-insensitive matching of the prompt.
'(?i)password:': "{{ addc_admin_password }}"
no_log: true # CRITICAL: Prevents the password from being logged
register: auth_verification
changed_when: false # This is a verification/check, not a change
- name: Verify LDAP Service Record (SRV _ldap._tcp)
ansible.builtin.command: host -t SRV _ldap._tcp.{{ addc_auth_domain | lower }}.
register: ldap_srv_check
changed_when: false
failed_when: "'has SRV record' not in ldap_srv_check.stdout"
- name: Assert that the domain is provisioned
assert:
that:
- "'Netbios name' in domain_info.stdout"
- "'Server Role: ACTIVE DIRECTORY DOMAIN CONTROLLER' in domain_info.stdout"
- name: Debug - Show LDAP SRV check result
ansible.builtin.debug:
var: ldap_srv_check.stdout
- name: Attempt kinit with administrator
command: echo "{{ samba_admin_password }}" | kinit administrator@{{ samba_realm }}
register: kinit_result
- name: Verify Kerberos Service Record (SRV _kerberos._udp)
ansible.builtin.command: host -t SRV _kerberos._udp.{{ addc_auth_domain | lower }}.
register: kerberos_srv_check
changed_when: false
failed_when: kinit_result.rc != 0
failed_when: "'has SRV record' not in kerberos_srv_check.stdout"
- name: Check Kerberos ticket
command: klist
register: klist_result
- name: Debug - Show Kerberos SRV check result
ansible.builtin.debug:
var: kerberos_srv_check.stdout
- name: Verify DC's A (Forward) Record
ansible.builtin.command: host -t A {{ addc_hostname | lower }}.{{ addc_auth_domain | lower }}.
register: a_record_check
changed_when: false
failed_when: "{{ addc_ansible_host }} not in a_record_check.stdout"
- name: Assert Kerberos ticket exists
assert:
that:
- "'krbtgt/{{ samba_realm }}@{{ samba_realm }}' in klist_result.stdout"
- name: Debug - Show A Record check result
ansible.builtin.debug:
var: a_record_check.stdout
- name: Check Samba AD DC service status
service_facts:
- name: Assert samba-ad-dc service is active
assert:
that:
- "'samba-ad-dc' in ansible_facts.services"
- ansible_facts.services['samba-ad-dc'].state == 'running'
- name: Verify DC's PTR (Reverse) Record
ansible.builtin.command: host -t PTR {{ addc_ansible_host }}
register: ptr_record_check
changed_when: false
# Assuming dc1.{{ addc_auth_domain }} is the expected output for the reverse record
failed_when: "'domain name pointer {{ addc_hostname | lower }}.{{ addc_auth_domain | lower }}' not in ptr_record_check.stdout"
- name: Debug - Show PTR Record check result
ansible.builtin.debug:
var: ptr_record_check.stdout
- name: Verify Kerberos authentication using kinit
community.general.expect:
# Command to run: kinit administrator
command: kinit administrator
responses:
# Expects the standard Kerberos password prompt
# The (?i) flag ensures case-insensitive matching.
'(?i)password for administrator.*:': "{{ addc_admin_password }}"
no_log: true # CRITICAL: Prevents the password from being logged
register: kinit_check
changed_when: false # This is a verification/check, not a change
- name: Debug - Show kinit verification result (should be empty on success)
ansible.builtin.debug:
msg: "Kerberos kinit verification successful. Output: {{ kinit_check.stdout }}"
- name: Optional - Show the cached Kerberos ticket
ansible.builtin.command: klist
register: klist_output
changed_when: false
when: kinit_check is succeeded
- name: Debug - Show klist output
ansible.builtin.debug:
var: klist_output.stdout
when: klist_check is defined
# - name: Run 'samba-tool domain info'
# command: samba-tool domain info 127.0.0.1
# register: domain_info
# changed_when: false
# - name: Assert that the domain is provisioned
# assert:
# that:
# - "'Netbios name' in domain_info.stdout"
# - "'Server Role: ACTIVE DIRECTORY DOMAIN CONTROLLER' in domain_info.stdout"
# - name: Attempt kinit with administrator
# command: echo "{{ samba_admin_password }}" | kinit administrator@{{ samba_realm }}
# register: kinit_result
# changed_when: false
# failed_when: kinit_result.rc != 0
# - name: Check Kerberos ticket
# command: klist
# register: klist_result
# changed_when: false
# - name: Assert Kerberos ticket exists
# assert:
# that:
# - "'krbtgt/{{ samba_realm }}@{{ samba_realm }}' in klist_result.stdout"
# - name: Check Samba AD DC service status
# service_facts:
# - name: Assert samba-ad-dc service is active
# assert:
# that:
# - "'samba-ad-dc' in ansible_facts.services"
# - ansible_facts.services['samba-ad-dc'].state == 'running'

View File

@@ -1,17 +0,0 @@
[libdefaults]
default_realm = {{ samba_realm }}
dns_lookup_realm = false
dns_lookup_kdc = true
ticket_lifetime = 24h
forwardable = yes
rdns = false
[realms]
{{ samba_realm }} = {
kdc = {{ samba_hostname }}
admin_server = {{ samba_hostname }}
}
[domain_realm]
.{{ samba_realm | lower }} = {{ samba_realm }}
{{ samba_realm | lower }} = {{ samba_realm }}

37
templates/ntp.conf.j2 Normal file
View File

@@ -0,0 +1,37 @@
# This configuration file is managed by Ansible.
# It is configured to run as an Active Directory Domain Controller.
# Enable kernel time discipline (important for a DC)
tos maxclock 10
# Use your own local clock as a reliable fallback/default
# Local clock. Note that is not the "localhost" address!
server 127.127.1.0 # Undisciplined local clock
fudge 127.127.1.0 stratum 10
# Where to retrieve the time from
# Optionally, add external sources for greater accuracy (NTP pool)
# You should choose servers close to your location or use a reliable pool.
server 0.pool.ntp.org iburst prefer
server 1.pool.ntp.org iburst prefer
server 1.pool.ntp.org iburst prefer
# Drift file location
driftfile /var/lib/ntp/ntp.drift
logfile /var/log/ntp
ntpsigndsocket {{ ntp_signd_path }}
# Access control
# Default restriction: Allow clients only to query the time
restrict default kod nomodify notrap nopeer limited mssntp
# No restrictions for "localhost"
restrict 127.0.0.1
# Enable the time sources to only provide time to this host
restrict 0.pool.ntp.org mask 255.255.255.255 nomodify notrap nopeer noquery
restrict 1.pool.ntp.org mask 255.255.255.255 nomodify notrap nopeer noquery
restrict 2.pool.ntp.org mask 255.255.255.255 nomodify notrap nopeer noquery
# tell NTP not to panic and exit
tinker panic 0

View File

@@ -1,5 +1,3 @@
# Managed by Ansible - Samba AD DC DNS
{% for ns in samba_dns_nameservers %}
nameserver {{ ns }}
{% endfor %}
# Managed by Ansible - Samba AD DC DNS- DO NOT EDIT MANUALLY
search {{ addc_auth_domain | lower }}
nameserver {{ addc_ansible_host }}

View File

@@ -1,15 +0,0 @@
[global]
workgroup = {{ samba_domain }}
realm = {{ samba_realm }}
netbios name = {{ samba_hostname | upper }}
server role = active directory domain controller
dns forwarder = 8.8.8.8
idmap_ldb:use rfc2307 = yes
[sysvol]
path = /var/lib/samba/sysvol
read only = no
[netlogon]
path = /var/lib/samba/sysvol/{{ samba_realm | lower }}/scripts
read only = no