# Ansible Bootstrap Playbook Automate Linux system bootstrap across multiple distributions and hypervisors in an Infrastructure-as-Code workflow. ## Supported Distributions | Distribution | Versions | |---|---| | AlmaLinux | 8.x, 9.x, 10.x | | Alpine Linux | latest | | Arch Linux | latest | | Debian | 10, 11, 12, 13, unstable | | Fedora | 40, 41, 42, 43 | | openSUSE Tumbleweed | latest | | RHEL | 8.x, 9.x, 10.x | | Rocky Linux | 8.x, 9.x, 10.x | | Ubuntu | latest | | Ubuntu LTS | latest | | Void Linux | latest | ## Supported Hypervisors | Hypervisor | Value | |---|---| | libvirt | `libvirt` | | Proxmox | `proxmox` | | VMware | `vmware` | | Xen | `xen` | | Bare metal | `none` | ## Compatibility Notes - `rhel_iso` is required for `system.os: rhel`. - RHEL installs should use `system.filesystem: ext4` or `system.filesystem: xfs` (not `btrfs`). - For RHEL 8 specifically, prefer `ext4` over `xfs` if you hit installer/filesystem edge cases. - `custom_iso: true` skips ArchISO validation and pacman preparation; your installer image must already provide required tooling. - On non-Arch installers, set `system.features.chroot.tool` (`arch-chroot`, `chroot`, or `systemd-nspawn`) explicitly as needed. ## Configuration Model The project uses only dict-based variables: - `system` for host/runtime/install configuration - `hypervisor` for virtualization backend configuration These dictionaries are normal Ansible variables and belong in host/group vars. You can define them in inventory host entries, `group_vars/*`, or `host_vars/*`. Dictionary variables are merged across scopes (`group_vars` -> `host_vars`) by project config. Set shared values like `system.filesystem` once in group vars, then override only host-specific keys per host. ### Variable Placement | Location | Scope | Typical use | |---|---|---| | `group_vars/all.yml` | All hosts | Shared defaults like `hypervisor`, `system.filesystem`, `boot_iso` | | `group_vars/.yml` | Group | Environment or role-specific defaults | | `host_vars/.yml` | Single host | Host-specific overrides | | Inventory host vars | Single host | Inline definitions for quick setup | ### Example Host Definition ```yaml all: vars: system: filesystem: btrfs boot_iso: "local:iso/archlinux-x86_64.iso" hypervisor: type: proxmox url: pve01.example.com username: root@pam password: CHANGE_ME host: pve01 storage: local-lvm children: bootstrap: hosts: app01.example.com: ansible_host: 10.0.0.10 system: type: virtual os: debian version: "12" name: app01.example.com id: 101 cpus: 2 memory: 4096 balloon: 0 network: vmbr0 ip: 10.0.0.10 prefix: 24 gateway: 10.0.0.1 dns: servers: [1.1.1.1, 1.0.0.1] search: [example.com] disks: - size: 40 - size: 120 mount: path: /data fstype: xfs user: name: ops password: CHANGE_ME key: "ssh-ed25519 AAAA..." root: password: CHANGE_ME luks: enabled: true passphrase: CHANGE_ME auto: true method: tpm2 tpm2: pcrs: "7" features: firewall: enabled: true backend: firewalld toolkit: nftables ``` ## Core Variables | Variable | Type | Description | |---|---|---| | `boot_iso` | string | Required when `system.type=virtual` | | `rhel_iso` | string | Required when `system.os=rhel` | | `custom_iso` | bool | Skip Arch ISO-specific preparation checks | ## `system` Dictionary Top-level host install/runtime settings. Use these keys under `system`. | Key | Type | Default | Description | |---|---|---|---| | `type` | string | `virtual` | `virtual` or `physical` | | `os` | string | empty (`archlinux` if omitted on physical) | Target distribution | | `version` | string | empty | Version selector for distro families | | `filesystem` | string | empty | `btrfs`, `ext4`, or `xfs` | | `name` | string | inventory hostname | Final hostname | | `id` | int/string | empty | VMID for Proxmox | | `cpus` | int | `0` | vCPU count | | `memory` | int | `0` | Memory in MiB | | `balloon` | int | `0` | Balloon memory in MiB | | `network` | string | empty | Hypervisor network/bridge | | `vlan` | string/int | empty | VLAN tag | | `ip` | string | empty | Static IP (optional) | | `prefix` | int | empty | Prefix for static IP | | `gateway` | string | empty | Static gateway | | `path` | string | empty | Hypervisor folder/path (libvirt/vmware) | | `packages` | list/string | empty | Post-reboot packages | | `dns` | dict | `{servers: [], search: []}` | DNS nested dictionary | | `disks` | list | `[]` | Disk layout list | | `user` | dict | `{name:'', password:'', key:''}` | User account dictionary | | `root` | dict | `{password:''}` | Root account dictionary | | `luks` | dict | see below | Encryption dictionary | | `features` | dict | see below | Feature flags dictionary | ### `system.dns` DNS options used by network configuration tasks. Use these keys under `system.dns`. | Key | Type | Default | Description | |---|---|---|---| | `servers` | list/string | `[]` | DNS resolvers; comma-separated string is normalized | | `search` | list/string | `[]` | Search domains; comma-separated string is normalized | ### `system.user` Target user account settings. Use these keys under `system.user`. | Key | Type | Default | Description | |---|---|---|---| | `name` | string | empty | Username created on target | | `password` | string | empty | User password (also used for sudo/become) | | `key` | string | empty | SSH public key for `authorized_keys` | ### `system.root` Use these keys under `system.root`. | Key | Type | Default | Description | |---|---|---|---| | `password` | string | empty | Root password | ### `system.luks` LUKS container, unlock, and initramfs-related settings. Use these keys under `system.luks`. | Key | Type | Default | Allowed | Description | |---|---|---|---|---| | `enabled` | bool | `false` | `true`/`false` | Enable encrypted root workflow | | `passphrase` | string | empty | any string | Passphrase used for format/open/enroll | | `mapper` | string | `SYSTEM_DECRYPTED` | mapper name | Mapper name under `/dev/mapper` | | `auto` | bool | `true` | `true`/`false` | Auto-unlock behavior toggle | | `method` | string | `tpm2` | `tpm2`,`keyfile` | Auto-unlock backend when `auto=true` | | `keysize` | int | `64` | positive int | Keyfile size (bytes) for keyfile mode | | `options` | string | `discard,tries=3` | crypttab opts | Additional crypttab/kernel options | | `type` | string | `luks2` | cryptsetup type | LUKS format type | | `cipher` | string | `aes-xts-plain64` | cipher name | Cryptsetup cipher | | `hash` | string | `sha512` | hash name | Cryptsetup hash | | `iter` | int | `4000` | positive int | PBKDF iteration time (ms) | | `bits` | int | `512` | positive int | Key size (bits) | | `pbkdf` | string | `argon2id` | pbkdf name | PBKDF algorithm | | `urandom` | bool | `true` | `true`/`false` | Use urandom during key generation | | `verify` | bool | `true` | `true`/`false` | Verify passphrase during format | ### `system.luks.tpm2` TPM2-specific policy settings used when `system.luks.method=tpm2`. Use these keys under `system.luks.tpm2`. | Key | Type | Default | Allowed | Description | |---|---|---|---|---| | `device` | string | `auto` | `auto` or device path | TPM2 device selector | | `pcrs` | string/list | empty | PCR expression | PCR binding policy (for example `"7"` or `"0+7"`) | ### `system.features` Feature toggles for optional system configuration. Use these keys under `system.features`. | Key | Type | Default | Allowed | Description | |---|---|---|---|---| | `cis.enabled` | bool | `false` | `true`/`false` | Enable CIS hardening role | | `selinux.enabled` | bool | `true` | `true`/`false` | SELinux management | | `firewall.enabled` | bool | `true` | `true`/`false` | Enable firewall role actions | | `firewall.backend` | string | `firewalld` | `firewalld`,`ufw` | Firewall service backend | | `firewall.toolkit` | string | `nftables` | `nftables`,`iptables` | Packet filtering toolkit selection | | `ssh.enabled` | bool | `true` | `true`/`false` | SSH service/package management | | `zstd.enabled` | bool | `true` | `true`/`false` | zstd related tuning | | `swap.enabled` | bool | `true` | `true`/`false` | Swap setup toggle | | `banner.motd` | bool | `true` | `true`/`false` | MOTD banner management | | `banner.sudo` | bool | `true` | `true`/`false` | Sudo banner management | | `chroot.tool` | string | `arch-chroot` | `arch-chroot`,`chroot`,`systemd-nspawn` | Chroot wrapper command | ## Multi-Disk Schema `system.disks[0]` is always the OS disk. Additional entries can define data disks. | Key | Type | Description | |---|---|---| | `size` | number | Disk size in GB (required for virtual) | | `device` | string | Explicit disk device (required for physical data disks) | | `mount.path` | string | Mount target (for additional disks) | | `mount.fstype` | string | `btrfs`, `ext4`, or `xfs` | | `mount.label` | string | Optional filesystem label | | `mount.opts` | string | Mount options (`defaults` by default) | Example: ```yaml system: disks: - size: 80 - size: 200 mount: path: /data fstype: xfs label: DATA opts: defaults,noatime - size: 300 mount: path: /backup fstype: ext4 ``` For physical installs, include device paths: ```yaml system: type: physical disks: - device: /dev/sda size: 120 - device: /dev/sdb size: 500 mount: path: /data fstype: ext4 ``` ## Advanced Partitioning Overrides Use these only when you need to override default layout behavior. | Variable | Description | Default | |---|---|---| | `partitioning_efi_size_mib` | EFI system partition size in MiB | `512` | | `partitioning_boot_size_mib` | Separate `/boot` size in MiB (when used) | `1024` | | `partitioning_separate_boot` | Force separate `/boot` partition logic | auto-derived | | `partitioning_boot_fs_fstype` | Filesystem for `/boot` when separate | auto-derived | | `partitioning_use_full_disk` | Consume remaining VG space for root LV | `true` | ## `hypervisor` Dictionary Use these keys under `hypervisor`. | Key | Type | Description | |---|---|---| | `type` | string | `libvirt`, `proxmox`, `vmware`, `xen`, `none` | | `url` | string | Proxmox/VMware API host | | `username` | string | API username | | `password` | string | API password | | `host` | string | Proxmox node name | | `storage` | string | Proxmox/VMware storage | | `datacenter` | string | VMware datacenter | | `cluster` | string | VMware cluster | | `certs` | bool | TLS cert validation for VMware | | `ssh` | bool | VMware installer SSH bootstrap helper | ## VMware Guest Operations Variables When `hypervisor.type: vmware` and connection uses `vmware_tools`, ensure these variables are set in inventory/group/host vars as needed by your vCenter/ESXi environment. | Variable | Description | |---|---| | `ansible_vmware_tools_user` | Guest OS username for guest operations | | `ansible_vmware_tools_password` | Guest OS password for guest operations | | `ansible_vmware_guest_path` | VM inventory path (`/datacenter/vm/folder/name`) | | `ansible_vmware_host` | vCenter/ESXi host | | `ansible_vmware_user` | vCenter/ESXi API username | | `ansible_vmware_password` | vCenter/ESXi API password | | `ansible_vmware_validate_certs` | Enable/disable TLS certificate validation | ## Prerequisites - Ansible installed on the control machine. - Inventory and variables prepared for your target hosts. - Disposable/non-production targets (the playbook enforces production-safety checks). ## Usage ```bash ansible-playbook -i inventory_example.yml main.yml ansible-playbook -i inventory_libvirt_example.yml main.yml ansible-playbook -i inventory.yml main.yml -e @vars_example.yml ``` ## Security Store sensitive data (passwords, API tokens, private keys) with Ansible Vault instead of plaintext inventory files. ## Operational Notes - For virtual installs, `system.cpus`, `system.memory`, and `system.disks[0].size` are required and validated. - For physical installs, sizing is derived from the detected install drive; set installer access (`ansible_user`/`ansible_password`) when needed. - `system.dns.servers` and `system.dns.search` accept either YAML lists or comma-separated strings. - `hypervisor.type` selects backend-specific provisioning/cleanup behavior. - Guest tools are selected automatically by hypervisor: `qemu-guest-agent` (`libvirt`/`proxmox`) and `open-vm-tools` (`vmware`). - With `system.luks.method: tpm2` on virtual installs, the virtualization role enables a TPM2 device where supported. - With LUKS on non-Arch targets, provisioning may use a separate `/boot`; tune with `partitioning_efi_size_mib` and `partitioning_boot_size_mib`. - For VMware, `hypervisor.ssh: true` enables SSH on the guest and switches the connection to SSH for remaining tasks. - Molecule scenario is lint-focused (`delegated` driver with non-destructive placeholder converge). ## Safety This playbook intentionally aborts if it detects a non-live/production target. It also refuses to touch pre-existing VMs and only cleans up VMs created in the current run. ## Validation Always run lint after changes: ```bash ansible-lint ```