Files
Ansible-Bootstrap/main.yml

223 lines
7.9 KiB
YAML

---
# Bootstrap pipeline — role execution order:
# 1. global_defaults — normalize + validate system/hypervisor/disk input
# 2. system_check — pre-flight hardware/environment safety checks
# 3. virtualization — create VM on hypervisor (libvirt/proxmox/vmware/xen)
# 4. environment — detect live ISO, configure installer network, install tools
# 5. partitioning — partition disk, create FS, LUKS, LVM, mount everything
# 6. bootstrap — debootstrap/pacstrap/dnf install the target OS into /mnt
# 7. configuration — users, network, encryption, fstab, bootloader, services
# 8. cis — CIS hardening (optional, per system.features.cis.enabled)
# 9. cleanup — unmount, remove cloud-init artifacts, reboot/shutdown
- name: Create and configure VMs
hosts: "{{ bootstrap_target | default('all') }}"
strategy: free # noqa: run-once[play]
gather_facts: false
become: true
vars_prompt:
- name: user_name
prompt: |
What is your username?
default: ""
private: false
- name: user_public_key
prompt: |
What is your ssh key?
default: ""
private: false
- name: user_password
prompt: |
What is your password?
default: ""
confirm: true
- name: root_password
prompt: |
What is your root password?
default: ""
confirm: true
pre_tasks:
- name: Apply prompted authentication values to system input
no_log: true
vars:
system_input: "{{ system | default({}) }}"
system_users_input: "{{ system_input.users | default({}) }}"
_first_entry: "{{ system_users_input | dict2items | first | default({'key': '', 'value': {}}) }}"
_first_name: "{{ _first_entry.key }}"
_first_attrs: "{{ _first_entry.value if _first_entry.value 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 | 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_name: "{{ _first_name if (_first_name | length > 0) else prompt_user_name }}"
resolved_attrs:
keys: >-
{{
_first_attrs['keys']
if (_first_attrs['keys'] is defined
and _first_attrs['keys'] is iterable
and _first_attrs['keys'] is not string
and _first_attrs['keys'] | length > 0)
else (
[prompt_user_key]
if (prompt_user_key | length > 0)
else []
)
}}
password: >-
{{
_first_attrs.password | string
if (_first_attrs.password | default('') | string | length) > 0
else prompt_user_password
}}
ansible.builtin.set_fact:
system: >-
{{
system_input
| combine(
{
'users': system_users_input | combine({resolved_name: (_first_attrs | combine(resolved_attrs, recursive=True))}),
'root': {
'password': (
(system_root_input.password | default('') | string | length) > 0
) | ternary(system_root_input.password | string, prompt_root_password)
}
},
recursive=True
)
}}
- name: Load global defaults
ansible.builtin.import_role:
name: global_defaults
- name: Perform safety checks
ansible.builtin.import_role:
name: system_check
tasks:
- name: Bootstrap pipeline
block:
- name: Record that no pre-existing VM was found
ansible.builtin.set_fact:
_vm_absent_before_bootstrap: true
- name: Create virtual machine
when: system_cfg.type == "virtual"
ansible.builtin.include_role:
name: virtualization
public: true
vars:
ansible_connection: local
ansible_become: false
- name: Configure environment
ansible.builtin.include_role:
name: environment
public: true
- name: Partition disks
ansible.builtin.include_role:
name: partitioning
public: true
vars:
partitioning_boot_partition_suffix: 1
partitioning_main_partition_suffix: 2
- name: Install base system
ansible.builtin.include_role:
name: bootstrap
public: true
- name: Apply system configuration
ansible.builtin.include_role:
name: configuration
public: true
- name: Apply CIS hardening
when: system_cfg.features.cis.enabled | bool
ansible.builtin.include_role:
name: cis
public: true
- name: Clean up and finalize
when: system_cfg.type in ["virtual", "physical"]
ansible.builtin.include_role:
name: cleanup
public: true
rescue:
- name: Delete VM on bootstrap failure
when:
- _vm_absent_before_bootstrap | default(false) | bool
- virtualization_vm_created_in_run | default(false) | bool
- system_cfg.type == "virtual"
ansible.builtin.include_role:
name: virtualization
tasks_from: delete
vars:
ansible_connection: local
ansible_become: false
tags:
- rescue_cleanup
- name: Fail host after bootstrap rescue
ansible.builtin.fail:
msg: >-
Bootstrap failed for {{ hostname }}.
{{ 'VM was deleted to allow clean retry.'
if (virtualization_vm_created_in_run | default(false))
else 'VM was not created in this run (kept).' }}
post_tasks:
- name: Set post-reboot connection flags
ansible.builtin.set_fact:
post_reboot_can_connect: >-
{{
(ansible_connection | default('ssh')) != 'ssh'
or ((system_cfg.network.ip | default('') | string | length) > 0)
or (
system_cfg.type == 'physical'
and (ansible_host | default('') | string | length) > 0
)
}}
- 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
no_log: true
vars:
_primary: "{{ (system_cfg.users | dict2items | selectattr('value.password', 'defined') | first) }}"
ansible.builtin.set_fact:
ansible_user: "{{ _primary.key }}"
ansible_password: "{{ _primary.value.password }}"
ansible_become_password: "{{ _primary.value.password }}"
ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
ansible_python_interpreter: /usr/bin/python3
- name: Re-gather facts for target OS after reboot
when:
- post_reboot_can_connect | bool
ansible.builtin.setup:
gather_subset:
- "!all"
- min
- pkg_mgr
- name: Install post-reboot packages
when:
- post_reboot_can_connect | bool
- system_cfg.packages is defined
- system_cfg.packages | length > 0
ansible.builtin.package:
name: "{{ system_cfg.packages }}"
state: present