Compare commits
5 Commits
master
...
0f54698fc3
| Author | SHA1 | Date | |
|---|---|---|---|
| 0f54698fc3 | |||
| 8071a7c56c | |||
| d7260a8078 | |||
| 1b9ef51048 | |||
| 0bd11d5463 |
31
README.md
31
README.md
@@ -122,10 +122,11 @@ all:
|
||||
mount:
|
||||
path: /data
|
||||
fstype: xfs
|
||||
user:
|
||||
name: ops
|
||||
users:
|
||||
- name: ops
|
||||
password: CHANGE_ME
|
||||
key: "ssh-ed25519 AAAA..."
|
||||
keys:
|
||||
- "ssh-ed25519 AAAA..."
|
||||
root:
|
||||
password: CHANGE_ME
|
||||
luks:
|
||||
@@ -173,7 +174,7 @@ Top-level host install/runtime settings. Use these keys under `system`.
|
||||
| `packages` | list | `[]` | Additional packages installed post-reboot |
|
||||
| `network` | dict | see below | Network configuration |
|
||||
| `disks` | list | `[]` | Disk layout (see [Multi-Disk Schema](#45-multi-disk-schema)) |
|
||||
| `user` | dict | see below | User account settings |
|
||||
| `users` | list | `[]` | User accounts (see below) |
|
||||
| `root` | dict | see below | Root account settings |
|
||||
| `luks` | dict | see below | Encryption settings |
|
||||
| `features` | dict | see below | Feature toggles |
|
||||
@@ -181,24 +182,28 @@ Top-level host install/runtime settings. Use these keys under `system`.
|
||||
#### `system.network`
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| -------------- | ----------- | ------- | --------------------------------------------------- |
|
||||
| -------------- | ---------- | ------- | ---------------------------------------------------- |
|
||||
| `bridge` | string | empty | Hypervisor network/bridge name |
|
||||
| `vlan` | string/int | empty | VLAN tag |
|
||||
| `ip` | string | empty | Static IP (omit for DHCP) |
|
||||
| `prefix` | int | empty | CIDR prefix for static IP |
|
||||
| `gateway` | string | empty | Default gateway (static only) |
|
||||
| `dns.servers` | list/string | `[]` | DNS resolvers; comma-separated string is normalized |
|
||||
| `dns.search` | list/string | `[]` | Search domains; comma-separated string is normalized |
|
||||
| `dns.servers` | list | `[]` | DNS resolvers (must be a YAML list) |
|
||||
| `dns.search` | list | `[]` | Search domains (must be a YAML list) |
|
||||
| `interfaces` | list | `[]` | Multi-NIC config (overrides flat fields above) |
|
||||
|
||||
#### `system.user`
|
||||
When `interfaces` is empty, the flat fields (`bridge`, `ip`, `prefix`, `gateway`, `vlan`) are auto-wrapped into a single-entry `interfaces[]` list. When `interfaces` is set, it takes precedence and the flat fields are back-populated from `interfaces[0]` for backward compatibility. Each `interfaces[]` entry supports: `name`, `bridge` (required), `vlan`, `ip`, `prefix`, `gateway`.
|
||||
|
||||
Credentials are prompted interactively by default via `vars_prompt` in `main.yml`, but can be supplied via inventory, vars files, or `-e` for non-interactive runs.
|
||||
#### `system.users`
|
||||
|
||||
A list of user account dictionaries. Credentials for the first user are prompted interactively by default via `vars_prompt` in `main.yml`, but can be supplied via inventory, vars files, or `-e` for non-interactive runs.
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ---------- | ------ | ------- | ------------------------------------- |
|
||||
| `name` | string | empty | Username created on target |
|
||||
| ---------- | ------ | ------- | -------------------------------------------- |
|
||||
| `name` | string | empty | Username created on target (required) |
|
||||
| `password` | string | empty | User password (also used for sudo) |
|
||||
| `key` | string | empty | SSH public key for `authorized_keys` |
|
||||
| `keys` | list | `[]` | SSH public keys for `authorized_keys` |
|
||||
| `sudo` | string | empty | Custom sudoers rule (optional, per-user) |
|
||||
|
||||
#### `system.root`
|
||||
|
||||
@@ -387,7 +392,7 @@ To protect sensitive information such as passwords, API keys, and other confiden
|
||||
|
||||
- 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 the installer environment differs from the prompted user credentials.
|
||||
- `system.network.dns.servers` and `system.network.dns.search` accept either YAML lists or comma-separated strings.
|
||||
- `system.network.dns.servers` and `system.network.dns.search` must be YAML lists.
|
||||
- `hypervisor.type` selects backend-specific provisioning and cleanup behavior.
|
||||
- Guest tools are selected automatically by hypervisor: `qemu-guest-agent` for `libvirt`/`proxmox`, `open-vm-tools` for `vmware`.
|
||||
- With `system.luks.method: tpm2` on virtual installs, the virtualization role enables a TPM2 device where supported (libvirt/proxmox/vmware).
|
||||
|
||||
@@ -42,10 +42,11 @@ all:
|
||||
fstype: xfs
|
||||
label: DATA
|
||||
opts: defaults
|
||||
user:
|
||||
name: "ops"
|
||||
users:
|
||||
- name: "ops"
|
||||
password: "CHANGE_ME"
|
||||
key: "ssh-ed25519 AAAA..."
|
||||
keys:
|
||||
- "ssh-ed25519 AAAA..."
|
||||
root:
|
||||
password: "CHANGE_ME"
|
||||
packages:
|
||||
@@ -89,17 +90,20 @@ all:
|
||||
prefix: 24
|
||||
gateway: 10.0.0.1
|
||||
dns:
|
||||
servers: "1.1.1.1,1.0.0.1"
|
||||
servers:
|
||||
- "1.1.1.1"
|
||||
- "1.0.0.1"
|
||||
disks:
|
||||
- size: 80
|
||||
- size: 200
|
||||
mount:
|
||||
path: /srv/data
|
||||
fstype: ext4
|
||||
user:
|
||||
name: "dbadmin"
|
||||
users:
|
||||
- name: "dbadmin"
|
||||
password: "CHANGE_ME"
|
||||
key: "ssh-ed25519 AAAA..."
|
||||
keys:
|
||||
- "ssh-ed25519 AAAA..."
|
||||
root:
|
||||
password: "CHANGE_ME"
|
||||
luks:
|
||||
|
||||
@@ -39,10 +39,11 @@ all:
|
||||
mount:
|
||||
path: /var/www
|
||||
fstype: xfs
|
||||
user:
|
||||
name: "web"
|
||||
users:
|
||||
- name: "web"
|
||||
password: "CHANGE_ME"
|
||||
key: "ssh-ed25519 AAAA..."
|
||||
keys:
|
||||
- "ssh-ed25519 AAAA..."
|
||||
root:
|
||||
password: "CHANGE_ME"
|
||||
packages:
|
||||
@@ -80,10 +81,11 @@ all:
|
||||
mount:
|
||||
path: /data
|
||||
fstype: ext4
|
||||
user:
|
||||
name: "db"
|
||||
users:
|
||||
- name: "db"
|
||||
password: "CHANGE_ME"
|
||||
key: "ssh-ed25519 AAAA..."
|
||||
keys:
|
||||
- "ssh-ed25519 AAAA..."
|
||||
root:
|
||||
password: "CHANGE_ME"
|
||||
luks:
|
||||
@@ -111,17 +113,20 @@ all:
|
||||
prefix: 24
|
||||
gateway: 192.168.122.1
|
||||
dns:
|
||||
servers: "1.1.1.1,1.0.0.1"
|
||||
servers:
|
||||
- "1.1.1.1"
|
||||
- "1.0.0.1"
|
||||
disks:
|
||||
- size: 80
|
||||
- size: 200
|
||||
mount:
|
||||
path: /data
|
||||
fstype: btrfs
|
||||
user:
|
||||
name: "compute"
|
||||
users:
|
||||
- name: "compute"
|
||||
password: "CHANGE_ME"
|
||||
key: "ssh-ed25519 AAAA..."
|
||||
keys:
|
||||
- "ssh-ed25519 AAAA..."
|
||||
root:
|
||||
password: "CHANGE_ME"
|
||||
features:
|
||||
|
||||
69
main.yml
69
main.yml
@@ -28,29 +28,59 @@
|
||||
- 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_users_input: "{{ system_input.users | default([]) }}"
|
||||
system_first_user: >-
|
||||
{{
|
||||
system_users_input[0]
|
||||
if (system_users_input is iterable and system_users_input is not string
|
||||
and system_users_input is not mapping and system_users_input | length > 0)
|
||||
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_key: "{{ user_public_key | default(user_key | default(system_user_key | default(''), true), true) | string | trim }}"
|
||||
prompt_user_password: "{{ user_password | default(system_user_password | default(''), true) | string }}"
|
||||
prompt_root_password: "{{ root_password | default(system_root_password | default(''), true) | string }}"
|
||||
resolved_user:
|
||||
name: >-
|
||||
{{
|
||||
system_first_user.name | string
|
||||
if (system_first_user.name | default('') | string | length) > 0
|
||||
else prompt_user_name
|
||||
}}
|
||||
keys: >-
|
||||
{{
|
||||
system_first_user['keys']
|
||||
if (system_first_user['keys'] is defined
|
||||
and system_first_user['keys'] is iterable
|
||||
and system_first_user['keys'] is not string
|
||||
and system_first_user['keys'] | length > 0)
|
||||
else (
|
||||
[prompt_user_key]
|
||||
if (prompt_user_key | length > 0)
|
||||
else []
|
||||
)
|
||||
}}
|
||||
password: >-
|
||||
{{
|
||||
system_first_user.password | string
|
||||
if (system_first_user.password | default('') | string | length) > 0
|
||||
else prompt_user_password
|
||||
}}
|
||||
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)
|
||||
},
|
||||
'users': (
|
||||
[resolved_user]
|
||||
+ (system_users_input[1:]
|
||||
if (system_users_input is sequence
|
||||
and system_users_input is not string
|
||||
and system_users_input | length > 1)
|
||||
else [])
|
||||
),
|
||||
'root': {
|
||||
'password': (
|
||||
(system_root_input.password | default('') | string | length) > 0
|
||||
@@ -60,7 +90,6 @@
|
||||
recursive=True
|
||||
)
|
||||
}}
|
||||
changed_when: false
|
||||
|
||||
- name: Load global defaults
|
||||
ansible.builtin.import_role:
|
||||
@@ -109,15 +138,19 @@
|
||||
and (ansible_host | default('') | string | length) > 0
|
||||
)
|
||||
}}
|
||||
changed_when: false
|
||||
|
||||
- name: Reset SSH connection before post-reboot tasks
|
||||
when:
|
||||
- post_reboot_can_connect | bool
|
||||
ansible.builtin.meta: reset_connection
|
||||
|
||||
- name: Set final SSH credentials for post-reboot tasks
|
||||
when:
|
||||
- post_reboot_can_connect | bool
|
||||
ansible.builtin.set_fact:
|
||||
ansible_user: "{{ system_cfg.user.name }}"
|
||||
ansible_password: "{{ system_cfg.user.password }}"
|
||||
ansible_become_password: "{{ system_cfg.user.password }}"
|
||||
ansible_user: "{{ system_cfg.users[0].name }}"
|
||||
ansible_password: "{{ system_cfg.users[0].password }}"
|
||||
ansible_become_password: "{{ system_cfg.users[0].password }}"
|
||||
ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
|
||||
|
||||
- name: Install post-reboot packages
|
||||
|
||||
@@ -175,7 +175,9 @@ bootstrap_ubuntu:
|
||||
{{
|
||||
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']
|
||||
+ ['bash-completion', 'dnsutils', 'duf', 'eza', 'fdupes', 'fio',
|
||||
'ncurses-term', 'software-properties-common', 'systemd-zram-generator',
|
||||
'tldr', 'traceroute', 'util-linux-extra', 'yq', 'zoxide']
|
||||
}}
|
||||
|
||||
bootstrap_ubuntu_lts:
|
||||
@@ -185,7 +187,9 @@ bootstrap_ubuntu_lts:
|
||||
{{
|
||||
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']
|
||||
+ ['bash-completion', 'dnsutils', 'duf', 'eza', 'fdupes', 'fio',
|
||||
'ncurses-term', 'software-properties-common', 'systemd-zram-generator',
|
||||
'tldr', 'traceroute', 'util-linux-extra', 'yq', 'zoxide']
|
||||
}}
|
||||
|
||||
bootstrap_archlinux:
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
---
|
||||
- name: Generate UUID for Network Profile
|
||||
ansible.builtin.set_fact:
|
||||
configuration_net_uuid: "{{ ('LAN-' ~ hostname) | ansible.builtin.to_uuid }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Read network interfaces
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
@@ -15,81 +10,41 @@
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Resolve network interface and MAC address
|
||||
- name: Detect available network interface names
|
||||
vars:
|
||||
configuration_net_inf_from_facts: "{{ (ansible_default_ipv4 | default({})).get('interface', '') }}"
|
||||
configuration_net_inf_from_ip: >-
|
||||
configuration_detected_interfaces: >-
|
||||
{{
|
||||
(
|
||||
configuration_ip_link.stdout
|
||||
| default('')
|
||||
| regex_findall('^[0-9]+: ([^:]+):', multiline=True)
|
||||
| reject('equalto', 'lo')
|
||||
| list
|
||||
| first
|
||||
)
|
||||
| default('')
|
||||
}}
|
||||
configuration_net_inf_detected: >-
|
||||
{{ configuration_net_inf_from_facts | default(configuration_net_inf_from_ip, true) }}
|
||||
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_facts: >-
|
||||
{{
|
||||
(
|
||||
(ansible_facts | default({})).get(configuration_net_inf_detected, {}).get('macaddress', '')
|
||||
)
|
||||
| default(
|
||||
(ansible_facts | default({})).get('ansible_' + configuration_net_inf_detected, {}).get('macaddress', ''),
|
||||
true
|
||||
)
|
||||
}}
|
||||
configuration_net_mac_from_ip: >-
|
||||
{{
|
||||
(
|
||||
configuration_ip_link.stdout
|
||||
| default('')
|
||||
| regex_findall(
|
||||
'^\\d+: ' ~ configuration_net_inf_regex ~ ':.*?link/ether\\s+([0-9A-Fa-f:]{17})',
|
||||
multiline=True
|
||||
)
|
||||
| first
|
||||
)
|
||||
| default('')
|
||||
}}
|
||||
ansible.builtin.set_fact:
|
||||
configuration_net_inf: "{{ configuration_net_inf_detected }}"
|
||||
configuration_net_mac: >-
|
||||
{{
|
||||
(
|
||||
configuration_net_mac_from_virtualization
|
||||
| default(configuration_net_mac_from_facts, true)
|
||||
| default(configuration_net_mac_from_ip, true)
|
||||
)
|
||||
| upper
|
||||
}}
|
||||
changed_when: false
|
||||
configuration_detected_interfaces: "{{ configuration_detected_interfaces }}"
|
||||
|
||||
- name: Validate Network Interface Name
|
||||
- name: Validate at least one network interface detected
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- configuration_net_inf | length > 0
|
||||
fail_msg: Failed to detect an active network interface.
|
||||
- configuration_detected_interfaces | length > 0
|
||||
fail_msg: Failed to detect any network interfaces.
|
||||
|
||||
- name: Validate Network Interface MAC Address
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- configuration_net_mac | length > 0
|
||||
fail_msg: Failed to detect the MAC address for network interface {{ configuration_net_inf }}.
|
||||
|
||||
- name: Configure NetworkManager profile
|
||||
- name: Configure NetworkManager profiles
|
||||
when: os | lower not in ["alpine", "void"]
|
||||
block:
|
||||
- name: Copy NetworkManager keyfile
|
||||
- name: Copy NetworkManager keyfile per interface
|
||||
vars:
|
||||
configuration_iface: "{{ item }}"
|
||||
configuration_iface_name: "{{ configuration_detected_interfaces[idx] | default('eth' ~ idx) }}"
|
||||
configuration_net_uuid: "{{ ('LAN-' ~ idx ~ '-' ~ hostname) | ansible.builtin.to_uuid }}"
|
||||
ansible.builtin.template:
|
||||
src: network.j2
|
||||
dest: /mnt/etc/NetworkManager/system-connections/LAN.nmconnection
|
||||
dest: "/mnt/etc/NetworkManager/system-connections/LAN-{{ idx }}.nmconnection"
|
||||
mode: "0600"
|
||||
loop: "{{ system_cfg.network.interfaces }}"
|
||||
loop_control:
|
||||
index_var: idx
|
||||
label: "LAN-{{ idx }}"
|
||||
|
||||
- name: Fix Ubuntu unmanaged devices
|
||||
when: os | lower in ["ubuntu", "ubuntu-lts"]
|
||||
@@ -102,13 +57,6 @@
|
||||
when: os | lower == "alpine"
|
||||
vars:
|
||||
configuration_dns_list: "{{ system_cfg.network.dns.servers | default([]) }}"
|
||||
configuration_alpine_static: >-
|
||||
{{
|
||||
system_cfg.network.ip is defined
|
||||
and system_cfg.network.ip | string | length > 0
|
||||
and system_cfg.network.prefix is defined
|
||||
and (system_cfg.network.prefix | string | length) > 0
|
||||
}}
|
||||
block:
|
||||
- name: Write Alpine network interfaces
|
||||
ansible.builtin.copy:
|
||||
@@ -117,15 +65,19 @@
|
||||
content: |
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
{% for iface in system_cfg.network.interfaces %}
|
||||
{% set iface_name = configuration_detected_interfaces[loop.index0] | default(iface.name | default('eth' ~ loop.index0)) %}
|
||||
{% set has_static = (iface.ip | default('') | string | length) > 0 %}
|
||||
|
||||
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.network.ip }}/{{ system_cfg.network.prefix }}
|
||||
{% if system_cfg.network.gateway is defined and system_cfg.network.gateway | string | length %}
|
||||
gateway {{ system_cfg.network.gateway }}
|
||||
auto {{ iface_name }}
|
||||
iface {{ iface_name }} inet {{ 'static' if has_static else 'dhcp' }}
|
||||
{% if has_static %}
|
||||
address {{ iface.ip }}/{{ iface.prefix }}
|
||||
{% if iface.gateway | default('') | string | length %}
|
||||
gateway {{ iface.gateway }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
- name: Set Alpine DNS resolvers
|
||||
when: configuration_dns_list | length > 0
|
||||
@@ -141,25 +93,24 @@
|
||||
when: os | lower == "void"
|
||||
vars:
|
||||
configuration_dns_list: "{{ system_cfg.network.dns.servers | default([]) }}"
|
||||
configuration_void_static: >-
|
||||
{{
|
||||
system_cfg.network.ip is defined
|
||||
and system_cfg.network.ip | string | length > 0
|
||||
and system_cfg.network.prefix is defined
|
||||
and (system_cfg.network.prefix | string | length) > 0
|
||||
}}
|
||||
block:
|
||||
- name: Write dhcpcd configuration for static networking
|
||||
when: configuration_void_static | bool
|
||||
- name: Write dhcpcd configuration
|
||||
ansible.builtin.copy:
|
||||
dest: /mnt/etc/dhcpcd.conf
|
||||
mode: "0644"
|
||||
content: |
|
||||
interface {{ configuration_net_inf }}
|
||||
static ip_address={{ system_cfg.network.ip }}/{{ system_cfg.network.prefix }}
|
||||
{% if system_cfg.network.gateway is defined and system_cfg.network.gateway | string | length %}
|
||||
static routers={{ system_cfg.network.gateway }}
|
||||
{% for iface in system_cfg.network.interfaces %}
|
||||
{% set iface_name = configuration_detected_interfaces[loop.index0] | default(iface.name | default('eth' ~ loop.index0)) %}
|
||||
{% set has_static = (iface.ip | default('') | string | length) > 0 %}
|
||||
{% if has_static %}
|
||||
interface {{ iface_name }}
|
||||
static ip_address={{ iface.ip }}/{{ iface.prefix }}
|
||||
{% if iface.gateway | default('') | string | length %}
|
||||
static routers={{ iface.gateway }}
|
||||
{% endif %}
|
||||
{% if configuration_dns_list | length > 0 %}
|
||||
{% if loop.index0 == 0 and configuration_dns_list | length > 0 %}
|
||||
static domain_name_servers={{ configuration_dns_list | join(' ') }}
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -5,3 +5,14 @@
|
||||
dest: /mnt/etc/sudoers.d/01-wheel
|
||||
mode: "0440"
|
||||
validate: /usr/sbin/visudo --check --file=%s
|
||||
|
||||
- name: Deploy per-user sudoers rules
|
||||
when: item.sudo is defined and (item.sudo | string | length) > 0
|
||||
ansible.builtin.copy:
|
||||
content: "{{ item.name }} {{ item.sudo }}\n"
|
||||
dest: "/mnt/etc/sudoers.d/{{ item.name }}"
|
||||
mode: "0440"
|
||||
validate: /usr/sbin/visudo --check --file=%s
|
||||
loop: "{{ system_cfg.users }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
|
||||
@@ -1,37 +1,53 @@
|
||||
---
|
||||
- name: Create user account
|
||||
- name: Set root password
|
||||
vars:
|
||||
configuration_root_cmd: >-
|
||||
{{ chroot_command }} /usr/sbin/usermod --password
|
||||
'{{ system_cfg.root.password | password_hash('sha512') }}' root --shell /bin/bash
|
||||
ansible.builtin.command: "{{ configuration_root_cmd }}"
|
||||
register: configuration_root_result
|
||||
changed_when: configuration_root_result.rc == 0
|
||||
|
||||
- name: Create user accounts
|
||||
vars:
|
||||
configuration_user_group: >-
|
||||
{{ "sudo" if is_debian | bool else "wheel" }}
|
||||
configuration_useradd_cmd: >-
|
||||
{{ chroot_command }} /usr/sbin/useradd --create-home --user-group
|
||||
--groups {{ configuration_user_group }} {{ system_cfg.user.name }}
|
||||
--password {{ system_cfg.user.password | password_hash('sha512') }} --shell /bin/bash
|
||||
configuration_root_cmd: >-
|
||||
{{ chroot_command }} /usr/sbin/usermod --password
|
||||
'{{ system_cfg.root.password | password_hash('sha512') }}' root --shell /bin/bash
|
||||
ansible.builtin.command: "{{ item }}"
|
||||
loop:
|
||||
- "{{ configuration_useradd_cmd }}"
|
||||
- "{{ configuration_root_cmd }}"
|
||||
--uid {{ 1000 + ansible_loop.index0 }}
|
||||
--groups {{ configuration_user_group }} {{ item.name }}
|
||||
--password {{ item.password | password_hash('sha512') }} --shell /bin/bash
|
||||
ansible.builtin.command: "{{ configuration_useradd_cmd }}"
|
||||
loop: "{{ system_cfg.users }}"
|
||||
loop_control:
|
||||
extended: true
|
||||
label: "{{ item.name }}"
|
||||
register: configuration_user_result
|
||||
changed_when: configuration_user_result.rc == 0
|
||||
|
||||
- name: Ensure .ssh directory exists
|
||||
when: system_cfg.user.key | length > 0
|
||||
when: item.keys | default([]) | length > 0
|
||||
ansible.builtin.file:
|
||||
path: /mnt/home/{{ system_cfg.user.name }}/.ssh
|
||||
path: "/mnt/home/{{ item.name }}/.ssh"
|
||||
state: directory
|
||||
owner: 1000
|
||||
group: 1000
|
||||
owner: "{{ 1000 + ansible_loop.index0 }}"
|
||||
group: "{{ 1000 + ansible_loop.index0 }}"
|
||||
mode: "0700"
|
||||
loop: "{{ system_cfg.users }}"
|
||||
loop_control:
|
||||
extended: true
|
||||
label: "{{ item.name }}"
|
||||
|
||||
- name: Add SSH public key to authorized_keys
|
||||
when: system_cfg.user.key | length > 0
|
||||
- name: Add SSH public keys to authorized_keys
|
||||
vars:
|
||||
_uid: "{{ 1000 + (system_cfg.users | map(attribute='name') | list).index(item.0.name) }}"
|
||||
ansible.builtin.lineinfile:
|
||||
path: /mnt/home/{{ system_cfg.user.name }}/.ssh/authorized_keys
|
||||
line: "{{ system_cfg.user.key }}"
|
||||
owner: 1000
|
||||
group: 1000
|
||||
path: "/mnt/home/{{ item.0.name }}/.ssh/authorized_keys"
|
||||
line: "{{ item.1 }}"
|
||||
owner: "{{ _uid }}"
|
||||
group: "{{ _uid }}"
|
||||
mode: "0600"
|
||||
create: true
|
||||
loop: "{{ system_cfg.users | subelements('keys', skip_missing=True) }}"
|
||||
loop_control:
|
||||
label: "{{ item.0.name }}: {{ item.1[:40] }}..."
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
[connection]
|
||||
id=LAN
|
||||
id=LAN-{{ idx }}
|
||||
uuid={{ configuration_net_uuid }}
|
||||
type=ethernet
|
||||
interface-name={{ configuration_iface_name }}
|
||||
|
||||
[ipv4]
|
||||
{% set iface = configuration_iface %}
|
||||
{% set dns_list = system_cfg.network.dns.servers | default([]) %}
|
||||
{% set search_list = system_cfg.network.dns.search | default([]) %}
|
||||
{% if system_cfg.network.ip is defined and system_cfg.network.ip | string | length %}
|
||||
address1={{ system_cfg.network.ip }}/{{ system_cfg.network.prefix }}{{ (',' ~ system_cfg.network.gateway) if (system_cfg.network.gateway is defined and system_cfg.network.gateway | string | length) else '' }}
|
||||
{% if iface.ip | default('') | string | length %}
|
||||
address1={{ iface.ip }}/{{ iface.prefix }}{{ (',' ~ iface.gateway) if (iface.gateway | default('') | string | length) else '' }}
|
||||
method=manual
|
||||
{% else %}
|
||||
method=auto
|
||||
{% endif %}
|
||||
{% if dns_list %}
|
||||
{% if idx | int == 0 and dns_list %}
|
||||
dns={{ dns_list | join(';') }}
|
||||
{% endif %}
|
||||
{% if dns_list %}
|
||||
{% if idx | int == 0 and dns_list %}
|
||||
ignore-auto-dns=true
|
||||
{% endif %}
|
||||
{% if search_list %}
|
||||
{% if idx | int == 0 and search_list %}
|
||||
dns-search={{ search_list | join(';') }}
|
||||
{% endif %}
|
||||
|
||||
|
||||
@@ -36,13 +36,11 @@ system_defaults:
|
||||
dns:
|
||||
servers: []
|
||||
search: []
|
||||
interfaces: []
|
||||
path: ""
|
||||
packages: []
|
||||
disks: []
|
||||
user:
|
||||
name: ""
|
||||
password: ""
|
||||
key: ""
|
||||
users: []
|
||||
root:
|
||||
password: ""
|
||||
luks:
|
||||
|
||||
@@ -66,9 +66,9 @@
|
||||
- system_cfg.type == "virtual"
|
||||
- hypervisor_type != "vmware"
|
||||
ansible.builtin.set_fact:
|
||||
ansible_user: "{{ system_cfg.user.name }}"
|
||||
ansible_password: "{{ system_cfg.user.password }}"
|
||||
ansible_become_password: "{{ system_cfg.user.password }}"
|
||||
ansible_user: "{{ system_cfg.users[0].name }}"
|
||||
ansible_password: "{{ system_cfg.users[0].password }}"
|
||||
ansible_become_password: "{{ system_cfg.users[0].password }}"
|
||||
ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
|
||||
changed_when: false
|
||||
|
||||
|
||||
@@ -2,20 +2,42 @@
|
||||
- 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.network is not defined or system.network is mapping
|
||||
- system.user is not defined or system.user is mapping
|
||||
- system.users is not defined or (system.users is iterable and system.users is not string and system.users is not 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 (network, user, root, luks, features) must be dictionaries."
|
||||
fail_msg: "system and its nested keys (network, root, luks, features) must be dictionaries; system.users must be a list."
|
||||
quiet: true
|
||||
|
||||
- name: Validate DNS lists (not strings)
|
||||
when: system.network is defined and system.network.dns is defined
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- system.network.dns.servers is not defined or (system.network.dns.servers is iterable and system.network.dns.servers is not string)
|
||||
- system.network.dns.search is not defined or (system.network.dns.search is iterable and system.network.dns.search is not string)
|
||||
fail_msg: "system.network.dns.servers and system.network.dns.search must be lists, not strings."
|
||||
quiet: true
|
||||
|
||||
- name: Validate system.users entries
|
||||
when: system.users is defined and system.users | length > 0
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- item is mapping
|
||||
- item.name is defined and (item.name | string | length) > 0
|
||||
- item.keys is not defined or (item.keys is iterable and item.keys is not string)
|
||||
fail_msg: "Each system.users[] entry must be a dict with 'name'; 'keys' must be a list."
|
||||
quiet: true
|
||||
loop: "{{ system.users }}"
|
||||
loop_control:
|
||||
label: "{{ item.name | default('(unnamed)') }}"
|
||||
|
||||
- name: Validate system features input types
|
||||
when: system.features is defined
|
||||
loop: "{{ system_defaults.features | dict2items | map(attribute='key') | list }}"
|
||||
@@ -69,27 +91,28 @@
|
||||
}}
|
||||
gateway: "{{ system_raw.network.gateway | default('') | string }}"
|
||||
dns:
|
||||
servers: >-
|
||||
servers: "{{ system_raw.network.dns.servers | default([]) }}"
|
||||
search: "{{ system_raw.network.dns.search | default([]) }}"
|
||||
interfaces: >-
|
||||
{{
|
||||
(
|
||||
system_raw.network.dns.servers
|
||||
if system_raw.network.dns.servers is iterable and system_raw.network.dns.servers is not string
|
||||
else (system_raw.network.dns.servers | string).split(',')
|
||||
system_raw.network.interfaces
|
||||
if (system_raw.network.interfaces | default([]) | length > 0)
|
||||
else (
|
||||
[{
|
||||
'name': 'eth0',
|
||||
'bridge': system_raw.network.bridge | default('') | string,
|
||||
'vlan': system_raw.network.vlan | default('') | string,
|
||||
'ip': system_raw.network.ip | default('') | string,
|
||||
'prefix': (
|
||||
(system_raw.network.prefix | int | string)
|
||||
if (system_raw.network.prefix | default('') | string | length) > 0
|
||||
else ''
|
||||
),
|
||||
'gateway': system_raw.network.gateway | default('') | string
|
||||
}]
|
||||
if (system_raw.network.bridge | default('') | string | length > 0)
|
||||
else []
|
||||
)
|
||||
| map('trim')
|
||||
| reject('equalto', '')
|
||||
| list
|
||||
}}
|
||||
search: >-
|
||||
{{
|
||||
(
|
||||
system_raw.network.dns.search
|
||||
if system_raw.network.dns.search is iterable and system_raw.network.dns.search is not string
|
||||
else (system_raw.network.dns.search | string).split(',')
|
||||
)
|
||||
| map('trim')
|
||||
| reject('equalto', '')
|
||||
| list
|
||||
}}
|
||||
path: "{{ system_raw.path | default('') | string }}"
|
||||
packages: >-
|
||||
@@ -104,10 +127,7 @@
|
||||
| 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 }}"
|
||||
users: "{{ system_raw.users | default([]) }}"
|
||||
root:
|
||||
password: "{{ system_raw.root.password | string }}"
|
||||
luks:
|
||||
@@ -152,7 +172,26 @@
|
||||
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: Populate primary network fields from first interface
|
||||
when:
|
||||
- system_cfg.network.interfaces | length > 0
|
||||
- system_cfg.network.bridge | default('') | string | length == 0
|
||||
vars:
|
||||
_primary: "{{ system_cfg.network.interfaces[0] }}"
|
||||
ansible.builtin.set_fact:
|
||||
system_cfg: >-
|
||||
{{
|
||||
system_cfg | combine({
|
||||
'network': system_cfg.network | combine({
|
||||
'bridge': _primary.bridge | default(''),
|
||||
'vlan': _primary.vlan | default(''),
|
||||
'ip': _primary.ip | default(''),
|
||||
'prefix': _primary.prefix | default(''),
|
||||
'gateway': _primary.gateway | default('')
|
||||
})
|
||||
}, recursive=True)
|
||||
}}
|
||||
|
||||
- name: Normalize system disks input
|
||||
vars:
|
||||
@@ -187,7 +226,7 @@
|
||||
- name: Initialize normalized disk list
|
||||
ansible.builtin.set_fact:
|
||||
system_disks_cfg: []
|
||||
changed_when: false
|
||||
|
||||
|
||||
- name: Build normalized system disk configuration
|
||||
vars:
|
||||
@@ -247,7 +286,7 @@
|
||||
- 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:
|
||||
@@ -255,4 +294,4 @@
|
||||
- system_disks_cfg[0].device | string | length > 0
|
||||
ansible.builtin.set_fact:
|
||||
install_drive: "{{ system_disks_cfg[0].device }}"
|
||||
changed_when: false
|
||||
|
||||
|
||||
@@ -48,25 +48,9 @@
|
||||
fail_msg: "Unsupported system keys: {{ system_unknown_keys | join(', ') }}."
|
||||
quiet: true
|
||||
|
||||
- name: Validate nested system mappings
|
||||
loop:
|
||||
- network
|
||||
- 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:
|
||||
- network
|
||||
- user
|
||||
- root
|
||||
- luks
|
||||
loop_control:
|
||||
@@ -171,8 +155,12 @@
|
||||
- hypervisor_cfg.host | string | length > 0
|
||||
- hypervisor_cfg.storage | string | length > 0
|
||||
- system_cfg.id | string | length > 0
|
||||
- system_cfg.network.bridge | string | length > 0
|
||||
fail_msg: "Missing required Proxmox inputs. Define hypervisor.(url,username,password,host,storage), system.id, and system.network.bridge."
|
||||
- >-
|
||||
(system_cfg.network.bridge | default('') | string | length > 0)
|
||||
or (system_cfg.network.interfaces | default([]) | length > 0)
|
||||
fail_msg: >-
|
||||
Missing required Proxmox inputs. Define hypervisor.(url,username,password,host,storage),
|
||||
system.id, and system.network.bridge (or system.network.interfaces[]).
|
||||
quiet: true
|
||||
|
||||
- name: Validate VMware hypervisor inputs
|
||||
@@ -187,8 +175,12 @@
|
||||
- hypervisor_cfg.datacenter | string | length > 0
|
||||
- hypervisor_cfg.cluster | string | length > 0
|
||||
- hypervisor_cfg.storage | string | length > 0
|
||||
- system_cfg.network.bridge | string | length > 0
|
||||
fail_msg: "Missing required VMware inputs. Define hypervisor.(url,username,password,datacenter,cluster,storage) and system.network.bridge."
|
||||
- >-
|
||||
(system_cfg.network.bridge | default('') | string | length > 0)
|
||||
or (system_cfg.network.interfaces | default([]) | length > 0)
|
||||
fail_msg: >-
|
||||
Missing required VMware inputs. Define hypervisor.(url,username,password,datacenter,cluster,storage)
|
||||
and system.network.bridge (or system.network.interfaces[]).
|
||||
quiet: true
|
||||
|
||||
- name: Validate Xen hypervisor inputs
|
||||
@@ -197,8 +189,10 @@
|
||||
- hypervisor_type == "xen"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- system_cfg.network.bridge | string | length > 0
|
||||
fail_msg: "Missing required Xen inputs. Define system.network.bridge."
|
||||
- >-
|
||||
(system_cfg.network.bridge | default('') | string | length > 0)
|
||||
or (system_cfg.network.interfaces | default([]) | length > 0)
|
||||
fail_msg: "Missing required Xen inputs. Define system.network.bridge (or system.network.interfaces[])."
|
||||
quiet: true
|
||||
|
||||
- name: Validate virtual installer ISO requirement
|
||||
@@ -329,3 +323,15 @@
|
||||
- (system_cfg.network.prefix | int) > 0
|
||||
fail_msg: "system.network.prefix is required when system.network.ip is set."
|
||||
quiet: true
|
||||
|
||||
- name: Validate network interfaces entries
|
||||
when: system_cfg.network.interfaces | default([]) | length > 0
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- item is mapping
|
||||
- item.bridge is defined and (item.bridge | string | length) > 0
|
||||
fail_msg: "Each system.network.interfaces[] entry must be a dict with at least a 'bridge' key."
|
||||
quiet: true
|
||||
loop: "{{ system_cfg.network.interfaces }}"
|
||||
loop_control:
|
||||
label: "{{ item | to_json }}"
|
||||
|
||||
@@ -9,8 +9,6 @@ virtualization_libvirt_disk_path: >-
|
||||
{{ [virtualization_libvirt_image_dir, hostname ~ '.qcow2'] | ansible.builtin.path_join }}
|
||||
virtualization_libvirt_cloudinit_path: >-
|
||||
{{ [virtualization_libvirt_image_dir, hostname ~ '-cloudinit.iso'] | ansible.builtin.path_join }}
|
||||
virtualization_mac_address: >-
|
||||
{{ '52:54:00' | community.general.random_mac(seed=hostname) }}
|
||||
virtualization_xen_disk_path: /var/lib/xen/images
|
||||
|
||||
virtualization_tpm2_enabled: >-
|
||||
|
||||
@@ -8,12 +8,34 @@
|
||||
{%- set _ = out.update({ 'scsi' ~ loop.index0: hypervisor_cfg.storage ~ ':' ~ (disk.size | int) }) -%}
|
||||
{%- endfor -%}
|
||||
{{ out }}
|
||||
virtualization_proxmox_net: >-
|
||||
{%- set out = {} -%}
|
||||
{%- for iface in system_cfg.network.interfaces -%}
|
||||
{%- set val = 'virtio,bridge=' ~ iface.bridge -%}
|
||||
{%- if iface.vlan | default('') | string | length > 0 -%}
|
||||
{%- set val = val ~ ',tag=' ~ iface.vlan -%}
|
||||
{%- endif -%}
|
||||
{%- set _ = out.update({ 'net' ~ loop.index0: val }) -%}
|
||||
{%- endfor -%}
|
||||
{{ out }}
|
||||
virtualization_proxmox_ipconfig: >-
|
||||
{%- set out = {} -%}
|
||||
{%- for iface in system_cfg.network.interfaces -%}
|
||||
{%- if iface.ip | default('') | string | length > 0 -%}
|
||||
{%- set val = 'ip=' ~ iface.ip ~ '/' ~ iface.prefix
|
||||
~ ((',gw=' ~ iface.gateway) if (iface.gateway | default('') | length > 0) else '') -%}
|
||||
{%- else -%}
|
||||
{%- set val = 'ip=dhcp' -%}
|
||||
{%- endif -%}
|
||||
{%- set _ = out.update({ 'ipconfig' ~ loop.index0: val }) -%}
|
||||
{%- endfor -%}
|
||||
{{ out }}
|
||||
community.proxmox.proxmox_kvm:
|
||||
api_host: "{{ hypervisor_cfg.url }}"
|
||||
api_user: "{{ hypervisor_cfg.username }}"
|
||||
api_password: "{{ hypervisor_cfg.password }}"
|
||||
ciuser: "{{ system_cfg.user.name }}"
|
||||
cipassword: "{{ system_cfg.user.password }}"
|
||||
ciuser: "{{ system_cfg.users[0].name }}"
|
||||
cipassword: "{{ system_cfg.users[0].password }}"
|
||||
ciupgrade: false
|
||||
node: "{{ hypervisor_cfg.host }}"
|
||||
vmid: "{{ system_cfg.id }}"
|
||||
@@ -46,17 +68,8 @@
|
||||
ide0: "{{ boot_iso }},media=cdrom"
|
||||
ide1: "{{ rhel_iso + ',media=cdrom' if rhel_iso is defined and rhel_iso | length > 0 else omit }}"
|
||||
ide2: "{{ hypervisor_cfg.storage }}:cloudinit"
|
||||
net:
|
||||
net0: >-
|
||||
virtio,bridge={{ system_cfg.network.bridge }}{% if system_cfg.network.vlan is defined and system_cfg.network.vlan | string | length > 0 %},tag={{ system_cfg.network.vlan }}{% endif %}
|
||||
ipconfig:
|
||||
ipconfig0: >-
|
||||
{{
|
||||
'ip=' ~ system_cfg.network.ip ~ '/' ~ system_cfg.network.prefix
|
||||
~ (',gw=' ~ system_cfg.network.gateway if system_cfg.network.gateway is defined and system_cfg.network.gateway | length else '')
|
||||
if system_cfg.network.ip is defined and system_cfg.network.ip | string | length
|
||||
else 'ip=dhcp'
|
||||
}}
|
||||
net: "{{ virtualization_proxmox_net }}"
|
||||
ipconfig: "{{ virtualization_proxmox_ipconfig }}"
|
||||
nameservers: "{{ system_cfg.network.dns.servers if system_cfg.network.dns.servers | length else omit }}"
|
||||
searchdomains: "{{ system_cfg.network.dns.search if system_cfg.network.dns.search | length else omit }}"
|
||||
onboot: true
|
||||
|
||||
@@ -14,6 +14,17 @@
|
||||
|
||||
- name: Create VM in vCenter
|
||||
delegate_to: localhost
|
||||
vars:
|
||||
virtualization_vmware_networks: >-
|
||||
{%- set ns = namespace(out=[]) -%}
|
||||
{%- for iface in system_cfg.network.interfaces -%}
|
||||
{%- set entry = {'name': iface.bridge, 'type': 'dhcp'} -%}
|
||||
{%- if (iface.vlan | default('') | string | length) > 0 -%}
|
||||
{%- set entry = entry | combine({'vlan': iface.vlan | int}) -%}
|
||||
{%- endif -%}
|
||||
{%- set ns.out = ns.out + [entry] -%}
|
||||
{%- endfor -%}
|
||||
{{ ns.out }}
|
||||
community.vmware.vmware_guest:
|
||||
hostname: "{{ hypervisor_cfg.url }}"
|
||||
username: "{{ hypervisor_cfg.username }}"
|
||||
@@ -53,10 +64,7 @@
|
||||
"iso_path": rhel_iso
|
||||
} ] if rhel_iso is defined and rhel_iso | length > 0 else [] )
|
||||
}}
|
||||
networks:
|
||||
- name: "{{ system_cfg.network.bridge }}"
|
||||
type: dhcp
|
||||
vlan: "{{ system_cfg.network.vlan if system_cfg.network.vlan is defined and system_cfg.network.vlan | string | length > 0 else omit }}"
|
||||
networks: "{{ virtualization_vmware_networks }}"
|
||||
register: virtualization_vmware_create_result
|
||||
|
||||
- name: Set VM created fact when VM was powered on during creation
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
id0:
|
||||
match:
|
||||
macaddress: "{{ virtualization_mac_address }}"
|
||||
{% set has_static = system_cfg.network.ip is defined and system_cfg.network.ip | string | length %}
|
||||
{% set dns_list = system_cfg.network.dns.servers | default([]) %}
|
||||
{% set search_list = system_cfg.network.dns.search | default([]) %}
|
||||
{% for iface in system_cfg.network.interfaces %}
|
||||
{% set iface_mac = '52:54:00' | community.general.random_mac(seed=hostname if loop.index0 == 0 else hostname ~ '-nic' ~ loop.index0) %}
|
||||
{% set has_static = (iface.ip | default('') | string | length) > 0 %}
|
||||
id{{ loop.index0 }}:
|
||||
match:
|
||||
macaddress: "{{ iface_mac }}"
|
||||
{% if has_static %}
|
||||
addresses:
|
||||
- "{{ system_cfg.network.ip }}/{{ system_cfg.network.prefix }}"
|
||||
{% if system_cfg.network.gateway is defined and system_cfg.network.gateway | string | length %}
|
||||
gateway4: "{{ system_cfg.network.gateway }}"
|
||||
- "{{ iface.ip }}/{{ iface.prefix }}"
|
||||
{% if iface.gateway | default('') | string | length %}
|
||||
gateway4: "{{ iface.gateway }}"
|
||||
{% endif %}
|
||||
{% else %}
|
||||
dhcp4: true
|
||||
{% if dns_list | length or search_list | length %}
|
||||
{% if loop.index0 == 0 and (dns_list | length or search_list | length) %}
|
||||
dhcp4-overrides:
|
||||
{% if dns_list | length %}
|
||||
use-dns: false
|
||||
@@ -25,7 +27,7 @@ network:
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if dns_list or search_list %}
|
||||
{% if loop.index0 == 0 and (dns_list or search_list) %}
|
||||
nameservers:
|
||||
{% if dns_list %}
|
||||
addresses:
|
||||
@@ -40,3 +42,4 @@ network:
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -4,9 +4,18 @@ ssh_pwauth: true
|
||||
package_update: false
|
||||
package_upgrade: false
|
||||
users:
|
||||
- name: "{{ system_cfg.user.name }}"
|
||||
primary_group: "{{ system_cfg.user.name }}"
|
||||
{% for user in system_cfg.users %}
|
||||
- name: "{{ user.name }}"
|
||||
primary_group: "{{ user.name }}"
|
||||
groups: users
|
||||
sudo: ALL=(ALL) NOPASSWD:ALL
|
||||
passwd: "{{ system_cfg.user.password | password_hash('sha512') }}"
|
||||
lock_passwd: False
|
||||
sudo: "{{ user.sudo | default('ALL=(ALL) NOPASSWD:ALL') }}"
|
||||
passwd: "{{ user.password | password_hash('sha512') }}"
|
||||
lock_passwd: false
|
||||
{% set ssh_keys = user.keys | default([]) %}
|
||||
{% if ssh_keys | length > 0 %}
|
||||
ssh_authorized_keys:
|
||||
{% for key in ssh_keys %}
|
||||
- "{{ key }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -46,11 +46,13 @@
|
||||
<target dev="sdc" bus="sata"/>
|
||||
</disk>
|
||||
{% endif %}
|
||||
{% for iface in system_cfg.network.interfaces %}
|
||||
<interface type='network'>
|
||||
<mac address="{{ virtualization_mac_address }}"/>
|
||||
<source network='{{ system_cfg.network.bridge if (system_cfg.network.bridge | default('' ) | string | length) > 0 else "default" }}'/>
|
||||
<mac address="{{ '52:54:00' | community.general.random_mac(seed=hostname if loop.index0 == 0 else hostname ~ '-nic' ~ loop.index0) }}"/>
|
||||
<source network='{{ iface.bridge | default("default") }}'/>
|
||||
<model type='virtio'/>
|
||||
</interface>
|
||||
{% endfor %}
|
||||
{% if virtualization_tpm2_enabled %}
|
||||
<tpm model='tpm-crb'>
|
||||
<backend type='emulator' version='2.0'/>
|
||||
|
||||
@@ -10,7 +10,11 @@ disk = [
|
||||
'{{ 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.bridge }},model=e1000' ]
|
||||
vif = [
|
||||
{%- for iface in system_cfg.network.interfaces -%}
|
||||
'bridge={{ iface.bridge }},model=e1000'{% if not loop.last %}, {% endif %}
|
||||
{%- endfor -%}
|
||||
]
|
||||
boot = "{{ 'dc' if xen_installer_media_enabled | bool else 'c' }}"
|
||||
on_crash = "preserve"
|
||||
on_poweroff = "destroy"
|
||||
|
||||
@@ -25,10 +25,11 @@ system:
|
||||
mount:
|
||||
path: /data
|
||||
fstype: ext4
|
||||
user:
|
||||
name: "admin"
|
||||
users:
|
||||
- name: "admin"
|
||||
password: "CHANGE_ME"
|
||||
key: "ssh-ed25519 AAAA..."
|
||||
keys:
|
||||
- "ssh-ed25519 AAAA..."
|
||||
root:
|
||||
password: "CHANGE_ME"
|
||||
luks:
|
||||
|
||||
@@ -25,6 +25,7 @@ system:
|
||||
memory: 8192
|
||||
balloon: 0
|
||||
network:
|
||||
# Flat fields (AWX survey compatibility, builds single-entry interfaces[])
|
||||
bridge: "vmbr0"
|
||||
ip: "{{ inventory_hostname }}"
|
||||
prefix: 24
|
||||
@@ -35,6 +36,16 @@ system:
|
||||
- "1.0.0.1"
|
||||
search:
|
||||
- "example.com"
|
||||
# Multi-NIC: use interfaces[] instead of flat fields above
|
||||
# interfaces:
|
||||
# - name: "eth0"
|
||||
# bridge: "vmbr0"
|
||||
# ip: "10.0.0.10"
|
||||
# prefix: 24
|
||||
# gateway: "10.0.0.1"
|
||||
# - name: "eth1"
|
||||
# bridge: "vmbr1"
|
||||
# vlan: "100"
|
||||
path: "/Lab/Example"
|
||||
disks:
|
||||
- size: 80
|
||||
@@ -44,10 +55,11 @@ system:
|
||||
fstype: xfs
|
||||
label: DATA
|
||||
opts: defaults
|
||||
user:
|
||||
name: "ops"
|
||||
users:
|
||||
- name: "ops"
|
||||
password: "CHANGE_ME"
|
||||
key: "ssh-ed25519 AAAA..."
|
||||
keys:
|
||||
- "ssh-ed25519 AAAA..."
|
||||
root:
|
||||
password: "CHANGE_ME"
|
||||
luks:
|
||||
|
||||
Reference in New Issue
Block a user