diff --git a/README.md b/README.md index 6d1ea5b..4f2d2b5 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,58 @@ -# Ansible Bootstrap Playbook +# Ansible Bootstrap -Automate Linux system bootstrap across multiple distributions and hypervisors in an Infrastructure-as-Code workflow. +An Ansible playbook for automating Linux system bootstrap in an Infrastructure-as-Code manner. It uses the Arch Linux ISO as a foundational tool to provide an efficient and systematic method for the automatic deployment of a variety of Linux distributions on designated target systems, ensuring a standardized setup across different platforms. -## Supported Distributions +Most roles are adaptable for use with systems beyond Arch Linux, requiring only that the target system can install the necessary package manager (e.g. `dnf` for RHEL-based systems). A replacement for the `arch-chroot` command may also be required; set `system.features.chroot.tool` accordingly. -| 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 | +## Table of Contents -## Supported Hypervisors +1. [Supported Platforms](#1-supported-platforms) +2. [Compatibility Notes](#2-compatibility-notes) +3. [Configuration Model](#3-configuration-model) +4. [Variable Reference](#4-variable-reference) + - 4.1 [Core Variables](#41-core-variables) + - 4.2 [`system` Dictionary](#42-system-dictionary) + - 4.3 [`hypervisor` Dictionary](#43-hypervisor-dictionary) + - 4.4 [VMware Guest Operations](#44-vmware-guest-operations) + - 4.5 [Multi-Disk Schema](#45-multi-disk-schema) + - 4.6 [Advanced Partitioning Overrides](#46-advanced-partitioning-overrides) +5. [How to Use the Playbook](#5-how-to-use-the-playbook) + - 5.1 [Prerequisites](#51-prerequisites) + - 5.2 [Running the Playbook](#52-running-the-playbook) + - 5.3 [Example Usage](#53-example-usage) +6. [Security](#6-security) +7. [Operational Notes](#7-operational-notes) +8. [Safety](#8-safety) -| Hypervisor | Value | -|---|---| -| libvirt | `libvirt` | -| Proxmox | `proxmox` | -| VMware | `vmware` | -| Xen | `xen` | -| Bare metal | `none` | +## 1. Supported Platforms -## Compatibility Notes +### Distributions + +| `system.os` | Distribution | `system.version` | +| ------------ | ------------------------ | ------------------------------- | +| `almalinux` | AlmaLinux | `8`, `9`, `10` | +| `alpine` | Alpine Linux | latest (rolling) | +| `archlinux` | Arch Linux | latest (rolling) | +| `debian` | Debian | `10`, `11`, `12`, `13`, `unstable` | +| `fedora` | Fedora | `40`, `41`, `42`, `43` | +| `opensuse` | openSUSE Tumbleweed | latest (rolling) | +| `rhel` | Red Hat Enterprise Linux | `8`, `9`, `10` | +| `rocky` | Rocky Linux | `8`, `9`, `10` | +| `ubuntu` | Ubuntu | latest | +| `ubuntu-lts` | Ubuntu LTS | latest | +| `void` | Void Linux | latest (rolling) | + +### Hypervisors + +| Hypervisor | `hypervisor.type` | +| ----------- | ----------------- | +| libvirt | `libvirt` | +| Proxmox VE | `proxmox` | +| VMware | `vmware` | +| Xen | `xen` | +| Bare metal | `none` | + +## 2. Compatibility Notes - `rhel_iso` is required for `system.os: rhel`. - RHEL installs should use `system.filesystem: ext4` or `system.filesystem: xfs` (not `btrfs`). @@ -36,28 +60,25 @@ Automate Linux system bootstrap across multiple distributions and hypervisors in - `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 +## 3. Configuration Model -The project uses only dict-based variables: +The project uses two 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. +These 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 (`hash_behaviour = merge`), so you can set shared values like `system.filesystem` once in group vars and 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 | +| 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 inline host vars | Single host | Inline definitions for quick setup | -### Example Host Definition +### Example Inventory ```yaml all: @@ -120,139 +141,162 @@ all: toolkit: nftables ``` -## Core Variables +## 4. Variable Reference -| 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 | +### 4.1 Core Variables -## `system` Dictionary +These top-level variables sit outside the `system`/`hypervisor` dictionaries. -Top-level host install/runtime settings. -Use these keys under `system`. +| Variable | Type | Description | +| ------------ | ------ | ------------------------------------------------ | +| `boot_iso` | string | Path to the boot ISO image (required for virtual installs). | +| `rhel_iso` | string | Path to the RHEL ISO (required when `system.os: rhel`). | +| `custom_iso` | bool | Skip ArchISO validation and pacman setup. Default `false`. | -| 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 | +### 4.2 `system` Dictionary -### `system.dns` +Top-level host install/runtime settings. Use these keys under `system`. -DNS options used by network configuration tasks. -Use these keys under `system.dns`. +| Key | Type | Default | Description | +| ------------ | ---------- | -------------------- | ---------------------------------------- | +| `type` | string | `virtual` | `virtual` or `physical` | +| `os` | string | empty | Target distribution (see [table](#distributions)) | +| `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 (required 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 (omit for DHCP) | +| `prefix` | int | empty | CIDR prefix for static IP | +| `gateway` | string | empty | Default gateway (static only) | +| `path` | string | empty | Hypervisor folder/path (libvirt/vmware) | +| `packages` | list | `[]` | Additional packages installed post-reboot | +| `dns` | dict | see below | DNS configuration | +| `disks` | list | `[]` | Disk layout (see [Multi-Disk Schema](#45-multi-disk-schema)) | +| `user` | dict | see below | User account settings | +| `root` | dict | see below | Root account settings | +| `luks` | dict | see below | Encryption settings | +| `features` | dict | see below | Feature toggles | -| 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.dns` -### `system.user` +| Key | Type | Default | Description | +| --------- | ----------- | ------- | --------------------------------------------------- | +| `servers` | list/string | `[]` | DNS resolvers; comma-separated string is normalized | +| `search` | list/string | `[]` | Search domains; comma-separated string is normalized | -Target user account settings. -Use these keys under `system.user`. +#### `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` | +Credentials are prompted interactively by default via `vars_prompt` in `main.yml`, but can be supplied via inventory, vars files, or `-e` for non-interactive runs. -### `system.root` +| Key | Type | Default | Description | +| ---------- | ------ | ------- | ------------------------------------- | +| `name` | string | empty | Username created on target | +| `password` | string | empty | User password (also used for sudo) | +| `key` | string | empty | SSH public key for `authorized_keys` | -Use these keys under `system.root`. +#### `system.root` -| Key | Type | Default | Description | -|---|---|---|---| -| `password` | string | empty | Root password | +| Key | Type | Default | Description | +| ---------- | ------ | ------- | -------------- | +| `password` | string | empty | Root password | -### `system.luks` +#### `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 | +| 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` +#### `system.luks.tpm2` -TPM2-specific policy settings used when `system.luks.method=tpm2`. -Use these keys under `system.luks.tpm2`. +TPM2-specific policy settings used when `system.luks.method: 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"`) | +| Key | Type | Default | Allowed | Description | +| ------ | ----------- | ------- | --------------------- | --------------------------------------------------- | +| `device` | string | `auto` | `auto` or device path | TPM2 device selector | +| `pcrs` | string/list | empty | PCR expression | PCR binding policy (e.g. `"7"` or `"0+7"`) | -### `system.features` +#### `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 | +| 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 | +| `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 | `false` | `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 +### 4.3 `hypervisor` Dictionary -`system.disks[0]` is always the OS disk. Additional entries can define data disks. +| Key | Type | Description | +| ------------ | ------ | -------------------------------------------------------- | +| `type` | string | `libvirt`, `proxmox`, `vmware`, `xen`, or `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 identifier | +| `datacenter` | string | VMware datacenter | +| `cluster` | string | VMware cluster | +| `certs` | bool | TLS certificate validation for VMware | +| `ssh` | bool | VMware: enable SSH on guest and switch connection to SSH | -| 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) | +### 4.4 VMware Guest Operations -Example: +When `hypervisor.type: vmware` uses the `vmware_tools` connection, these Ansible connection variables are required. + +| 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 hostname | +| `ansible_vmware_user` | vCenter/ESXi API username | +| `ansible_vmware_password` | vCenter/ESXi API password | +| `ansible_vmware_validate_certs` | Enable/disable TLS certificate validation | + +### 4.5 Multi-Disk Schema + +`system.disks[0]` is always the OS disk. Additional entries define data disks. + +| Key | Type | Description | +| ------------- | ------ | ---------------------------------------------------- | +| `size` | number | Disk size in GB (required for virtual installs) | +| `device` | string | Explicit block device (required for physical data disks) | +| `mount.path` | string | Mount point (for additional disks) | +| `mount.fstype`| string | `btrfs`, `ext4`, or `xfs` | +| `mount.label` | string | Optional filesystem label | +| `mount.opts` | string | Mount options (default: `defaults`) | + +Virtual install example: ```yaml system: @@ -270,7 +314,7 @@ system: fstype: ext4 ``` -For physical installs, include device paths: +Physical install example (device paths required): ```yaml system: @@ -285,85 +329,74 @@ system: fstype: ext4 ``` -## Advanced Partitioning Overrides +### 4.6 Advanced Partitioning Overrides -Use these only when you need to override default layout behavior. +Use these only when you need to override the default partition layout logic. -| 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` | +| 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 a separate `/boot` partition | 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 +## 5. How to Use the Playbook -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 +### 5.1 Prerequisites - Ansible installed on the control machine. -- Inventory and variables prepared for your target hosts. +- Inventory file with target systems defined and variables configured. - Disposable/non-production targets (the playbook enforces production-safety checks). -## Usage +### 5.2 Running the Playbook + +Execute the playbook using `ansible-playbook`, ensuring that all necessary variables are defined either in the inventory, in a vars file, or passed via `-e`. Credentials (`root_password`, `user_name`, `user_password`, `user_public_key`) are prompted interactively unless supplied through inventory or extra vars. ```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 ansible-playbook -i inventory.yml main.yml -e @vars_example.yml ``` -## Security +### 5.3 Example Usage -Store sensitive data (passwords, API tokens, private keys) with Ansible Vault instead of plaintext inventory files. +Use the bundled example files as starting points for new inventories: -## Operational Notes +- `inventory_example.yml` -- Proxmox virtual setup +- `inventory_libvirt_example.yml` -- libvirt virtual setup +- `inventory_baremetal_example.yml` -- bare-metal physical setup +- `vars_example.yml` -- shared variable overrides + +```bash +# Proxmox example +ansible-playbook -i inventory_example.yml main.yml + +# libvirt example +ansible-playbook -i inventory_libvirt_example.yml main.yml + +# Custom inventory with separate vars file +ansible-playbook -i inventory.yml main.yml -e @vars_example.yml +``` + +## 6. Security + +To protect sensitive information such as passwords, API keys, and other confidential variables (e.g. `hypervisor.password`, `system.luks.passphrase`), **use Ansible Vault** instead of plaintext inventory files. + +## 7. 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. +- For physical installs, sizing is derived from the detected install drive; set installer access (`ansible_user`/`ansible_password`) when the installer environment differs from the prompted user credentials. - `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). +- `hypervisor.type` selects backend-specific provisioning and cleanup behavior. +- Guest tools are selected automatically by hypervisor: `qemu-guest-agent` for `libvirt`/`proxmox`, `open-vm-tools` for `vmware`. +- With `system.luks.method: tpm2` on virtual installs, the virtualization role enables a TPM2 device where supported (libvirt/proxmox/vmware). +- With LUKS enabled on non-Arch targets, provisioning uses an ESP (512 MiB), a separate `/boot` (1 GiB), and the encrypted root; adjust sizes via `partitioning_efi_size_mib` and `partitioning_boot_size_mib` if needed. +- For VMware, `hypervisor.ssh: true` enables SSH on the guest and switches the connection to SSH for the remaining tasks. +- Molecule is scaffolded with a delegated driver and a no-op converge for lint-only validation. -## Safety +## 8. 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 +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. Always run lint after changes: