Compare commits

...

25 Commits

Author SHA1 Message Date
74ae98db35 fix(banner): align MOTD star border and default motd to disabled 2026-02-11 08:02:27 +01:00
fc23f84cc3 fix(libvirt): restore missing virtualization_mac_address default 2026-02-11 08:02:27 +01:00
3c7d9e16da refactor(safety): remove redundant live environment detection from system_check 2026-02-11 08:02:27 +01:00
e4b9f1c579 refactor(playbook): rename prompt variables with backwards-compatible fallbacks 2026-02-11 08:02:27 +01:00
920969d60e refactor(validation): deduplicate hypervisor combine and collapse schema checks 2026-02-11 08:02:27 +01:00
9d723630cb refactor(system): simplify normalization by removing redundant intermediate merges 2026-02-11 08:02:27 +01:00
0c8242589c fix(bootstrap): repair version-specific package availability across distributions 2026-02-11 08:02:27 +01:00
2885ba9ffa docu(readme): consolidate final documentation state 2026-02-11 05:37:18 +01:00
81d63029a4 fix(config): enable dictionary merge for scoped overrides 2026-02-11 05:37:18 +01:00
2fa0fba4c4 refactor(schema): move filesystem into system dictionary 2026-02-11 05:37:18 +01:00
055b6de68b refactor(configuration): simplify grub commandline variable assembly 2026-02-11 05:37:18 +01:00
4e85740e0a refactor(configuration): reduce LUKS runtime temporary facts 2026-02-11 05:37:18 +01:00
0ee2806c62 refactor(schema): simplify dict normalization and schema checks 2026-02-11 05:37:18 +01:00
1027afc6ea docu(schema): update docs and examples to compact dict keys 2026-02-11 05:37:18 +01:00
74cb09ffee refactor(schema): rename nested dict keys and simplify validation 2026-02-11 05:37:18 +01:00
9f5096d69d docu(schema): align docs and baremetal example with dict model 2026-02-11 05:37:18 +01:00
6da46a03ed fix(validation): reject deprecated top-level schema keys 2026-02-11 05:37:18 +01:00
e7c898d653 refactor(vars): simplify normalization and remove effective intermediates 2026-02-11 05:37:18 +01:00
0388dca0a4 fix(system): default physical installs to archlinux when os is omitted 2026-02-11 05:37:18 +01:00
1d545fbbc8 docu(readme): document dict-based variables and examples 2026-02-11 05:37:18 +01:00
53bb4589b6 fix(runtime): migrate roles to nested system fields 2026-02-11 05:37:18 +01:00
73f0b81b5a feat(disks): add standardized multi-disk mount schema 2026-02-11 05:37:18 +01:00
2d46df8f5a refactor(vars): enforce nested system and hypervisor schema 2026-02-11 05:37:18 +01:00
45d3fef4e2 refactor(vars): remove legacy variable inputs
- Require hypervisor as dict input and use hypervisor_cfg/hypervisor_type internally

- Remove vm_* and hypervisor_* compatibility aliases

- Update roles and docs to use system/hypervisor dictionaries only
2026-02-11 05:37:18 +01:00
a6b051d9e4 refactor(vars): add system/hypervisor dict inputs
- Normalize new system_cfg + hypervisor_cfg and keep legacy vm_* and hypervisor_* aliases

- Support multiple system.disks (creation + optional mount + fstab generation)

- Add system_check safety role (production + existing system detection)

- Update README and example inventories
2026-02-11 05:37:18 +01:00
70 changed files with 3459 additions and 1510 deletions

553
README.md
View File

@@ -1,223 +1,372 @@
# Ansible-Bootstrap # Ansible Bootstrap Playbook
An Ansible playbook for automating system bootstrap processes in an Infrastructure-as-Code manner. Automate Linux system bootstrap across multiple distributions and hypervisors in an Infrastructure-as-Code workflow.
# Info ## Supported Distributions
Most of the roles are adaptable for use with systems beyond ArchLinux, requiring only that the target system can install a necessary package manager, such as `dnf` for RHEL-based systems. Additionally, a replacement for the `arch-chroot` command may be required for these systems. | 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 |
**NOTE**: ## Supported Hypervisors
- For RHEL 8, RHEL 9, and RHEL 10, repository access requires the `rhel_iso` variable. This variable specifies a local ISO or proxy repository. | Hypervisor | Value |
- RHEL systems do not support `btrfs`. Use `ext4` or `xfs` as alternatives. |---|---|
- For RHEL 8, `xfs` may cause installation issues; `ext4` is recommended. | libvirt | `libvirt` |
- `custom_iso: true` skips ArchISO validation and pacman setup, your installer ISO must provide the tools required by the selected roles. | Proxmox | `proxmox` |
| VMware | `vmware` |
| Xen | `xen` |
| Bare metal | `none` |
# Supported Distributions ## Compatibility Notes
This playbook supports multiple Linux distributions with specific versions tailored to each. - `rhel_iso` is required for `system.os: rhel`.
Below is a list of supported distributions: - 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.
| `os` | Distribution | ## Configuration Model
| ---------- | ---------------------------------- |
| archlinux | ArchLinux (Latest rolling release) |
| almalinux | AlmaLinux 9.x |
| debian11 | Debian 11 (Bullseye) |
| debian12 | Debian 12 (Bookworm) |
| debian13 | Debian 13 (Trixie) |
| fedora | Fedora 43 |
| rhel8 | Red Hat Enterprise Linux 8 |
| rhel9 | Red Hat Enterprise Linux 9 |
| rhel10 | Red Hat Enterprise Linux 10 |
| rocky | Rocky Linux 9.x |
| ubuntu | Ubuntu 25.04 (Plucky Puffin) |
| ubuntu-lts | Ubuntu 24.04 LTS (Noble Numbat) |
# Documentation The project uses only dict-based variables:
## Table of Contents - `system` for host/runtime/install configuration
- `hypervisor` for virtualization backend configuration
1. [Overview](#1-overview) These dictionaries are normal Ansible variables and belong in host/group vars.
2. [Global Variables](#2-global-variables) You can define them in inventory host entries, `group_vars/*`, or `host_vars/*`.
3. [Inventory Variables](#3-inventory-variables) Dictionary variables are merged across scopes (`group_vars` -> `host_vars`) by project config.
4. [How to Use the Playbook](#4-how-to-use-the-playbook) Set shared values like `system.filesystem` once in group vars, then override only host-specific keys per host.
- 4.1 [Prerequisites](#41-prerequisites)
- 4.2 [Running the Playbook](#42-running-the-playbook)
- 4.3 [Example Usage](#43-example-usage)
## 1. Overview ### Variable Placement
The playbook uses the ArchLinux ISO as a foundational tool to provides an efficient and systematic method for the automatic deployment of a variety of Linux distributions on designated target systems. It ensures a standardized setup across different platforms, equipping each system with the essential configurations and software necessary for its designated role. | Location | Scope | Typical use |
|---|---|---|
| `group_vars/all.yml` | All hosts | Shared defaults like `hypervisor`, `system.filesystem`, `boot_iso` |
| `group_vars/<group>.yml` | Group | Environment or role-specific defaults |
| `host_vars/<host>.yml` | Single host | Host-specific overrides |
| Inventory host vars | Single host | Inline definitions for quick setup |
## 2. Global Variables ### Example Host Definition
Global variables apply across your Ansible project and can be supplied via inventory or `-e @vars_example.yml`. These variables define common settings such as hypervisor connection details and the boot ISO path. They can be overridden by inventory variables for specific hosts or VMs if needed. ```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
### 2.1 Core Provisioning children:
bootstrap:
| Variable | Description | Example Value | hosts:
| ----------------------- | ---------------------------------------------------------- | ----------------------------------------- | app01.example.com:
| `install_type` | Type of installation. | `virtual`, `physical` | ansible_host: 10.0.0.10
| `hypervisor` | Type of hypervisor (required for virtual installs). | `libvirt`, `proxmox`, `vmware`, `none` | system:
| `install_drive` | Drive where the system will be installed. | `/dev/sda` | type: virtual
| `boot_iso` | Path to the boot ISO image. | `local-btrfs:iso/archlinux-x86_64.iso` | os: debian
| `rhel_iso` | Path to the RHEL ISO file, required for RHEL 8/9/10. | `local-btrfs:iso/rhel-9.4-x86_64-dvd.iso` | version: "12"
| `custom_iso` (optional) | Skip ArchISO checks and pacman setup on installer media. | `true`, `false (default)` | name: app01.example.com
| `cis` (optional) | Adjusts the installation to be CIS level 3 conformant. | `true`, `false (default)` | id: 101
| `selinux` (optional) | Toggle SELinux where supported. | `true (default)`, `false` | cpus: 2
| `firewalld_enabled` (optional) | Toggle firewalld package/service enablement. | `true (default)`, `false` | memory: 4096
| `ssh_enabled` (optional) | Toggle SSH server package/service enablement. | `true (default)`, `false` | balloon: 0
network: vmbr0
### 2.2 Hypervisor Access (virtual installs) ip: 10.0.0.10
prefix: 24
| Variable | Description | Example Value | gateway: 10.0.0.1
| ----------------------- | ---------------------------------------------------------- | -------------------- | dns:
| `hypervisor_url` | URL/IP address for the hypervisor interface. | `192.168.0.2` | servers: [1.1.1.1, 1.0.0.1]
| `hypervisor_username` | Username for hypervisor authentication. | `root@pam` | search: [example.com]
| `hypervisor_password` | Password for hypervisor authentication. | `123456` | disks:
| `hypervisor_datacenter` | Name of the hypervisor datacenter. | `default-datacenter` | - size: 40
| `hypervisor_cluster` | Name of the hypervisor cluster. | `default-cluster` | - size: 120
| `hypervisor_node` | Hypervisor node name. | `node01` | mount:
| `hypervisor_storage` | Storage identifier for VM disks. | `local-btrfs` | path: /data
| `vm_path` (optional) | Libvirt image dir or VMware folder path. | `/var/lib/libvirt/images` | fstype: xfs
| `vmware_ssh` | If Ansible should use SSH after base VMware setup. | `true`, `false (default)` | user:
| `vlan_name` (optional) | VLAN for the VM's network interface. | `vlan100` | name: ops
| `note` (optional) | VMware VM annotation. | `Provisioned by Ansible` | password: CHANGE_ME
key: "ssh-ed25519 AAAA..."
### 2.3 VMware Tools connection (VMware installs) root:
password: CHANGE_ME
These are required when `hypervisor: vmware` uses the `vmware_tools` connection. luks:
enabled: true
| Variable | Description | Example Value | passphrase: CHANGE_ME
| ------------------------------- | ------------------------------------------ | -------------------------------------- | auto: true
| `ansible_vmware_tools_user` | Guest OS user for guest operations. | `root` | method: tpm2
| `ansible_vmware_tools_password` | Guest OS password for guest operations. | `""` | tpm2:
| `ansible_vmware_guest_path` | VM inventory path (datacenter + folder). | `/dc01/vm/Folder/vm01.example.com` | pcrs: "7"
| `ansible_vmware_host` | vCenter/ESXi hostname. | `vcenter01.example.com` | features:
| `ansible_vmware_user` | vCenter/ESXi username. | `administrator@vsphere.local` | firewall:
| `ansible_vmware_password` | vCenter/ESXi password. | `********` | enabled: true
| `ansible_vmware_validate_certs` | Validate vCenter/ESXi TLS certs. | `false` | backend: firewalld
toolkit: nftables
### 2.4 Disk Encryption (optional)
| Variable | Description | Example Value |
| -------------------------- | ----------------------------------------------- | ------------------ |
| `luks_enabled` | Enable LUKS encryption for the root volume. | `true`, `false` |
| `luks_passphrase` | Passphrase used for initial LUKS format/unlock. | `1234` |
| `luks_mapper_name` | Decrypted mapper name. | `SYSTEM_DECRYPTED` |
| `luks_auto_decrypt` | Enable automatic unlock on boot. | `true`, `false` |
| `luks_auto_decrypt_method` | Auto-unlock method. | `tpm2`, `keyfile`, `manual` |
| `luks_tpm2_device` | TPM2 device for enrollment. | `auto` |
| `luks_tpm2_pcrs` | TPM2 PCR list (systemd-cryptenroll). | `7` |
| `luks_keyfile_size` | Keyfile size in bytes for initramfs. | `64` |
| `luks_options` | LUKS options passed to crypttab/kernel. | `discard,tries=3` |
| `luks_type` | LUKS format type. | `luks2` |
| `luks_cipher` | LUKS cipher. | `aes-xts-plain64` |
| `luks_hash` | LUKS hash. | `sha512` |
| `luks_iter_time` | LUKS iter time in milliseconds. | `4000` |
| `luks_key_size` | LUKS key size in bits. | `512` |
| `luks_pbkdf` | LUKS PBKDF algorithm. | `argon2id` |
| `luks_use_urandom` | Reserved; module uses cryptsetup defaults. | `true` |
| `luks_verify_passphrase` | Reserved; module uses cryptsetup defaults. | `true` |
### 2.5 Partitioning Overrides (advanced)
Use these only when you need to override the default layout logic.
| Variable | Description | Example Value |
| ---------------------------- | -------------------------------------------------------- | ------------- |
| `partitioning_efi_size_mib` | ESP size in MiB. | `512` |
| `partitioning_boot_size_mib` | `/boot` size in MiB when a separate boot is used. | `1024` |
| `partitioning_separate_boot` | Force a separate `/boot` partition. | `true` |
| `partitioning_boot_fs_fstype` | Filesystem for `/boot` when separate. | `ext4` |
| `partitioning_use_full_disk` | Use remaining LVM space for the root volume. | `true` |
To protect sensitive information, such as passwords, API keys, and other confidential variables (e.g., `hypervisor_password`), **it is recommended to use Ansible Vault**.
## 3. Inventory Variables
Inventory variables are defined for individual hosts or VMs in the inventory file, allowing customization of settings such as the operating system, filesystem, and compliance with CIS benchmarks. These variables can be set globally and overridden for specific hosts or VMs.
### 3.1 System Identity and OS
| Variable | Description | Example Value |
| ------------ | -------------------------------------- | ---------------------- |
| `ansible_host` | Ansible connection address for the host. | `192.168.0.10` |
| `os` | Operating system to be installed. | `ubuntu-lts` |
| `filesystem` | Filesystem type for the root volume. | `btrfs`, `ext4`, `xfs` |
| `hostname` | The hostname assigned to the system. | `vm01` |
### 3.2 Credentials and Access
These are prompted by default via `vars_prompt` in `main.yml`, but can be supplied via inventory/vars/`-e` for non-interactive runs.
| Variable | Description | Example Value |
| ----------------- | ---------------------------------- | ----------------- |
| `root_password` | Root password (vault recommended). | `SecurePass123` |
| `user_name` | Username for a user account. | `adminuser` |
| `user_password` | Password for the user account. | `UserPass123` |
| `user_public_key` | SSH Key for the user account. | `ssh-ed25519 AAAA` |
### 3.3 Networking
| Variable | Description | Example Value |
| --------------- | -------------------------------------------------------------- | ----------------- |
| `vm_ip` | IP address assigned to the system (omit to use DHCP). | `192.168.0.10` |
| `vm_nms` | Netmask bits for static addressing. | `24` |
| `vm_gw` | Default gateway IP address (static only). | `192.168.0.1` |
| `vm_dns` | DNS server IP address(es). | `1.0.0.1,1.1.1.1` |
| `vm_dns_search` | DNS search zone(s) for the network configuration. | `example.com` |
| `vm_nif` | Network interface/bridge for the VM's network connection. | `vmbr0` |
### 3.4 VM Sizing (virtual installs)
| Variable | Description | Example Value |
| ----------- | --------------------------------- | ------------- |
| `vm_id` | Unique identifier for the VM. | `101` |
| `vm_size` | Disk size allocated in GB (min 20). | `20` |
| `vm_memory` | Amount of memory in MB. | `2048` |
| `vm_cpus` | Number of CPU cores (virtual installs). | `4` |
| `vm_ballo` | Ballooning memory size (optional).| `2048` |
### 3.5 Post-install Packages
| Variable | Description | Example Value |
| ------------------------ | --------------------------------------------------------------------- | ------------------ |
| `extra_packages` (optional) | Additional packages installed after the first boot into the installed OS. | `["git", "jq"]` |
## 4. How to Use the Playbook
### 4.1 Prerequisites
Before running the playbook, ensure you have Ansible installed and configured correctly, and your inventory file is set up with the target systems defined.
### 4.2 Running the Playbook
Execute the playbook using the `ansible-playbook` command, ensuring that all necessary variables are defined, typically by specifying a vars file (such as `vars_example.yml`) containing the required configurations.
### 4.3 Example Usage
An effective way to use the playbook involves defining all necessary configurations within a vars file (for example, `vars_example.yml`). This file should include all relevant global variables tailored to your specific deployment requirements. Additionally, you should prepare an inventory file (`inventory.yml`) that lists all the hosts along with any specific inventory variables they might need. Then, you can run the playbook as follows:
```bash
ansible-playbook -i inventory.yml -e @vars_example.yml main.yml
``` ```
This command prompts Ansible to execute the `main.yml` playbook, applying configurations defined in both the vars file and the inventory file. ## Core Variables
Use `inventory_example.yml`, `inventory_libvirt_example.yml`, `vars_example.yml`, and the bare-metal examples as starting points for new inventories. | 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 |
## Notes ## `system` Dictionary
- `vm_size`/`vm_memory`/`vm_cpus` are required for virtual installs only, physical installs use the full disk. Top-level host install/runtime settings.
- `vm_dns` and `vm_dns_search` accept comma-separated strings or YAML lists. Use these keys under `system`.
- `hypervisor` determines which backend-specific roles run.
- Guest tools are installed based on `hypervisor`: `qemu-guest-agent` for `libvirt`/`proxmox`, `open-vm-tools` for `vmware`, otherwise none. | Key | Type | Default | Description |
- Molecule is scaffolded with a delegated driver and a no-op converge for lint-only validation. |---|---|---|---|
- With LUKS enabled on Debian/Ubuntu and RHEL-based systems, provisioning uses an ESP (512 MiB), a separate `/boot` | `type` | string | `virtual` | `virtual` or `physical` |
(1 GiB, same as `filesystem` unless `btrfs` uses ext4 on Debian/Ubuntu or xfs on RHEL-based), and the encrypted root; | `os` | string | empty (`archlinux` if omitted on physical) | Target distribution |
adjust sizes via | `version` | string | empty | Version selector for distro families |
`partitioning_efi_size_mib` and `partitioning_boot_size_mib` if needed. | `filesystem` | string | empty | `btrfs`, `ext4`, or `xfs` |
- With `luks_auto_decrypt_method: tpm2` on virtual installs, the virtualization role enables a TPM2 device (libvirt/proxmox/vmware). | `name` | string | inventory hostname | Final hostname |
- For VMware, `vmware_ssh: true` enables SSH on the guest and switches the connection to SSH for the remaining tasks. | `id` | int/string | empty | VMID for Proxmox |
- For physical installs, set `ansible_user`/`ansible_password` for the installer environment when it differs from the prompted user credentials. | `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
```

2
ansible.cfg Normal file
View File

@@ -0,0 +1,2 @@
[defaults]
hash_behaviour = merge

View File

@@ -1,9 +1,20 @@
--- ---
all: all:
vars:
hypervisor:
type: "none"
system:
filesystem: "ext4"
hosts: hosts:
baremetal01.example.com: baremetal01.example.com:
ansible_host: 10.0.0.162 ansible_host: 10.0.0.162
ansible_user: root ansible_user: root
ansible_password: "1234" ansible_password: "1234"
ansible_become_password: "1234" ansible_become_password: "1234"
hostname: "baremetal01.example.com" system:
type: "physical"
os: "archlinux"
name: "baremetal01.example.com"
disks:
- device: "/dev/sda"
size: 120

View File

@@ -1,50 +1,127 @@
--- ---
all: all:
vars: vars:
install_type: "virtual" hypervisor:
hypervisor: "proxmox" type: "proxmox"
install_drive: "/dev/sda" url: "pve01.example.com"
username: "root@pam"
password: "CHANGE_ME"
host: "pve01"
storage: "local-lvm"
boot_iso: "local:iso/archlinux-x86_64.iso" boot_iso: "local:iso/archlinux-x86_64.iso"
vm_nif: "vmbr0"
children: children:
proxmox: proxmox:
hosts: hosts:
app01.example.com: app01.example.com:
ansible_host: 10.0.0.10 ansible_host: 10.0.0.10
hostname: "app01.example.com" system:
os: "archlinux"
filesystem: "btrfs" filesystem: "btrfs"
vm_id: 100 type: "virtual"
vm_cpus: 2 os: "archlinux"
vm_memory: 4096 name: "app01.example.com"
vm_size: 40 id: 100
vm_ip: 10.0.0.10 cpus: 2
vm_nms: 24 memory: 4096
vm_gw: 10.0.0.1 balloon: 0
vm_dns: network: "vmbr0"
ip: 10.0.0.10
prefix: 24
gateway: 10.0.0.1
dns:
servers:
- 1.1.1.1 - 1.1.1.1
- 1.0.0.1 - 1.0.0.1
extra_packages: search:
- example.com
disks:
- size: 40
- size: 80
mount:
path: /data
fstype: xfs
label: DATA
opts: defaults
user:
name: "ops"
password: "CHANGE_ME"
key: "ssh-ed25519 AAAA..."
root:
password: "CHANGE_ME"
packages:
- jq - jq
- tmux - tmux
features:
cis:
enabled: false
selinux:
enabled: true
firewall:
enabled: true
backend: "firewalld"
toolkit: "nftables"
ssh:
enabled: true
zstd:
enabled: true
swap:
enabled: true
banner:
motd: true
sudo: true
chroot:
tool: "arch-chroot"
db01.example.com: db01.example.com:
ansible_host: 10.0.0.11 ansible_host: 10.0.0.11
hostname: "db01.example.com"
os: "rhel9"
filesystem: "xfs"
vm_id: 101
vm_cpus: 4
vm_memory: 8192
vm_size: 80
vm_ip: 10.0.0.11
vm_nms: 24
vm_gw: 10.0.0.1
vm_dns: "1.1.1.1,1.0.0.1"
rhel_iso: "local:iso/rhel-9.4-x86_64-dvd.iso" rhel_iso: "local:iso/rhel-9.4-x86_64-dvd.iso"
luks_enabled: true system:
luks_passphrase: "CHANGE_ME" filesystem: "xfs"
luks_auto_decrypt_method: "keyfile" type: "virtual"
luks_keyfile_size: 128 os: "rhel"
cis: true version: "9"
selinux: false name: "db01.example.com"
firewalld_enabled: false id: 101
cpus: 4
memory: 8192
network: "vmbr0"
ip: 10.0.0.11
prefix: 24
gateway: 10.0.0.1
dns:
servers: "1.1.1.1,1.0.0.1"
disks:
- size: 80
- size: 200
mount:
path: /srv/data
fstype: ext4
user:
name: "dbadmin"
password: "CHANGE_ME"
key: "ssh-ed25519 AAAA..."
root:
password: "CHANGE_ME"
luks:
enabled: true
passphrase: "CHANGE_ME"
method: "keyfile"
keysize: 128
features:
cis:
enabled: true
selinux:
enabled: false
firewall:
enabled: false
backend: "firewalld"
toolkit: "nftables"
ssh:
enabled: true
zstd:
enabled: true
swap:
enabled: true
banner:
motd: true
sudo: true
chroot:
tool: "arch-chroot"

View File

@@ -1,56 +1,126 @@
--- ---
all: all:
vars: vars:
install_type: "virtual" hypervisor:
hypervisor: "libvirt" type: "libvirt"
install_drive: "/dev/vda" url: "localhost"
username: ""
password: ""
host: ""
storage: "default"
boot_iso: "/var/lib/libvirt/images/archlinux-x86_64.iso" boot_iso: "/var/lib/libvirt/images/archlinux-x86_64.iso"
children: children:
libvirt: libvirt:
hosts: hosts:
web01.example.com: web01.local:
ansible_host: 192.168.122.10 ansible_host: 192.168.122.20
hostname: "web01.example.com" system:
os: "debian12"
filesystem: "ext4" filesystem: "ext4"
vm_cpus: 2 type: "virtual"
vm_memory: 2048 os: "debian"
vm_size: 30 version: "12"
vm_ip: 192.168.122.10 name: "web01.local"
vm_nms: 24 cpus: 2
vm_gw: 192.168.122.1 memory: 2048
vm_dns: 1.1.1.1 network: "default"
extra_packages: ip: 192.168.122.20
prefix: 24
gateway: 192.168.122.1
dns:
servers:
- 1.1.1.1
search:
- lab.local
path: "/var/lib/libvirt/images"
disks:
- size: 30
- size: 80
mount:
path: /var/www
fstype: xfs
user:
name: "web"
password: "CHANGE_ME"
key: "ssh-ed25519 AAAA..."
root:
password: "CHANGE_ME"
packages:
- nginx - nginx
- fail2ban - curl
vault01.example.com: features:
ansible_host: 192.168.122.11 firewall:
hostname: "vault01.example.com" enabled: true
os: "ubuntu-lts" backend: "ufw"
filesystem: "btrfs" toolkit: "nftables"
vm_cpus: 2 db01.local:
vm_memory: 4096 ansible_host: 192.168.122.21
vm_size: 40
vm_ip: 192.168.122.11
vm_nms: 24
vm_gw: 192.168.122.1
vm_dns_search: "example.com"
luks_enabled: true
luks_passphrase: "CHANGE_ME"
luks_auto_decrypt_method: "keyfile"
firewalld_enabled: false
rhel9.example.com:
ansible_host: 192.168.122.12
hostname: "rhel9.example.com"
os: "rhel9"
filesystem: "xfs"
vm_cpus: 4
vm_memory: 8192
vm_size: 80
vm_ip: 192.168.122.12
vm_nms: 24
vm_gw: 192.168.122.1
vm_dns: "1.1.1.1,1.0.0.1"
vm_path: "/srv/libvirt/images"
rhel_iso: "/var/lib/libvirt/images/rhel-9.4-x86_64-dvd.iso" rhel_iso: "/var/lib/libvirt/images/rhel-9.4-x86_64-dvd.iso"
vlan_name: "100" system:
filesystem: "xfs"
type: "virtual"
os: "rhel"
version: "9"
name: "db01.local"
cpus: 4
memory: 4096
network: "default"
ip: 192.168.122.21
prefix: 24
gateway: 192.168.122.1
dns:
servers:
- 9.9.9.9
search:
- example.com
disks:
- size: 60
- size: 120
mount:
path: /data
fstype: ext4
user:
name: "db"
password: "CHANGE_ME"
key: "ssh-ed25519 AAAA..."
root:
password: "CHANGE_ME"
luks:
enabled: true
passphrase: "CHANGE_ME"
method: "keyfile"
features:
firewall:
enabled: false
backend: "firewalld"
toolkit: "nftables"
compute01.local:
ansible_host: 192.168.122.22
system:
filesystem: "btrfs"
type: "virtual"
os: "fedora"
version: "41"
name: "compute01.local"
cpus: 8
memory: 8192
network: "default"
ip: 192.168.122.22
prefix: 24
gateway: 192.168.122.1
dns:
servers: "1.1.1.1,1.0.0.1"
disks:
- size: 80
- size: 200
mount:
path: /data
fstype: btrfs
user:
name: "compute"
password: "CHANGE_ME"
key: "ssh-ed25519 AAAA..."
root:
password: "CHANGE_ME"
features:
cis:
enabled: true

View File

@@ -25,20 +25,61 @@
What is your root password? What is your root password?
confirm: true confirm: true
pre_tasks: pre_tasks:
- name: Apply prompted authentication values to system input
vars:
system_input: "{{ system | default({}) }}"
system_user_input: "{{ (system_input.user | default({})) if (system_input.user is mapping) else {} }}"
system_root_input: "{{ (system_input.root | default({})) if (system_input.root is mapping) else {} }}"
prompt_user_name: "{{ user_name | default(system_user_name | default(''), true) | string }}"
prompt_user_key: "{{ user_public_key | default(user_key | default(system_user_key | default(''), true), true) | string }}"
prompt_user_password: "{{ user_password | default(system_user_password | default(''), true) | string }}"
prompt_root_password: "{{ root_password | default(system_root_password | default(''), true) | string }}"
ansible.builtin.set_fact:
system: >-
{{
system_input
| combine(
{
'user': {
'name': (
(system_user_input.name | default('') | string | length) > 0
) | ternary(system_user_input.name | string, prompt_user_name),
'key': (
(system_user_input.key | default('') | string | length) > 0
) | ternary(system_user_input.key | string, prompt_user_key),
'password': (
(system_user_input.password | default('') | string | length) > 0
) | ternary(system_user_input.password | string, prompt_user_password)
},
'root': {
'password': (
(system_root_input.password | default('') | string | length) > 0
) | ternary(system_root_input.password | string, prompt_root_password)
}
},
recursive=True
)
}}
changed_when: false
- name: Load global defaults - name: Load global defaults
ansible.builtin.import_role: ansible.builtin.import_role:
name: global_defaults name: global_defaults
- name: Perform safety checks
ansible.builtin.import_role:
name: system_check
roles: roles:
- role: virtualization - role: virtualization
when: install_type == "virtual" when: system_cfg.type == "virtual"
become: false become: false
vars: vars:
ansible_connection: local ansible_connection: local
- role: environment - role: environment
vars: vars:
ansible_connection: "{{ 'vmware_tools' if hypervisor == 'vmware' else 'ssh' }}" ansible_connection: "{{ 'vmware_tools' if hypervisor_type == 'vmware' else 'ssh' }}"
- role: partitioning - role: partitioning
vars: vars:
@@ -50,10 +91,10 @@
- role: configuration - role: configuration
- role: cis - role: cis
when: cis_enabled when: system_cfg.features.cis.enabled | bool
- role: cleanup - role: cleanup
when: install_type in ["virtual", "physical"] when: system_cfg.type in ["virtual", "physical"]
become: false become: false
post_tasks: post_tasks:
@@ -62,9 +103,9 @@
post_reboot_can_connect: >- post_reboot_can_connect: >-
{{ {{
(ansible_connection | default('ssh')) != 'ssh' (ansible_connection | default('ssh')) != 'ssh'
or (vm_ip is defined and (vm_ip | string | length) > 0) or ((system_cfg.ip | default('') | string | length) > 0)
or ( or (
install_type == 'physical' system_cfg.type == 'physical'
and (ansible_host | default('') | string | length) > 0 and (ansible_host | default('') | string | length) > 0
) )
}} }}
@@ -74,29 +115,16 @@
when: when:
- post_reboot_can_connect | bool - post_reboot_can_connect | bool
ansible.builtin.set_fact: ansible.builtin.set_fact:
ansible_user: "{{ user_name }}" ansible_user: "{{ system_cfg.user.name }}"
ansible_password: "{{ user_password }}" ansible_password: "{{ system_cfg.user.password }}"
ansible_become_password: "{{ user_password }}" ansible_become_password: "{{ system_cfg.user.password }}"
ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
- name: Install post-reboot extra packages - name: Install post-reboot packages
vars:
post_install_extra_packages: >-
{{
(
extra_packages
if (extra_packages is iterable and extra_packages is not string)
else (extra_packages | string).split(',')
)
| map('trim')
| reject('equalto', '')
| list
}}
when: when:
- post_reboot_can_connect | bool - post_reboot_can_connect | bool
- extra_packages is defined - system_cfg.packages is defined
- extra_packages | length > 0 - system_cfg.packages | length > 0
- post_install_extra_packages | length > 0
ansible.builtin.package: ansible.builtin.package:
name: "{{ post_install_extra_packages }}" name: "{{ system_cfg.packages }}"
state: present state: present

View File

@@ -1,20 +1,35 @@
--- ---
- name: Bootstrap AlmaLinux 9 - name: Bootstrap AlmaLinux
vars: vars:
bootstrap_alma_extra: >- bootstrap_almalinux_extra: >-
{{ {{
lookup('vars', bootstrap_var_key) lookup('vars', bootstrap_var_key)
| reject('equalto', '') | reject('equalto', '')
| join(' ') | join(' ')
}} }}
ansible.builtin.command: "{{ item }}" block:
loop: - name: Install AlmaLinux base system
- >- ansible.builtin.command: >-
dnf --releasever=9 --best --repo=alma-baseos --installroot=/mnt dnf --releasever={{ os_version }} --best --repo=baseos --repo=appstream
--setopt=install_weak_deps=False groupinstall -y base core --installroot=/mnt --setopt=install_weak_deps=False
- ln -sf /run/NetworkManager/resolv.conf /mnt/etc/resolv.conf groupinstall -y core
- >- register: bootstrap_almalinux_base_result
{{ chroot_command }} /mnt dnf --releasever=9 --setopt=install_weak_deps=False changed_when: bootstrap_almalinux_base_result.rc == 0
install -y {{ bootstrap_alma_extra }}
register: bootstrap_result - name: Ensure chroot has resolv.conf
changed_when: bootstrap_result.rc == 0 ansible.builtin.file:
src: /run/NetworkManager/resolv.conf
dest: /mnt/etc/resolv.conf
state: link
- name: Install extra packages
ansible.builtin.command: >-
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
install -y {{ bootstrap_almalinux_extra }}
register: bootstrap_almalinux_extra_result
changed_when: bootstrap_almalinux_extra_result.rc == 0
- name: Reinstall kernel core
ansible.builtin.command: "{{ chroot_command }} dnf reinstall -y kernel-core"
register: bootstrap_almalinux_kernel_result
changed_when: bootstrap_almalinux_kernel_result.rc == 0

View File

@@ -0,0 +1,33 @@
---
- name: Bootstrap Alpine Linux
vars:
bootstrap_alpine_packages: >-
{{
lookup('vars', 'bootstrap_alpine') | reject('equalto', '') | join(' ')
}}
block:
- name: Ensure chroot has resolv.conf
ansible.builtin.file:
src: /run/NetworkManager/resolv.conf
dest: /mnt/etc/resolv.conf
state: link
force: true
- name: Install Alpine Linux packages
ansible.builtin.command: >
apk --root /mnt --no-cache add alpine-base
register: bootstrap_alpine_bootstrap_result
changed_when: bootstrap_alpine_bootstrap_result.rc == 0
- name: Install extra packages
when: bootstrap_alpine_packages | length > 0
ansible.builtin.command: >
apk --root /mnt add {{ bootstrap_alpine_packages }}
register: bootstrap_alpine_extra_result
changed_when: bootstrap_alpine_extra_result.rc == 0
- name: Install bootloader
ansible.builtin.command: >
apk --root /mnt add grub grub-efi efibootmgr
register: bootstrap_alpine_bootloader_result
changed_when: bootstrap_alpine_bootloader_result.rc == 0

View File

@@ -3,27 +3,65 @@
vars: vars:
bootstrap_debian_release: >- bootstrap_debian_release: >-
{{ {{
'bullseye' if bootstrap_os_key == 'debian11' 'buster' if (os_version | string) == '10'
else 'bookworm' if bootstrap_os_key == 'debian12' else 'bullseye' if (os_version | string) == '11'
else 'bookworm' if (os_version | string) == '12'
else 'trixie' if (os_version | string) == '13'
else 'sid' if (os_version | string) == 'unstable'
else 'trixie' else 'trixie'
}} }}
bootstrap_debian_base_list: "{{ lookup('vars', bootstrap_var_key).base | default([]) }}" bootstrap_debian_package_config: >-
bootstrap_debian_extra_list: "{{ lookup('vars', bootstrap_var_key).extra | default([]) }}"
bootstrap_debian_base: "{{ bootstrap_debian_base_list | reject('equalto', '') | join(',') }}"
bootstrap_debian_extra: >-
{{ {{
( lookup('vars', bootstrap_var_key)
bootstrap_debian_extra_list }}
) bootstrap_debian_base_packages: >-
{{
bootstrap_debian_package_config.base
| default([])
| reject('equalto', '') | reject('equalto', '')
| list
}}
bootstrap_debian_extra_packages: >-
{{
bootstrap_debian_package_config.extra
| default([])
| reject('equalto', '')
| list
}}
bootstrap_debian_base_csv: "{{ bootstrap_debian_base_packages | join(',') }}"
bootstrap_debian_extra_args: >-
{{
bootstrap_debian_extra_packages
| join(' ') | join(' ')
}} }}
ansible.builtin.command: "{{ item }}" block:
loop: - name: Validate Debian package configuration
- >- ansible.builtin.assert:
debootstrap --include={{ bootstrap_debian_base }} that:
- bootstrap_debian_package_config is mapping
- bootstrap_debian_package_config.base is defined
- bootstrap_debian_package_config.base is sequence
- bootstrap_debian_package_config.base is not string
- bootstrap_debian_package_config.extra is defined
- bootstrap_debian_package_config.extra is sequence
- bootstrap_debian_package_config.extra is not string
fail_msg: "bootstrap package definition for {{ bootstrap_var_key }} must be a mapping with base/extra lists."
quiet: true
- name: Install Debian base system
ansible.builtin.command: >-
debootstrap --include={{ bootstrap_debian_base_csv }}
{{ bootstrap_debian_release }} /mnt http://deb.debian.org/debian/ {{ bootstrap_debian_release }} /mnt http://deb.debian.org/debian/
- "{{ chroot_command }} /mnt apt install -y {{ bootstrap_debian_extra }}" register: bootstrap_debian_base_result
- "{{ chroot_command }} /mnt apt remove -y libcups2 libavahi-common3 libavahi-common-data" changed_when: bootstrap_debian_base_result.rc == 0
register: bootstrap_result
changed_when: bootstrap_result.rc == 0 - name: Install extra packages
when: bootstrap_debian_extra_packages | length > 0
ansible.builtin.command: "{{ chroot_command }} apt install -y {{ bootstrap_debian_extra_args }}"
register: bootstrap_debian_extra_result
changed_when: bootstrap_debian_extra_result.rc == 0
- name: Remove unnecessary packages
ansible.builtin.command: "{{ chroot_command }} apt remove -y libcups2 libavahi-common3 libavahi-common-data"
register: bootstrap_debian_remove_result
changed_when: bootstrap_debian_remove_result.rc == 0

View File

@@ -1,5 +1,5 @@
--- ---
- name: Bootstrap Fedora 43 - name: Bootstrap Fedora
vars: vars:
bootstrap_fedora_extra: >- bootstrap_fedora_extra: >-
{{ {{
@@ -7,16 +7,29 @@
| reject('equalto', '') | reject('equalto', '')
| join(' ') | join(' ')
}} }}
ansible.builtin.command: "{{ item }}" block:
loop: - name: Install Fedora base system
- >- ansible.builtin.command: >-
dnf --releasever=43 --best --repo=fedora --repo=fedora-updates dnf --releasever={{ os_version }} --best --repo=fedora --repo=fedora-updates
--installroot=/mnt --setopt=install_weak_deps=False --installroot=/mnt --setopt=install_weak_deps=False
groupinstall -y critical-path-base core groupinstall -y critical-path-base core
- ln -sf /run/NetworkManager/resolv.conf /mnt/etc/resolv.conf register: bootstrap_fedora_base_result
- >- changed_when: bootstrap_fedora_base_result.rc == 0
{{ chroot_command }} /mnt dnf --releasever=43 --setopt=install_weak_deps=False
- name: Ensure chroot has resolv.conf
ansible.builtin.file:
src: /run/NetworkManager/resolv.conf
dest: /mnt/etc/resolv.conf
state: link
- name: Install extra packages
ansible.builtin.command: >-
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
install -y {{ bootstrap_fedora_extra }} install -y {{ bootstrap_fedora_extra }}
- "{{ chroot_command }} /mnt dnf reinstall -y kernel-core" register: bootstrap_fedora_extra_result
register: bootstrap_result changed_when: bootstrap_fedora_extra_result.rc == 0
changed_when: bootstrap_result.rc == 0
- name: Reinstall kernel core
ansible.builtin.command: "{{ chroot_command }} dnf reinstall -y kernel-core"
register: bootstrap_fedora_kernel_result
changed_when: bootstrap_fedora_kernel_result.rc == 0

View File

@@ -1,27 +1,35 @@
--- ---
- name: Run OS-specific bootstrap process - name: Run OS-specific bootstrap process
vars: vars:
bootstrap_os_key: "{{ os | lower }}" bootstrap_os_key: "{{ (os_resolved | default(os)) | lower }}"
bootstrap_var_key: "{{ 'bootstrap_' + (os | lower | replace('-', '_')) }}" bootstrap_var_key: "{{ 'bootstrap_' + ((os_resolved | default(os)) | lower | replace('-', '_')) }}"
block: block:
- name: Include AlmaLinux bootstrap tasks - name: Include AlmaLinux bootstrap tasks
when: bootstrap_os_key == 'almalinux' when: bootstrap_os_key in ['almalinux', 'almalinux8', 'almalinux9', 'almalinux10']
ansible.builtin.include_tasks: almalinux.yml ansible.builtin.include_tasks: almalinux.yml
- name: Include Alpine bootstrap tasks
when: bootstrap_os_key == 'alpine'
ansible.builtin.include_tasks: alpine.yml
- name: Include ArchLinux bootstrap tasks - name: Include ArchLinux bootstrap tasks
when: bootstrap_os_key == 'archlinux' when: bootstrap_os_key == 'archlinux'
ansible.builtin.include_tasks: archlinux.yml ansible.builtin.include_tasks: archlinux.yml
- name: Include Debian bootstrap tasks - name: Include Debian bootstrap tasks
when: bootstrap_os_key in ['debian11', 'debian12', 'debian13'] when: bootstrap_os_key in ['debian10', 'debian11', 'debian12', 'debian13', 'debianunstable']
ansible.builtin.include_tasks: debian.yml ansible.builtin.include_tasks: debian.yml
- name: Include Fedora bootstrap tasks - name: Include Fedora bootstrap tasks
when: bootstrap_os_key == 'fedora' when: bootstrap_os_key in ['fedora', 'fedora40', 'fedora41', 'fedora42', 'fedora43']
ansible.builtin.include_tasks: fedora.yml ansible.builtin.include_tasks: fedora.yml
- name: Include openSUSE bootstrap tasks
when: bootstrap_os_key == 'opensuse'
ansible.builtin.include_tasks: opensuse.yml
- name: Include Rocky bootstrap tasks - name: Include Rocky bootstrap tasks
when: bootstrap_os_key == 'rocky' when: bootstrap_os_key in ['rocky', 'rocky8', 'rocky9', 'rocky10']
ansible.builtin.include_tasks: rocky.yml ansible.builtin.include_tasks: rocky.yml
- name: Include RHEL bootstrap tasks - name: Include RHEL bootstrap tasks
@@ -31,3 +39,7 @@
- name: Include Ubuntu bootstrap tasks - name: Include Ubuntu bootstrap tasks
when: bootstrap_os_key in ['ubuntu', 'ubuntu-lts'] when: bootstrap_os_key in ['ubuntu', 'ubuntu-lts']
ansible.builtin.include_tasks: ubuntu.yml ansible.builtin.include_tasks: ubuntu.yml
- name: Include Void bootstrap tasks
when: bootstrap_os_key == 'void'
ansible.builtin.include_tasks: void.yml

View File

@@ -0,0 +1,33 @@
---
- name: Bootstrap openSUSE
vars:
bootstrap_opensuse_packages: >-
{{
lookup('vars', 'bootstrap_opensuse') | reject('equalto', '') | join(' ')
}}
block:
- name: Ensure chroot has resolv.conf
ansible.builtin.file:
src: /run/NetworkManager/resolv.conf
dest: /mnt/etc/resolv.conf
state: link
force: true
- name: Install openSUSE base packages
ansible.builtin.command: >
zypper --root /mnt --non-interactive install -t pattern patterns-base-base
register: bootstrap_opensuse_base_result
changed_when: bootstrap_opensuse_base_result.rc == 0
- name: Install openSUSE extra packages
when: bootstrap_opensuse_packages | length > 0
ansible.builtin.command: >
zypper --root /mnt --non-interactive install {{ bootstrap_opensuse_packages }}
register: bootstrap_opensuse_extra_result
changed_when: bootstrap_opensuse_extra_result.rc == 0
- name: Install bootloader
ansible.builtin.command: >
zypper --root /mnt --non-interactive install grub2 grub2-efi efibootmgr
register: bootstrap_opensuse_bootloader_result
changed_when: bootstrap_opensuse_bootloader_result.rc == 0

View File

@@ -17,7 +17,6 @@
src: /run/NetworkManager/resolv.conf src: /run/NetworkManager/resolv.conf
dest: /mnt/etc/resolv.conf dest: /mnt/etc/resolv.conf
state: link state: link
force: true
- name: Ensure chroot RHEL DVD directory exists - name: Ensure chroot RHEL DVD directory exists
ansible.builtin.file: ansible.builtin.file:
@@ -34,7 +33,7 @@
state: mounted state: mounted
- name: Rebuild RPM database inside chroot - name: Rebuild RPM database inside chroot
ansible.builtin.command: "{{ chroot_command }} /mnt rpm --rebuilddb" ansible.builtin.command: "{{ chroot_command }} rpm --rebuilddb"
register: bootstrap_rpm_rebuild_result register: bootstrap_rpm_rebuild_result
changed_when: bootstrap_rpm_rebuild_result.rc == 0 changed_when: bootstrap_rpm_rebuild_result.rc == 0
@@ -55,7 +54,7 @@
| join(' ') | join(' ')
}} }}
ansible.builtin.command: >- ansible.builtin.command: >-
{{ chroot_command }} /mnt dnf --releasever={{ bootstrap_rhel_release }} {{ chroot_command }} dnf --releasever={{ bootstrap_rhel_release }}
--setopt=install_weak_deps=False install -y {{ bootstrap_rhel_extra }} --setopt=install_weak_deps=False install -y {{ bootstrap_rhel_extra }}
register: bootstrap_result register: bootstrap_result
changed_when: bootstrap_result.rc == 0 changed_when: bootstrap_result.rc == 0

View File

@@ -1,5 +1,5 @@
--- ---
- name: Bootstrap RockyLinux 9 - name: Bootstrap Rocky Linux
vars: vars:
bootstrap_rocky_extra: >- bootstrap_rocky_extra: >-
{{ {{
@@ -7,15 +7,29 @@
| reject('equalto', '') | reject('equalto', '')
| join(' ') | join(' ')
}} }}
ansible.builtin.command: "{{ item }}" block:
loop: - name: Install Rocky Linux base system
- >- ansible.builtin.command: >-
dnf --releasever=9 --best --repo=rocky-baseos --installroot=/mnt dnf --releasever={{ os_version }} --best --repo=baseos --repo=appstream
--setopt=install_weak_deps=False --setopt=optional_metadata_types=filelists --installroot=/mnt --setopt=install_weak_deps=False
groupinstall -y base core groupinstall -y core
- ln -sf /run/NetworkManager/resolv.conf /mnt/etc/resolv.conf register: bootstrap_rocky_base_result
- >- changed_when: bootstrap_rocky_base_result.rc == 0
{{ chroot_command }} /mnt dnf --releasever=9 --setopt=install_weak_deps=False
- name: Ensure chroot has resolv.conf
ansible.builtin.file:
src: /run/NetworkManager/resolv.conf
dest: /mnt/etc/resolv.conf
state: link
- name: Install extra packages
ansible.builtin.command: >-
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
install -y {{ bootstrap_rocky_extra }} install -y {{ bootstrap_rocky_extra }}
register: bootstrap_result register: bootstrap_rocky_extra_result
changed_when: bootstrap_result.rc == 0 changed_when: bootstrap_rocky_extra_result.rc == 0
- name: Reinstall kernel core
ansible.builtin.command: "{{ chroot_command }} dnf reinstall -y kernel-core"
register: bootstrap_rocky_kernel_result
changed_when: bootstrap_rocky_kernel_result.rc == 0

View File

@@ -3,25 +3,66 @@
vars: vars:
bootstrap_ubuntu_release: >- bootstrap_ubuntu_release: >-
{{ 'plucky' if bootstrap_os_key == 'ubuntu' else 'noble' }} {{ 'plucky' if bootstrap_os_key == 'ubuntu' else 'noble' }}
bootstrap_ubuntu_base_list: "{{ lookup('vars', bootstrap_var_key).base | default([]) }}" bootstrap_ubuntu_package_config: >-
bootstrap_ubuntu_extra_list: "{{ lookup('vars', bootstrap_var_key).extra | default([]) }}"
bootstrap_ubuntu_base: "{{ bootstrap_ubuntu_base_list | reject('equalto', '') | join(',') }}"
bootstrap_ubuntu_extra: >-
{{ {{
( lookup('vars', bootstrap_var_key)
bootstrap_ubuntu_extra_list
)
| reject('equalto', '')
| join(' ')
}} }}
ansible.builtin.command: "{{ item }}" bootstrap_ubuntu_base_packages: >-
loop: {{
- >- bootstrap_ubuntu_package_config.base
debootstrap --include={{ bootstrap_ubuntu_base }} | default([])
{{ bootstrap_ubuntu_release }} /mnt http://archive.ubuntu.com/ubuntu/ | reject('equalto', '')
- ln -sf /run/NetworkManager/resolv.conf /mnt/etc/resolv.conf | list
- "{{ chroot_command }} /mnt sed -i '1s|$| universe|' /etc/apt/sources.list" }}
- "{{ chroot_command }} /mnt apt update" bootstrap_ubuntu_extra_packages: >-
- "{{ chroot_command }} /mnt apt install -y {{ bootstrap_ubuntu_extra }}" {{
register: bootstrap_result bootstrap_ubuntu_package_config.extra
changed_when: bootstrap_result.rc == 0 | default([])
| reject('equalto', '')
| list
}}
bootstrap_ubuntu_base_csv: "{{ bootstrap_ubuntu_base_packages | join(',') }}"
bootstrap_ubuntu_extra: "{{ bootstrap_ubuntu_extra_packages | join(' ') }}"
block:
- name: Validate Ubuntu package configuration
ansible.builtin.assert:
that:
- bootstrap_ubuntu_package_config is mapping
- bootstrap_ubuntu_package_config.base is defined
- bootstrap_ubuntu_package_config.base is sequence
- bootstrap_ubuntu_package_config.base is not string
- bootstrap_ubuntu_package_config.extra is defined
- bootstrap_ubuntu_package_config.extra is sequence
- bootstrap_ubuntu_package_config.extra is not string
fail_msg: "bootstrap package definition for {{ bootstrap_var_key }} must be a mapping with base/extra lists."
quiet: true
- name: Install Ubuntu base system
ansible.builtin.command: >-
debootstrap --include={{ bootstrap_ubuntu_base_csv }}
{{ bootstrap_ubuntu_release }} /mnt
http://archive.ubuntu.com/ubuntu/
register: bootstrap_ubuntu_base_result
changed_when: bootstrap_ubuntu_base_result.rc == 0
- name: Ensure chroot has resolv.conf
ansible.builtin.file:
src: /run/NetworkManager/resolv.conf
dest: /mnt/etc/resolv.conf
state: link
- name: Enable universe repository
ansible.builtin.command: "{{ chroot_command }} sed -i '1s|$| universe|' /etc/apt/sources.list"
register: bootstrap_ubuntu_repo_result
changed_when: bootstrap_ubuntu_repo_result.rc == 0
- name: Update package lists
ansible.builtin.command: "{{ chroot_command }} apt update"
register: bootstrap_ubuntu_update_result
changed_when: bootstrap_ubuntu_update_result.rc == 0
- name: Install extra packages
when: bootstrap_ubuntu_extra_packages | length > 0
ansible.builtin.command: "{{ chroot_command }} apt install -y {{ bootstrap_ubuntu_extra }}"
register: bootstrap_ubuntu_extra_result
changed_when: bootstrap_ubuntu_extra_result.rc == 0

View File

@@ -0,0 +1,33 @@
---
- name: Bootstrap Void Linux
vars:
bootstrap_void_packages: >-
{{
lookup('vars', 'bootstrap_void') | reject('equalto', '') | join(' ')
}}
block:
- name: Ensure chroot has resolv.conf
ansible.builtin.file:
src: /run/NetworkManager/resolv.conf
dest: /mnt/etc/resolv.conf
state: link
force: true
- name: Install Void Linux base packages
ansible.builtin.command: >
xbps-install -Sy -r /mnt -R https://repo-default.voidlinux.org/current void-repo-nonfree base-system
register: bootstrap_void_base_result
changed_when: bootstrap_void_base_result.rc == 0
- name: Install extra packages
when: bootstrap_void_packages | length > 0
ansible.builtin.command: >
xbps-install -Su -r /mnt {{ bootstrap_void_packages }}
register: bootstrap_void_extra_result
changed_when: bootstrap_void_extra_result.rc == 0
- name: Install bootloader
ansible.builtin.command: >
xbps-install -Sy -r /mnt grub-x86_64-efi efibootmgr
register: bootstrap_void_bootloader_result
changed_when: bootstrap_void_bootloader_result.rc == 0

View File

@@ -1,229 +1,56 @@
--- ---
bootstrap_almalinux: bootstrap_rhel_base:
- bind-utils - bind-utils
- dbus-daemon
- dhcp-client - dhcp-client
- efibootmgr - efibootmgr
- "{{ 'firewalld' if firewalld_enabled | bool else '' }}" - "{{ 'firewalld' if system_cfg.features.firewall.backend == 'firewalld' and system_cfg.features.firewall.enabled | bool else '' }}"
- "{{ 'ufw' if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else '' }}"
- "{{ 'iptables' if system_cfg.features.firewall.toolkit == 'iptables' else '' }}"
- "{{ 'nftables' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
- glibc-langpack-de - glibc-langpack-de
- glibc-langpack-en - glibc-langpack-en
- grub2
- grub2-efi
- lrzsz - lrzsz
- lvm2 - lvm2
- nc
- nfs-utils
- nfsv4-client-utils
- mtr - mtr
- ppp - ncurses-term
- nfs-utils
- policycoreutils-python-utils
- shim - shim
- tmux - tmux
- "{{ 'cryptsetup' if luks_enabled else '' }}" - "{{ 'cryptsetup' if system_cfg.luks.enabled else '' }}"
- "{{ 'tpm2-tools' if luks_enabled else '' }}" - "{{ 'tpm2-tools' if system_cfg.luks.enabled else '' }}"
- "{{ 'qemu-guest-agent' if hypervisor | lower in ['libvirt', 'proxmox'] else '' }}" - "{{ 'qemu-guest-agent' if hypervisor_type in ['libvirt', 'proxmox'] else '' }}"
- "{{ 'open-vm-tools' if hypervisor | lower == 'vmware' else '' }}" - "{{ 'open-vm-tools' if hypervisor_type == 'vmware' else '' }}"
- vim - vim
- wget
- zram-generator
- zstd - zstd
bootstrap_archlinux: bootstrap_rhel_versioned:
- base - grub2
- btrfs-progs - "{{ 'grub2-efi-x64' if os_version_major | default('') == '8' else 'grub2-efi' }}"
- cronie - "{{ 'grub2-tools-extra' if os_version_major | default('') in ['8', '9'] else '' }}"
- dhcpcd - "{{ 'python39' if os_version_major | default('') == '8' else 'python' }}"
- efibootmgr - "{{ 'kernel' if os_version_major | default('') == '10' else '' }}"
- fastfetch - "{{ 'zram-generator' if os_version_major | default('') in ['9', '10'] else '' }}"
- "{{ 'firewalld' if firewalld_enabled | bool else '' }}"
- fish
- fzf
- grub
- htop
- libpwquality
- linux
- logrotate
- lrzsz
- lsof
- lvm2
- ncdu
- networkmanager
- nfs-utils
- "{{ 'openssh' if ssh_enabled | bool else '' }}"
- ppp
- prometheus-node-exporter
- python-psycopg2
- reflector
- rsync
- sudo
- tldr
- tmux
- "{{ 'cryptsetup' if luks_enabled else '' }}"
- "{{ 'tpm2-tools' if luks_enabled else '' }}"
- "{{ 'qemu-guest-agent' if hypervisor | lower in ['libvirt', 'proxmox'] else '' }}"
- "{{ 'open-vm-tools' if hypervisor | lower == 'vmware' else '' }}"
- vim
- wireguard-tools
- zram-generator
bootstrap_debian11: bootstrap_rhel_common: "{{ bootstrap_rhel_base + bootstrap_rhel_versioned }}"
base:
- apparmor-utils
- btrfs-progs
- chrony
- cron
- gnupg
- grub-efi
- grub-efi-amd64-signed
- grub2-common
- "{{ 'cryptsetup' if luks_enabled else '' }}"
- "{{ 'cryptsetup-initramfs' if luks_enabled else '' }}"
- linux-image-amd64
- locales
- logrotate
- lvm2
- net-tools
- "{{ 'openssh-server' if ssh_enabled | bool else '' }}"
- python3
- sudo
- xfsprogs
extra: bootstrap_rhel8: "{{ bootstrap_rhel_common }}"
- bat bootstrap_rhel9: "{{ bootstrap_rhel_common }}"
- curl bootstrap_rhel10: "{{ bootstrap_rhel_common }}"
- entr
- "{{ 'firewalld' if firewalld_enabled | bool else '' }}"
- fish
- fzf
- htop
- jq
- libpam-pwquality
- lrzsz
- mtr
- ncdu
- neofetch
- network-manager
- python-is-python3
- ripgrep
- rsync
- screen
- software-properties-common
- syslog-ng
- tcpd
- tldr
- "{{ 'tpm2-tools' if luks_enabled else '' }}"
- "{{ 'qemu-guest-agent' if hypervisor | lower in ['libvirt', 'proxmox'] else '' }}"
- "{{ 'open-vm-tools' if hypervisor | lower == 'vmware' else '' }}"
- vim
- wget
- zstd
bootstrap_debian12: bootstrap_almalinux:
base: "{{ bootstrap_rhel_base + ['grub2', 'grub2-efi', 'dbus-daemon', 'lrzsz', 'nfsv4-client-utils', 'nc', 'ppp', 'zram-generator'] }}"
- btrfs-progs
- cron
- gnupg
- grub-efi
- grub-efi-amd64-signed
- grub2-common
- "{{ 'cryptsetup' if luks_enabled else '' }}"
- "{{ 'cryptsetup-initramfs' if luks_enabled else '' }}"
- linux-image-amd64
- locales
- logrotate
- lvm2
- xfsprogs
extra: bootstrap_rocky:
- apparmor-utils "{{ bootstrap_rhel_base + ['grub2', 'grub2-efi', 'nfsv4-client-utils', 'nc', 'ppp', 'telnet', 'util-linux-core', 'wget', 'zram-generator'] }}"
- bat
- chrony
- curl
- duf
- entr
- "{{ 'firewalld' if firewalld_enabled | bool else '' }}"
- fish
- fzf
- htop
- jq
- libpam-pwquality
- logrotate
- lrzsz
- mtr
- ncdu
- neofetch
- net-tools
- network-manager
- "{{ 'openssh-server' if ssh_enabled | bool else '' }}"
- python-is-python3
- python3
- ripgrep
- rsync
- screen
- software-properties-common
- sudo
- syslog-ng
- systemd-zram-generator
- tcpd
- tldr
- "{{ 'tpm2-tools' if luks_enabled else '' }}"
- "{{ 'qemu-guest-agent' if hypervisor | lower in ['libvirt', 'proxmox'] else '' }}"
- "{{ 'open-vm-tools' if hypervisor | lower == 'vmware' else '' }}"
- vim
- wget
- zstd
bootstrap_debian13: bootstrap_almalinux8: "{{ bootstrap_almalinux }}"
base: bootstrap_almalinux9: "{{ bootstrap_almalinux }}"
- btrfs-progs bootstrap_almalinux10: "{{ bootstrap_almalinux }}"
- cron
- gnupg
- grub-efi
- grub-efi-amd64-signed
- grub2-common
- "{{ 'cryptsetup' if luks_enabled else '' }}"
- "{{ 'cryptsetup-initramfs' if luks_enabled else '' }}"
- linux-image-amd64
- locales
- logrotate
- lvm2
- xfsprogs
extra: bootstrap_rocky8: "{{ bootstrap_rocky }}"
- apparmor-utils bootstrap_rocky9: "{{ bootstrap_rocky }}"
- bat bootstrap_rocky10: "{{ bootstrap_rocky }}"
- chrony
- curl
- duf
- entr
- fastfetch
- "{{ 'firewalld' if firewalld_enabled | bool else '' }}"
- fish
- fzf
- htop
- jq
- libpam-pwquality
- logrotate
- lrzsz
- mtr
- ncdu
- net-tools
- network-manager
- "{{ 'openssh-server' if ssh_enabled | bool else '' }}"
- python-is-python3
- python3
- ripgrep
- rsync
- screen
- sudo
- syslog-ng
- systemd-zram-generator
- tcpd
- "{{ 'tpm2-tools' if luks_enabled else '' }}"
- "{{ 'qemu-guest-agent' if hypervisor | lower in ['libvirt', 'proxmox'] else '' }}"
- "{{ 'open-vm-tools' if hypervisor | lower == 'vmware' else '' }}"
- vim
- wget
- zstd
bootstrap_fedora: bootstrap_fedora:
- bat - bat
@@ -234,7 +61,10 @@ bootstrap_fedora:
- duf - duf
- efibootmgr - efibootmgr
- entr - entr
- "{{ 'firewalld' if firewalld_enabled | bool else '' }}" - "{{ 'firewalld' if system_cfg.features.firewall.backend == 'firewalld' and system_cfg.features.firewall.enabled | bool else '' }}"
- "{{ 'ufw' if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else '' }}"
- "{{ 'iptables' if system_cfg.features.firewall.toolkit == 'iptables' else '' }}"
- "{{ 'nftables' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
- fish - fish
- fzf - fzf
- glibc-langpack-de - glibc-langpack-de
@@ -254,247 +84,184 @@ bootstrap_fedora:
- ripgrep - ripgrep
- shim - shim
- tmux - tmux
- "{{ 'cryptsetup' if luks_enabled else '' }}" - "{{ 'cryptsetup' if system_cfg.luks.enabled else '' }}"
- "{{ 'tpm2-tools' if luks_enabled else '' }}" - "{{ 'tpm2-tools' if system_cfg.luks.enabled else '' }}"
- "{{ 'qemu-guest-agent' if hypervisor | lower in ['libvirt', 'proxmox'] else '' }}" - "{{ 'qemu-guest-agent' if hypervisor_type in ['libvirt', 'proxmox'] else '' }}"
- "{{ 'open-vm-tools' if hypervisor | lower == 'vmware' else '' }}" - "{{ 'open-vm-tools' if hypervisor_type == 'vmware' else '' }}"
- vim-default-editor - vim-default-editor
- wget - wget
- zoxide - zoxide
- zram-generator - zram-generator
- zstd - zstd
bootstrap_rhel8: bootstrap_fedora40: "{{ bootstrap_fedora }}"
- bind-utils bootstrap_fedora41: "{{ bootstrap_fedora }}"
- dhcp-client bootstrap_fedora42: "{{ bootstrap_fedora }}"
- efibootmgr bootstrap_fedora43: "{{ bootstrap_fedora }}"
- "{{ 'firewalld' if firewalld_enabled | bool else '' }}"
- glibc-langpack-de
- glibc-langpack-en
- grub2
- grub2-efi-x64
- grub2-tools-extra
- lrzsz
- lvm2
- mtr
- ncurses-term
- nfs-utils
- policycoreutils-python-utils
- python39
- shim
- tmux
- "{{ 'cryptsetup' if luks_enabled else '' }}"
- "{{ 'tpm2-tools' if luks_enabled else '' }}"
- "{{ 'qemu-guest-agent' if hypervisor | lower in ['libvirt', 'proxmox'] else '' }}"
- "{{ 'open-vm-tools' if hypervisor | lower == 'vmware' else '' }}"
- vim
- zstd
bootstrap_rhel9: bootstrap_debian_base_common:
- bind-utils - btrfs-progs
- dhcp-client - cron
- efibootmgr - gnupg
- "{{ 'firewalld' if firewalld_enabled | bool else '' }}" - grub-efi
- glibc-langpack-de - grub-efi-amd64-signed
- glibc-langpack-en - grub2-common
- grub2 - "{{ 'cryptsetup' if system_cfg.luks.enabled else '' }}"
- grub2-efi - "{{ 'cryptsetup-initramfs' if system_cfg.luks.enabled else '' }}"
- grub2-tools-extra - locales
- lrzsz - logrotate
- lvm2 - lvm2
- mtr - "{{ 'iptables' if system_cfg.features.firewall.toolkit == 'iptables' else '' }}"
- ncurses-term - "{{ 'nftables' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
- nfs-utils - "{{ 'openssh-server' if system_cfg.features.ssh.enabled | bool else '' }}"
- policycoreutils-python-utils - python3
- python - xfsprogs
- shim
- tmux
- "{{ 'cryptsetup' if luks_enabled else '' }}"
- "{{ 'tpm2-tools' if luks_enabled else '' }}"
- "{{ 'qemu-guest-agent' if hypervisor | lower in ['libvirt', 'proxmox'] else '' }}"
- "{{ 'open-vm-tools' if hypervisor | lower == 'vmware' else '' }}"
- vim
- zram-generator
- zstd
bootstrap_rhel10: bootstrap_debian_extra_common:
- bind-utils - apparmor-utils
- efibootmgr - bat
- "{{ 'firewalld' if firewalld_enabled | bool else '' }}" - chrony
- glibc-langpack-de - curl
- glibc-langpack-en - entr
- grub2 - "{{ 'firewalld' if system_cfg.features.firewall.backend == 'firewalld' and system_cfg.features.firewall.enabled | bool else '' }}"
- grub2-efi - "{{ 'ufw' if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else '' }}"
- kernel - fish
- fzf
- htop
- jq
- libpam-pwquality
- lrzsz - lrzsz
- lvm2
- mtr - mtr
- ncurses-term - ncdu
- nfs-utils - net-tools
- policycoreutils-python-utils - network-manager
- python - python-is-python3
- shim - ripgrep
- tmux - rsync
- "{{ 'cryptsetup' if luks_enabled else '' }}" - screen
- "{{ 'tpm2-tools' if luks_enabled else '' }}" - sudo
- "{{ 'qemu-guest-agent' if hypervisor | lower in ['libvirt', 'proxmox'] else '' }}" - syslog-ng
- "{{ 'open-vm-tools' if hypervisor | lower == 'vmware' else '' }}" - tcpd
- vim - "{{ 'tpm2-tools' if system_cfg.luks.enabled else '' }}"
- zram-generator - "{{ 'qemu-guest-agent' if hypervisor_type in ['libvirt', 'proxmox'] else '' }}"
- zstd - "{{ 'open-vm-tools' if hypervisor_type == 'vmware' else '' }}"
bootstrap_rocky:
- bind-utils
- dbus-daemon
- dhcp-client
- efibootmgr
- "{{ 'firewalld' if firewalld_enabled | bool else '' }}"
- glibc-langpack-de
- glibc-langpack-en
- grub2
- grub2-efi
- lrzsz
- lvm2
- mtr
- nc
- nfs-utils
- nfsv4-client-utils
- ppp
- shim
- telnet
- tmux
- "{{ 'cryptsetup' if luks_enabled else '' }}"
- "{{ 'tpm2-tools' if luks_enabled else '' }}"
- "{{ 'qemu-guest-agent' if hypervisor | lower in ['libvirt', 'proxmox'] else '' }}"
- "{{ 'open-vm-tools' if hypervisor | lower == 'vmware' else '' }}"
- util-linux-core
- vim - vim
- wget - wget
- zram-generator
- zstd - zstd
bootstrap_debian_extra_versioned:
- linux-image-amd64
- "{{ 'duf' if (os_version | string) not in ['10', '11'] else '' }}"
- "{{ 'fastfetch' if (os_version | string) in ['12', '13', 'unstable'] else '' }}"
- "{{ 'neofetch' if (os_version | string) == '12' else '' }}"
- "{{ 'software-properties-common' if (os_version | string) not in ['13', 'unstable'] else '' }}"
- "{{ 'systemd-zram-generator' if (os_version | string) not in ['10', '11'] else '' }}"
- "{{ 'tldr' if (os_version | string) not in ['13', 'unstable'] else '' }}"
bootstrap_debian:
base: "{{ bootstrap_debian_base_common }}"
extra: "{{ bootstrap_debian_extra_common + bootstrap_debian_extra_versioned }}"
bootstrap_debian10: "{{ bootstrap_debian }}"
bootstrap_debian11: "{{ bootstrap_debian }}"
bootstrap_debian12: "{{ bootstrap_debian }}"
bootstrap_debian13: "{{ bootstrap_debian }}"
bootstrap_debianunstable: "{{ bootstrap_debian }}"
bootstrap_ubuntu: bootstrap_ubuntu:
base: base:
- btrfs-progs
- cron
- gnupg
- grub-efi
- grub-efi-amd64-signed
- grub2-common
- "{{ 'cryptsetup' if luks_enabled else '' }}"
- "{{ 'cryptsetup-initramfs' if luks_enabled else '' }}"
- linux-image-generic - linux-image-generic
- locales extra: >-
- lvm2 {{
- xfsprogs bootstrap_debian_base_common
+ bootstrap_debian_extra_common
extra: + ['bash-completion', 'dnsutils', 'duf', 'eza', 'fdupes', 'fio', 'ncurses-term', 'software-properties-common', 'systemd-zram-generator', 'tldr', 'traceroute', 'util-linux-extra', 'yq', 'zoxide']
- apparmor-utils }}
- bash-completion
- bat
- chrony
- curl
- dnsutils
- duf
- entr
- eza
- fdupes
- fio
- "{{ 'firewalld' if firewalld_enabled | bool else '' }}"
- fish
- htop
- jq
- libpam-pwquality
- logrotate
- lrzsz
- mtr
- ncdu
- ncurses-term
- net-tools
- network-manager
- "{{ 'openssh-server' if ssh_enabled | bool else '' }}"
- python-is-python3
- python3
- ripgrep
- rsync
- screen
- software-properties-common
- sudo
- syslog-ng
- systemd-zram-generator
- tcpd
- tldr
- tmux
- "{{ 'tpm2-tools' if luks_enabled else '' }}"
- "{{ 'qemu-guest-agent' if hypervisor | lower in ['libvirt', 'proxmox'] else '' }}"
- "{{ 'open-vm-tools' if hypervisor | lower == 'vmware' else '' }}"
- traceroute
- util-linux-extra
- vim
- wget
- yq
- zoxide
- zstd
bootstrap_ubuntu_lts: bootstrap_ubuntu_lts:
base: base:
- btrfs-progs
- cron
- gnupg
- grub-efi
- grub-efi-amd64-signed
- grub2-common
- "{{ 'cryptsetup' if luks_enabled else '' }}"
- "{{ 'cryptsetup-initramfs' if luks_enabled else '' }}"
- linux-image-generic - linux-image-generic
- locales extra: >-
- lvm2 {{
- xfsprogs bootstrap_debian_base_common
+ bootstrap_debian_extra_common
+ ['bash-completion', 'dnsutils', 'duf', 'eza', 'fdupes', 'fio', 'ncurses-term', 'software-properties-common', 'systemd-zram-generator', 'tldr', 'traceroute', 'util-linux-extra', 'yq', 'zoxide']
}}
extra: bootstrap_archlinux:
- apparmor-utils - base
- bash-completion - btrfs-progs
- bat - cronie
- chrony - dhcpcd
- curl - efibootmgr
- dnsutils - fastfetch
- duf - "{{ 'firewalld' if system_cfg.features.firewall.backend == 'firewalld' and system_cfg.features.firewall.enabled | bool else '' }}"
- entr - "{{ 'ufw' if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else '' }}"
- eza - "{{ 'iptables' if system_cfg.features.firewall.toolkit == 'iptables' else '' }}"
- fdupes - "{{ 'iptables-nft' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
- fio
- "{{ 'firewalld' if firewalld_enabled | bool else '' }}"
- fish - fish
- fzf
- grub
- htop - htop
- jq - libpwquality
- libpam-pwquality - linux
- logrotate - logrotate
- lrzsz - lrzsz
- mtr - lsof
- lvm2
- ncdu - ncdu
- ncurses-term - networkmanager
- net-tools - nfs-utils
- network-manager - "{{ 'openssh' if system_cfg.features.ssh.enabled | bool else '' }}"
- "{{ 'openssh-server' if ssh_enabled | bool else '' }}" - ppp
- python-is-python3 - prometheus-node-exporter
- python3 - python-psycopg2
- ripgrep - reflector
- rsync - rsync
- screen
- software-properties-common
- sudo - sudo
- syslog-ng
- systemd-zram-generator
- tcpd
- tldr - tldr
- tmux - tmux
- "{{ 'tpm2-tools' if luks_enabled else '' }}" - "{{ 'cryptsetup' if system_cfg.luks.enabled else '' }}"
- "{{ 'qemu-guest-agent' if hypervisor | lower in ['libvirt', 'proxmox'] else '' }}" - "{{ 'tpm2-tools' if system_cfg.luks.enabled else '' }}"
- "{{ 'open-vm-tools' if hypervisor | lower == 'vmware' else '' }}" - "{{ 'qemu-guest-agent' if hypervisor_type in ['libvirt', 'proxmox'] else '' }}"
- traceroute - "{{ 'open-vm-tools' if hypervisor_type == 'vmware' else '' }}"
- util-linux-extra
- vim - vim
- wget - wireguard-tools
- yq - zram-generator
- zoxide
- zstd bootstrap_alpine:
- alpine-base
- vim
- "{{ 'openssh' if system_cfg.features.ssh.enabled | bool else '' }}"
- "{{ 'qemu-guest-agent' if hypervisor_type in ['libvirt', 'proxmox'] else '' }}"
- "{{ 'open-vm-tools' if hypervisor_type == 'vmware' else '' }}"
- "{{ 'firewalld' if system_cfg.features.firewall.backend == 'firewalld' and system_cfg.features.firewall.enabled | bool else '' }}"
- "{{ 'ufw' if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else '' }}"
- "{{ 'iptables' if system_cfg.features.firewall.toolkit == 'iptables' else '' }}"
- "{{ 'nftables' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
- "{{ 'cryptsetup' if system_cfg.luks.enabled else '' }}"
- "{{ 'tpm2-tools' if system_cfg.luks.enabled else '' }}"
bootstrap_opensuse:
- vim
- "{{ 'openssh' if system_cfg.features.ssh.enabled | bool else '' }}"
- "{{ 'qemu-guest-agent' if hypervisor_type in ['libvirt', 'proxmox'] else '' }}"
- "{{ 'open-vm-tools' if hypervisor_type == 'vmware' else '' }}"
- "{{ 'firewalld' if system_cfg.features.firewall.backend == 'firewalld' and system_cfg.features.firewall.enabled | bool else '' }}"
- "{{ 'ufw' if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else '' }}"
- "{{ 'iptables' if system_cfg.features.firewall.toolkit == 'iptables' else '' }}"
- "{{ 'nftables' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
- "{{ 'cryptsetup' if system_cfg.luks.enabled else '' }}"
- "{{ 'tpm2-tools' if system_cfg.luks.enabled else '' }}"
bootstrap_void:
- vim
- "{{ 'openssh' if system_cfg.features.ssh.enabled | bool else '' }}"
- "{{ 'qemu-guest-agent' if hypervisor_type in ['libvirt', 'proxmox'] else '' }}"
- "{{ 'open-vm-tools' if hypervisor_type == 'vmware' else '' }}"
- "{{ 'firewalld' if system_cfg.features.firewall.backend == 'firewalld' and system_cfg.features.firewall.enabled | bool else '' }}"
- "{{ 'ufw' if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else '' }}"
- "{{ 'iptables' if system_cfg.features.firewall.toolkit == 'iptables' else '' }}"
- "{{ 'nftables' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
- "{{ 'cryptsetup' if system_cfg.luks.enabled else '' }}"
- "{{ 'tpm2-tools' if system_cfg.luks.enabled else '' }}"

View File

@@ -10,12 +10,12 @@ cis_permission_targets: >-
{ "path": "/mnt/etc/cron.d", "mode": "0700" }, { "path": "/mnt/etc/cron.d", "mode": "0700" },
{ "path": "/mnt/etc/crontab", "mode": "0600" }, { "path": "/mnt/etc/crontab", "mode": "0600" },
{ "path": "/mnt/etc/logrotate.conf", "mode": "0644" }, { "path": "/mnt/etc/logrotate.conf", "mode": "0644" },
{ "path": "/mnt/usr/sbin/pppd", "mode": "0754" } if os not in ["rhel8", "rhel9", "rhel10"] else None, { "path": "/mnt/usr/sbin/pppd", "mode": "0754" } if os != "rhel" else None,
{ {
"path": "/mnt/usr/bin/" "path": "/mnt/usr/bin/"
+ ("fusermount3" if os in ["archlinux", "debian12", "fedora", "rhel9", "rhel10", "rocky"] else "fusermount"), + ("fusermount3" if os in ["archlinux", "fedora", "rocky"] or os == "rhel" or (os == "debian" and (os_version | string) == "12") else "fusermount"),
"mode": "755" "mode": "755"
}, },
{ "path": "/mnt/usr/bin/" + ("write.ul" if os == "debian11" else "write"), "mode": "755" } { "path": "/mnt/usr/bin/" + ("write.ul" if os == "debian" and (os_version | string) == "11" else "write"), "mode": "755" }
] | reject("none") ] | reject("none")
}} }}

View File

@@ -1,12 +1,12 @@
--- ---
- name: Configure System Cryptography Policy - name: Configure System Cryptography Policy
when: os in ["almalinux", "rhel9", "rhel10", "rocky"] when: os == "rhel" or os in ["almalinux", "rocky"]
ansible.builtin.command: "{{ chroot_command }} /mnt /usr/bin/update-crypto-policies --set DEFAULT:NO-SHA1" ansible.builtin.command: "{{ chroot_command }} /usr/bin/update-crypto-policies --set DEFAULT:NO-SHA1"
register: cis_crypto_policy_result register: cis_crypto_policy_result
changed_when: "'Setting system-wide crypto-policies to' in cis_crypto_policy_result.stdout" changed_when: "'Setting system-wide crypto-policies to' in cis_crypto_policy_result.stdout"
- name: Mask Systemd Services - name: Mask Systemd Services
ansible.builtin.command: > ansible.builtin.command: >
{{ chroot_command }} /mnt systemctl mask nftables bluetooth rpcbind {{ chroot_command }} systemctl mask {{ 'nftables' if system_cfg.features.firewall.toolkit == 'iptables' else 'iptables' }} bluetooth rpcbind
register: cis_mask_services_result register: cis_mask_services_result
changed_when: cis_mask_services_result.rc == 0 changed_when: cis_mask_services_result.rc == 0

View File

@@ -18,7 +18,7 @@
install rds /bin/false install rds /bin/false
install tipc /bin/false install tipc /bin/false
- name: Remove legacy USB rules file - name: Remove old USB rules file
ansible.builtin.file: ansible.builtin.file:
path: /mnt/etc/udev/rules.d/10-cis_usb_devices.sh path: /mnt/etc/udev/rules.d/10-cis_usb_devices.sh
state: absent state: absent

View File

@@ -4,21 +4,21 @@
path: "{{ item.path }}" path: "{{ item.path }}"
line: "{{ item.content }}" line: "{{ item.content }}"
loop: loop:
- {path: /mnt/etc/security/limits.conf, content: "* hard core 0"} - { path: /mnt/etc/security/limits.conf, content: "* hard core 0" }
- {path: /mnt/etc/security/pwquality.conf, content: minlen = 14} - { path: /mnt/etc/security/pwquality.conf, content: minlen = 14 }
- {path: /mnt/etc/security/pwquality.conf, content: dcredit = -1} - { path: /mnt/etc/security/pwquality.conf, content: dcredit = -1 }
- {path: /mnt/etc/security/pwquality.conf, content: ucredit = -1} - { path: /mnt/etc/security/pwquality.conf, content: ucredit = -1 }
- {path: /mnt/etc/security/pwquality.conf, content: ocredit = -1} - { path: /mnt/etc/security/pwquality.conf, content: ocredit = -1 }
- {path: /mnt/etc/security/pwquality.conf, content: lcredit = -1} - { path: /mnt/etc/security/pwquality.conf, content: lcredit = -1 }
- {path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}', content: umask 077} - { path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}', content: umask 077 }
- {path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}', content: export TMOUT=3000} - { path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}', content: export TMOUT=3000 }
- {path: '/mnt/{{ "usr/lib/systemd/journald.conf" if os == "fedora" else "etc/systemd/journald.conf" }}', content: Storage=persistent} - { path: '/mnt/{{ "usr/lib/systemd/journald.conf" if os == "fedora" else "etc/systemd/journald.conf" }}', content: Storage=persistent }
- {path: /mnt/etc/sudoers, content: Defaults logfile="/var/log/sudo.log"} - { path: /mnt/etc/sudoers, content: Defaults logfile="/var/log/sudo.log" }
- {path: /mnt/etc/pam.d/su, content: auth required pam_wheel.so} - { path: /mnt/etc/pam.d/su, content: auth required pam_wheel.so }
- path: >- - path: >-
/mnt/etc/{{ /mnt/etc/{{
"pam.d/common-auth" "pam.d/common-auth"
if os in ["debian11", "debian12", "ubuntu", "ubuntu-lts"] if is_debian | bool
else "authselect/system-auth" else "authselect/system-auth"
if os == "fedora" if os == "fedora"
else "pam.d/system-auth" else "pam.d/system-auth"
@@ -28,7 +28,7 @@
- path: >- - path: >-
/mnt/etc/{{ /mnt/etc/{{
"pam.d/common-account" "pam.d/common-account"
if os in ["debian11", "debian12", "ubuntu", "ubuntu-lts"] if is_debian | bool
else "authselect/system-auth" else "authselect/system-auth"
if os == "fedora" if os == "fedora"
else "pam.d/system-auth" else "pam.d/system-auth"
@@ -37,10 +37,10 @@
- path: >- - path: >-
/mnt/etc/pam.d/{{ /mnt/etc/pam.d/{{
"common-password" "common-password"
if os in ["debian11", "debian12", "ubuntu", "ubuntu-lts"] if is_debian | bool
else "passwd" else "passwd"
}} }}
content: >- content: >-
password [success=1 default=ignore] pam_unix.so obscure sha512 remember=5 password [success=1 default=ignore] pam_unix.so obscure sha512 remember=5
- {path: /mnt/etc/hosts.deny, content: "ALL: ALL"} - { path: /mnt/etc/hosts.deny, content: "ALL: ALL" }
- {path: /mnt/etc/hosts.allow, content: "sshd: ALL"} - { path: /mnt/etc/hosts.allow, content: "sshd: ALL" }

View File

@@ -5,30 +5,30 @@
regexp: ^\s*#?{{ item.option }}\s+.*$ regexp: ^\s*#?{{ item.option }}\s+.*$
line: "{{ item.option }} {{ item.value }}" line: "{{ item.option }} {{ item.value }}"
loop: loop:
- {option: LogLevel, value: VERBOSE} - { option: LogLevel, value: VERBOSE }
- {option: LoginGraceTime, value: "60"} - { option: LoginGraceTime, value: "60" }
- {option: PermitRootLogin, value: "no"} - { option: PermitRootLogin, value: "no" }
- {option: StrictModes, value: "yes"} - { option: StrictModes, value: "yes" }
- {option: MaxAuthTries, value: "4"} - { option: MaxAuthTries, value: "4" }
- {option: MaxSessions, value: "10"} - { option: MaxSessions, value: "10" }
- {option: MaxStartups, value: "10:30:60"} - { option: MaxStartups, value: "10:30:60" }
- {option: PubkeyAuthentication, value: "yes"} - { option: PubkeyAuthentication, value: "yes" }
- {option: HostbasedAuthentication, value: "no"} - { option: HostbasedAuthentication, value: "no" }
- {option: IgnoreRhosts, value: "yes"} - { option: IgnoreRhosts, value: "yes" }
- {option: PasswordAuthentication, value: "no"} - { option: PasswordAuthentication, value: "no" }
- {option: PermitEmptyPasswords, value: "no"} - { option: PermitEmptyPasswords, value: "no" }
- {option: KerberosAuthentication, value: "no"} - { option: KerberosAuthentication, value: "no" }
- {option: GSSAPIAuthentication, value: "no"} - { option: GSSAPIAuthentication, value: "no" }
- {option: AllowAgentForwarding, value: "no"} - { option: AllowAgentForwarding, value: "no" }
- {option: AllowTcpForwarding, value: "no"} - { option: AllowTcpForwarding, value: "no" }
- {option: ChallengeResponseAuthentication, value: "no"} - { option: ChallengeResponseAuthentication, value: "no" }
- {option: GatewayPorts, value: "no"} - { option: GatewayPorts, value: "no" }
- {option: X11Forwarding, value: "no"} - { option: X11Forwarding, value: "no" }
- {option: PermitUserEnvironment, value: "no"} - { option: PermitUserEnvironment, value: "no" }
- {option: ClientAliveInterval, value: "300"} - { option: ClientAliveInterval, value: "300" }
- {option: ClientAliveCountMax, value: "1"} - { option: ClientAliveCountMax, value: "1" }
- {option: PermitTunnel, value: "no"} - { option: PermitTunnel, value: "no" }
- {option: Banner, value: /etc/issue.net} - { option: Banner, value: /etc/issue.net }
- name: Append CIS specific configurations to sshd_config - name: Append CIS specific configurations to sshd_config
ansible.builtin.blockinfile: ansible.builtin.blockinfile:

View File

@@ -1,5 +1,9 @@
--- ---
cleanup_libvirt_image_dir: >- cleanup_libvirt_image_dir: >-
{{ vm_path if vm_path is defined and vm_path | length > 0 else '/var/lib/libvirt/images' }} {{
system_cfg.path
if system_cfg is defined and (system_cfg.path | string | length) > 0
else '/var/lib/libvirt/images'
}}
cleanup_libvirt_cloudinit_path: >- cleanup_libvirt_cloudinit_path: >-
{{ [cleanup_libvirt_image_dir, hostname ~ '-cloudinit.iso'] | ansible.builtin.path_join }} {{ [cleanup_libvirt_image_dir, hostname ~ '-cloudinit.iso'] | ansible.builtin.path_join }}

View File

@@ -1,6 +1,6 @@
--- ---
- name: Remove Archiso and cloud-init disks - name: Remove Archiso and cloud-init disks
when: hypervisor == "libvirt" when: hypervisor_type == "libvirt"
delegate_to: localhost delegate_to: localhost
become: false become: false
block: block:
@@ -102,3 +102,5 @@
delegate_to: "{{ inventory_hostname }}" delegate_to: "{{ inventory_hostname }}"
ansible.builtin.wait_for_connection: ansible.builtin.wait_for_connection:
timeout: 300 timeout: 300
failed_when: false
changed_when: false

View File

@@ -1,8 +1,8 @@
--- ---
- name: Cleanup physical install - name: Cleanup physical install
when: install_type == "physical" when: system_cfg.type == "physical"
ansible.builtin.include_tasks: physical.yml ansible.builtin.include_tasks: physical.yml
- name: Cleanup virtual install - name: Cleanup virtual install
when: install_type == "virtual" when: system_cfg.type == "virtual"
ansible.builtin.include_tasks: virtual.yml ansible.builtin.include_tasks: virtual.yml

View File

@@ -1,16 +1,16 @@
--- ---
- name: Setup Cleanup - name: Setup Cleanup
when: hypervisor == "proxmox" when: hypervisor_type == "proxmox"
delegate_to: localhost delegate_to: localhost
become: false become: false
block: block:
- name: Cleanup Setup Disks - name: Cleanup Setup Disks
community.proxmox.proxmox_disk: community.proxmox.proxmox_disk:
api_host: "{{ hypervisor_url }}" api_host: "{{ hypervisor_cfg.url }}"
api_user: "{{ hypervisor_username }}" api_user: "{{ hypervisor_cfg.username }}"
api_password: "{{ hypervisor_password }}" api_password: "{{ hypervisor_cfg.password }}"
name: "{{ hostname }}" name: "{{ hostname }}"
vmid: "{{ vm_id }}" vmid: "{{ system_cfg.id }}"
disk: "{{ item }}" disk: "{{ item }}"
state: absent state: absent
loop: loop:
@@ -19,9 +19,9 @@
- name: Start the VM - name: Start the VM
community.proxmox.proxmox_kvm: community.proxmox.proxmox_kvm:
api_host: "{{ hypervisor_url }}" api_host: "{{ hypervisor_cfg.url }}"
api_user: "{{ hypervisor_username }}" api_user: "{{ hypervisor_cfg.username }}"
api_password: "{{ hypervisor_password }}" api_password: "{{ hypervisor_cfg.password }}"
node: "{{ hypervisor_node }}" node: "{{ hypervisor_cfg.host }}"
vmid: "{{ vm_id }}" vmid: "{{ system_cfg.id }}"
state: restarted state: restarted

View File

@@ -13,3 +13,196 @@
- name: Cleanup libvirt resources - name: Cleanup libvirt resources
ansible.builtin.include_tasks: libvirt.yml ansible.builtin.include_tasks: libvirt.yml
- name: Cleanup Xen resources
ansible.builtin.include_tasks: xen.yml
- name: Determine post-reboot connectivity
ansible.builtin.set_fact:
cleanup_post_reboot_can_connect: >-
{{
(
post_reboot_can_connect
if post_reboot_can_connect is defined
else (
(ansible_connection | default('ssh')) != 'ssh'
or ((system_cfg.ip | default('') | string | length) > 0)
or (
system_cfg.type == 'physical'
and (ansible_host | default('') | string | length) > 0
)
)
) | bool
}}
changed_when: false
- name: Check VM accessibility after reboot
when:
- system_cfg.type == "virtual"
- cleanup_post_reboot_can_connect | bool
block:
- name: Attempt to connect to VM
delegate_to: "{{ inventory_hostname }}"
ansible.builtin.wait_for_connection:
timeout: 300
register: cleanup_vm_connection_check
failed_when: false
changed_when: false
- name: VM failed to boot - initiate cleanup
when:
- cleanup_vm_connection_check is defined
- cleanup_vm_connection_check.failed | bool
block:
- name: VM boot failure detected - removing VM
ansible.builtin.debug:
msg: |
VM {{ hostname }} failed to boot after provisioning.
This VM was created in the current playbook run and will be removed
to prevent orphaned resources.
- name: Remove VM for libvirt
when:
- hypervisor_type == "libvirt"
- virtualization_vm_created_in_run | default(false) | bool
delegate_to: localhost
become: false
community.libvirt.virt:
name: "{{ hostname }}"
state: destroyed
- name: Undefine VM for libvirt
when:
- hypervisor_type == "libvirt"
- virtualization_vm_created_in_run | default(false) | bool
delegate_to: localhost
become: false
community.libvirt.virt:
name: "{{ hostname }}"
command: undefine
- name: Remove VM disk for libvirt
when:
- hypervisor_type == "libvirt"
- virtualization_vm_created_in_run | default(false) | bool
delegate_to: localhost
become: false
ansible.builtin.file:
path: "{{ item.path }}"
state: absent
loop: "{{ virtualization_libvirt_disks | default([]) }}"
loop_control:
label: "{{ item.path }}"
- name: Remove cloud-init disk for libvirt
when:
- hypervisor_type == "libvirt"
- virtualization_vm_created_in_run | default(false) | bool
delegate_to: localhost
become: false
ansible.builtin.file:
path: "{{ virtualization_libvirt_cloudinit_path }}"
state: absent
- name: Remove VM for proxmox
when:
- hypervisor_type == "proxmox"
- virtualization_vm_created_in_run | default(false) | bool
delegate_to: localhost
become: false
community.proxmox.proxmox_kvm:
api_host: "{{ hypervisor_cfg.url }}"
api_user: "{{ hypervisor_cfg.username }}"
api_password: "{{ hypervisor_cfg.password }}"
node: "{{ hypervisor_cfg.host }}"
name: "{{ hostname }}"
vmid: "{{ system_cfg.id }}"
state: stopped
- name: Delete VM for proxmox
when:
- hypervisor_type == "proxmox"
- virtualization_vm_created_in_run | default(false) | bool
delegate_to: localhost
become: false
community.proxmox.proxmox_kvm:
api_host: "{{ hypervisor_cfg.url }}"
api_user: "{{ hypervisor_cfg.username }}"
api_password: "{{ hypervisor_cfg.password }}"
node: "{{ hypervisor_cfg.host }}"
name: "{{ hostname }}"
vmid: "{{ system_cfg.id }}"
state: absent
unprivileged: false
- name: Remove VM for VMware
when:
- hypervisor_type == "vmware"
- virtualization_vm_created_in_run | default(false) | bool
delegate_to: localhost
become: false
community.vmware.vmware_guest:
hostname: "{{ hypervisor_cfg.url }}"
username: "{{ hypervisor_cfg.username }}"
password: "{{ hypervisor_cfg.password }}"
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
name: "{{ hostname }}"
folder: "{{ system_cfg.path | default('/') }}"
state: poweredoff
- name: Delete VM for VMware
when:
- hypervisor_type == "vmware"
- virtualization_vm_created_in_run | default(false) | bool
delegate_to: localhost
become: false
community.vmware.vmware_guest:
hostname: "{{ hypervisor_cfg.url }}"
username: "{{ hypervisor_cfg.username }}"
password: "{{ hypervisor_cfg.password }}"
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
name: "{{ hostname }}"
folder: "{{ system_cfg.path | default('/') }}"
state: absent
- name: Destroy Xen VM if running
when:
- hypervisor_type == "xen"
- virtualization_vm_created_in_run | default(false) | bool
delegate_to: localhost
become: false
ansible.builtin.command:
argv:
- xl
- destroy
- "{{ hostname }}"
register: cleanup_xen_destroy
failed_when: false
changed_when: cleanup_xen_destroy.rc == 0
- name: Remove Xen VM disk
when:
- hypervisor_type == "xen"
- virtualization_vm_created_in_run | default(false) | bool
delegate_to: localhost
become: false
ansible.builtin.file:
path: "{{ item.path }}"
state: absent
loop: "{{ virtualization_xen_disks | default([]) }}"
loop_control:
label: "{{ item.path }}"
- name: Remove Xen VM config file
when:
- hypervisor_type == "xen"
- virtualization_vm_created_in_run | default(false) | bool
delegate_to: localhost
become: false
ansible.builtin.file:
path: "/tmp/xen-{{ hostname }}.cfg"
state: absent
- name: VM cleanup completed
ansible.builtin.debug:
msg: VM {{ hostname }} has been successfully removed due to boot failure.

View File

@@ -1,17 +1,17 @@
--- ---
- name: Clean vCenter VM - name: Clean vCenter VM
when: hypervisor == "vmware" when: hypervisor_type == "vmware"
delegate_to: localhost delegate_to: localhost
become: false become: false
block: block:
- name: Remove CD-ROM from VM in vCenter - name: Remove CD-ROM from VM in vCenter
when: hypervisor == "vmware" when: hypervisor_type == "vmware"
community.vmware.vmware_guest: community.vmware.vmware_guest:
hostname: "{{ hypervisor_url }}" hostname: "{{ hypervisor_cfg.url }}"
username: "{{ hypervisor_username }}" username: "{{ hypervisor_cfg.username }}"
password: "{{ hypervisor_password }}" password: "{{ hypervisor_cfg.password }}"
validate_certs: false validate_certs: "{{ hypervisor_cfg.certs | bool }}"
datacenter: "{{ hypervisor_datacenter }}" datacenter: "{{ hypervisor_cfg.datacenter }}"
name: "{{ hostname }}" name: "{{ hostname }}"
cdrom: cdrom:
- controller_number: 0 - controller_number: 0
@@ -29,12 +29,12 @@
failed_when: false failed_when: false
- name: Start VM in vCenter - name: Start VM in vCenter
when: hypervisor == "vmware" when: hypervisor_type == "vmware"
vmware.vmware.vm_powerstate: vmware.vmware.vm_powerstate:
hostname: "{{ hypervisor_url }}" hostname: "{{ hypervisor_cfg.url }}"
username: "{{ hypervisor_username }}" username: "{{ hypervisor_cfg.username }}"
password: "{{ hypervisor_password }}" password: "{{ hypervisor_cfg.password }}"
validate_certs: false validate_certs: "{{ hypervisor_cfg.certs | bool }}"
datacenter: "{{ hypervisor_datacenter }}" datacenter: "{{ hypervisor_cfg.datacenter }}"
name: "{{ hostname }}" name: "{{ hostname }}"
state: powered-on state: powered-on

View File

@@ -0,0 +1,58 @@
---
- name: Cleanup Xen installer media
when: hypervisor_type == "xen"
delegate_to: localhost
become: false
block:
- name: Ensure Xen disk definitions exist
when: virtualization_xen_disks is not defined
ansible.builtin.set_fact:
cleanup_xen_disks: "{{ cleanup_xen_disks | default([]) + [cleanup_xen_disk_cfg] }}"
vars:
device_letter_map: "abcdefghijklmnopqrstuvwxyz"
device_letter: "{{ device_letter_map[ansible_loop.index0] }}"
cleanup_xen_disk_cfg: >-
{{
{
'path': (
virtualization_xen_disk_path ~ '/' ~ hostname ~ '.qcow2'
if ansible_loop.index0 == 0
else virtualization_xen_disk_path ~ '/' ~ hostname ~ '-disk' ~ ansible_loop.index0 ~ '.qcow2'
),
'target': 'xvd' ~ device_letter,
'size': (item.size | float)
}
}}
loop: "{{ system_cfg.disks }}"
loop_control:
label: "{{ item | to_json }}"
extended: true
changed_when: false
- name: Render Xen VM configuration without installer media
vars:
xen_installer_media_enabled: false
virtualization_xen_disks: "{{ virtualization_xen_disks | default(cleanup_xen_disks | default([])) }}"
ansible.builtin.template:
src: xen.cfg.j2
dest: /tmp/xen-{{ hostname }}.cfg
mode: "0644"
- name: Destroy Xen VM if running
ansible.builtin.command:
argv:
- xl
- destroy
- "{{ hostname }}"
register: cleanup_xen_destroy
failed_when: false
changed_when: cleanup_xen_destroy.rc == 0
- name: Start Xen VM without installer media
ansible.builtin.command:
argv:
- xl
- create
- /tmp/xen-{{ hostname }}.cfg
register: cleanup_xen_start_result
changed_when: cleanup_xen_start_result.rc == 0

View File

@@ -0,0 +1,5 @@
---
configuration_motd_enabled: "{{ system_cfg.features.banner.motd | bool }}"
configuration_sudo_banner_enabled: "{{ system_cfg.features.banner.sudo | bool }}"
configuration_firewall_enabled: "{{ system_cfg.features.firewall.enabled | bool }}"
configuration_luks_enabled: "{{ system_cfg.luks.enabled | bool }}"

View File

@@ -0,0 +1,55 @@
---
- name: Configure MOTD
when: configuration_motd_enabled | bool
block:
- name: Create MOTD file
ansible.builtin.copy:
content: |
********************************************************************
* AUTHORIZED ACCESS ONLY. ALL ACTIVITIES ARE MONITORED AND LOGGED. *
********************************************************************
dest: /mnt/etc/motd
mode: "0644"
owner: root
group: root
- name: Remove other MOTD files
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- /mnt/etc/motd.d/99-motd
- /mnt/etc/motd.d/cockpit
- /mnt/etc/motd.d/insights-client
failed_when: false
- name: Configure sudo banner
when: configuration_sudo_banner_enabled | bool
block:
- name: Create sudoers banner directory
ansible.builtin.file:
path: /mnt/etc/sudoers.d
state: directory
mode: "0755"
owner: root
group: root
- name: Create sudo banner file
ansible.builtin.copy:
content: |
I am Groot, and I know what I'm doing.
dest: /mnt/etc/sudoers.d/banner
mode: "0644"
owner: root
group: root
- name: Enable sudo banner in sudoers
ansible.builtin.lineinfile:
path: /mnt/etc/sudoers
line: "Defaults lecture=@/etc/sudoers.d/banner"
state: present
create: true
mode: "0440"
owner: root
group: root
validate: "visudo -cf - %s"

View File

@@ -8,7 +8,7 @@
configuration_bootloader_id: >- configuration_bootloader_id: >-
{{ "ubuntu" if os | lower in ["ubuntu", "ubuntu-lts"] else os }} {{ "ubuntu" if os | lower in ["ubuntu", "ubuntu-lts"] else os }}
configuration_efi_vendor: >- configuration_efi_vendor: >-
{{ "redhat" if os | lower in ["rhel8", "rhel9", "rhel10"] else os | lower }} {{ "redhat" if os | lower == "rhel" else os | lower }}
configuration_efibootmgr_cmd: >- configuration_efibootmgr_cmd: >-
/usr/sbin/efibootmgr -c -L '{{ os }}' -d "{{ install_drive }}" -p 1 /usr/sbin/efibootmgr -c -L '{{ os }}' -d "{{ install_drive }}" -p 1
-l '\efi\EFI\{{ configuration_efi_vendor }}\shimx64.efi' -l '\efi\EFI\{{ configuration_efi_vendor }}\shimx64.efi'
@@ -18,19 +18,20 @@
--bootloader-id={{ configuration_bootloader_id }} --bootloader-id={{ configuration_bootloader_id }}
configuration_bootloader_cmd: >- configuration_bootloader_cmd: >-
{{ configuration_efibootmgr_cmd if configuration_use_efibootmgr else configuration_grub_cmd }} {{ configuration_efibootmgr_cmd if configuration_use_efibootmgr else configuration_grub_cmd }}
ansible.builtin.command: "{{ chroot_command }} /mnt {{ configuration_bootloader_cmd }}" ansible.builtin.command: "{{ chroot_command }} {{ configuration_bootloader_cmd }}"
register: configuration_bootloader_result register: configuration_bootloader_result
changed_when: configuration_bootloader_result.rc == 0 changed_when: configuration_bootloader_result.rc == 0
- name: Ensure lvm2 for non btrfs filesystems - name: Ensure lvm2 for non btrfs filesystems
when: os | lower == "archlinux" and filesystem != "btrfs" when: os | lower == "archlinux" and system_cfg.filesystem != "btrfs"
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: /mnt/etc/mkinitcpio.conf path: /mnt/etc/mkinitcpio.conf
regexp: "^(HOOKS=.*block)(?!.*lvm2)(.*)" regexp: "^(HOOKS=.*block)(?!.*lvm2)(.*)"
line: '\1 lvm2\2' line: "\\1 lvm2\\2"
backrefs: true backrefs: true
- name: Regenerate initramfs - name: Regenerate initramfs
when: os | lower not in ["alpine", "void"]
vars: vars:
configuration_initramfs_cmd: >- configuration_initramfs_cmd: >-
{{ {{
@@ -43,14 +44,14 @@
else '/usr/bin/dracut --regenerate-all --force' else '/usr/bin/dracut --regenerate-all --force'
) )
}} }}
ansible.builtin.command: "{{ chroot_command }} /mnt {{ configuration_initramfs_cmd }}" ansible.builtin.command: "{{ chroot_command }} {{ configuration_initramfs_cmd }}"
register: configuration_initramfs_result register: configuration_initramfs_result
changed_when: configuration_initramfs_result.rc == 0 changed_when: configuration_initramfs_result.rc == 0
- name: Generate grub config - name: Generate grub config
vars: vars:
configuration_efi_vendor: >- configuration_efi_vendor: >-
{{ "redhat" if os | lower in ["rhel8", "rhel9", "rhel10"] else os | lower }} {{ "redhat" if os | lower == "rhel" else os | lower }}
configuration_grub_cfg_cmd: >- configuration_grub_cfg_cmd: >-
{{ {{
'/usr/sbin/grub2-mkconfig -o ' '/usr/sbin/grub2-mkconfig -o '
@@ -59,6 +60,6 @@
if is_rhel | bool if is_rhel | bool
else '/usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg' else '/usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg'
}} }}
ansible.builtin.command: "{{ chroot_command }} /mnt {{ configuration_grub_cfg_cmd }}" ansible.builtin.command: "{{ chroot_command }} {{ configuration_grub_cfg_cmd }}"
register: configuration_grub_result register: configuration_grub_result
changed_when: configuration_grub_result.rc == 0 changed_when: configuration_grub_result.rc == 0

View File

@@ -2,22 +2,17 @@
- name: Configure disk encryption - name: Configure disk encryption
when: partitioning_luks_enabled | bool when: partitioning_luks_enabled | bool
vars: vars:
configuration_luks_passphrase_effective: >- configuration_luks_passphrase: >-
{{ partitioning_luks_passphrase | string }} {{ partitioning_luks_passphrase | string }}
block: block:
- name: Set LUKS configuration facts - name: Set LUKS configuration facts
vars: vars:
configuration_luks_mapper_name_value: >- luks_tpm2_pcrs: >-
{{ partitioning_luks_mapper_name }}
configuration_luks_device_value: "{{ partitioning_luks_device }}"
configuration_luks_tpm2_pcrs_raw: >-
{{ partitioning_luks_tpm2_pcrs }}
configuration_luks_tpm2_pcrs_effective_value: >-
{{ {{
( (
configuration_luks_tpm2_pcrs_raw partitioning_luks_tpm2_pcrs
if configuration_luks_tpm2_pcrs_raw is string if partitioning_luks_tpm2_pcrs is string
else (configuration_luks_tpm2_pcrs_raw | map('string') | join('+')) else (partitioning_luks_tpm2_pcrs | map('string') | join('+'))
) )
| string | string
| replace(',', '+') | replace(',', '+')
@@ -25,11 +20,10 @@
| regex_replace('^\\+|\\+$', '') | regex_replace('^\\+|\\+$', '')
}} }}
ansible.builtin.set_fact: ansible.builtin.set_fact:
configuration_luks_mapper_name: "{{ configuration_luks_mapper_name_value }}" configuration_luks_mapper_name: "{{ partitioning_luks_mapper_name }}"
configuration_luks_uuid: "{{ partitioning_luks_uuid | default('') }}" configuration_luks_uuid: "{{ partitioning_luks_uuid | default('') }}"
configuration_luks_device: "{{ configuration_luks_device_value }}" configuration_luks_device: "{{ partitioning_luks_device }}"
configuration_luks_options: >- configuration_luks_options: "{{ partitioning_luks_options }}"
{{ partitioning_luks_options }}
configuration_luks_auto_method: >- configuration_luks_auto_method: >-
{{ {{
(partitioning_luks_auto_decrypt | bool) (partitioning_luks_auto_decrypt | bool)
@@ -38,12 +32,9 @@
'manual' 'manual'
) )
}} }}
configuration_luks_tpm2_device: >- configuration_luks_tpm2_device: "{{ partitioning_luks_tpm2_device }}"
{{ partitioning_luks_tpm2_device }} configuration_luks_tpm2_pcrs: "{{ luks_tpm2_pcrs }}"
configuration_luks_tpm2_pcrs: "{{ configuration_luks_tpm2_pcrs_raw }}" configuration_luks_keyfile_path: "/etc/cryptsetup-keys.d/{{ partitioning_luks_mapper_name }}.key"
configuration_luks_tpm2_pcrs_effective: "{{ configuration_luks_tpm2_pcrs_effective_value }}"
configuration_luks_keyfile_path: >-
/etc/cryptsetup-keys.d/{{ configuration_luks_mapper_name_value }}.key
changed_when: false changed_when: false
- name: Validate LUKS UUID is available - name: Validate LUKS UUID is available
@@ -56,8 +47,8 @@
when: configuration_luks_auto_method in ['tpm2', 'keyfile'] when: configuration_luks_auto_method in ['tpm2', 'keyfile']
ansible.builtin.assert: ansible.builtin.assert:
that: that:
- configuration_luks_passphrase_effective | length > 0 - configuration_luks_passphrase | length > 0
fail_msg: luks_passphrase (or partitioning_luks_passphrase) must be set for LUKS auto-decrypt. fail_msg: system.luks.passphrase must be set for LUKS auto-decrypt.
no_log: true no_log: true
- name: Enroll TPM2 for LUKS - name: Enroll TPM2 for LUKS
@@ -70,54 +61,52 @@
- name: Build LUKS parameters - name: Build LUKS parameters
vars: vars:
configuration_luks_keyfile_in_use_value: "{{ configuration_luks_auto_method == 'keyfile' }}" luks_keyfile_in_use: "{{ configuration_luks_auto_method == 'keyfile' }}"
configuration_luks_option_list_value: >- luks_option_list: >-
{{ {{
(configuration_luks_options | trim).split(',') (configuration_luks_options | trim).split(',')
if configuration_luks_options | trim | length > 0 if configuration_luks_options | trim | length > 0
else [] else []
}} }}
configuration_luks_tpm2_option_list_value: >- luks_tpm2_option_list: >-
{{ {{
(configuration_luks_auto_method == 'tpm2') (configuration_luks_auto_method == 'tpm2')
| ternary( | ternary(
['tpm2-device=' + configuration_luks_tpm2_device] ['tpm2-device=' + configuration_luks_tpm2_device]
+ (['tpm2-pcrs=' + configuration_luks_tpm2_pcrs_effective] + (['tpm2-pcrs=' + configuration_luks_tpm2_pcrs]
if configuration_luks_tpm2_pcrs_effective | length > 0 else []), if configuration_luks_tpm2_pcrs | length > 0 else []),
[] []
) )
}} }}
configuration_luks_crypttab_keyfile_value: >- luks_crypttab_keyfile: "{{ configuration_luks_keyfile_path if luks_keyfile_in_use else 'none' }}"
{{ configuration_luks_keyfile_path if configuration_luks_keyfile_in_use_value else 'none' }} luks_crypttab_options: >-
configuration_luks_crypttab_options_value: >-
{{ {{
(['luks'] + configuration_luks_option_list_value + configuration_luks_tpm2_option_list_value) (['luks'] + luks_option_list + luks_tpm2_option_list)
| join(',') | join(',')
}} }}
configuration_luks_rd_options_value: >- luks_rd_options: "{{ (luks_option_list + luks_tpm2_option_list) | join(',') }}"
{{ (configuration_luks_option_list_value + configuration_luks_tpm2_option_list_value) | join(',') }} luks_kernel_args: >-
configuration_luks_kernel_args_value: >-
{{ {{
( (
['rd.luks.name=' + configuration_luks_uuid + '=' + configuration_luks_mapper_name] ['rd.luks.name=' + configuration_luks_uuid + '=' + configuration_luks_mapper_name]
+ ( + (
['rd.luks.options=' + configuration_luks_uuid + '=' + configuration_luks_rd_options_value] ['rd.luks.options=' + configuration_luks_uuid + '=' + luks_rd_options]
if configuration_luks_rd_options_value | length > 0 else [] if luks_rd_options | length > 0 else []
) )
+ ( + (
['rd.luks.key=' + configuration_luks_uuid + '=' + configuration_luks_keyfile_path] ['rd.luks.key=' + configuration_luks_uuid + '=' + configuration_luks_keyfile_path]
if configuration_luks_keyfile_in_use_value else [] if luks_keyfile_in_use else []
) )
) | join(' ') ) | join(' ')
}} }}
ansible.builtin.set_fact: ansible.builtin.set_fact:
configuration_luks_keyfile_in_use: "{{ configuration_luks_keyfile_in_use_value }}" configuration_luks_keyfile_in_use: "{{ luks_keyfile_in_use }}"
configuration_luks_option_list: "{{ configuration_luks_option_list_value }}" configuration_luks_option_list: "{{ luks_option_list }}"
configuration_luks_tpm2_option_list: "{{ configuration_luks_tpm2_option_list_value }}" configuration_luks_tpm2_option_list: "{{ luks_tpm2_option_list }}"
configuration_luks_crypttab_keyfile: "{{ configuration_luks_crypttab_keyfile_value }}" configuration_luks_crypttab_keyfile: "{{ luks_crypttab_keyfile }}"
configuration_luks_crypttab_options: "{{ configuration_luks_crypttab_options_value }}" configuration_luks_crypttab_options: "{{ luks_crypttab_options }}"
configuration_luks_rd_options: "{{ configuration_luks_rd_options_value }}" configuration_luks_rd_options: "{{ luks_rd_options }}"
configuration_luks_kernel_args: "{{ configuration_luks_kernel_args_value }}" configuration_luks_kernel_args: "{{ luks_kernel_args }}"
- name: Remove LUKS keyfile if TPM2 auto-decrypt is active - name: Remove LUKS keyfile if TPM2 auto-decrypt is active
when: configuration_luks_auto_method == 'tpm2' when: configuration_luks_auto_method == 'tpm2'
@@ -141,8 +130,8 @@
- configuration_luks_keyfile_in_use - configuration_luks_keyfile_in_use
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: /mnt/etc/cryptsetup-initramfs/conf-hook path: /mnt/etc/cryptsetup-initramfs/conf-hook
regexp: '^KEYFILE_PATTERN=' regexp: "^KEYFILE_PATTERN="
line: 'KEYFILE_PATTERN=/etc/cryptsetup-keys.d/*.key' line: "KEYFILE_PATTERN=/etc/cryptsetup-keys.d/*.key"
create: true create: true
mode: "0644" mode: "0644"
@@ -150,7 +139,7 @@
when: os | lower == 'archlinux' when: os | lower == 'archlinux'
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: /mnt/etc/mkinitcpio.conf path: /mnt/etc/mkinitcpio.conf
regexp: '^HOOKS=' regexp: "^HOOKS="
line: >- line: >-
HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole
block sd-encrypt lvm2 filesystems fsck) block sd-encrypt lvm2 filesystems fsck)
@@ -164,7 +153,7 @@
- name: Build mkinitcpio FILES list - name: Build mkinitcpio FILES list
when: os | lower == 'archlinux' when: os | lower == 'archlinux'
vars: vars:
configuration_mkinitcpio_files_list_value: >- mkinitcpio_files_list: >-
{{ {{
( (
configuration_mkinitcpio_slurp.content | b64decode configuration_mkinitcpio_slurp.content | b64decode
@@ -174,13 +163,13 @@
| default('') | default('')
).split() ).split()
}} }}
configuration_mkinitcpio_files_list_new_value: >- mkinitcpio_files_list_new: >-
{{ {{
( (
(configuration_mkinitcpio_files_list_value + [configuration_luks_keyfile_path]) (mkinitcpio_files_list + [configuration_luks_keyfile_path])
if configuration_luks_keyfile_in_use if configuration_luks_keyfile_in_use
else ( else (
configuration_mkinitcpio_files_list_value mkinitcpio_files_list
| reject('equalto', configuration_luks_keyfile_path) | reject('equalto', configuration_luks_keyfile_path)
| list | list
) )
@@ -188,13 +177,13 @@
| unique | unique
}} }}
ansible.builtin.set_fact: ansible.builtin.set_fact:
configuration_mkinitcpio_files_list_new: "{{ configuration_mkinitcpio_files_list_new_value }}" configuration_mkinitcpio_files_list_new: "{{ mkinitcpio_files_list_new }}"
- name: Configure mkinitcpio FILES list - name: Configure mkinitcpio FILES list
when: os | lower == 'archlinux' when: os | lower == 'archlinux'
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: /mnt/etc/mkinitcpio.conf path: /mnt/etc/mkinitcpio.conf
regexp: '^FILES=' regexp: "^FILES="
line: >- line: >-
FILES=({{ FILES=({{
configuration_mkinitcpio_files_list_new | join(' ') configuration_mkinitcpio_files_list_new | join(' ')
@@ -227,27 +216,27 @@
- name: Build kernel cmdline with LUKS args - name: Build kernel cmdline with LUKS args
when: is_rhel | bool when: is_rhel | bool
vars: vars:
configuration_kernel_cmdline_current_value: >- kernel_cmdline_current: >-
{{ configuration_kernel_cmdline_slurp.content | b64decode | trim }} {{ configuration_kernel_cmdline_slurp.content | b64decode | trim }}
configuration_kernel_cmdline_list_value: >- kernel_cmdline_list: >-
{{ {{
configuration_kernel_cmdline_current_value.split() kernel_cmdline_current.split()
if configuration_kernel_cmdline_current_value | length > 0 else [] if kernel_cmdline_current | length > 0 else []
}} }}
configuration_kernel_cmdline_filtered_value: >- kernel_cmdline_filtered: >-
{{ {{
configuration_kernel_cmdline_list_value kernel_cmdline_list
| reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=') | reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=')
| list | list
}} }}
configuration_kernel_cmdline_new_value: >- kernel_cmdline_new: >-
{{ {{
(configuration_kernel_cmdline_filtered_value + configuration_luks_kernel_args.split()) (kernel_cmdline_filtered + configuration_luks_kernel_args.split())
| unique | unique
| join(' ') | join(' ')
}} }}
ansible.builtin.set_fact: ansible.builtin.set_fact:
configuration_kernel_cmdline_new: "{{ configuration_kernel_cmdline_new_value }}" configuration_kernel_cmdline_new: "{{ kernel_cmdline_new }}"
changed_when: false changed_when: false
- name: Write kernel cmdline with LUKS args - name: Write kernel cmdline with LUKS args
@@ -271,7 +260,7 @@
- configuration_kernel_bls_entries.files | length > 0 - configuration_kernel_bls_entries.files | length > 0
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: "{{ item.path }}" path: "{{ item.path }}"
regexp: '^options ' regexp: "^options "
line: "options {{ configuration_kernel_cmdline_new }}" line: "options {{ configuration_kernel_cmdline_new }}"
loop: "{{ configuration_kernel_bls_entries.files }}" loop: "{{ configuration_kernel_bls_entries.files }}"
loop_control: loop_control:
@@ -286,70 +275,70 @@
- name: Build grub command lines with LUKS args - name: Build grub command lines with LUKS args
when: not is_rhel | bool when: not is_rhel | bool
vars: vars:
configuration_grub_content_value: "{{ configuration_grub_slurp.content | b64decode }}" grub_content: "{{ configuration_grub_slurp.content | b64decode }}"
configuration_grub_cmdline_linux_value: >- grub_cmdline_linux: >-
{{ {{
configuration_grub_content_value grub_content
| regex_findall('^GRUB_CMDLINE_LINUX=\"(.*)\"', multiline=True) | regex_findall('^GRUB_CMDLINE_LINUX=\"(.*)\"', multiline=True)
| default([]) | default([])
| first | first
| default('') | default('')
}} }}
configuration_grub_cmdline_default_value: >- grub_cmdline_default: >-
{{ {{
configuration_grub_content_value grub_content
| regex_findall('^GRUB_CMDLINE_LINUX_DEFAULT=\"(.*)\"', multiline=True) | regex_findall('^GRUB_CMDLINE_LINUX_DEFAULT=\"(.*)\"', multiline=True)
| default([]) | default([])
| first | first
| default('') | default('')
}} }}
configuration_grub_cmdline_linux_list_value: >- grub_cmdline_linux_list: >-
{{ {{
configuration_grub_cmdline_linux_value.split() grub_cmdline_linux.split()
if configuration_grub_cmdline_linux_value | length > 0 else [] if grub_cmdline_linux | length > 0 else []
}} }}
configuration_grub_cmdline_default_list_value: >- grub_cmdline_default_list: >-
{{ {{
configuration_grub_cmdline_default_value.split() grub_cmdline_default.split()
if configuration_grub_cmdline_default_value | length > 0 else [] if grub_cmdline_default | length > 0 else []
}} }}
configuration_luks_kernel_args_list_value: "{{ configuration_luks_kernel_args.split() }}" luks_kernel_args_list: "{{ configuration_luks_kernel_args.split() }}"
configuration_grub_cmdline_linux_new_value: >- grub_cmdline_linux_new: >-
{{ {{
( (
( (
configuration_grub_cmdline_linux_list_value grub_cmdline_linux_list
| reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=') | reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=')
| list | list
) )
+ configuration_luks_kernel_args_list_value + luks_kernel_args_list
) )
| unique | unique
| join(' ') | join(' ')
}} }}
configuration_grub_cmdline_default_new_value: >- grub_cmdline_default_new: >-
{{ {{
( (
( (
configuration_grub_cmdline_default_list_value grub_cmdline_default_list
| reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=') | reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=')
| list | list
) )
+ configuration_luks_kernel_args_list_value + luks_kernel_args_list
) )
| unique | unique
| join(' ') | join(' ')
}} }}
ansible.builtin.set_fact: ansible.builtin.set_fact:
configuration_grub_content: "{{ configuration_grub_content_value }}" configuration_grub_content: "{{ grub_content }}"
configuration_grub_cmdline_linux: "{{ configuration_grub_cmdline_linux_value }}" configuration_grub_cmdline_linux: "{{ grub_cmdline_linux }}"
configuration_grub_cmdline_default: "{{ configuration_grub_cmdline_default_value }}" configuration_grub_cmdline_default: "{{ grub_cmdline_default }}"
configuration_grub_cmdline_linux_new: "{{ configuration_grub_cmdline_linux_new_value }}" configuration_grub_cmdline_linux_new: "{{ grub_cmdline_linux_new }}"
configuration_grub_cmdline_default_new: "{{ configuration_grub_cmdline_default_new_value }}" configuration_grub_cmdline_default_new: "{{ grub_cmdline_default_new }}"
- name: Update GRUB_CMDLINE_LINUX_DEFAULT for LUKS - name: Update GRUB_CMDLINE_LINUX_DEFAULT for LUKS
when: not is_rhel | bool when: not is_rhel | bool
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: /mnt/etc/default/grub path: /mnt/etc/default/grub
regexp: '^GRUB_CMDLINE_LINUX_DEFAULT=' regexp: "^GRUB_CMDLINE_LINUX_DEFAULT="
line: 'GRUB_CMDLINE_LINUX_DEFAULT="{{ configuration_grub_cmdline_default_new }}"' line: 'GRUB_CMDLINE_LINUX_DEFAULT="{{ configuration_grub_cmdline_default_new }}"'

View File

@@ -52,7 +52,7 @@
when: configuration_luks_keyfile_unlock_test.rc != 0 when: configuration_luks_keyfile_unlock_test.rc != 0
community.crypto.luks_device: community.crypto.luks_device:
device: "{{ configuration_luks_device }}" device: "{{ configuration_luks_device }}"
passphrase: "{{ configuration_luks_passphrase_effective }}" passphrase: "{{ configuration_luks_passphrase }}"
new_keyfile: "/mnt{{ configuration_luks_keyfile_path }}" new_keyfile: "/mnt{{ configuration_luks_keyfile_path }}"
register: configuration_luks_addkey_result register: configuration_luks_addkey_result
failed_when: false failed_when: false
@@ -84,7 +84,7 @@
- name: Retry adding keyfile to LUKS header - name: Retry adding keyfile to LUKS header
community.crypto.luks_device: community.crypto.luks_device:
device: "{{ configuration_luks_device }}" device: "{{ configuration_luks_device }}"
passphrase: "{{ configuration_luks_passphrase_effective }}" passphrase: "{{ configuration_luks_passphrase }}"
new_keyfile: "/mnt{{ configuration_luks_keyfile_path }}" new_keyfile: "/mnt{{ configuration_luks_keyfile_path }}"
register: configuration_luks_addkey_retry register: configuration_luks_addkey_retry
failed_when: false failed_when: false

View File

@@ -11,7 +11,7 @@
- name: Write passphrase into temporary file for TPM2 enrollment - name: Write passphrase into temporary file for TPM2 enrollment
ansible.builtin.copy: ansible.builtin.copy:
dest: "{{ configuration_luks_tpm2_passphrase_tempfile.path }}" dest: "{{ configuration_luks_tpm2_passphrase_tempfile.path }}"
content: "{{ configuration_luks_passphrase_effective }}" content: "{{ configuration_luks_passphrase }}"
owner: root owner: root
group: root group: root
mode: "0600" mode: "0600"
@@ -31,12 +31,12 @@
| regex_replace('^/mnt', '') | regex_replace('^/mnt', '')
) )
] ]
+ (['--tpm2-pcrs=' + configuration_luks_tpm2_pcrs_effective] + (['--tpm2-pcrs=' + configuration_luks_tpm2_pcrs]
if configuration_luks_tpm2_pcrs_effective | length > 0 else []) if configuration_luks_tpm2_pcrs | length > 0 else [])
+ [configuration_luks_device] + [configuration_luks_device]
}} }}
configuration_luks_enroll_chroot_cmd: >- configuration_luks_enroll_chroot_cmd: >-
{{ chroot_command }} /mnt {{ configuration_luks_enroll_args | join(' ') }} {{ chroot_command }} {{ configuration_luks_enroll_args | join(' ') }}
ansible.builtin.command: "{{ configuration_luks_enroll_chroot_cmd }}" ansible.builtin.command: "{{ configuration_luks_enroll_chroot_cmd }}"
register: configuration_luks_tpm2_enroll_chroot register: configuration_luks_tpm2_enroll_chroot
changed_when: configuration_luks_tpm2_enroll_chroot.rc == 0 changed_when: configuration_luks_tpm2_enroll_chroot.rc == 0
@@ -55,8 +55,8 @@
'--wipe-slot=tpm2', '--wipe-slot=tpm2',
'--unlock-key-file=' + configuration_luks_tpm2_passphrase_tempfile.path '--unlock-key-file=' + configuration_luks_tpm2_passphrase_tempfile.path
] ]
+ (['--tpm2-pcrs=' + configuration_luks_tpm2_pcrs_effective] + (['--tpm2-pcrs=' + configuration_luks_tpm2_pcrs]
if configuration_luks_tpm2_pcrs_effective | length > 0 else []) if configuration_luks_tpm2_pcrs | length > 0 else [])
+ [configuration_luks_device] + [configuration_luks_device]
}} }}
ansible.builtin.command: ansible.builtin.command:

View File

@@ -27,14 +27,15 @@
- name: Create zram config - name: Create zram config
when: when:
- os | lower not in ['debian11', 'rhel8'] - (os != "debian" or (os_version | string) != "11") and os != "rhel"
- swap_enabled | bool - os | lower not in ["alpine", "void"]
- system_cfg.features.swap.enabled | bool
ansible.builtin.copy: ansible.builtin.copy:
dest: /mnt/etc/systemd/zram-generator.conf dest: /mnt/etc/systemd/zram-generator.conf
content: | content: |
[zram0] [zram0]
zram-size = ram / 2 zram-size = ram / 2
compression-algorithm = {{ 'zstd' if zstd_enabled | bool else 'lz4' }} compression-algorithm = {{ 'zstd' if system_cfg.features.zstd.enabled | bool else 'lz4' }}
swap-priority = 100 swap-priority = 100
fs-type = swap fs-type = swap
mode: "0644" mode: "0644"
@@ -62,7 +63,7 @@
- /mnt/etc/issue.net - /mnt/etc/issue.net
- name: Remove motd files - name: Remove motd files
when: os | lower in ["rhel8", "rhel9", "rhel10"] when: os == "rhel"
ansible.builtin.file: ansible.builtin.file:
path: "{{ item }}" path: "{{ item }}"
state: absent state: absent

View File

@@ -16,30 +16,30 @@
group: root group: root
mode: "0644" mode: "0644"
- name: Remove deprecated attr2 and disable large extent - name: Adjust XFS mount options and disable large extent
when: os | lower in ["almalinux", "rhel8", "rhel9", "rhel10", "rocky"] and filesystem == "xfs" when: os in ["almalinux", "rocky", "rhel"] and system_cfg.filesystem == "xfs"
ansible.builtin.replace: ansible.builtin.replace:
path: /mnt/etc/fstab path: /mnt/etc/fstab
regexp: "(xfs.*?)(attr2)" regexp: "(xfs.*?)(attr2)"
replace: '\1allocsize=64m' replace: "\\1allocsize=64m"
- name: Replace ISO UUID entry with /dev/sr0 in fstab - name: Replace ISO UUID entry with /dev/sr0 in fstab
when: os in ["rhel8", "rhel9", "rhel10"] when: os == "rhel"
vars: vars:
configuration_fstab_dvd_line: >- configuration_fstab_dvd_line: >-
{{ {{
'/usr/local/install/redhat/rhel.iso /usr/local/install/redhat/dvd iso9660 loop,nofail 0 0' '/usr/local/install/redhat/rhel.iso /usr/local/install/redhat/dvd iso9660 loop,nofail 0 0'
if hypervisor == 'vmware' if hypervisor_type == 'vmware'
else '/dev/sr0 /usr/local/install/redhat/dvd iso9660 ro,relatime,nojoliet,check=s,map=n,nofail 0 0' else '/dev/sr0 /usr/local/install/redhat/dvd iso9660 ro,relatime,nojoliet,check=s,map=n,nofail 0 0'
}} }}
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: /mnt/etc/fstab path: /mnt/etc/fstab
regexp: '^.*\/dvd.*$' regexp: "^.*\\/dvd.*$"
line: "{{ configuration_fstab_dvd_line }}" line: "{{ configuration_fstab_dvd_line }}"
state: present state: present
- name: Write image from RHEL ISO to the target machine - name: Write image from RHEL ISO to the target machine
when: os in ["rhel8", "rhel9", "rhel10"] and hypervisor == 'vmware' when: os == "rhel" and hypervisor_type == 'vmware'
ansible.builtin.command: ansible.builtin.command:
argv: argv:
- dd - dd
@@ -57,9 +57,9 @@
line: "{{ fstab_entry.line }}" line: "{{ fstab_entry.line }}"
insertafter: EOF insertafter: EOF
loop: loop:
- {regexp: '^# TempFS$', line: '# TempFS'} - { regexp: "^# TempFS$", line: "# TempFS" }
- {regexp: '^tmpfs\\s+/tmp\\s+', line: 'tmpfs /tmp tmpfs defaults,nosuid,nodev,noexec 0 0'} - { regexp: "^tmpfs\\\\s+/tmp\\\\s+", line: "tmpfs /tmp tmpfs defaults,nosuid,nodev,noexec 0 0" }
- {regexp: '^tmpfs\\s+/var/tmp\\s+', line: 'tmpfs /var/tmp tmpfs defaults,nosuid,nodev,noexec 0 0'} - { regexp: "^tmpfs\\\\s+/var/tmp\\\\s+", line: "tmpfs /var/tmp tmpfs defaults,nosuid,nodev,noexec 0 0" }
- {regexp: '^tmpfs\\s+/dev/shm\\s+', line: 'tmpfs /dev/shm tmpfs defaults,nosuid,nodev,noexec 0 0'} - { regexp: "^tmpfs\\\\s+/dev/shm\\\\s+", line: "tmpfs /dev/shm tmpfs defaults,nosuid,nodev,noexec 0 0" }
loop_control: loop_control:
loop_var: fstab_entry loop_var: fstab_entry

View File

@@ -16,50 +16,50 @@
block: block:
- name: Build RHEL kernel command line defaults - name: Build RHEL kernel command line defaults
vars: vars:
configuration_grub_root_uuid_value: >- grub_root_uuid: >-
{{ {{
( (
partitioning_main_uuid.stdout partitioning_main_uuid.stdout
if (filesystem | lower) == 'btrfs' if (system_cfg.filesystem | lower) == 'btrfs'
else (partitioning_uuid_root | default([]) | first | default('')) else (partitioning_uuid_root | default([]) | first | default(''))
) )
| default('') | default('')
| trim | trim
}} }}
configuration_grub_lvm_args_value: >- grub_lvm_args: >-
{{ {{
( (
['rd.lvm.lv=sys/root'] ['rd.lvm.lv=sys/root']
+ ( + (
['rd.lvm.lv=sys/swap', 'resume=/dev/mapper/sys-swap'] ['rd.lvm.lv=sys/swap', 'resume=/dev/mapper/sys-swap']
if swap_enabled | bool if system_cfg.features.swap.enabled | bool
else [] else []
) )
) )
if (filesystem | lower) != 'btrfs' if (system_cfg.filesystem | lower) != 'btrfs'
else [] else []
}} }}
configuration_grub_root_flags_value: >- grub_root_flags: >-
{{ ['rootflags=subvol=@'] if (filesystem | lower) == 'btrfs' else [] }} {{ ['rootflags=subvol=@'] if (system_cfg.filesystem | lower) == 'btrfs' else [] }}
configuration_grub_cmdline_linux_base_value: >- grub_cmdline_linux_base: >-
{{ {{
(['crashkernel=auto'] + configuration_grub_lvm_args_value) (['crashkernel=auto'] + grub_lvm_args)
| join(' ') | join(' ')
}} }}
configuration_grub_kernel_cmdline_base_value: >- grub_kernel_cmdline_base: >-
{{ {{
( (
(['root=UUID=' + configuration_grub_root_uuid_value] (['root=UUID=' + grub_root_uuid]
if configuration_grub_root_uuid_value | length > 0 else []) if grub_root_uuid | length > 0 else [])
+ ['ro', 'crashkernel=auto'] + ['ro', 'crashkernel=auto']
+ configuration_grub_lvm_args_value + grub_lvm_args
+ configuration_grub_root_flags_value + grub_root_flags
) )
| join(' ') | join(' ')
}} }}
ansible.builtin.set_fact: ansible.builtin.set_fact:
configuration_grub_cmdline_linux_base: "{{ configuration_grub_cmdline_linux_base_value }}" configuration_grub_cmdline_linux_base: "{{ grub_cmdline_linux_base }}"
configuration_kernel_cmdline_base: "{{ configuration_grub_kernel_cmdline_base_value }}" configuration_kernel_cmdline_base: "{{ grub_kernel_cmdline_base }}"
changed_when: false changed_when: false
- name: Check if grub defaults file exists - name: Check if grub defaults file exists
@@ -106,7 +106,7 @@
when: configuration_grub_bls_entries.files | length > 0 when: configuration_grub_bls_entries.files | length > 0
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: "{{ item.path }}" path: "{{ item.path }}"
regexp: '^options ' regexp: "^options "
line: "options {{ configuration_kernel_cmdline_base }}" line: "options {{ configuration_kernel_cmdline_base }}"
loop: "{{ configuration_grub_bls_entries.files }}" loop: "{{ configuration_grub_bls_entries.files }}"
loop_control: loop_control:
@@ -116,5 +116,5 @@
when: partitioning_grub_enable_cryptodisk | bool when: partitioning_grub_enable_cryptodisk | bool
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: /mnt/etc/default/grub path: /mnt/etc/default/grub
regexp: '^GRUB_ENABLE_CRYPTODISK=' regexp: "^GRUB_ENABLE_CRYPTODISK="
line: GRUB_ENABLE_CRYPTODISK=y line: GRUB_ENABLE_CRYPTODISK=y

View File

@@ -1,5 +1,6 @@
--- ---
- name: Reload systemd in installer environment - name: Reload systemd in installer environment
when: ansible_service_mgr == 'systemd'
ansible.builtin.systemd: ansible.builtin.systemd:
daemon_reload: true daemon_reload: true
@@ -19,23 +20,25 @@
regexp: "{{ item.regex }}" regexp: "{{ item.regex }}"
line: "{{ item.line }}" line: "{{ item.line }}"
loop: loop:
- {regex: en_US\.UTF-8 UTF-8, line: en_US.UTF-8 UTF-8} - { regex: en_US\.UTF-8 UTF-8, line: en_US.UTF-8 UTF-8 }
- name: Generate locales - name: Generate locales
when: not is_rhel | bool when: not is_rhel | bool
ansible.builtin.command: "{{ chroot_command }} /mnt /usr/sbin/locale-gen" ansible.builtin.command: "{{ chroot_command }} /usr/sbin/locale-gen"
register: configuration_locale_result register: configuration_locale_result
changed_when: configuration_locale_result.rc == 0 changed_when: configuration_locale_result.rc == 0
- name: Set hostname - name: Set hostname
vars: vars:
configuration_dns_domain: "{{ (system_cfg.dns.search | default([]) | first | default('')) | string }}"
configuration_hostname_fqdn: >- configuration_hostname_fqdn: >-
{{ {{
hostname hostname
if '.' in hostname if '.' in hostname
else ( else (
hostname + '.' + vm_dns_search hostname + '.' + configuration_dns_domain
if vm_dns_search is defined and vm_dns_search | length if configuration_dns_domain | length > 0
else hostname else hostname
) )
}} }}
@@ -46,21 +49,28 @@
- name: Add host entry to /etc/hosts - name: Add host entry to /etc/hosts
vars: vars:
configuration_dns_domain: "{{ (system_cfg.dns.search | default([]) | first | default('')) | string }}"
configuration_hostname_fqdn: >- configuration_hostname_fqdn: >-
{{ {{
hostname hostname
if '.' in hostname if '.' in hostname
else ( else (
hostname + '.' + vm_dns_search hostname + '.' + configuration_dns_domain
if vm_dns_search is defined and vm_dns_search | length if configuration_dns_domain | length > 0
else hostname else hostname
) )
}} }}
configuration_hostname_short: "{{ hostname.split('.')[0] }}" configuration_hostname_short: "{{ hostname.split('.')[0] }}"
configuration_hostname_entries: >- configuration_hostname_entries: >-
{{ [configuration_hostname_fqdn, configuration_hostname_short] | unique | join(' ') }} {{ [configuration_hostname_fqdn, configuration_hostname_short] | unique | join(' ') }}
configuration_hosts_ip: >-
{{
system_cfg.ip
if system_cfg.ip is defined and (system_cfg.ip | string | length) > 0
else inventory_hostname
}}
configuration_hosts_line: >- configuration_hosts_line: >-
{{ (vm_ip if vm_ip is defined and vm_ip | length > 0 else inventory_hostname) }} {{ configuration_hostname_entries }} {{ configuration_hosts_ip }} {{ configuration_hostname_entries }}
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: /mnt/etc/hosts path: /mnt/etc/hosts
line: "{{ configuration_hosts_line }}" line: "{{ configuration_hosts_line }}"

View File

@@ -2,6 +2,7 @@
- name: Include configuration tasks - name: Include configuration tasks
ansible.builtin.include_tasks: "{{ configuration_task }}" ansible.builtin.include_tasks: "{{ configuration_task }}"
loop: loop:
- banner.yml
- fstab.yml - fstab.yml
- locales.yml - locales.yml
- services.yml - services.yml

View File

@@ -30,17 +30,17 @@
) )
| default('') | default('')
}} }}
configuration_net_inf_effective: >- configuration_net_inf_detected: >-
{{ configuration_net_inf_from_facts | default(configuration_net_inf_from_ip, true) }} {{ configuration_net_inf_from_facts | default(configuration_net_inf_from_ip, true) }}
configuration_net_inf_regex: "{{ configuration_net_inf_effective | ansible.builtin.regex_escape }}" configuration_net_inf_regex: "{{ configuration_net_inf_detected | ansible.builtin.regex_escape }}"
configuration_net_mac_from_virtualization: "{{ virtualization_mac_address | default('') }}" configuration_net_mac_from_virtualization: "{{ virtualization_mac_address | default('') }}"
configuration_net_mac_from_facts: >- configuration_net_mac_from_facts: >-
{{ {{
( (
(ansible_facts | default({})).get(configuration_net_inf_effective, {}).get('macaddress', '') (ansible_facts | default({})).get(configuration_net_inf_detected, {}).get('macaddress', '')
) )
| default( | default(
(ansible_facts | default({})).get('ansible_' + configuration_net_inf_effective, {}).get('macaddress', ''), (ansible_facts | default({})).get('ansible_' + configuration_net_inf_detected, {}).get('macaddress', ''),
true true
) )
}} }}
@@ -58,7 +58,7 @@
| default('') | default('')
}} }}
ansible.builtin.set_fact: ansible.builtin.set_fact:
configuration_net_inf: "{{ configuration_net_inf_effective }}" configuration_net_inf: "{{ configuration_net_inf_detected }}"
configuration_net_mac: >- configuration_net_mac: >-
{{ {{
( (
@@ -82,15 +82,84 @@
- configuration_net_mac | length > 0 - configuration_net_mac | length > 0
fail_msg: Failed to detect the MAC address for network interface {{ configuration_net_inf }}. fail_msg: Failed to detect the MAC address for network interface {{ configuration_net_inf }}.
- name: Copy NetworkManager keyfile - name: Configure NetworkManager profile
when: os | lower not in ["alpine", "void"]
block:
- name: Copy NetworkManager keyfile
ansible.builtin.template: ansible.builtin.template:
src: network.j2 src: network.j2
dest: /mnt/etc/NetworkManager/system-connections/LAN.nmconnection dest: /mnt/etc/NetworkManager/system-connections/LAN.nmconnection
mode: "0600" mode: "0600"
- name: Fix Ubuntu unmanaged devices - name: Fix Ubuntu unmanaged devices
when: os | lower in ["ubuntu", "ubuntu-lts"] when: os | lower in ["ubuntu", "ubuntu-lts"]
ansible.builtin.file: ansible.builtin.file:
path: /mnt/etc/NetworkManager/conf.d/10-globally-managed-devices.conf path: /mnt/etc/NetworkManager/conf.d/10-globally-managed-devices.conf
state: touch state: touch
mode: "0644" mode: "0644"
- name: Configure Alpine networking
when: os | lower == "alpine"
vars:
configuration_dns_list: "{{ system_cfg.dns.servers | default([]) }}"
configuration_alpine_static: >-
{{
system_cfg.ip is defined
and system_cfg.ip | string | length > 0
and system_cfg.prefix is defined
and (system_cfg.prefix | string | length) > 0
}}
block:
- name: Write Alpine network interfaces
ansible.builtin.copy:
dest: /mnt/etc/network/interfaces
mode: "0644"
content: |
auto lo
iface lo inet loopback
auto {{ configuration_net_inf }}
iface {{ configuration_net_inf }} inet {{ 'static' if configuration_alpine_static | bool else 'dhcp' }}
{% if configuration_alpine_static | bool %}
address {{ system_cfg.ip }}/{{ system_cfg.prefix }}
{% if system_cfg.gateway is defined and system_cfg.gateway | string | length %}
gateway {{ system_cfg.gateway }}
{% endif %}
{% endif %}
- name: Set Alpine DNS resolvers
when: configuration_dns_list | length > 0
ansible.builtin.copy:
dest: /mnt/etc/resolv.conf
mode: "0644"
content: |
{% for resolver in configuration_dns_list %}
nameserver {{ resolver }}
{% endfor %}
- name: Configure Void networking
when: os | lower == "void"
vars:
configuration_dns_list: "{{ system_cfg.dns.servers | default([]) }}"
configuration_void_static: >-
{{
system_cfg.ip is defined
and system_cfg.ip | string | length > 0
and system_cfg.prefix is defined
and (system_cfg.prefix | string | length) > 0
}}
block:
- name: Write dhcpcd configuration for static networking
when: configuration_void_static | bool
ansible.builtin.copy:
dest: /mnt/etc/dhcpcd.conf
mode: "0644"
content: |
interface {{ configuration_net_inf }}
static ip_address={{ system_cfg.ip }}/{{ system_cfg.prefix }}
{% if system_cfg.gateway is defined and system_cfg.gateway | string | length %}
static routers={{ system_cfg.gateway }}
{% endif %}
{% if configuration_dns_list | length > 0 %}
static domain_name_servers={{ configuration_dns_list | join(' ') }}
{% endif %}

View File

@@ -3,16 +3,16 @@
when: is_rhel | bool when: is_rhel | bool
block: block:
- name: Fix SELinux by pre-labeling the filesystem before first boot - name: Fix SELinux by pre-labeling the filesystem before first boot
when: os | lower in ['almalinux', 'rhel8', 'rhel9', 'rhel10', 'rocky'] and selinux | bool when: os in ['almalinux', 'rocky', 'rhel'] and system_cfg.features.selinux.enabled | bool
ansible.builtin.command: > ansible.builtin.command: >
{{ chroot_command }} /mnt /sbin/setfiles -v -F {{ chroot_command }} /sbin/setfiles -v -F
-e /dev -e /proc -e /sys -e /run -e /dev -e /proc -e /sys -e /run
/etc/selinux/targeted/contexts/files/file_contexts / /etc/selinux/targeted/contexts/files/file_contexts /
register: configuration_setfiles_result register: configuration_setfiles_result
changed_when: configuration_setfiles_result.rc == 0 changed_when: configuration_setfiles_result.rc == 0
- name: Disable SELinux - name: Disable SELinux
when: os | lower == "fedora" or not selinux | bool when: os | lower == "fedora" or not system_cfg.features.selinux.enabled | bool
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: /mnt/etc/selinux/config path: /mnt/etc/selinux/config
regexp: ^SELINUX= regexp: ^SELINUX=

View File

@@ -1,12 +1,13 @@
--- ---
- name: Enable Systemd Services - name: Enable Systemd Services
when: os | lower not in ['alpine', 'void']
ansible.builtin.command: > ansible.builtin.command: >
{{ chroot_command }} /mnt systemctl enable NetworkManager {{ chroot_command }} systemctl enable NetworkManager
{{ ' firewalld' if firewalld_enabled | bool else '' }} {{ ' firewalld' if system_cfg.features.firewall.backend == 'firewalld' and system_cfg.features.firewall.enabled | bool else '' }}
{{ ' ufw' if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else '' }}
{{ {{
(' ssh' if os | lower in ['ubuntu', 'ubuntu-lts'] else (' ssh' if is_debian | bool else ' sshd')
(' sshd' if os | lower not in ['debian11', 'debian12', 'debian13'] else '')) if system_cfg.features.ssh.enabled | bool else ''
if ssh_enabled | bool else ''
}} }}
{{ {{
'logrotate systemd-resolved systemd-timesyncd systemd-networkd' 'logrotate systemd-resolved systemd-timesyncd systemd-networkd'
@@ -14,3 +15,65 @@
}} }}
register: configuration_enable_services_result register: configuration_enable_services_result
changed_when: configuration_enable_services_result.rc == 0 changed_when: configuration_enable_services_result.rc == 0
- name: Enable OpenRC services
when: os | lower == 'alpine'
vars:
configuration_openrc_services: >-
{{
['networking']
+ (['sshd'] if system_cfg.features.ssh.enabled | bool else [])
+ ([system_cfg.features.firewall.backend] if system_cfg.features.firewall.enabled | bool else [])
}}
block:
- name: Ensure OpenRC runlevel directory exists
ansible.builtin.file:
path: /mnt/etc/runlevels/default
state: directory
mode: "0755"
- name: Check OpenRC init scripts
ansible.builtin.stat:
path: "/mnt/etc/init.d/{{ item }}"
loop: "{{ configuration_openrc_services }}"
register: configuration_openrc_service_stats
changed_when: false
- name: Enable OpenRC services
ansible.builtin.file:
src: "/mnt/etc/init.d/{{ item.item }}"
dest: "/mnt/etc/runlevels/default/{{ item.item }}"
state: link
loop: "{{ configuration_openrc_service_stats.results }}"
when: item.stat.exists
- name: Enable runit services
when: os | lower == 'void'
vars:
configuration_runit_services: >-
{{
['dhcpcd']
+ (['sshd'] if system_cfg.features.ssh.enabled | bool else [])
+ ([system_cfg.features.firewall.backend] if system_cfg.features.firewall.enabled | bool else [])
}}
block:
- name: Ensure runit service directory exists
ansible.builtin.file:
path: /mnt/var/service
state: directory
mode: "0755"
- name: Check runit service definitions
ansible.builtin.stat:
path: "/mnt/etc/sv/{{ item }}"
loop: "{{ configuration_runit_services }}"
register: configuration_runit_service_stats
changed_when: false
- name: Enable runit services
ansible.builtin.file:
src: "/mnt/etc/sv/{{ item.item }}"
dest: "/mnt/var/service/{{ item.item }}"
state: link
loop: "{{ configuration_runit_service_stats.results }}"
when: item.stat.exists

View File

@@ -4,12 +4,12 @@
configuration_user_group: >- configuration_user_group: >-
{{ "sudo" if is_debian | bool else "wheel" }} {{ "sudo" if is_debian | bool else "wheel" }}
configuration_useradd_cmd: >- configuration_useradd_cmd: >-
{{ chroot_command }} /mnt /usr/sbin/useradd --create-home --user-group {{ chroot_command }} /usr/sbin/useradd --create-home --user-group
--groups {{ configuration_user_group }} {{ user_name }} --groups {{ configuration_user_group }} {{ system_cfg.user.name }}
--password {{ user_password | password_hash('sha512') }} --shell /bin/bash --password {{ system_cfg.user.password | password_hash('sha512') }} --shell /bin/bash
configuration_root_cmd: >- configuration_root_cmd: >-
{{ chroot_command }} /mnt /usr/sbin/usermod --password {{ chroot_command }} /usr/sbin/usermod --password
'{{ root_password | password_hash('sha512') }}' root --shell /bin/bash '{{ system_cfg.root.password | password_hash('sha512') }}' root --shell /bin/bash
ansible.builtin.command: "{{ item }}" ansible.builtin.command: "{{ item }}"
loop: loop:
- "{{ configuration_useradd_cmd }}" - "{{ configuration_useradd_cmd }}"
@@ -18,19 +18,19 @@
changed_when: configuration_user_result.rc == 0 changed_when: configuration_user_result.rc == 0
- name: Ensure .ssh directory exists - name: Ensure .ssh directory exists
when: user_public_key | length > 0 when: system_cfg.user.key | length > 0
ansible.builtin.file: ansible.builtin.file:
path: /mnt/home/{{ user_name }}/.ssh path: /mnt/home/{{ system_cfg.user.name }}/.ssh
state: directory state: directory
owner: 1000 owner: 1000
group: 1000 group: 1000
mode: "0700" mode: "0700"
- name: Add SSH public key to authorized_keys - name: Add SSH public key to authorized_keys
when: user_public_key | length > 0 when: system_cfg.user.key | length > 0
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: /mnt/home/{{ user_name }}/.ssh/authorized_keys path: /mnt/home/{{ system_cfg.user.name }}/.ssh/authorized_keys
line: "{{ user_public_key }}" line: "{{ system_cfg.user.key }}"
owner: 1000 owner: 1000
group: 1000 group: 1000
mode: "0600" mode: "0600"

View File

@@ -4,14 +4,10 @@ uuid={{ configuration_net_uuid }}
type=ethernet type=ethernet
[ipv4] [ipv4]
{% set dns_value = vm_dns if vm_dns is defined else '' %} {% set dns_list = system_cfg.dns.servers | default([]) %}
{% set dns_list_raw = dns_value if dns_value is iterable and dns_value is not string else dns_value.split(',') %} {% set search_list = system_cfg.dns.search | default([]) %}
{% set dns_list = dns_list_raw | map('trim') | reject('equalto', '') | list %} {% if system_cfg.ip is defined and system_cfg.ip | string | length %}
{% set search_value = vm_dns_search if vm_dns_search is defined else '' %} address1={{ system_cfg.ip }}/{{ system_cfg.prefix }}{{ (',' ~ system_cfg.gateway) if (system_cfg.gateway is defined and system_cfg.gateway | string | length) else '' }}
{% set search_list_raw = search_value if search_value is iterable and search_value is not string else search_value.split(',') %}
{% set search_list = search_list_raw | map('trim') | reject('equalto', '') | list %}
{% if vm_ip is defined and vm_ip | length %}
address1={{ vm_ip }}/{{ vm_nms }}{{ (',' ~ vm_gw) if (vm_gw is defined and vm_gw | length) else '' }}
method=manual method=manual
{% else %} {% else %}
method=auto method=auto

View File

@@ -1,6 +1,6 @@
--- ---
- name: Configure work environment - name: Configure work environment
become: "{{ hypervisor != 'vmware' }}" become: "{{ hypervisor_type != 'vmware' }}"
block: block:
- name: Wait for connection - name: Wait for connection
ansible.builtin.wait_for_connection: ansible.builtin.wait_for_connection:
@@ -10,20 +10,78 @@
- name: Gather facts - name: Gather facts
ansible.builtin.setup: ansible.builtin.setup:
- name: Check if host is booted from the Arch install media - name: Check for live environment markers
ansible.builtin.stat: ansible.builtin.stat:
path: /run/archiso path: "{{ item }}"
register: environment_archiso_stat loop:
- /run/archiso
- /run/live
- /run/initramfs
- /run/initramfs/live
register: environment_live_marker_stat
changed_when: false
- name: Determine root filesystem type
ansible.builtin.set_fact:
environment_root_fstype: >-
{{
ansible_mounts
| selectattr('mount', 'equalto', '/')
| map(attribute='fstype')
| list
| first
| default('')
| lower
}}
environment_archiso_present: >-
{{
(
environment_live_marker_stat.results
| selectattr('item', 'equalto', '/run/archiso')
| selectattr('stat.exists')
| list
| length
) > 0
}}
changed_when: false
- name: Identify live environment indicators
ansible.builtin.set_fact:
environment_is_live_environment: >-
{{
(
environment_live_marker_stat.results
| selectattr('stat.exists')
| list
| length
) > 0
or environment_root_fstype in ['overlay', 'overlayfs', 'squashfs', 'aufs']
or (ansible_hostname | default('') | lower is search('live'))
}}
changed_when: false
- name: Abort if target is not a live environment
ansible.builtin.assert:
that:
- environment_is_live_environment | bool
fail_msg: |
PRODUCTION SYSTEM DETECTED - ABORTING
The target system does not appear to be a live installer environment.
This playbook must run from a live ISO to avoid wiping production data.
Boot from a live installer (Arch, Debian, Ubuntu, etc.) and retry.
quiet: true
- name: Abort if the host is not booted from the Arch install media - name: Abort if the host is not booted from the Arch install media
when: when:
- not (custom_iso | bool) - not (custom_iso | bool)
- not environment_archiso_stat.stat.exists - not environment_archiso_present | bool
ansible.builtin.fail: ansible.builtin.fail:
msg: This host is not booted from the Arch install media! msg: This host is not booted from the Arch install media!
- name: Select primary Network Interface - name: Select primary Network Interface
when: hypervisor == "vmware" when: hypervisor_type == "vmware"
ansible.builtin.set_fact: ansible.builtin.set_fact:
environment_interface_name: >- environment_interface_name: >-
{{ {{
@@ -39,20 +97,20 @@
- name: Set IP-Address - name: Set IP-Address
when: when:
- hypervisor == "vmware" - hypervisor_type == "vmware"
- vm_ip is defined and vm_ip | length > 0 - system_cfg.ip is defined and system_cfg.ip | string | length > 0
ansible.builtin.command: >- ansible.builtin.command: >-
ip addr replace {{ vm_ip }}/{{ vm_nms }} ip addr replace {{ system_cfg.ip }}/{{ system_cfg.prefix }}
dev {{ environment_interface_name }} dev {{ environment_interface_name }}
register: environment_ip_result register: environment_ip_result
changed_when: environment_ip_result.rc == 0 changed_when: environment_ip_result.rc == 0
- name: Set Default Gateway - name: Set Default Gateway
when: when:
- hypervisor == "vmware" - hypervisor_type == "vmware"
- vm_gw is defined and vm_gw | length > 0 - system_cfg.gateway is defined and system_cfg.gateway | string | length > 0
- vm_ip is defined and vm_ip | length > 0 - system_cfg.ip is defined and system_cfg.ip | string | length > 0
ansible.builtin.command: "ip route replace default via {{ vm_gw }}" ansible.builtin.command: "ip route replace default via {{ system_cfg.gateway }}"
register: environment_gateway_result register: environment_gateway_result
changed_when: environment_gateway_result.rc == 0 changed_when: environment_gateway_result.rc == 0
@@ -62,7 +120,7 @@
changed_when: false changed_when: false
- name: Configure SSH for root login - name: Configure SSH for root login
when: hypervisor == "vmware" and vmware_ssh | bool when: hypervisor_type == "vmware" and hypervisor_cfg.ssh | bool
block: block:
- name: Allow login - name: Allow login
ansible.builtin.replace: ansible.builtin.replace:
@@ -105,23 +163,23 @@
- name: Setup Pacman - name: Setup Pacman
when: when:
- not (custom_iso | bool) - not (custom_iso | bool)
- "'os' not in item or os in item.os" - item.os is not defined or (os_resolved | default(os)) in item.os
community.general.pacman: community.general.pacman:
update_cache: true update_cache: true
force: true force: true
name: "{{ item.name }}" name: "{{ item.name }}"
state: latest state: latest
loop: loop:
- {name: glibc} - { name: glibc }
- {name: dnf, os: [almalinux, fedora, rhel8, rhel9, rhel10, rocky]} - { name: dnf, os: [almalinux8, almalinux9, almalinux10, fedora40, fedora41, fedora42, fedora43, rhel8, rhel9, rhel10, rocky8, rocky9, rocky10] }
- {name: debootstrap, os: [debian11, debian12, debian13, ubuntu, ubuntu-lts]} - { name: debootstrap, os: [debian10, debian11, debian12, debian13, debianunstable, ubuntu, ubuntu-lts] }
- {name: debian-archive-keyring, os: [debian11, debian12, debian13]} - { name: debian-archive-keyring, os: [debian10, debian11, debian12, debian13, debianunstable] }
- {name: ubuntu-keyring, os: [ubuntu, ubuntu-lts]} - { name: ubuntu-keyring, os: [ubuntu, ubuntu-lts] }
retries: 4 retries: 4
delay: 15 delay: 15
- name: Prepare /iso mount and repository for RHEL-based systems - name: Prepare /iso mount and repository for RHEL-based systems
when: os | lower in ["rhel8", "rhel9", "rhel10"] when: os == "rhel"
block: block:
- name: Create /iso directory - name: Create /iso directory
ansible.builtin.file: ansible.builtin.file:
@@ -129,9 +187,19 @@
state: directory state: directory
mode: "0755" mode: "0755"
- name: Select RHEL ISO device
ansible.builtin.set_fact:
environment_rhel_iso_device: >-
{{
'/dev/sr2'
if hypervisor_type == 'libvirt'
else '/dev/sr1'
}}
changed_when: false
- name: Mount RHEL ISO - name: Mount RHEL ISO
ansible.posix.mount: ansible.posix.mount:
src: "{{ '/dev/sr1' if hypervisor == 'vmware' else '/dev/sr2' }}" src: "{{ environment_rhel_iso_device }}"
path: /usr/local/install/redhat/dvd path: /usr/local/install/redhat/dvd
fstype: iso9660 fstype: iso9660
opts: "ro,loop" opts: "ro,loop"
@@ -140,6 +208,16 @@
- name: Configure RHEL Repos for installation - name: Configure RHEL Repos for installation
when: is_rhel | bool when: is_rhel | bool
block: block:
- name: Select repository template
ansible.builtin.set_fact:
environment_repo_template: >-
{{
(os_resolved | default(os)) | lower
if os == 'rhel'
else os | lower
}}
changed_when: false
- name: Create directories for repository files and RPM GPG keys - name: Create directories for repository files and RPM GPG keys
ansible.builtin.file: ansible.builtin.file:
path: /etc/yum.repos.d path: /etc/yum.repos.d
@@ -148,8 +226,8 @@
- name: Create RHEL repository file - name: Create RHEL repository file
ansible.builtin.template: ansible.builtin.template:
src: "{{ os | lower }}.repo.j2" src: "{{ environment_repo_template }}.repo.j2"
dest: /etc/yum.repos.d/{{ os | lower }}.repo dest: /etc/yum.repos.d/{{ environment_repo_template }}.repo
mode: "0644" mode: "0644"
- name: Check for third-party preparation tasks - name: Check for third-party preparation tasks

View File

@@ -1,31 +1,94 @@
--- ---
hypervisor: "none" # User input. Normalized into hypervisor_cfg + hypervisor_type.
hypervisor:
type: "none"
hypervisor_defaults:
type: "none"
url: ""
username: ""
password: ""
host: ""
storage: ""
datacenter: ""
cluster: ""
certs: false
ssh: false
custom_iso: false custom_iso: false
cis: false
selinux: true
vmware_ssh: false
firewalld_enabled: true
ssh_enabled: true
zstd_enabled: true
swap_enabled: true
chroot_command: "arch-chroot"
thirdparty_preparation_tasks_path: "dropins/preparation.yml" thirdparty_preparation_tasks_path: "dropins/preparation.yml"
cis_enabled: "{{ cis | bool }}" system_defaults:
type: "virtual" # virtual|physical
os: ""
version: ""
filesystem: ""
name: ""
id: ""
cpus: 0
memory: 0 # MiB
balloon: 0 # MiB
network: ""
vlan: ""
ip: ""
prefix: ""
gateway: ""
dns:
servers: []
search: []
path: ""
packages: []
disks: []
user:
name: ""
password: ""
key: ""
root:
password: ""
luks:
enabled: false
passphrase: ""
mapper: "SYSTEM_DECRYPTED"
auto: true
method: "tpm2"
tpm2:
device: "auto"
pcrs: ""
keysize: 64
options: "discard,tries=3"
type: "luks2"
cipher: "aes-xts-plain64"
hash: "sha512"
iter: 4000
bits: 512
pbkdf: "argon2id"
urandom: true
verify: true
features:
cis:
enabled: false
selinux:
enabled: true
firewall:
enabled: true
backend: "firewalld" # firewalld|ufw
toolkit: "nftables" # nftables|iptables
ssh:
enabled: true
zstd:
enabled: true
swap:
enabled: true
banner:
motd: false
sudo: true
chroot:
tool: "arch-chroot" # arch-chroot|chroot|systemd-nspawn
luks_enabled: false system_disk_defaults:
luks_mapper_name: "SYSTEM_DECRYPTED" size: 0
luks_auto_decrypt: true device: ""
luks_auto_decrypt_method: "tpm2" mount:
luks_tpm2_device: "auto" path: ""
luks_tpm2_pcrs: "" fstype: ""
luks_keyfile_size: 64 label: ""
luks_options: "discard,tries=3" opts: "defaults"
luks_type: "luks2"
luks_cipher: "aes-xts-plain64"
luks_hash: "sha512"
luks_iter_time: 4000
luks_key_size: 512
luks_pbkdf: "argon2id"
luks_use_urandom: true
luks_verify_passphrase: true

View File

@@ -0,0 +1,22 @@
---
- name: Ensure hypervisor input is a dictionary
ansible.builtin.set_fact:
hypervisor: "{{ hypervisor | default({}) }}"
changed_when: false
- name: Validate hypervisor input
ansible.builtin.assert:
that:
- hypervisor is mapping
- hypervisor.type is defined
- hypervisor.type | string | length > 0
fail_msg: "hypervisor must be a dictionary and hypervisor.type must be set (e.g. libvirt|proxmox|vmware|xen|none)."
quiet: true
- name: Normalize hypervisor configuration
vars:
merged: "{{ hypervisor_defaults | combine(hypervisor, recursive=True) }}"
ansible.builtin.set_fact:
hypervisor_cfg: "{{ merged }}"
hypervisor_type: "{{ merged.type | string | lower }}"
changed_when: false

View File

@@ -4,111 +4,76 @@
msg: Global defaults loaded. msg: Global defaults loaded.
changed_when: false changed_when: false
- name: Normalize hypervisor inputs
ansible.builtin.include_tasks: hypervisor.yml
- name: Normalize system inputs
ansible.builtin.include_tasks: system.yml
- name: Validate variables - name: Validate variables
ansible.builtin.assert: ansible.builtin.include_tasks: validation.yml
that:
- install_type is defined and install_type in ["virtual", "physical"]
- hypervisor in ["libvirt", "proxmox", "vmware", "none"]
- >-
install_type is defined and (
install_type == "physical"
or hypervisor in ["libvirt", "proxmox", "vmware"]
)
- filesystem is defined and filesystem in ["btrfs", "ext4", "xfs"]
- install_drive is defined and install_drive | length > 0
- hostname is defined and hostname | length > 0
- >-
os is defined and os in [
"archlinux", "almalinux", "debian11", "debian12", "debian13", "fedora",
"rhel8", "rhel9", "rhel10", "rocky", "ubuntu", "ubuntu-lts"
]
- >-
os is defined and (
os not in ["rhel8", "rhel9", "rhel10"]
or (rhel_iso is defined and rhel_iso | length > 0)
)
- >-
install_type is defined and (
install_type == "physical"
or (boot_iso is defined and boot_iso | length > 0)
)
- >-
install_type is defined and (
install_type == "physical"
or (vm_cpus is defined and (vm_cpus | int) > 0)
)
- >-
install_type is defined and (
install_type == "physical"
or (vm_size is defined and (vm_size | float) > 0)
)
- >-
install_type is defined and (
install_type == "physical"
or (vm_memory is defined and (vm_memory | float) > 0)
)
- >-
install_type is defined and filesystem is defined and (
install_type == "physical"
or (
vm_size is defined
and (vm_size | int) >= 20
)
)
- >-
install_type is defined and (
install_type == "physical"
or (
vm_size is defined
and vm_memory is defined
and filesystem is defined
and (
filesystem != "btrfs"
or (
(vm_size | float)
>= (
(vm_memory | float / 1024 >= 16.0)
| ternary(
(vm_memory | float / 2048),
[vm_memory | float / 1024, 4.0] | max
)
+ 5.5
)
)
)
)
)
- >-
vm_ip is not defined
or vm_ip | length == 0
or (vm_nms is defined and (vm_nms | int) > 0)
fail_msg: Invalid input specified, please try again.
- name: Set OS family flags - name: Set OS family flags
ansible.builtin.set_fact: ansible.builtin.set_fact:
is_rhel: "{{ os | lower in ['almalinux', 'fedora', 'rhel8', 'rhel9', 'rhel10', 'rocky'] }}" is_rhel: "{{ os | lower in ['almalinux', 'fedora', 'rhel', 'rocky'] }}"
is_debian: "{{ os | lower in ['debian11', 'debian12', 'debian13', 'ubuntu', 'ubuntu-lts'] }}" is_debian: "{{ os | lower in ['debian', 'ubuntu', 'ubuntu-lts'] }}"
changed_when: false
- name: Normalize OS version for keying
when:
- os_version is defined
- (os_version | string | length) > 0
ansible.builtin.set_fact:
os_version_major: "{{ (os_version | string).split('.')[0] }}"
changed_when: false
- name: Resolve final OS key with version
when:
- os_version is defined
- (os_version | string | length) > 0
ansible.builtin.set_fact:
os_resolved: >-
{{
'debian' + os_version | string if os == 'debian'
else 'fedora' + os_version | string if os == 'fedora'
else 'rocky' + os_version_major if os == 'rocky'
else 'almalinux' + os_version_major if os == 'almalinux'
else 'rhel' + os_version_major if os == 'rhel'
else os
}}
changed_when: false
- name: Set chroot command wrapper
ansible.builtin.set_fact:
chroot_command: >-
{{
'systemd-nspawn -D /mnt'
if (system_cfg.features.chroot.tool | default('arch-chroot')) == 'systemd-nspawn'
else (system_cfg.features.chroot.tool | default('arch-chroot')) ~ ' /mnt'
}}
changed_when: false changed_when: false
- name: Set Python interpreter for RHEL-based installers - name: Set Python interpreter for RHEL-based installers
when: when:
- ansible_python_interpreter is not defined - ansible_python_interpreter is not defined
- os | lower in ["almalinux", "rhel8", "rhel9", "rhel10", "rocky"] - is_rhel | bool
ansible.builtin.set_fact: ansible.builtin.set_fact:
ansible_python_interpreter: /usr/bin/python3 ansible_python_interpreter: /usr/bin/python3
changed_when: false changed_when: false
- name: Set SSH access - name: Set SSH access
when: when:
- install_type == "virtual" - system_cfg.type == "virtual"
- hypervisor != "vmware" - hypervisor_type != "vmware"
ansible.builtin.set_fact: ansible.builtin.set_fact:
ansible_user: "{{ user_name }}" ansible_user: "{{ system_cfg.user.name }}"
ansible_password: "{{ user_password }}" ansible_password: "{{ system_cfg.user.password }}"
ansible_become_password: "{{ user_password }}" ansible_become_password: "{{ system_cfg.user.password }}"
ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
changed_when: false
- name: Set connection for VMware - name: Set connection for VMware
when: hypervisor == "vmware" when: hypervisor_type == "vmware"
ansible.builtin.set_fact: ansible.builtin.set_fact:
ansible_connection: vmware_tools ansible_connection: vmware_tools
changed_when: false

View File

@@ -0,0 +1,257 @@
---
- name: Ensure system input is a dictionary
ansible.builtin.set_fact:
system: "{{ system | default({}) }}"
changed_when: false
- name: Validate system input types
ansible.builtin.assert:
that:
- system is mapping
- system.dns is not defined or system.dns is mapping
- system.user is not defined or system.user is mapping
- system.root is not defined or system.root is mapping
- system.luks is not defined or system.luks is mapping
- system.features is not defined or system.features is mapping
fail_msg: "system and its nested keys (dns, user, root, luks, features) must be dictionaries."
quiet: true
- name: Validate system features input types
when: system.features is defined
loop: "{{ system_defaults.features | dict2items | map(attribute='key') | list }}"
loop_control:
label: "system.features.{{ item }}"
ansible.builtin.assert:
that:
- (system.features[item] | default({})) is mapping
fail_msg: "system.features.{{ item }} must be a dictionary."
quiet: true
- name: Validate system LUKS TPM2 input type
when: system.luks is defined and system.luks is mapping
ansible.builtin.assert:
that:
- system.luks.tpm2 is not defined or system.luks.tpm2 is mapping
fail_msg: "system.luks.tpm2 must be a dictionary."
quiet: true
- name: Build normalized system configuration
vars:
system_raw: "{{ system_defaults | combine(system, recursive=True) }}"
system_type: "{{ system_raw.type | string | lower }}"
system_os_input: "{{ system_raw.os | default('') | string | lower }}"
system_name: >-
{{
system_raw.name | string | trim
if (system_raw.name | default('') | string | trim | length) > 0
else inventory_hostname
}}
ansible.builtin.set_fact:
system_cfg:
type: "{{ system_type }}"
os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}"
version: "{{ system_raw.version | default('') | string }}"
filesystem: "{{ system_raw.filesystem | default('') | string | lower }}"
name: "{{ system_name }}"
id: "{{ system_raw.id | default('') | string }}"
cpus: "{{ [system_raw.cpus | default(0) | int, 0] | max }}"
memory: "{{ [system_raw.memory | default(0) | int, 0] | max }}"
balloon: "{{ [system_raw.balloon | default(0) | int, 0] | max }}"
network: "{{ system_raw.network | default('') | string }}"
vlan: "{{ system_raw.vlan | default('') | string }}"
ip: "{{ system_raw.ip | default('') | string }}"
prefix: >-
{{
(system_raw.prefix | int)
if (system_raw.prefix | default('') | string | length) > 0
else ''
}}
gateway: "{{ system_raw.gateway | default('') | string }}"
dns:
servers: >-
{{
(
system_raw.dns.servers
if system_raw.dns.servers is iterable and system_raw.dns.servers is not string
else (system_raw.dns.servers | string).split(',')
)
| map('trim')
| reject('equalto', '')
| list
}}
search: >-
{{
(
system_raw.dns.search
if system_raw.dns.search is iterable and system_raw.dns.search is not string
else (system_raw.dns.search | string).split(',')
)
| map('trim')
| reject('equalto', '')
| list
}}
path: "{{ system_raw.path | default('') | string }}"
packages: >-
{{
(
system_raw.packages
if system_raw.packages is iterable and system_raw.packages is not string
else (system_raw.packages | string).split(',')
)
| map('trim')
| reject('equalto', '')
| list
}}
disks: "{{ system_raw.disks | default([]) }}"
user:
name: "{{ system_raw.user.name | string }}"
password: "{{ system_raw.user.password | string }}"
key: "{{ system_raw.user.key | string }}"
root:
password: "{{ system_raw.root.password | string }}"
luks:
enabled: "{{ system_raw.luks.enabled | bool }}"
passphrase: "{{ system_raw.luks.passphrase | string }}"
mapper: "{{ system_raw.luks.mapper | string }}"
auto: "{{ system_raw.luks.auto | bool }}"
method: "{{ system_raw.luks.method | string | lower }}"
tpm2:
device: "{{ system_raw.luks.tpm2.device | string }}"
pcrs: "{{ system_raw.luks.tpm2.pcrs | string }}"
keysize: "{{ system_raw.luks.keysize | int }}"
options: "{{ system_raw.luks.options | string }}"
type: "{{ system_raw.luks.type | string }}"
cipher: "{{ system_raw.luks.cipher | string }}"
hash: "{{ system_raw.luks.hash | string }}"
iter: "{{ system_raw.luks.iter | int }}"
bits: "{{ system_raw.luks.bits | int }}"
pbkdf: "{{ system_raw.luks.pbkdf | string }}"
urandom: "{{ system_raw.luks.urandom | bool }}"
verify: "{{ system_raw.luks.verify | bool }}"
features:
cis:
enabled: "{{ system_raw.features.cis.enabled | bool }}"
selinux:
enabled: "{{ system_raw.features.selinux.enabled | bool }}"
firewall:
enabled: "{{ system_raw.features.firewall.enabled | bool }}"
backend: "{{ system_raw.features.firewall.backend | string | lower }}"
toolkit: "{{ system_raw.features.firewall.toolkit | string | lower }}"
ssh:
enabled: "{{ system_raw.features.ssh.enabled | bool }}"
zstd:
enabled: "{{ system_raw.features.zstd.enabled | bool }}"
swap:
enabled: "{{ system_raw.features.swap.enabled | bool }}"
banner:
motd: "{{ system_raw.features.banner.motd | bool }}"
sudo: "{{ system_raw.features.banner.sudo | bool }}"
chroot:
tool: "{{ system_raw.features.chroot.tool | string }}"
hostname: "{{ system_name }}"
os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}"
os_version: "{{ system_raw.version | default('') | string }}"
changed_when: false
- name: Normalize system disks input
vars:
system_disks: "{{ system_cfg.disks | default([]) }}"
system_disk_letter_map: "abcdefghijklmnopqrstuvwxyz"
system_disk_device_prefix: >-
{{
{'libvirt': '/dev/vd', 'xen': '/dev/xvd', 'proxmox': '/dev/sd', 'vmware': '/dev/sd'}.get(hypervisor_type, '')
if system_cfg.type == 'virtual'
else ''
}}
block:
- name: Validate system disks structure
ansible.builtin.assert:
that:
- system_disks is sequence
- (system_disks | length) <= 26
fail_msg: "system.disks must be a list with at most 26 entries."
quiet: true
- name: Validate system disk entries
ansible.builtin.assert:
that:
- item is mapping
- item.mount is not defined or item.mount is mapping
fail_msg: "Each disk entry must be a dictionary, and disk.mount (if set) must be a dictionary."
quiet: true
loop: "{{ system_disks }}"
loop_control:
label: "{{ item | to_json }}"
- name: Initialize normalized disk list
ansible.builtin.set_fact:
system_disks_cfg: []
changed_when: false
- name: Build normalized system disk configuration
vars:
disk_idx: "{{ ansible_loop.index0 }}"
disk_letter: "{{ system_disk_letter_map[disk_idx] }}"
disk_cfg_base: "{{ system_disk_defaults | combine(item, recursive=True) }}"
disk_mount: "{{ system_disk_defaults.mount | combine((disk_cfg_base.mount | default({})), recursive=True) }}"
disk_mount_path: "{{ (disk_mount.path | default('') | string) | trim }}"
disk_mount_fstype: >-
{{
disk_mount.fstype
if (disk_mount.fstype | default('') | string | length) > 0
else ('ext4' if disk_mount_path | length > 0 else '')
}}
disk_device: >-
{{
disk_cfg_base.device
if (disk_cfg_base.device | string | length) > 0
else (
(system_disk_device_prefix ~ disk_letter)
if system_cfg.type == 'virtual'
else ''
)
}}
disk_partition: >-
{{
disk_device ~ ('p1' if (disk_device | regex_search('\\d$')) else '1')
if disk_device | length > 0
else ''
}}
ansible.builtin.set_fact:
system_disks_cfg: >-
{{
system_disks_cfg + [
disk_cfg_base
| combine(
{
'device': disk_device,
'mount': {
'path': disk_mount_path,
'fstype': disk_mount_fstype,
'label': disk_mount.label | default('') | string,
'opts': disk_mount.opts | default('defaults') | string
},
'partition': disk_partition
},
recursive=True
)
]
}}
loop: "{{ system_disks }}"
loop_control:
loop_var: item
extended: true
label: "{{ item | to_json }}"
- name: Update system configuration with normalized disks
ansible.builtin.set_fact:
system_cfg: "{{ system_cfg | combine({'disks': system_disks_cfg}, recursive=True) }}"
changed_when: false
- name: Set install_drive from primary disk
when:
- system_disks_cfg | length > 0
- system_disks_cfg[0].device | string | length > 0
ansible.builtin.set_fact:
install_drive: "{{ system_disks_cfg[0].device }}"
changed_when: false

View File

@@ -0,0 +1,331 @@
---
- name: Validate core variables
ansible.builtin.assert:
that:
- system_cfg is defined
- system_cfg is mapping
- system_cfg.type is defined
- system_cfg.type in ["virtual", "physical"]
- hypervisor_cfg is defined
- hypervisor_cfg is mapping
- hypervisor_type is defined
- hypervisor_type in ["libvirt", "proxmox", "vmware", "xen", "none"]
- system_cfg.filesystem is defined
- system_cfg.filesystem in ["btrfs", "ext4", "xfs"]
- install_drive is defined
- install_drive | string | length > 0
- hostname is defined
- hostname | string | length > 0
fail_msg: Invalid core variables were specified, please check your inventory/vars.
quiet: true
- name: Validate hypervisor relationship
ansible.builtin.assert:
that:
- system_cfg.type == "physical" or hypervisor_type in ["libvirt", "proxmox", "vmware", "xen"]
fail_msg: "hypervisor.type must be one of: libvirt, proxmox, vmware, xen when system.type=virtual."
quiet: true
- name: Validate hypervisor schema
vars:
hypervisor_allowed_keys: "{{ hypervisor_defaults | dict2items | map(attribute='key') | list }}"
hypervisor_keys: "{{ (hypervisor | default({})) | dict2items | map(attribute='key') | list }}"
hypervisor_unknown_keys: "{{ hypervisor_keys | difference(hypervisor_allowed_keys) }}"
ansible.builtin.assert:
that:
- hypervisor_unknown_keys | length == 0
fail_msg: "Unsupported hypervisor keys: {{ hypervisor_unknown_keys | join(', ') }}"
quiet: true
- name: Validate system schema
vars:
system_allowed_keys: "{{ system_defaults | dict2items | map(attribute='key') | list }}"
system_keys: "{{ (system | default({})) | dict2items | map(attribute='key') | list }}"
system_unknown_keys: "{{ system_keys | difference(system_allowed_keys) }}"
ansible.builtin.assert:
that:
- system_unknown_keys | length == 0
fail_msg: "Unsupported system keys: {{ system_unknown_keys | join(', ') }}."
quiet: true
- name: Validate nested system mappings
loop:
- dns
- user
- root
- luks
- features
loop_control:
label: "{{ item }}"
ansible.builtin.assert:
that:
- system[item] is not defined or system[item] is mapping
fail_msg: "system.{{ item }} must be a dictionary."
quiet: true
- name: Validate system sub-dict schemas
loop:
- dns
- user
- root
- luks
loop_control:
label: "system.{{ item }}"
vars:
sub_input: "{{ (system[item] | default({})) | dict2items | map(attribute='key') | list }}"
sub_allowed: "{{ system_defaults[item] | dict2items | map(attribute='key') | list }}"
sub_unknown: "{{ sub_input | difference(sub_allowed) }}"
ansible.builtin.assert:
that:
- sub_unknown | length == 0
fail_msg: "Unsupported system.{{ item }} keys: {{ sub_unknown | join(', ') }}"
quiet: true
- name: Validate system.luks.tpm2 schema
vars:
tpm2_input: >-
{{
(system.luks if (system.luks is defined and system.luks is mapping) else {}).tpm2
| default({})
}}
tpm2_allowed_keys: "{{ system_defaults.luks.tpm2 | dict2items | map(attribute='key') | list }}"
tpm2_unknown: "{{ (tpm2_input | dict2items | map(attribute='key') | list) | difference(tpm2_allowed_keys) }}"
ansible.builtin.assert:
that:
- system.luks is not defined or system.luks.tpm2 is not defined or system.luks.tpm2 is mapping
- tpm2_unknown | length == 0
fail_msg: "Unsupported system.luks.tpm2 keys: {{ tpm2_unknown | join(', ') }}"
quiet: true
- name: Validate system.features schema
vars:
features_allowed_keys: "{{ system_defaults.features | dict2items | map(attribute='key') | list }}"
features_unknown: >-
{{
((system.features | default({})) | dict2items | map(attribute='key') | list)
| difference(features_allowed_keys)
}}
ansible.builtin.assert:
that:
- features_unknown | length == 0
fail_msg: "Unsupported system.features keys: {{ features_unknown | join(', ') }}"
quiet: true
- name: Validate system.features leaf schemas
loop: "{{ system_defaults.features | dict2items }}"
loop_control:
label: "system.features.{{ item.key }}"
vars:
feature_input: "{{ (system.features | default({}))[item.key] | default({}) }}"
feature_allowed_keys: "{{ item.value | dict2items | map(attribute='key') | list }}"
feature_unknown: "{{ (feature_input | dict2items | map(attribute='key') | list) | difference(feature_allowed_keys) }}"
ansible.builtin.assert:
that:
- feature_input is mapping
- feature_unknown | length == 0
fail_msg: "Unsupported system.features.{{ item.key }} keys: {{ feature_unknown | join(', ') }}"
quiet: true
- name: Validate OS and version inputs
ansible.builtin.assert:
that:
- os is defined
- os in ["almalinux", "alpine", "archlinux", "debian", "fedora", "opensuse", "rhel", "rocky", "ubuntu", "ubuntu-lts", "void"]
- >-
os not in ["debian", "fedora", "rocky", "almalinux", "rhel"]
or (os_version is defined and (os_version | string | length) > 0)
- >-
os_version is not defined or (os_version | string | length) == 0
or (
os == "debian" and (os_version | string) in ["10", "11", "12", "13", "unstable"]
) or (
os == "fedora" and (os_version | string) in ["40", "41", "42", "43"]
) or (
os in ["rocky", "almalinux"]
and (os_version | string) is match("^(8|9|10)(\\.\\d+)?$")
) or (
os == "rhel"
and (os_version | string) is match("^(8|9|10)(\\.\\d+)?$")
) or (
os in ["alpine", "archlinux", "opensuse", "ubuntu", "ubuntu-lts", "void"]
)
fail_msg: "Invalid os/version specified. Please check README.md for supported values."
quiet: true
- name: Validate RHEL ISO requirement
ansible.builtin.assert:
that:
- os != "rhel" or (rhel_iso is defined and (rhel_iso | string | length) > 0)
fail_msg: "rhel_iso is required when os=rhel."
quiet: true
- name: Validate Proxmox hypervisor inputs
when:
- system_cfg.type == "virtual"
- hypervisor_type == "proxmox"
ansible.builtin.assert:
that:
- hypervisor_cfg.url | string | length > 0
- hypervisor_cfg.username | string | length > 0
- hypervisor_cfg.password | string | length > 0
- hypervisor_cfg.host | string | length > 0
- hypervisor_cfg.storage | string | length > 0
- system_cfg.id | string | length > 0
- system_cfg.network | string | length > 0
fail_msg: "Missing required Proxmox inputs. Define hypervisor.(url,username,password,host,storage) and system.(id,network)."
quiet: true
- name: Validate VMware hypervisor inputs
when:
- system_cfg.type == "virtual"
- hypervisor_type == "vmware"
ansible.builtin.assert:
that:
- hypervisor_cfg.url | string | length > 0
- hypervisor_cfg.username | string | length > 0
- hypervisor_cfg.password | string | length > 0
- hypervisor_cfg.datacenter | string | length > 0
- hypervisor_cfg.cluster | string | length > 0
- hypervisor_cfg.storage | string | length > 0
- system_cfg.network | string | length > 0
fail_msg: "Missing required VMware inputs. Define hypervisor.(url,username,password,datacenter,cluster,storage) and system.network."
quiet: true
- name: Validate Xen hypervisor inputs
when:
- system_cfg.type == "virtual"
- hypervisor_type == "xen"
ansible.builtin.assert:
that:
- system_cfg.network | string | length > 0
fail_msg: "Missing required Xen inputs. Define system.network."
quiet: true
- name: Validate virtual installer ISO requirement
ansible.builtin.assert:
that:
- system_cfg.type == "physical" or (boot_iso is defined and (boot_iso | string | length) > 0)
fail_msg: "boot_iso is required when system.type=virtual."
quiet: true
- name: Validate firewall and feature flags
ansible.builtin.assert:
that:
- system_cfg.features.firewall.backend is defined
- system_cfg.features.firewall.backend in ["firewalld", "ufw"]
- system_cfg.features.firewall.toolkit is defined
- system_cfg.features.firewall.toolkit in ["iptables", "nftables"]
- system_cfg.features.firewall.enabled is defined
- system_cfg.features.banner.motd is defined
- system_cfg.features.banner.sudo is defined
- system_cfg.luks.enabled is defined
- system_cfg.features.chroot.tool is defined
- system_cfg.features.chroot.tool in ["arch-chroot", "chroot", "systemd-nspawn"]
fail_msg: Invalid feature flags were specified, please check your inventory/vars.
quiet: true
- name: Validate virtual system sizing
when: system_cfg.type == "virtual"
ansible.builtin.assert:
that:
- system_cfg.cpus is defined and (system_cfg.cpus | int) > 0
- system_cfg.memory is defined and (system_cfg.memory | int) > 0
- system_cfg.disks is defined and (system_cfg.disks | length) > 0
- (system_cfg.disks[0].size | float) > 0
- (system_cfg.disks[0].size | float) >= 20
- >-
system_cfg.filesystem != "btrfs"
or (
(system_cfg.disks[0].size | float)
>= (
(
(system_cfg.memory | float / 1024 >= 16.0)
| ternary(
(system_cfg.memory | float / 2048),
[system_cfg.memory | float / 1024, 4.0] | max
)
)
+ 5.5
)
)
fail_msg: "Invalid system sizing. Check system.cpus, system.memory, and system.disks[0].size."
quiet: true
- name: Validate all virtual disks have a positive size
when: system_cfg.type == "virtual"
ansible.builtin.assert:
that:
- item.size is defined
- (item.size | float) > 0
fail_msg: "Each system disk must have a positive size when system.type=virtual: {{ item | to_json }}"
quiet: true
loop: "{{ system_cfg.disks | default([]) }}"
loop_control:
label: "{{ item | to_json }}"
- name: Validate primary disk mount is not used
when:
- system_cfg.disks is defined
- system_cfg.disks | length > 0
ansible.builtin.assert:
that:
- (system_cfg.disks[0].mount.path | default('') | string | trim) == ''
fail_msg: "system.disks[0].mount.path must be empty; use system.disks[1:] for additional mounts."
quiet: true
- name: Validate disk mountpoint inputs
vars:
system_disk_mounts: >-
{{
(system_cfg.disks | default([]))
| map(attribute='mount')
| map(attribute='path')
| map('string')
| map('trim')
| reject('equalto', '')
| list
}}
ansible.builtin.assert:
that:
- system_disk_mounts | length == (system_disk_mounts | unique | list | length)
fail_msg: "Duplicate disk mountpoints found in system.disks."
quiet: true
- name: Validate disk mount definitions
when: system_cfg.disks is defined
vars:
reserved_mounts:
- /boot
- /boot/efi
- /home
- /swap
- /var
- /var/cache/pacman/pkg
- /var/log
- /var/log/audit
disk_mount: "{{ (item.mount.path | default('') | string) | trim }}"
disk_fstype: "{{ (item.mount.fstype | default('') | string) | trim }}"
disk_device: "{{ (item.device | default('') | string) | trim }}"
disk_size: "{{ item.size | default(0) }}"
ansible.builtin.assert:
that:
- disk_mount == "" or disk_mount.startswith("/")
- disk_mount == "" or disk_mount != "/"
- disk_mount == "" or disk_mount not in reserved_mounts
- disk_mount == "" or disk_fstype in ["btrfs", "ext4", "xfs"]
- disk_mount == "" or system_cfg.type == "virtual" or (disk_device | length) > 0
- disk_mount == "" or system_cfg.type != "virtual" or (disk_size | float) > 0
fail_msg: "Invalid system disk entry: {{ item | to_json }}"
quiet: true
loop: "{{ system_cfg.disks }}"
loop_control:
label: "{{ item | to_json }}"
- name: Validate static IP requirements
when: system_cfg.ip is defined and (system_cfg.ip | string | length) > 0
ansible.builtin.assert:
that:
- system_cfg.prefix is defined
- (system_cfg.prefix | int) > 0
fail_msg: "system.prefix is required when system.ip is set."
quiet: true

View File

@@ -1,22 +1,22 @@
--- ---
partitioning_luks_enabled: "{{ luks_enabled | bool }}" partitioning_luks_enabled: "{{ system_cfg.luks.enabled | bool }}"
partitioning_luks_passphrase: "{{ luks_passphrase }}" partitioning_luks_passphrase: "{{ system_cfg.luks.passphrase }}"
partitioning_luks_mapper_name: "{{ luks_mapper_name }}" partitioning_luks_mapper_name: "{{ system_cfg.luks.mapper }}"
partitioning_luks_type: "{{ luks_type }}" partitioning_luks_type: "{{ system_cfg.luks.type }}"
partitioning_luks_cipher: "{{ luks_cipher }}" partitioning_luks_cipher: "{{ system_cfg.luks.cipher }}"
partitioning_luks_hash: "{{ luks_hash }}" partitioning_luks_hash: "{{ system_cfg.luks.hash }}"
partitioning_luks_iter_time: "{{ luks_iter_time }}" partitioning_luks_iter_time: "{{ system_cfg.luks.iter }}"
partitioning_luks_key_size: "{{ luks_key_size }}" partitioning_luks_key_size: "{{ system_cfg.luks.bits }}"
partitioning_luks_pbkdf: "{{ luks_pbkdf }}" partitioning_luks_pbkdf: "{{ system_cfg.luks.pbkdf }}"
partitioning_luks_use_urandom: "{{ luks_use_urandom | bool }}" partitioning_luks_use_urandom: "{{ system_cfg.luks.urandom | bool }}"
partitioning_luks_verify_passphrase: "{{ luks_verify_passphrase | bool }}" partitioning_luks_verify_passphrase: "{{ system_cfg.luks.verify | bool }}"
partitioning_luks_auto_decrypt: "{{ luks_auto_decrypt | bool }}" partitioning_luks_auto_decrypt: "{{ system_cfg.luks.auto | bool }}"
partitioning_luks_auto_decrypt_method: "{{ luks_auto_decrypt_method }}" partitioning_luks_auto_decrypt_method: "{{ system_cfg.luks.method }}"
partitioning_luks_tpm2_device: "{{ luks_tpm2_device }}" partitioning_luks_tpm2_device: "{{ system_cfg.luks.tpm2.device }}"
partitioning_luks_tpm2_pcrs: "{{ luks_tpm2_pcrs }}" partitioning_luks_tpm2_pcrs: "{{ system_cfg.luks.tpm2.pcrs }}"
partitioning_luks_keyfile_size: "{{ luks_keyfile_size }}" partitioning_luks_keyfile_size: "{{ system_cfg.luks.keysize }}"
partitioning_luks_options: "{{ luks_options }}" partitioning_luks_options: "{{ system_cfg.luks.options }}"
partitioning_btrfs_compress_opt: "{{ 'compress=zstd:15' if zstd_enabled | bool else '' }}" partitioning_btrfs_compress_opt: "{{ 'compress=zstd:15' if system_cfg.features.zstd.enabled | bool else '' }}"
partitioning_boot_partition_suffix: 1 partitioning_boot_partition_suffix: 1
partitioning_main_partition_suffix: 2 partitioning_main_partition_suffix: 2
partitioning_efi_size_mib: 512 partitioning_efi_size_mib: 512
@@ -31,8 +31,8 @@ partitioning_separate_boot: >-
}} }}
partitioning_boot_fs_fstype: >- partitioning_boot_fs_fstype: >-
{{ {{
(filesystem | lower) (system_cfg.filesystem | lower)
if (filesystem | lower) != 'btrfs' if (system_cfg.filesystem | lower) != 'btrfs'
else ('xfs' if is_rhel else 'ext4') else ('xfs' if is_rhel else 'ext4')
}} }}
partitioning_boot_fs_partition_suffix: >- partitioning_boot_fs_partition_suffix: >-
@@ -51,7 +51,7 @@ partitioning_efi_mountpoint: >-
if (partitioning_separate_boot | bool) if (partitioning_separate_boot | bool)
else ( else (
'/boot/efi' '/boot/efi'
if is_rhel or (os | lower in ['debian11', 'debian12', 'debian13', 'ubuntu', 'ubuntu-lts']) if is_rhel or (os in ['ubuntu', 'ubuntu-lts'] or (os == 'debian' and (os_version | string) in ['11', '12', '13']))
else '/boot' else '/boot'
) )
}} }}
@@ -114,29 +114,38 @@ partitioning_root_device: >-
if (partitioning_luks_enabled | bool) if (partitioning_luks_enabled | bool)
else install_drive ~ (partitioning_root_partition_suffix | string) else install_drive ~ (partitioning_root_partition_suffix | string)
}} }}
partitioning_vm_size_effective: >- partitioning_disk_size_gb: >-
{{ {{
( (
partitioning_vm_size partitioning_vm_size
if (partitioning_vm_size is defined and (partitioning_vm_size | float) > 0) if (partitioning_vm_size is defined and (partitioning_vm_size | float) > 0)
else (vm_size if vm_size is defined else 0) else (
(
(system_cfg.disks | default([]) | first | default({})).size
if system_cfg is defined
else 0
) | default(0)
)
) )
| float | float
}} }}
partitioning_vm_memory_effective: >- partitioning_memory_mb: >-
{{ {{
( (
partitioning_vm_memory partitioning_vm_memory
if (partitioning_vm_memory is defined and (partitioning_vm_memory | float) > 0) if (partitioning_vm_memory is defined and (partitioning_vm_memory | float) > 0)
else (vm_memory if vm_memory is defined else 0) else (
(system_cfg.memory if system_cfg is defined else 0)
| default(0)
)
) )
| float | float
}} }}
partitioning_swap_size_gb: >- partitioning_swap_size_gb: >-
{{ {{
((partitioning_vm_memory_effective / 1024) >= 16.0) ((partitioning_memory_mb / 1024) >= 16.0)
| ternary( | ternary(
(partitioning_vm_memory_effective / 2048) | int, (partitioning_memory_mb / 2048) | int,
[partitioning_vm_memory_effective / 1024, 4.0] | max | int [partitioning_memory_mb / 1024, 4.0] | max | int
) )
}} }}

View File

@@ -41,31 +41,31 @@
- name: Make root subvolumes - name: Make root subvolumes
when: when:
- cis_enabled or item.subvol not in ['var_log_audit'] - system_cfg.features.cis.enabled or item.subvol not in ['var_log_audit']
- swap_enabled | bool or item.subvol != 'swap' - system_cfg.features.swap.enabled | bool or item.subvol != 'swap'
ansible.builtin.command: btrfs su cr /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }} ansible.builtin.command: btrfs su cr /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
args: args:
creates: /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }} creates: /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
loop: loop:
- {subvol: root} - { subvol: root }
- {subvol: swap} - { subvol: swap }
- {subvol: home} - { subvol: home }
- {subvol: var} - { subvol: var }
- {subvol: pkg} - { subvol: pkg }
- {subvol: var_log} - { subvol: var_log }
- {subvol: var_log_audit} - { subvol: var_log_audit }
register: partitioning_btrfs_subvol_result register: partitioning_btrfs_subvol_result
- name: Set quotas for subvolumes - name: Set quotas for subvolumes
when: cis_enabled when: system_cfg.features.cis.enabled
ansible.builtin.command: btrfs qgroup limit {{ item.quota }} /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }} ansible.builtin.command: btrfs qgroup limit {{ item.quota }} /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
loop: loop:
- {subvol: home, quota: 2G} - { subvol: home, quota: 2G }
register: partitioning_btrfs_qgroup_result register: partitioning_btrfs_qgroup_result
changed_when: false changed_when: false
- name: Create a Btrfs swap file - name: Create a Btrfs swap file
when: swap_enabled | bool when: system_cfg.features.swap.enabled | bool
ansible.builtin.command: >- ansible.builtin.command: >-
btrfs filesystem mkswapfile --size {{ partitioning_swap_size_gb }}g --uuid clear /mnt/@swap/swapfile btrfs filesystem mkswapfile --size {{ partitioning_swap_size_gb }}g --uuid clear /mnt/@swap/swapfile
args: args:

View File

@@ -1,25 +1,27 @@
--- ---
- name: Create and format ext4 logical volumes - name: Create and format ext4 logical volumes
when: cis_enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit'] when: system_cfg.features.cis.enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit']
community.general.filesystem: community.general.filesystem:
dev: /dev/sys/{{ item.lv }} dev: /dev/sys/{{ item.lv }}
fstype: ext4 fstype: ext4
force: true force: true
loop: loop:
- {lv: root} - { lv: root }
- {lv: home} - { lv: home }
- {lv: var} - { lv: var }
- {lv: var_log} - { lv: var_log }
- {lv: var_log_audit} - { lv: var_log_audit }
- name: Remove Unsupported features for older Systems - name: Remove Unsupported features for older Systems
when: (os | lower in ['almalinux', 'debian11', 'rhel8', 'rhel9', 'rocky']) and (cis_enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit']) when: >
(os in ['almalinux', 'rocky', 'rhel'] or (os == 'debian' and (os_version | string) == '11'))
and (system_cfg.features.cis.enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit'])
ansible.builtin.command: tune2fs -O "^orphan_file,^metadata_csum_seed" "/dev/sys/{{ item.lv }}" ansible.builtin.command: tune2fs -O "^orphan_file,^metadata_csum_seed" "/dev/sys/{{ item.lv }}"
loop: loop:
- {lv: root} - { lv: root }
- {lv: home} - { lv: home }
- {lv: var} - { lv: var }
- {lv: var_log} - { lv: var_log }
- {lv: var_log_audit} - { lv: var_log_audit }
register: partitioning_ext4_tune_result register: partitioning_ext4_tune_result
changed_when: partitioning_ext4_tune_result.rc == 0 changed_when: partitioning_ext4_tune_result.rc == 0

View File

@@ -0,0 +1,85 @@
---
- name: Determine additional disks to auto-mount
ansible.builtin.set_fact:
partitioning_extra_disks: >-
{{
(system_cfg.disks | default([]))[1:]
| selectattr('mount.path')
| list
}}
changed_when: false
- name: Validate additional disks do not target install_drive
when: partitioning_extra_disks | length > 0
ansible.builtin.assert:
that:
- item.device is defined
- item.device | string | length > 0
- item.device != install_drive
- item.partition is defined
- item.partition | string | length > 0
- item.mount.fstype is defined
- item.mount.fstype in ['btrfs', 'ext4', 'xfs']
- item.mount.path is defined
- item.mount.path | string | length > 0
- item.mount.path.startswith('/')
- item.mount.path != '/'
fail_msg: "Invalid additional disk definition: {{ item | to_json }}"
quiet: true
loop: "{{ partitioning_extra_disks }}"
loop_control:
label: "{{ item | to_json }}"
- name: Partition additional disks
when: partitioning_extra_disks | length > 0
community.general.parted:
device: "{{ item.device }}"
label: gpt
number: 1
part_start: "1MiB"
part_end: "100%"
name: "{{ (item.mount.label | default('') | string | length > 0) | ternary(item.mount.label, 'data') }}"
state: present
loop: "{{ partitioning_extra_disks }}"
loop_control:
label: "{{ item.device }}"
- name: Settle partition tables for additional disks
when: partitioning_extra_disks | length > 0
ansible.builtin.command: udevadm settle
changed_when: false
- name: Create filesystems on additional disks
when: partitioning_extra_disks | length > 0
community.general.filesystem:
dev: "{{ item.partition }}"
fstype: "{{ item.mount.fstype }}"
opts: "{{ ('-L ' ~ item.mount.label) if (item.mount.label | default('') | string | length) > 0 else omit }}"
force: true
loop: "{{ partitioning_extra_disks }}"
loop_control:
label: "{{ item.partition }}"
- name: Ensure mount directories exist for additional disks
when: partitioning_extra_disks | length > 0
ansible.builtin.file:
path: "/mnt{{ item.mount.path }}"
state: directory
owner: root
group: root
mode: "0755"
loop: "{{ partitioning_extra_disks }}"
loop_control:
label: "{{ item.mount.path }}"
- name: Mount additional disks for fstab generation
when: partitioning_extra_disks | length > 0
ansible.posix.mount:
path: "/mnt{{ item.mount.path }}"
src: "{{ item.partition }}"
fstype: "{{ item.mount.fstype }}"
opts: "{{ item.mount.opts | default('defaults') }}"
state: mounted
loop: "{{ partitioning_extra_disks }}"
loop_control:
label: "{{ item.mount.path }}"

View File

@@ -1,9 +1,9 @@
--- ---
- name: Detect system memory for swap sizing - name: Detect system memory for swap sizing
when: when:
- swap_enabled | bool - system_cfg.features.swap.enabled | bool
- partitioning_vm_memory is not defined or (partitioning_vm_memory | float) <= 0 - partitioning_vm_memory is not defined or (partitioning_vm_memory | float) <= 0
- vm_memory is not defined or (vm_memory | float) <= 0 - system_cfg is not defined or (system_cfg.memory | default(0) | float) <= 0
block: block:
- name: Read system memory - name: Read system memory
ansible.builtin.command: awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo ansible.builtin.command: awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo
@@ -17,9 +17,8 @@
- name: Set partitioning vm_size for physical installs - name: Set partitioning vm_size for physical installs
when: when:
- install_type == "physical" - system_cfg.type == "physical"
- partitioning_vm_size is not defined or (partitioning_vm_size | float) <= 0 - partitioning_vm_size is not defined or (partitioning_vm_size | float) <= 0
- vm_size is not defined or (vm_size | float) <= 0
- install_drive | length > 0 - install_drive | length > 0
block: block:
- name: Detect install drive size - name: Detect install drive size
@@ -156,15 +155,12 @@
- name: Configure LUKS encryption - name: Configure LUKS encryption
when: partitioning_luks_enabled | bool when: partitioning_luks_enabled | bool
vars:
partitioning_luks_passphrase_effective: >-
{{ partitioning_luks_passphrase | string }}
block: block:
- name: Validate LUKS passphrase - name: Validate LUKS passphrase
ansible.builtin.assert: ansible.builtin.assert:
that: that:
- partitioning_luks_passphrase_effective | length > 0 - (partitioning_luks_passphrase | string | length) > 0
fail_msg: luks_passphrase (or partitioning_luks_passphrase) must be set when LUKS is enabled. fail_msg: system.luks.passphrase must be set when LUKS is enabled.
no_log: true no_log: true
- name: Ensure LUKS container exists - name: Ensure LUKS container exists
@@ -178,7 +174,7 @@
pbkdf: pbkdf:
algorithm: "{{ partitioning_luks_pbkdf }}" algorithm: "{{ partitioning_luks_pbkdf }}"
iteration_time: "{{ (partitioning_luks_iter_time | float) / 1000 }}" iteration_time: "{{ (partitioning_luks_iter_time | float) / 1000 }}"
passphrase: "{{ partitioning_luks_passphrase_effective }}" passphrase: "{{ partitioning_luks_passphrase | string }}"
register: partitioning_luks_format_result register: partitioning_luks_format_result
no_log: true no_log: true
@@ -207,7 +203,7 @@
device: "{{ partitioning_luks_device }}" device: "{{ partitioning_luks_device }}"
state: opened state: opened
name: "{{ partitioning_luks_mapper_name }}" name: "{{ partitioning_luks_mapper_name }}"
passphrase: "{{ partitioning_luks_passphrase_effective }}" passphrase: "{{ partitioning_luks_passphrase | string }}"
allow_discards: "{{ 'discard' in (partitioning_luks_options | lower) }}" allow_discards: "{{ 'discard' in (partitioning_luks_options | lower) }}"
register: partitioning_luks_open_result register: partitioning_luks_open_result
no_log: true no_log: true
@@ -235,7 +231,7 @@
device: "{{ partitioning_luks_device }}" device: "{{ partitioning_luks_device }}"
state: opened state: opened
name: "{{ partitioning_luks_mapper_name }}" name: "{{ partitioning_luks_mapper_name }}"
passphrase: "{{ partitioning_luks_passphrase_effective }}" passphrase: "{{ partitioning_luks_passphrase | string }}"
allow_discards: "{{ 'discard' in (partitioning_luks_options | lower) }}" allow_discards: "{{ 'discard' in (partitioning_luks_options | lower) }}"
register: partitioning_luks_open_retry register: partitioning_luks_open_retry
no_log: true no_log: true
@@ -250,7 +246,7 @@
partitioning_luks_uuid: "{{ partitioning_luks_uuid_result.stdout | trim }}" partitioning_luks_uuid: "{{ partitioning_luks_uuid_result.stdout | trim }}"
- name: Create LVM logical volumes - name: Create LVM logical volumes
when: filesystem != 'btrfs' when: system_cfg.filesystem != 'btrfs'
block: block:
- name: Create LVM volume group - name: Create LVM volume group
community.general.lvg: community.general.lvg:
@@ -259,8 +255,8 @@
- name: Create LVM logical volumes - name: Create LVM logical volumes
when: when:
- cis_enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit'] - system_cfg.features.cis.enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit']
- swap_enabled | bool or item.lv != 'swap' - system_cfg.features.swap.enabled | bool or item.lv != 'swap'
vars: vars:
partitioning_lvm_extent_reserve_count: 10 partitioning_lvm_extent_reserve_count: 10
partitioning_lvm_extent_size_mib: 4 partitioning_lvm_extent_size_mib: 4
@@ -276,11 +272,11 @@
{{ {{
( (
[ [
(partitioning_vm_memory_effective | float / 1024), (partitioning_memory_mb | float / 1024),
4 4
] | max | float ] | max | float
) )
if swap_enabled | bool if system_cfg.features.swap.enabled | bool
else 0 else 0
}} }}
partitioning_lvm_swap_cap_gb: >- partitioning_lvm_swap_cap_gb: >-
@@ -288,14 +284,14 @@
( (
4 4
+ [ + [
(partitioning_vm_size_effective | float) - 20, (partitioning_disk_size_gb | float) - 20,
0 0
] | max ] | max
) )
if swap_enabled | bool if system_cfg.features.swap.enabled | bool
else 0 else 0
}} }}
partitioning_lvm_swap_target_effective_gb: >- partitioning_lvm_swap_target_limited_gb: >-
{{ {{
( (
[ [
@@ -303,7 +299,7 @@
partitioning_lvm_swap_cap_gb partitioning_lvm_swap_cap_gb
] | min ] | min
) )
if swap_enabled | bool if system_cfg.features.swap.enabled | bool
else 0 else 0
}} }}
partitioning_lvm_swap_max_gb: >- partitioning_lvm_swap_max_gb: >-
@@ -311,31 +307,31 @@
( (
[ [
( (
(partitioning_vm_size_effective | float) (partitioning_disk_size_gb | float)
- (partitioning_reserved_gb | float) - (partitioning_reserved_gb | float)
- (cis_enabled | ternary(7.5, 0)) - (system_cfg.features.cis.enabled | ternary(7.5, 0))
- partitioning_lvm_extent_reserve_gb - partitioning_lvm_extent_reserve_gb
- 4 - 4
), ),
0 0
] | max ] | max
) )
if swap_enabled | bool if system_cfg.features.swap.enabled | bool
else 0 else 0
}} }}
partitioning_lvm_available_gb: >- partitioning_lvm_available_gb: >-
{{ {{
( (
(partitioning_vm_size_effective | float) (partitioning_disk_size_gb | float)
- (partitioning_reserved_gb | float) - (partitioning_reserved_gb | float)
- (cis_enabled | ternary(7.5, 0)) - (system_cfg.features.cis.enabled | ternary(7.5, 0))
- partitioning_lvm_extent_reserve_gb - partitioning_lvm_extent_reserve_gb
- partitioning_lvm_swap_target_effective_gb - partitioning_lvm_swap_target_limited_gb
) | float ) | float
}} }}
partitioning_lvm_home_gb: >- partitioning_lvm_home_gb: >-
{{ {{
([([(((partitioning_vm_size_effective | float) - 20) * 0.1), 2] | max), 20] | min) ([([(((partitioning_disk_size_gb | float) - 20) * 0.1), 2] | max), 20] | min)
}} }}
partitioning_lvm_root_default_gb: >- partitioning_lvm_root_default_gb: >-
{{ {{
@@ -347,7 +343,7 @@
( (
((partitioning_lvm_available_gb | float) > 12) ((partitioning_lvm_available_gb | float) > 12)
| ternary( | ternary(
((partitioning_vm_size_effective | float) * 0.4) ((partitioning_disk_size_gb | float) * 0.4)
| round(0, 'ceil'), | round(0, 'ceil'),
partitioning_lvm_available_gb partitioning_lvm_available_gb
) )
@@ -361,24 +357,24 @@
{{ {{
( (
[ [
partitioning_lvm_swap_target_effective_gb, partitioning_lvm_swap_target_limited_gb,
partitioning_lvm_swap_max_gb partitioning_lvm_swap_max_gb
] | min | round(2, 'floor') ] | min | round(2, 'floor')
) )
if swap_enabled | bool if system_cfg.features.swap.enabled | bool
else 0 else 0
}} }}
partitioning_lvm_root_full_gb: >- partitioning_lvm_root_full_gb: >-
{{ {{
[ [
( (
(partitioning_vm_size_effective | float) (partitioning_disk_size_gb | float)
- (partitioning_reserved_gb | float) - (partitioning_reserved_gb | float)
- (partitioning_lvm_swap_gb | float) - (partitioning_lvm_swap_gb | float)
- partitioning_lvm_extent_reserve_gb - partitioning_lvm_extent_reserve_gb
- ( - (
(partitioning_lvm_home_gb | float) + 5.5 (partitioning_lvm_home_gb | float) + 5.5
if cis_enabled if system_cfg.features.cis.enabled
else 0 else 0
) )
), ),
@@ -403,9 +399,9 @@
size: "{{ partitioning_lvm_swap_gb | string + 'G' }}" size: "{{ partitioning_lvm_swap_gb | string + 'G' }}"
- lv: home - lv: home
size: "{{ partitioning_lvm_home_gb | string + 'G' }}" size: "{{ partitioning_lvm_home_gb | string + 'G' }}"
- {lv: var, size: "2G"} - { lv: var, size: "2G" }
- {lv: var_log, size: "2G"} - { lv: var_log, size: "2G" }
- {lv: var_log_audit, size: "1.5G"} - { lv: var_log_audit, size: "1.5G" }
- name: Create filesystems - name: Create filesystems
block: block:
@@ -427,7 +423,7 @@
when: when:
- partitioning_separate_boot | bool - partitioning_separate_boot | bool
- partitioning_boot_fs_fstype == 'ext4' - partitioning_boot_fs_fstype == 'ext4'
- os | lower in ['almalinux', 'debian11', 'rhel8', 'rhel9', 'rocky'] - os in ['almalinux', 'rocky', 'rhel'] or (os == 'debian' and (os_version | string) == '11')
ansible.builtin.command: >- ansible.builtin.command: >-
tune2fs -O "^orphan_file,^metadata_csum_seed" tune2fs -O "^orphan_file,^metadata_csum_seed"
"{{ install_drive }}{{ partitioning_boot_fs_partition_suffix }}" "{{ install_drive }}{{ partitioning_boot_fs_partition_suffix }}"
@@ -436,14 +432,14 @@
- name: Create swap filesystem - name: Create swap filesystem
when: when:
- filesystem != 'btrfs' - system_cfg.filesystem != 'btrfs'
- swap_enabled | bool - system_cfg.features.swap.enabled | bool
community.general.filesystem: community.general.filesystem:
fstype: swap fstype: swap
dev: /dev/sys/swap dev: /dev/sys/swap
- name: Create filesystem - name: Create filesystem
ansible.builtin.include_tasks: "{{ filesystem }}.yml" ansible.builtin.include_tasks: "{{ system_cfg.filesystem }}.yml"
- name: Get UUID for boot filesystem - name: Get UUID for boot filesystem
ansible.builtin.command: blkid -s UUID -o value '{{ install_drive }}{{ partitioning_boot_partition_suffix }}' ansible.builtin.command: blkid -s UUID -o value '{{ install_drive }}{{ partitioning_boot_partition_suffix }}'
@@ -463,83 +459,83 @@
changed_when: false changed_when: false
- name: Get UUID for LVM root filesystem - name: Get UUID for LVM root filesystem
when: filesystem != 'btrfs' when: system_cfg.filesystem != 'btrfs'
ansible.builtin.command: blkid -s UUID -o value /dev/sys/root ansible.builtin.command: blkid -s UUID -o value /dev/sys/root
register: partitioning_uuid_root_result register: partitioning_uuid_root_result
changed_when: false changed_when: false
- name: Get UUID for LVM swap filesystem - name: Get UUID for LVM swap filesystem
when: when:
- filesystem != 'btrfs' - system_cfg.filesystem != 'btrfs'
- swap_enabled | bool - system_cfg.features.swap.enabled | bool
ansible.builtin.command: blkid -s UUID -o value /dev/sys/swap ansible.builtin.command: blkid -s UUID -o value /dev/sys/swap
register: partitioning_uuid_swap_result register: partitioning_uuid_swap_result
changed_when: false changed_when: false
- name: Get UUID for LVM home filesystem - name: Get UUID for LVM home filesystem
when: when:
- filesystem != 'btrfs' - system_cfg.filesystem != 'btrfs'
- cis_enabled - system_cfg.features.cis.enabled
ansible.builtin.command: blkid -s UUID -o value /dev/sys/home ansible.builtin.command: blkid -s UUID -o value /dev/sys/home
register: partitioning_uuid_home_result register: partitioning_uuid_home_result
changed_when: false changed_when: false
- name: Get UUID for LVM var filesystem - name: Get UUID for LVM var filesystem
when: when:
- filesystem != 'btrfs' - system_cfg.filesystem != 'btrfs'
- cis_enabled - system_cfg.features.cis.enabled
ansible.builtin.command: blkid -s UUID -o value /dev/sys/var ansible.builtin.command: blkid -s UUID -o value /dev/sys/var
register: partitioning_uuid_var_result register: partitioning_uuid_var_result
changed_when: false changed_when: false
- name: Get UUID for LVM var_log filesystem - name: Get UUID for LVM var_log filesystem
when: when:
- filesystem != 'btrfs' - system_cfg.filesystem != 'btrfs'
- cis_enabled - system_cfg.features.cis.enabled
ansible.builtin.command: blkid -s UUID -o value /dev/sys/var_log ansible.builtin.command: blkid -s UUID -o value /dev/sys/var_log
register: partitioning_uuid_var_log_result register: partitioning_uuid_var_log_result
changed_when: false changed_when: false
- name: Get UUID for LVM var_log_audit filesystem - name: Get UUID for LVM var_log_audit filesystem
when: when:
- filesystem != 'btrfs' - system_cfg.filesystem != 'btrfs'
- cis_enabled - system_cfg.features.cis.enabled
ansible.builtin.command: blkid -s UUID -o value /dev/sys/var_log_audit ansible.builtin.command: blkid -s UUID -o value /dev/sys/var_log_audit
register: partitioning_uuid_var_log_audit_result register: partitioning_uuid_var_log_audit_result
changed_when: false changed_when: false
- name: Assign UUIDs to Variables - name: Assign UUIDs to Variables
when: filesystem != 'btrfs' when: system_cfg.filesystem != 'btrfs'
ansible.builtin.set_fact: ansible.builtin.set_fact:
partitioning_uuid_root: "{{ partitioning_uuid_root_result.stdout_lines | default([]) }}" partitioning_uuid_root: "{{ partitioning_uuid_root_result.stdout_lines | default([]) }}"
partitioning_uuid_swap: >- partitioning_uuid_swap: >-
{{ {{
partitioning_uuid_swap_result.stdout_lines | default([]) partitioning_uuid_swap_result.stdout_lines | default([])
if swap_enabled | bool if system_cfg.features.swap.enabled | bool
else '' else ''
}} }}
partitioning_uuid_home: >- partitioning_uuid_home: >-
{{ {{
partitioning_uuid_home_result.stdout_lines | default([]) partitioning_uuid_home_result.stdout_lines | default([])
if cis_enabled if system_cfg.features.cis.enabled
else '' else ''
}} }}
partitioning_uuid_var: >- partitioning_uuid_var: >-
{{ {{
partitioning_uuid_var_result.stdout_lines | default([]) partitioning_uuid_var_result.stdout_lines | default([])
if cis_enabled if system_cfg.features.cis.enabled
else '' else ''
}} }}
partitioning_uuid_var_log: >- partitioning_uuid_var_log: >-
{{ {{
partitioning_uuid_var_log_result.stdout_lines | default([]) partitioning_uuid_var_log_result.stdout_lines | default([])
if cis_enabled if system_cfg.features.cis.enabled
else '' else ''
}} }}
partitioning_uuid_var_log_audit: >- partitioning_uuid_var_log_audit: >-
{{ {{
partitioning_uuid_var_log_audit_result.stdout_lines | default([]) partitioning_uuid_var_log_audit_result.stdout_lines | default([])
if cis_enabled if system_cfg.features.cis.enabled
else '' else ''
}} }}
@@ -548,19 +544,19 @@
- name: Mount filesystems and subvolumes - name: Mount filesystems and subvolumes
when: when:
- >- - >-
cis_enabled or ( system_cfg.features.cis.enabled or (
not cis_enabled and ( not system_cfg.features.cis.enabled and (
(filesystem == 'btrfs' and item.path in ['/home', '/var/log', '/var/cache/pacman/pkg']) (system_cfg.filesystem == 'btrfs' and item.path in ['/home', '/var/log', '/var/cache/pacman/pkg'])
or (item.path not in ['/home', '/var', '/var/log', '/var/log/audit', '/var/cache/pacman/pkg']) or (item.path not in ['/home', '/var', '/var/log', '/var/log/audit', '/var/cache/pacman/pkg'])
) )
) )
- >- - >-
not (item.path in ['/swap', '/var/cache/pacman/pkg'] and filesystem != 'btrfs') not (item.path in ['/swap', '/var/cache/pacman/pkg'] and system_cfg.filesystem != 'btrfs')
- swap_enabled | bool or item.path != '/swap' - system_cfg.features.swap.enabled | bool or item.path != '/swap'
ansible.posix.mount: ansible.posix.mount:
path: /mnt{{ item.path }} path: /mnt{{ item.path }}
src: "{{ 'UUID=' + (partitioning_main_uuid.stdout if filesystem == 'btrfs' else item.uuid) }}" src: "{{ 'UUID=' + (partitioning_main_uuid.stdout if system_cfg.filesystem == 'btrfs' else item.uuid) }}"
fstype: "{{ filesystem }}" fstype: "{{ system_cfg.filesystem }}"
opts: "{{ item.opts }}" opts: "{{ item.opts }}"
state: mounted state: mounted
loop: loop:
@@ -569,7 +565,7 @@
opts: >- opts: >-
{{ {{
'defaults' 'defaults'
if filesystem != 'btrfs' if system_cfg.filesystem != 'btrfs'
else [ else [
'rw', 'relatime', partitioning_btrfs_compress_opt, 'ssd', 'space_cache=v2', 'rw', 'relatime', partitioning_btrfs_compress_opt, 'ssd', 'space_cache=v2',
'discard=async', 'subvol=@' 'discard=async', 'subvol=@'
@@ -588,7 +584,7 @@
opts: >- opts: >-
{{ {{
'defaults,nosuid,nodev' 'defaults,nosuid,nodev'
if filesystem != 'btrfs' if system_cfg.filesystem != 'btrfs'
else [ else [
'rw', 'nosuid', 'nodev', 'relatime', partitioning_btrfs_compress_opt, 'ssd', 'rw', 'nosuid', 'nodev', 'relatime', partitioning_btrfs_compress_opt, 'ssd',
'space_cache=v2', 'discard=async', 'subvol=@home' 'space_cache=v2', 'discard=async', 'subvol=@home'
@@ -599,7 +595,7 @@
opts: >- opts: >-
{{ {{
'defaults,nosuid,nodev' 'defaults,nosuid,nodev'
if filesystem != 'btrfs' if system_cfg.filesystem != 'btrfs'
else [ else [
'rw', 'nosuid', 'nodev', 'relatime', partitioning_btrfs_compress_opt, 'ssd', 'rw', 'nosuid', 'nodev', 'relatime', partitioning_btrfs_compress_opt, 'ssd',
'space_cache=v2', 'discard=async', 'subvol=@var' 'space_cache=v2', 'discard=async', 'subvol=@var'
@@ -610,7 +606,7 @@
opts: >- opts: >-
{{ {{
'defaults,nosuid,nodev,noexec' 'defaults,nosuid,nodev,noexec'
if filesystem != 'btrfs' if system_cfg.filesystem != 'btrfs'
else [ else [
'rw', 'nosuid', 'nodev', 'noexec', 'relatime', partitioning_btrfs_compress_opt, 'rw', 'nosuid', 'nodev', 'noexec', 'relatime', partitioning_btrfs_compress_opt,
'ssd', 'space_cache=v2', 'discard=async', 'subvol=@var_log' 'ssd', 'space_cache=v2', 'discard=async', 'subvol=@var_log'
@@ -621,7 +617,7 @@
opts: >- opts: >-
{{ {{
'defaults,nosuid,nodev,noexec' 'defaults,nosuid,nodev,noexec'
if filesystem != 'btrfs' if system_cfg.filesystem != 'btrfs'
else [ else [
'rw', 'nosuid', 'nodev', 'noexec', 'relatime', partitioning_btrfs_compress_opt, 'rw', 'nosuid', 'nodev', 'noexec', 'relatime', partitioning_btrfs_compress_opt,
'ssd', 'space_cache=v2', 'discard=async', 'subvol=@pkg' 'ssd', 'space_cache=v2', 'discard=async', 'subvol=@pkg'
@@ -632,7 +628,7 @@
opts: >- opts: >-
{{ {{
'defaults,nosuid,nodev,noexec' 'defaults,nosuid,nodev,noexec'
if filesystem != 'btrfs' if system_cfg.filesystem != 'btrfs'
else [ else [
'rw', 'nosuid', 'nodev', 'noexec', 'relatime', partitioning_btrfs_compress_opt, 'rw', 'nosuid', 'nodev', 'noexec', 'relatime', partitioning_btrfs_compress_opt,
'ssd', 'space_cache=v2', 'discard=async', 'subvol=@var_log_audit' 'ssd', 'space_cache=v2', 'discard=async', 'subvol=@var_log_audit'
@@ -656,10 +652,13 @@
state: mounted state: mounted
- name: Activate swap - name: Activate swap
when: swap_enabled | bool when: system_cfg.features.swap.enabled | bool
vars: vars:
partitioning_swap_cmd: >- partitioning_swap_cmd: >-
{{ 'swapon /mnt/swap/swapfile' if filesystem == 'btrfs' else 'swapon -U ' + partitioning_uuid_swap[0] }} {{ 'swapon /mnt/swap/swapfile' if system_cfg.filesystem == 'btrfs' else 'swapon -U ' + partitioning_uuid_swap[0] }}
ansible.builtin.command: "{{ partitioning_swap_cmd }}" ansible.builtin.command: "{{ partitioning_swap_cmd }}"
register: partitioning_swap_activate_result register: partitioning_swap_activate_result
changed_when: partitioning_swap_activate_result.rc == 0 changed_when: partitioning_swap_activate_result.rc == 0
- name: Mount additional disks
ansible.builtin.include_tasks: extra_disks.yml

View File

@@ -1,13 +1,13 @@
--- ---
- name: Create and format XFS logical volumes - name: Create and format XFS logical volumes
when: cis_enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit'] when: system_cfg.features.cis.enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit']
community.general.filesystem: community.general.filesystem:
dev: /dev/sys/{{ item.lv }} dev: /dev/sys/{{ item.lv }}
fstype: xfs fstype: xfs
force: true force: true
loop: loop:
- {lv: root} - { lv: root }
- {lv: home} - { lv: home }
- {lv: var} - { lv: var }
- {lv: var_log} - { lv: var_log }
- {lv: var_log_audit} - { lv: var_log_audit }

View File

@@ -0,0 +1,109 @@
---
- name: VM existence protection check
when: system_cfg.type == "virtual"
block:
- name: Check if VM already exists on libvirt
when: hypervisor_type == "libvirt"
delegate_to: localhost
become: false
community.libvirt.virt:
command: list_vms
register: system_check_libvirt_existing_vms
changed_when: false
failed_when: false
- name: Abort if VM already exists on libvirt
when: hypervisor_type == "libvirt"
ansible.builtin.assert:
that:
- hostname not in system_check_libvirt_existing_vms.domains | default([])
fail_msg: |
VM {{ hostname }} already exists on libvirt hypervisor.
To avoid data loss, the playbook will not overwrite or delete existing VMs.
Please choose a different hostname or remove the existing VM manually before proceeding.
quiet: true
- name: Check if VM already exists on Proxmox
when: hypervisor_type == "proxmox"
delegate_to: localhost
become: false
community.proxmox.proxmox_vm_info:
api_host: "{{ hypervisor_cfg.url }}"
api_user: "{{ hypervisor_cfg.username }}"
api_password: "{{ hypervisor_cfg.password }}"
node: "{{ hypervisor_cfg.host }}"
vmid: "{{ system_cfg.id }}"
name: "{{ hostname }}"
type: qemu
register: system_check_proxmox_check_result
changed_when: false
- name: Abort if VM already exists on Proxmox
when: hypervisor_type == "proxmox"
ansible.builtin.assert:
that:
- system_check_proxmox_check_result.proxmox_vms | default([]) | length == 0
fail_msg: |
VM {{ hostname }} (ID: {{ system_cfg.id }}) already exists on Proxmox hypervisor.
To avoid data loss, the playbook will not overwrite or delete existing VMs.
Please choose a different hostname or VM ID, or remove the existing VM manually before proceeding.
quiet: true
- name: Check if VM already exists in vCenter
when: hypervisor_type == "vmware"
delegate_to: localhost
community.vmware.vmware_guest_info:
hostname: "{{ hypervisor_cfg.url }}"
username: "{{ hypervisor_cfg.username }}"
password: "{{ hypervisor_cfg.password }}"
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
datacenter: "{{ hypervisor_cfg.datacenter }}"
name: "{{ hostname }}"
folder: "{{ system_cfg.path if system_cfg.path | length > 0 else omit }}"
register: system_check_vmware_check_result
failed_when: false
changed_when: false
- name: Fail if vCenter lookup failed unexpectedly
when: hypervisor_type == "vmware"
ansible.builtin.assert:
that:
- not system_check_vmware_check_result.failed
or (system_check_vmware_check_result.msg is search('non-existing VM'))
fail_msg: |
Unable to verify VM existence in vCenter.
{{ system_check_vmware_check_result.msg | default('Unknown error') }}
quiet: true
- name: Abort if VM already exists in vCenter
when: hypervisor_type == "vmware"
ansible.builtin.assert:
that:
- system_check_vmware_check_result.instance is not defined
fail_msg: |
VM {{ hostname }} already exists in vCenter.
To avoid data loss, the playbook will not overwrite or delete existing VMs.
Please choose a different hostname or remove the existing VM manually before proceeding.
quiet: true
- name: Check if VM already exists on Xen
when: hypervisor_type == "xen"
delegate_to: localhost
ansible.builtin.command:
argv:
- xl
- list
register: system_check_xen_existing_vms
changed_when: false
failed_when: false
- name: Abort if VM already exists on Xen
when: hypervisor_type == "xen"
ansible.builtin.assert:
that:
- hostname not in system_check_xen_existing_vms.stdout | default('')
fail_msg: |
VM {{ hostname }} already exists on Xen hypervisor.
To avoid data loss, the playbook will not overwrite or delete existing VMs.
Please choose a different hostname or remove the existing VM manually before proceeding.
quiet: true

View File

@@ -1,12 +1,17 @@
--- ---
virtualization_libvirt_image_dir: >- virtualization_libvirt_image_dir: >-
{{ vm_path if vm_path is defined and vm_path | length > 0 else '/var/lib/libvirt/images' }} {{
system_cfg.path
if system_cfg is defined and (system_cfg.path | string | length) > 0
else '/var/lib/libvirt/images'
}}
virtualization_libvirt_disk_path: >- virtualization_libvirt_disk_path: >-
{{ [virtualization_libvirt_image_dir, hostname ~ '.qcow2'] | ansible.builtin.path_join }} {{ [virtualization_libvirt_image_dir, hostname ~ '.qcow2'] | ansible.builtin.path_join }}
virtualization_libvirt_cloudinit_path: >- virtualization_libvirt_cloudinit_path: >-
{{ [virtualization_libvirt_image_dir, hostname ~ '-cloudinit.iso'] | ansible.builtin.path_join }} {{ [virtualization_libvirt_image_dir, hostname ~ '-cloudinit.iso'] | ansible.builtin.path_join }}
virtualization_mac_address: >- virtualization_mac_address: >-
{{ '52:54:00' | community.general.random_mac(seed=hostname) }} {{ '52:54:00' | community.general.random_mac(seed=hostname) }}
virtualization_xen_disk_path: /var/lib/xen/images
virtualization_tpm2_enabled: >- virtualization_tpm2_enabled: >-
{{ {{

View File

@@ -1,5 +1,31 @@
--- ---
- name: Create VM disk - name: Build disk definitions
ansible.builtin.set_fact:
virtualization_libvirt_disks: "{{ virtualization_libvirt_disks | default([]) + [virtualization_libvirt_disk_cfg] }}"
vars:
device_letter_map: "abcdefghijklmnopqrstuvwxyz"
device_letter: "{{ device_letter_map[ansible_loop.index0] }}"
virtualization_libvirt_disk_cfg: >-
{{
{
'path': (
virtualization_libvirt_disk_path
if ansible_loop.index0 == 0
else ([virtualization_libvirt_image_dir, hostname ~ '-disk' ~ ansible_loop.index0 ~ '.qcow2'] | ansible.builtin.path_join)
),
'target': 'vd' ~ device_letter,
'bus': 'virtio',
'format': 'qcow2',
'size': (item.size | float)
}
}}
loop: "{{ system_cfg.disks }}"
loop_control:
label: "{{ item | to_json }}"
extended: true
changed_when: false
- name: Create VM disks
delegate_to: localhost delegate_to: localhost
ansible.builtin.command: ansible.builtin.command:
argv: argv:
@@ -7,19 +33,22 @@
- create - create
- -f - -f
- qcow2 - qcow2
- "{{ virtualization_libvirt_disk_path }}" - "{{ item.path }}"
- "{{ vm_size }}G" - "{{ item.size }}G"
creates: "{{ virtualization_libvirt_disk_path }}" creates: "{{ item.path }}"
loop: "{{ virtualization_libvirt_disks }}"
loop_control:
label: "{{ item.path }}"
- name: Render cloud config templates - name: Render cloud config templates
delegate_to: localhost delegate_to: localhost
ansible.builtin.template: ansible.builtin.template:
src: "{{ item.src }}" src: "{{ item.src }}"
dest: /tmp/{{ item.dest_prefix }}-{{ hostname }}.yml dest: /tmp/{{ item.dest_prefix }}-{{ hostname }}.yml
mode: '0644' mode: "0644"
loop: loop:
- {src: cloud-user-data.yml.j2, dest_prefix: cloud-user-data} - { src: cloud-user-data.yml.j2, dest_prefix: cloud-user-data }
- {src: cloud-network-config.yml.j2, dest_prefix: cloud-network-config} - { src: cloud-network-config.yml.j2, dest_prefix: cloud-network-config }
- name: Create cloud-init disk - name: Create cloud-init disk
delegate_to: localhost delegate_to: localhost
@@ -43,3 +72,9 @@
community.libvirt.virt: community.libvirt.virt:
name: "{{ hostname }}" name: "{{ hostname }}"
state: running state: running
register: virtualization_libvirt_start_result
- name: Set VM created fact
ansible.builtin.set_fact:
virtualization_vm_created_in_run: true
when: virtualization_libvirt_start_result is defined and virtualization_libvirt_start_result.changed | bool

View File

@@ -1,3 +1,3 @@
--- ---
- name: Create Virtual Machine - name: Create Virtual Machine
ansible.builtin.include_tasks: "{{ hypervisor }}.yml" ansible.builtin.include_tasks: "{{ hypervisor_type }}.yml"

View File

@@ -2,38 +2,26 @@
- name: Deploy VM on Proxmox - name: Deploy VM on Proxmox
delegate_to: localhost delegate_to: localhost
vars: vars:
virtualization_dns_value: "{{ vm_dns if vm_dns is defined else '' }}" virtualization_proxmox_scsi: >-
virtualization_dns_list_raw: >- {%- set out = {} -%}
{{ {%- for disk in system_cfg.disks -%}
virtualization_dns_value {%- set _ = out.update({ 'scsi' ~ loop.index0: hypervisor_cfg.storage ~ ':' ~ (disk.size | int) }) -%}
if virtualization_dns_value is iterable and virtualization_dns_value is not string {%- endfor -%}
else virtualization_dns_value.split(',') {{ out }}
}}
virtualization_dns_list: >-
{{ virtualization_dns_list_raw | map('trim') | reject('equalto', '') | list }}
virtualization_search_value: "{{ vm_dns_search if vm_dns_search is defined else '' }}"
virtualization_search_list_raw: >-
{{
virtualization_search_value
if virtualization_search_value is iterable and virtualization_search_value is not string
else virtualization_search_value.split(',')
}}
virtualization_search_list: >-
{{ virtualization_search_list_raw | map('trim') | reject('equalto', '') | list }}
community.proxmox.proxmox_kvm: community.proxmox.proxmox_kvm:
api_host: "{{ hypervisor_url }}" api_host: "{{ hypervisor_cfg.url }}"
api_user: "{{ hypervisor_username }}" api_user: "{{ hypervisor_cfg.username }}"
api_password: "{{ hypervisor_password }}" api_password: "{{ hypervisor_cfg.password }}"
ciuser: "{{ user_name }}" ciuser: "{{ system_cfg.user.name }}"
cipassword: "{{ user_password }}" cipassword: "{{ system_cfg.user.password }}"
ciupgrade: false ciupgrade: false
node: "{{ hypervisor_node }}" node: "{{ hypervisor_cfg.host }}"
vmid: "{{ vm_id }}" vmid: "{{ system_cfg.id }}"
name: "{{ hostname }}" name: "{{ hostname }}"
cpu: host cpu: host
cores: "{{ vm_cpus }}" cores: "{{ system_cfg.cpus }}"
memory: "{{ vm_memory }}" memory: "{{ system_cfg.memory }}"
balloon: "{{ vm_ballo if vm_ballo is defined and vm_ballo | int > 0 else omit }}" balloon: "{{ system_cfg.balloon if system_cfg.balloon is defined and system_cfg.balloon | int > 0 else omit }}"
numa_enabled: true numa_enabled: true
hotplug: network,disk hotplug: network,disk
update: "{{ virtualization_tpm2_enabled | bool }}" update: "{{ virtualization_tpm2_enabled | bool }}"
@@ -42,45 +30,51 @@
machine: "{{ 'q35' if virtualization_tpm2_enabled | bool else omit }}" machine: "{{ 'q35' if virtualization_tpm2_enabled | bool else omit }}"
boot: ac boot: ac
scsihw: virtio-scsi-single scsihw: virtio-scsi-single
scsi: scsi: "{{ virtualization_proxmox_scsi }}"
scsi0: "{{ hypervisor_storage }}:{{ vm_size }}"
efidisk0: efidisk0:
efitype: 4m efitype: 4m
format: raw format: raw
pre_enrolled_keys: false pre_enrolled_keys: false
storage: "{{ hypervisor_storage }}" storage: "{{ hypervisor_cfg.storage }}"
tpmstate0: >- tpmstate0: >-
{{ {{
{'storage': hypervisor_storage, 'version': '2.0'} {'storage': hypervisor_cfg.storage, 'version': '2.0'}
if virtualization_tpm2_enabled | bool if virtualization_tpm2_enabled | bool
else omit else omit
}} }}
ide: ide:
ide0: "{{ boot_iso }},media=cdrom" ide0: "{{ boot_iso }},media=cdrom"
ide1: "{{ rhel_iso + ',media=cdrom' if rhel_iso is defined and rhel_iso | length > 0 else omit }}" ide1: "{{ rhel_iso + ',media=cdrom' if rhel_iso is defined and rhel_iso | length > 0 else omit }}"
ide2: "{{ hypervisor_storage }}:cloudinit" ide2: "{{ hypervisor_cfg.storage }}:cloudinit"
net: net:
net0: virtio,bridge={{ vm_nif }}{% if vlan_name is defined and vlan_name | length > 0 %},tag={{ vlan_name }}{% endif %} net0: >-
virtio,bridge={{ system_cfg.network }}{% if system_cfg.vlan is defined and system_cfg.vlan | string | length > 0 %},tag={{ system_cfg.vlan }}{% endif %}
ipconfig: ipconfig:
ipconfig0: >- ipconfig0: >-
{{ {{
'ip=' ~ vm_ip ~ '/' ~ vm_nms 'ip=' ~ system_cfg.ip ~ '/' ~ system_cfg.prefix
~ (',gw=' ~ vm_gw if vm_gw is defined and vm_gw | length else '') ~ (',gw=' ~ system_cfg.gateway if system_cfg.gateway is defined and system_cfg.gateway | length else '')
if vm_ip is defined and vm_ip | length if system_cfg.ip is defined and system_cfg.ip | string | length
else 'ip=dhcp' else 'ip=dhcp'
}} }}
nameservers: "{{ virtualization_dns_list if virtualization_dns_list | length else omit }}" nameservers: "{{ system_cfg.dns.servers if system_cfg.dns.servers | length else omit }}"
searchdomains: "{{ virtualization_search_list if virtualization_search_list | length else omit }}" searchdomains: "{{ system_cfg.dns.search if system_cfg.dns.search | length else omit }}"
onboot: true onboot: true
state: present state: present
- name: Start VM on Proxmox - name: Start VM on Proxmox
delegate_to: localhost delegate_to: localhost
community.proxmox.proxmox_kvm: community.proxmox.proxmox_kvm:
api_host: "{{ hypervisor_url }}" api_host: "{{ hypervisor_cfg.url }}"
api_user: "{{ hypervisor_username }}" api_user: "{{ hypervisor_cfg.username }}"
api_password: "{{ hypervisor_password }}" api_password: "{{ hypervisor_cfg.password }}"
node: "{{ hypervisor_node }}" node: "{{ hypervisor_cfg.host }}"
name: "{{ hostname }}" name: "{{ hostname }}"
vmid: "{{ vm_id }}" vmid: "{{ system_cfg.id }}"
state: started state: started
register: virtualization_proxmox_start_result
- name: Set VM created fact
ansible.builtin.set_fact:
virtualization_vm_created_in_run: true
when: virtualization_proxmox_start_result is defined and virtualization_proxmox_start_result.changed | bool

View File

@@ -1,26 +1,36 @@
--- ---
- name: Build vCenter disk list
ansible.builtin.set_fact:
virtualization_vmware_disks: "{{ virtualization_vmware_disks | default([]) + [virtualization_vmware_disk_cfg] }}"
vars:
virtualization_vmware_disk_cfg:
size_gb: "{{ item.size | int }}"
type: thin
datastore: "{{ hypervisor_cfg.storage }}"
loop: "{{ system_cfg.disks }}"
loop_control:
label: "{{ item | to_json }}"
changed_when: false
- name: Create VM in vCenter - name: Create VM in vCenter
delegate_to: localhost delegate_to: localhost
community.vmware.vmware_guest: community.vmware.vmware_guest:
hostname: "{{ hypervisor_url }}" hostname: "{{ hypervisor_cfg.url }}"
username: "{{ hypervisor_username }}" username: "{{ hypervisor_cfg.username }}"
password: "{{ hypervisor_password }}" password: "{{ hypervisor_cfg.password }}"
validate_certs: false validate_certs: "{{ hypervisor_cfg.certs | bool }}"
datacenter: "{{ hypervisor_datacenter }}" datacenter: "{{ hypervisor_cfg.datacenter }}"
cluster: "{{ hypervisor_cluster }}" cluster: "{{ hypervisor_cfg.cluster }}"
folder: "{{ vm_path if vm_path is defined and vm_path | length > 0 else omit }}" folder: "{{ system_cfg.path if system_cfg.path | string | length > 0 else omit }}"
name: "{{ hostname }}" name: "{{ hostname }}"
guest_id: otherLinux64Guest guest_id: otherLinux64Guest
annotation: | annotation: |
{{ note if note is defined else '' }} {{ note if note is defined else '' }}
state: "{{ 'poweredoff' if virtualization_tpm2_enabled | bool else 'poweredon' }}" state: "{{ 'poweredoff' if virtualization_tpm2_enabled | bool else 'poweredon' }}"
disk: disk: "{{ virtualization_vmware_disks }}"
- size_gb: "{{ vm_size }}"
type: thin
datastore: "{{ hypervisor_storage }}"
hardware: hardware:
memory_mb: "{{ vm_memory }}" memory_mb: "{{ system_cfg.memory }}"
num_cpus: "{{ vm_cpus }}" num_cpus: "{{ system_cfg.cpus }}"
boot_firmware: efi boot_firmware: efi
secure_boot: false secure_boot: false
cdrom: >- cdrom: >-
@@ -44,20 +54,29 @@
} ] if rhel_iso is defined and rhel_iso | length > 0 else [] ) } ] if rhel_iso is defined and rhel_iso | length > 0 else [] )
}} }}
networks: networks:
- name: "{{ vm_nif }}" - name: "{{ system_cfg.network }}"
type: dhcp type: dhcp
vlan: "{{ vlan_name if vlan_name is defined and vlan_name | length > 0 else omit }}" vlan: "{{ system_cfg.vlan if system_cfg.vlan is defined and system_cfg.vlan | string | length > 0 else omit }}"
register: virtualization_vmware_create_result
- name: Set VM created fact when VM was powered on during creation
ansible.builtin.set_fact:
virtualization_vm_created_in_run: true
when:
- virtualization_vmware_create_result is defined
- not virtualization_tpm2_enabled | bool
- virtualization_vmware_create_result.changed | bool
- name: Ensure vTPM2 is enabled when required - name: Ensure vTPM2 is enabled when required
when: virtualization_tpm2_enabled | bool when: virtualization_tpm2_enabled | bool
delegate_to: localhost delegate_to: localhost
community.vmware.vmware_guest_tpm: community.vmware.vmware_guest_tpm:
hostname: "{{ hypervisor_url }}" hostname: "{{ hypervisor_cfg.url }}"
username: "{{ hypervisor_username }}" username: "{{ hypervisor_cfg.username }}"
password: "{{ hypervisor_password }}" password: "{{ hypervisor_cfg.password }}"
validate_certs: false validate_certs: "{{ hypervisor_cfg.certs | bool }}"
datacenter: "{{ hypervisor_datacenter }}" datacenter: "{{ hypervisor_cfg.datacenter }}"
folder: "{{ vm_path if vm_path is defined and vm_path | length > 0 else omit }}" folder: "{{ system_cfg.path if system_cfg.path | string | length > 0 else omit }}"
name: "{{ hostname }}" name: "{{ hostname }}"
state: present state: present
@@ -65,10 +84,19 @@
when: virtualization_tpm2_enabled | bool when: virtualization_tpm2_enabled | bool
delegate_to: localhost delegate_to: localhost
vmware.vmware.vm_powerstate: vmware.vmware.vm_powerstate:
hostname: "{{ hypervisor_url }}" hostname: "{{ hypervisor_cfg.url }}"
username: "{{ hypervisor_username }}" username: "{{ hypervisor_cfg.username }}"
password: "{{ hypervisor_password }}" password: "{{ hypervisor_cfg.password }}"
validate_certs: false validate_certs: "{{ hypervisor_cfg.certs | bool }}"
datacenter: "{{ hypervisor_datacenter }}" datacenter: "{{ hypervisor_cfg.datacenter }}"
name: "{{ hostname }}" name: "{{ hostname }}"
state: powered-on state: powered-on
register: virtualization_vmware_start_result
- name: Set VM created fact when VM was started separately (TPM2 case)
ansible.builtin.set_fact:
virtualization_vm_created_in_run: true
when:
- virtualization_tpm2_enabled | bool
- virtualization_vmware_start_result is defined
- virtualization_vmware_start_result.changed | bool

View File

@@ -0,0 +1,77 @@
---
- name: Deploy VM on Xen
block:
- name: Build disk definitions
ansible.builtin.set_fact:
virtualization_xen_disks: "{{ virtualization_xen_disks | default([]) + [virtualization_xen_disk_cfg] }}"
vars:
device_letter_map: "abcdefghijklmnopqrstuvwxyz"
device_letter: "{{ device_letter_map[ansible_loop.index0] }}"
virtualization_xen_disk_cfg: >-
{{
{
'path': (
virtualization_xen_disk_path ~ '/' ~ hostname ~ '.qcow2'
if ansible_loop.index0 == 0
else virtualization_xen_disk_path ~ '/' ~ hostname ~ '-disk' ~ ansible_loop.index0 ~ '.qcow2'
),
'target': 'xvd' ~ device_letter,
'size': (item.size | float)
}
}}
loop: "{{ system_cfg.disks }}"
loop_control:
label: "{{ item | to_json }}"
extended: true
changed_when: false
- name: Create VM disks for Xen
delegate_to: localhost
ansible.builtin.command:
argv:
- qemu-img
- create
- -f
- qcow2
- "{{ item.path }}"
- "{{ item.size }}G"
creates: "{{ item.path }}"
loop: "{{ virtualization_xen_disks }}"
loop_control:
label: "{{ item.path }}"
- name: Render Xen VM configuration
delegate_to: localhost
vars:
xen_installer_media_enabled: true
ansible.builtin.template:
src: xen.cfg.j2
dest: /tmp/xen-{{ hostname }}.cfg
mode: "0644"
- name: Create Xen VM
delegate_to: localhost
ansible.builtin.command:
argv:
- xl
- create
- /tmp/xen-{{ hostname }}.cfg
register: virtualization_xen_create_result
changed_when: virtualization_xen_create_result.rc == 0
- name: Ensure VM is running
delegate_to: localhost
ansible.builtin.command:
argv:
- xl
- list
register: virtualization_xen_list_result
changed_when: false
failed_when: false
- name: Set VM created fact
ansible.builtin.set_fact:
virtualization_vm_created_in_run: true
when:
- virtualization_xen_list_result is defined
- hostname in virtualization_xen_list_result.stdout

View File

@@ -4,27 +4,23 @@ network:
id0: id0:
match: match:
macaddress: "{{ virtualization_mac_address }}" macaddress: "{{ virtualization_mac_address }}"
{% set has_static = vm_ip is defined and vm_ip | length %} {% set has_static = system_cfg.ip is defined and system_cfg.ip | string | length %}
{% set dns_value = vm_dns if vm_dns is defined else '' %} {% set dns_list = system_cfg.dns.servers | default([]) %}
{% set dns_list_raw = dns_value if dns_value is iterable and dns_value is not string else dns_value.split(',') %} {% set search_list = system_cfg.dns.search | default([]) %}
{% set dns_list = dns_list_raw | map('trim') | reject('equalto', '') | list %}
{% set search_value = vm_dns_search if vm_dns_search is defined else '' %}
{% set search_list_raw = search_value if search_value is iterable and search_value is not string else search_value.split(',') %}
{% set search_list = search_list_raw | map('trim') | reject('equalto', '') | list %}
{% if has_static %} {% if has_static %}
addresses: addresses:
- "{{ vm_ip }}/{{ vm_nms }}" - "{{ system_cfg.ip }}/{{ system_cfg.prefix }}"
{% if vm_gw is defined and vm_gw | length %} {% if system_cfg.gateway is defined and system_cfg.gateway | string | length %}
gateway4: "{{ vm_gw }}" gateway4: "{{ system_cfg.gateway }}"
{% endif %} {% endif %}
{% else %} {% else %}
dhcp4: true dhcp4: true
{% if (vm_dns is defined and vm_dns | length) or (vm_dns_search is defined and vm_dns_search | length) %} {% if dns_list | length or search_list | length %}
dhcp4-overrides: dhcp4-overrides:
{% if vm_dns is defined and vm_dns | length %} {% if dns_list | length %}
use-dns: false use-dns: false
{% endif %} {% endif %}
{% if vm_dns_search is defined and vm_dns_search | length %} {% if search_list | length %}
use-domains: false use-domains: false
{% endif %} {% endif %}
{% endif %} {% endif %}

View File

@@ -4,9 +4,9 @@ ssh_pwauth: true
package_update: false package_update: false
package_upgrade: false package_upgrade: false
users: users:
- name: "{{ user_name }}" - name: "{{ system_cfg.user.name }}"
primary_group: "{{ user_name }}" primary_group: "{{ system_cfg.user.name }}"
groups: users groups: users
sudo: ALL=(ALL) NOPASSWD:ALL sudo: ALL=(ALL) NOPASSWD:ALL
passwd: "{{ user_password | password_hash('sha512') }}" passwd: "{{ system_cfg.user.password | password_hash('sha512') }}"
lock_passwd: False lock_passwd: False

View File

@@ -1,8 +1,8 @@
<domain type='kvm'> <domain type='kvm'>
<name>{{ hostname }}</name> <name>{{ hostname }}</name>
<memory>{{ vm_memory | int * 1024 }}</memory> <memory>{{ system_cfg.memory | int * 1024 }}</memory>
{% if vm_ballo is defined and vm_ballo | int > 0 %}<currentMemory>{{ vm_ballo | int * 1024 }}</currentMemory>{% endif %} {% if system_cfg.balloon is defined and system_cfg.balloon | int > 0 %}<currentMemory>{{ system_cfg.balloon | int * 1024 }}</currentMemory>{% endif %}
<vcpu placement='static'>{{ vm_cpus }}</vcpu> <vcpu placement='static'>{{ system_cfg.cpus }}</vcpu>
<os> <os>
<type arch='x86_64' machine="pc-q35-8.0">hvm</type> <type arch='x86_64' machine="pc-q35-8.0">hvm</type>
<bootmenu enable='no'/> <bootmenu enable='no'/>
@@ -22,11 +22,13 @@
<on_reboot>restart</on_reboot> <on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash> <on_crash>destroy</on_crash>
<devices> <devices>
{% for disk in virtualization_libvirt_disks | default([]) %}
<disk type='file' device='disk'> <disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/> <driver name='qemu' type='{{ disk.format }}'/>
<source file='{{ virtualization_libvirt_disk_path }}'/> <source file='{{ disk.path }}'/>
<target dev='vda' bus='virtio'/> <target dev='{{ disk.target }}' bus='{{ disk.bus }}'/>
</disk> </disk>
{% endfor %}
<disk type="file" device="cdrom"> <disk type="file" device="cdrom">
<driver name="qemu" type="raw"/> <driver name="qemu" type="raw"/>
<source file="{{ boot_iso }}"/> <source file="{{ boot_iso }}"/>

17
templates/xen.cfg.j2 Normal file
View File

@@ -0,0 +1,17 @@
builder = "hvm"
name = "{{ hostname }}"
memory = "{{ system_cfg.memory }}"
vcpus = "{{ system_cfg.cpus }}"
disk = [
{%- for disk in virtualization_xen_disks | default([]) -%}
'file:{{ disk.path }},{{ disk.target }},w'{% if not loop.last or xen_installer_media_enabled | bool %}, {% endif %}
{%- endfor -%}
{%- if xen_installer_media_enabled | bool -%}
'{{ boot_iso }},,hdc,cdrom'{% if rhel_iso is defined and rhel_iso | length > 0 %}, '{{ rhel_iso }},,hdd,cdrom'{% endif %}
{%- endif -%}
]
vif = [ 'bridge={{ system_cfg.network }},model=e1000' ]
boot = "{{ 'dc' if xen_installer_media_enabled | bool else 'c' }}"
on_crash = "preserve"
on_poweroff = "destroy"
serial = "pty"

View File

@@ -1,19 +1,61 @@
--- ---
hypervisor: "none" # Example variables for baremetal installs.
install_type: "physical" hypervisor:
install_drive: "/dev/sda" type: "none"
os: "archlinux" system:
filesystem: "btrfs" type: "physical"
os: "archlinux"
cis: false filesystem: "btrfs" # btrfs|ext4|xfs
selinux: true name: "{{ inventory_hostname }}"
firewalld_enabled: true cpus: 8
memory: 16384
luks_enabled: true ip: "{{ ansible_host | default('') }}"
luks_passphrase: "1234" prefix: 24
luks_mapper_name: "SYSTEM_DECRYPTED" gateway: "10.0.0.1"
luks_auto_decrypt: true dns:
luks_auto_decrypt_method: "tpm2" servers:
luks_tpm2_device: "auto" - "1.1.1.1"
luks_tpm2_pcrs: "7" disks:
- device: "/dev/sda"
size: 120
- device: "/dev/sdb"
size: 500
mount:
path: /data
fstype: ext4
user:
name: "admin"
password: "CHANGE_ME"
key: "ssh-ed25519 AAAA..."
root:
password: "CHANGE_ME"
luks:
enabled: true
passphrase: "CHANGE_ME"
mapper: "SYSTEM_DECRYPTED"
auto: true
method: "tpm2"
tpm2:
device: "auto"
pcrs: "7"
features:
cis:
enabled: false
selinux:
enabled: true
firewall:
enabled: true
backend: "firewalld"
toolkit: "nftables"
ssh:
enabled: true
zstd:
enabled: true
swap:
enabled: true
banner:
motd: true
sudo: true
chroot:
tool: "arch-chroot"

View File

@@ -1,42 +1,93 @@
--- ---
# Set vm_ip for static addressing. Remove vm_ip to use DHCP. # Example variables for virtual provisioning.
vm_ip: "{{ inventory_hostname }}" custom_iso: false
install_type: "virtual" hypervisor:
install_drive: "/dev/sda" # Use /dev/vda for virtio/libvirt. type: "proxmox" # libvirt|proxmox|vmware|xen|none
custom_iso: false # Set true to skip ArchISO-specific validation and pacman setup. url: "pve01.example.com"
cis: false # Set true to enable CIS hardening. username: "root@pam"
selinux: true # Toggle SELinux where supported. password: "CHANGE_ME"
firewalld_enabled: true # Toggle firewalld package and service. host: "pve01"
storage: "local-lvm"
datacenter: "dc01"
cluster: "cluster01"
certs: false
ssh: true # VMware only; enables temporary SSH in installer
hypervisor_url: "pve01.example.com" system:
hypervisor_username: "root@pam" type: "virtual" # virtual|physical
hypervisor_password: "CHANGE_ME" os: "archlinux"
hypervisor_node: "pve01" version: ""
hypervisor_storage: "local-lvm" filesystem: "btrfs" # btrfs|ext4|xfs
hypervisor_datacenter: "dc01" name: "{{ inventory_hostname }}"
hypervisor_cluster: "cluster01" id: 100
cpus: 4
# VMware (only needed when hypervisor: vmware) memory: 8192
# vm_path: "/Folder" # Optional folder path segment in vCenter. balloon: 0
vmware_ssh: true network: "vmbr0"
ip: "{{ inventory_hostname }}"
# LUKS disk encryption (optional) prefix: 24
# These map to partitioning_luks_* internally. gateway: "10.0.0.1"
luks_enabled: false dns:
luks_passphrase: "CHANGE_ME" servers:
luks_mapper_name: "SYSTEM_DECRYPTED" - "1.1.1.1"
luks_auto_decrypt: true - "1.0.0.1"
luks_auto_decrypt_method: "tpm2" search:
luks_tpm2_device: "auto" - "example.com"
luks_tpm2_pcrs: "7" path: "/Lab/Example"
luks_keyfile_size: 64 disks:
luks_options: "discard,tries=3" - size: 80
luks_type: "luks2" - size: 200
luks_cipher: "aes-xts-plain64" mount:
luks_hash: "sha512" path: /data
luks_iter_time: 4000 fstype: xfs
luks_key_size: 512 label: DATA
luks_pbkdf: "argon2id" opts: defaults
luks_use_urandom: true user:
luks_verify_passphrase: true name: "ops"
password: "CHANGE_ME"
key: "ssh-ed25519 AAAA..."
root:
password: "CHANGE_ME"
luks:
enabled: false
passphrase: "CHANGE_ME"
mapper: "SYSTEM_DECRYPTED"
auto: true
method: "tpm2"
tpm2:
device: "auto"
pcrs: "7"
keysize: 64
options: "discard,tries=3"
type: "luks2"
cipher: "aes-xts-plain64"
hash: "sha512"
iter: 4000
bits: 512
pbkdf: "argon2id"
urandom: true
verify: true
packages:
- jq
- tmux
features:
cis:
enabled: false
selinux:
enabled: true
firewall:
enabled: true
backend: "firewalld" # firewalld|ufw
toolkit: "nftables" # nftables|iptables
ssh:
enabled: true
zstd:
enabled: true
swap:
enabled: true
banner:
motd: true
sudo: true
chroot:
tool: "arch-chroot" # arch-chroot|chroot|systemd-nspawn