- 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
480 lines
14 KiB
YAML
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]
|