Files
infra-automation/plays/deploy-debian12-vm.yml
Infrastructure Team a5337029ff Add multi-distribution VM deployment playbooks
- Add deploy-debian12-vm.yml for basic Debian 12 deployment
- Add deploy-linux-vm.yml for multi-distribution support
  - Support for Debian, Ubuntu, RHEL, CentOS, Rocky, Alma, SUSE
  - Cloud-init based provisioning
  - Distribution-specific security hardening
  - Automatic security updates configuration
  - UFW/firewalld setup per OS family
  - SELinux enforcing for RHEL family
2025-11-10 22:51:30 +01:00

480 lines
14 KiB
YAML

---
# =============================================================================
# Debian 12 VM Deployment Playbook
# =============================================================================
# Deploys a new Debian 12 guest VM on grokbox KVM hypervisor
# Uses libvirt/KVM with cloud-init for unattended configuration
# =============================================================================
- name: Deploy Debian 12 VM on grokbox hypervisor
hosts: grokbox
gather_facts: yes
become: yes
vars:
# VM Configuration
vm_name: "debian12-guest"
vm_hostname: "debian12"
vm_domain: "localdomain"
vm_vcpus: 2
vm_memory_mb: 2048
vm_disk_size_gb: 20
# Network Configuration
vm_network: "default"
vm_bridge: "virbr0"
# Storage Configuration
vm_disk_path: "/var/lib/libvirt/images/{{ vm_name }}.qcow2"
cloud_init_iso_path: "/var/lib/libvirt/images/{{ vm_name }}-cloud-init.iso"
# Debian 12 Cloud Image
debian_cloud_image_url: "https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2"
debian_cloud_image_checksum_url: "https://cloud.debian.org/images/cloud/bookworm/latest/SHA512SUMS"
debian_image_cache: "/var/lib/libvirt/images/debian-12-generic-amd64.qcow2"
# Ansible User Configuration
ansible_user_ssh_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILBrnivsqjhAxWYeuuvnYc3neeRRuHsr2SjeKv+Drtpu user@debian"
tasks:
# =========================================================================
# Pre-flight Checks
# =========================================================================
- name: Check if VM already exists
command: virsh dominfo {{ vm_name }}
register: vm_exists
failed_when: false
changed_when: false
tags: [validate, preflight]
- name: Fail if VM already exists
fail:
msg: "VM '{{ vm_name }}' already exists on hypervisor. Please choose a different name or destroy the existing VM."
when: vm_exists.rc == 0
tags: [validate, preflight]
- name: Verify virtualization support
command: virt-host-validate qemu
register: virt_validation
failed_when: false
changed_when: false
tags: [validate, preflight]
- name: Display virtualization validation results
debug:
var: virt_validation.stdout_lines
tags: [validate, preflight]
# =========================================================================
# Package Installation
# =========================================================================
- name: Install required packages for VM deployment
package:
name:
- libvirt-daemon-system
- libvirt-clients
- virtinst
- qemu-kvm
- qemu-utils
- cloud-image-utils
- genisoimage
- wget
- python3-libvirt
state: present
tags: [install]
- name: Ensure libvirtd service is running
systemd:
name: libvirtd
state: started
enabled: yes
tags: [install]
# =========================================================================
# Download Debian Cloud Image
# =========================================================================
- name: Check if Debian cloud image already exists
stat:
path: "{{ debian_image_cache }}"
register: debian_image_stat
tags: [download]
- name: Download Debian 12 cloud image
get_url:
url: "{{ debian_cloud_image_url }}"
dest: "{{ debian_image_cache }}"
mode: '0644'
timeout: 600
when: not debian_image_stat.stat.exists
tags: [download]
- name: Download Debian 12 checksums
get_url:
url: "{{ debian_cloud_image_checksum_url }}"
dest: "/tmp/debian-12-SHA512SUMS"
mode: '0644'
tags: [download, verify]
- name: Verify cloud image checksum
shell: |
cd /var/lib/libvirt/images
grep "debian-12-generic-amd64.qcow2" /tmp/debian-12-SHA512SUMS | sha512sum -c -
register: checksum_result
changed_when: false
tags: [download, verify]
# =========================================================================
# Create VM Disk
# =========================================================================
- name: Create VM disk from cloud image
command: >
qemu-img create -f qcow2 -F qcow2
-b {{ debian_image_cache }}
{{ vm_disk_path }}
{{ vm_disk_size_gb }}G
args:
creates: "{{ vm_disk_path }}"
tags: [storage]
- name: Set proper permissions on VM disk
file:
path: "{{ vm_disk_path }}"
owner: libvirt-qemu
group: kvm
mode: '0600'
tags: [storage]
# =========================================================================
# Create Cloud-Init Configuration
# =========================================================================
- name: Create cloud-init directory
file:
path: /tmp/cloud-init-{{ vm_name }}
state: directory
mode: '0755'
tags: [cloud-init]
- name: Create cloud-init meta-data
copy:
content: |
instance-id: {{ vm_name }}
local-hostname: {{ vm_hostname }}
dest: /tmp/cloud-init-{{ vm_name }}/meta-data
mode: '0644'
tags: [cloud-init]
- name: Create cloud-init user-data
copy:
content: |
#cloud-config
hostname: {{ vm_hostname }}
fqdn: {{ vm_hostname }}.{{ vm_domain }}
manage_etc_hosts: true
# Create ansible user with sudo privileges
users:
- name: ansible
groups: sudo
shell: /bin/bash
sudo: ['ALL=(ALL) NOPASSWD:ALL']
ssh_authorized_keys:
- {{ ansible_user_ssh_key }}
- name: root
lock_passwd: false
# Set root password (for emergency console access)
chpasswd:
list: |
root:debian
expire: false
# SSH configuration - secure by default
ssh_pwauth: false
disable_root: false
# Install essential packages per CLAUDE.md guidelines
packages:
- sudo
- vim
- htop
- tmux
- curl
- wget
- rsync
- git
- python3
- python3-pip
- jq
- bc
- aide
- auditd
- chrony
- ufw
- lvm2
- cloud-guest-utils
- parted
- unattended-upgrades
- apt-listchanges
# Security configuration files
write_files:
- path: /etc/ssh/sshd_config.d/99-security.conf
content: |
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
MaxSessions 10
ClientAliveInterval 300
ClientAliveCountMax 2
permissions: '0644'
- path: /etc/sudoers.d/ansible
content: |
ansible ALL=(ALL) NOPASSWD:ALL
permissions: '0440'
- path: /etc/apt/apt.conf.d/50unattended-upgrades
content: |
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
};
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::MinimalSteps "true";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false";
permissions: '0644'
- path: /etc/apt/apt.conf.d/20auto-upgrades
content: |
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";
permissions: '0644'
# System configuration commands
runcmd:
# SSH service
- systemctl enable ssh
- systemctl restart ssh
# Time synchronization
- systemctl enable chrony
- systemctl start chrony
# Firewall - enable but allow SSH
- ufw --force enable
- ufw allow ssh
# Audit daemon
- systemctl enable auditd
- systemctl start auditd
# Grow root partition
- growpart /dev/vda 1 || true
- resize2fs /dev/vda1 || true
# Package updates
package_update: true
package_upgrade: true
package_reboot_if_required: false
# Set timezone
timezone: UTC
# Locale
locale: en_US.UTF-8
# Enable cloud-init logging
output:
all: '| tee -a /var/log/cloud-init-output.log'
# Final message
final_message: "Debian 12 VM deployment completed. System is ready after $UPTIME seconds."
dest: /tmp/cloud-init-{{ vm_name }}/user-data
mode: '0644'
tags: [cloud-init]
- name: Create cloud-init ISO
command: >
genisoimage -output {{ cloud_init_iso_path }}
-volid cidata -joliet -rock
/tmp/cloud-init-{{ vm_name }}/user-data
/tmp/cloud-init-{{ vm_name }}/meta-data
args:
creates: "{{ cloud_init_iso_path }}"
tags: [cloud-init]
- name: Set proper permissions on cloud-init ISO
file:
path: "{{ cloud_init_iso_path }}"
owner: libvirt-qemu
group: kvm
mode: '0644'
tags: [cloud-init]
# =========================================================================
# Create and Start VM
# =========================================================================
- name: Create VM using virt-install
command: >
virt-install
--name {{ vm_name }}
--memory {{ vm_memory_mb }}
--vcpus {{ vm_vcpus }}
--disk path={{ vm_disk_path }},format=qcow2,bus=virtio
--disk path={{ cloud_init_iso_path }},device=cdrom
--network network={{ vm_network }},model=virtio
--os-variant debian11
--graphics none
--console pty,target_type=serial
--import
--noautoconsole
register: vm_create
tags: [deploy]
- name: Wait for VM to boot and cloud-init to complete
pause:
seconds: 60
prompt: "Waiting for VM to boot and cloud-init to complete configuration..."
tags: [deploy]
- name: Get VM IP address
shell: |
virsh domifaddr {{ vm_name }} | grep -oP '(\d{1,3}\.){3}\d{1,3}' | head -1
register: vm_ip
retries: 10
delay: 10
until: vm_ip.stdout != ""
changed_when: false
tags: [deploy, validate]
- name: Display VM information
debug:
msg:
- "=== VM Deployment Successful ==="
- "VM Name: {{ vm_name }}"
- "IP Address: {{ vm_ip.stdout }}"
- "vCPUs: {{ vm_vcpus }}"
- "Memory: {{ vm_memory_mb }} MB"
- "Disk: {{ vm_disk_size_gb }} GB"
- "Access: ssh ansible@{{ vm_ip.stdout }}"
- ""
- "Note: Add this VM to your Ansible inventory:"
- " {{ vm_name }}:"
- " ansible_host: {{ vm_ip.stdout }}"
- " ansible_user: ansible"
- " ansible_ssh_common_args: '-o ProxyJump=grokbox -o StrictHostKeyChecking=accept-new'"
tags: [deploy, validate]
# =========================================================================
# Validation and Health Check
# =========================================================================
- name: Test SSH connectivity to new VM
wait_for:
host: "{{ vm_ip.stdout }}"
port: 22
timeout: 300
state: started
tags: [validate]
- name: Display VM console log (for troubleshooting)
command: virsh console {{ vm_name }} --force
async: 5
poll: 0
failed_when: false
changed_when: false
tags: [never, debug]
- name: Get VM details
command: virsh dominfo {{ vm_name }}
register: vm_details
changed_when: false
tags: [validate]
- name: Display VM details
debug:
var: vm_details.stdout_lines
tags: [validate]
# =========================================================================
# Cleanup
# =========================================================================
- name: Remove temporary cloud-init directory
file:
path: /tmp/cloud-init-{{ vm_name }}
state: absent
tags: [cleanup]
- name: Remove downloaded checksums
file:
path: /tmp/debian-12-SHA512SUMS
state: absent
tags: [cleanup]
# =============================================================================
# Post-Deployment Configuration
# =============================================================================
# After VM is deployed, run additional configuration playbooks
- name: Configure deployed VM
hosts: "{{ vm_ip.stdout }}"
gather_facts: yes
become: yes
vars:
ansible_user: ansible
ansible_ssh_common_args: '-o ProxyJump=grokbox -o StrictHostKeyChecking=accept-new'
tasks:
- name: Wait for cloud-init to complete
command: cloud-init status --wait
changed_when: false
tags: [validate]
- name: Gather system facts
setup:
tags: [validate]
- name: Display system information
debug:
msg:
- "=== System Information ==="
- "OS: {{ ansible_distribution }} {{ ansible_distribution_version }}"
- "Kernel: {{ ansible_kernel }}"
- "Architecture: {{ ansible_architecture }}"
- "Hostname: {{ ansible_hostname }}"
- "FQDN: {{ ansible_fqdn }}"
- "Python: {{ ansible_python_version }}"
tags: [validate]
- name: Gather disk usage
command: df -h
register: disk_usage
changed_when: false
tags: [validate]
- name: Display disk usage
debug:
var: disk_usage.stdout_lines
tags: [validate]
- name: Gather memory usage
command: free -h
register: memory_usage
changed_when: false
tags: [validate]
- name: Display memory usage
debug:
var: memory_usage.stdout_lines
tags: [validate]