homelab-server/docs/02_terraform_provisioning.md
2026-07-03 01:15:15 +09:00

3.1 KiB

IaC Provisioning with Terraform & UEFI

← Back to Main README

This section explains the Infrastructure as Code (IaC) deployment decisions, hypervisor CPU settings, and the workarounds required required to boot UEFI virtual machines running RHEL 10.


1. Microarchitecture Requirements: x86-64-v3

When I first booted RHEL 10 virtual machines, they crashed immediately and locked the host CPU cores at 100% capacity.

The Diagnostics:

  • Red Hat Enterprise Linux 10 mandates the x86-64-v3 CPU microarchitecture level.
  • By default, QEMU and the Terraform libvirt provider emulate a generic qemu64 CPU, which only supports x86-64-v1 (a baseline from 2004).
  • Because qemu64 lacks the AVX, AVX2, BMI1, and FMA3 instruction sets required by RHEL 10, the guest kernel crashes immediately upon boot.

The Solution:

I updated the CPU configuration block in main.tf to pass the host CPU instruction directly to the VM:

cpu {
    mode = "host-passthrough"
}
  • host-passthrough tells KVM to expose the physical AMD Ryzen processor capabilities directly to the VM kernel. This satisfies the x86-64-v3 microarchitecture check and enables high-performance virtualization.

2. UEFI Firmware Mapping (EDK2 / OVMF)

Traditional virtual machines use a legacy BIOS loader. However, modern RHEL 10 guests are designed to boot using UEFI (Unified Extensible Firmware Interface).

To enable UEFI in my KVM VMs, I configured the domain firmware blocks:

nvram {
    file        = "/var/lib/libvirt/qemu/nvram/${each.key}_VARS.fd"
    template    = "/usr/share/edk2/ovmf/OVMF_VARS.fd"
}
  • OVMF_CODE.fd: The read-only UEFI BIOS code file provided by the host's edk2-ovmf package.
  • OVMF_VARS.fd: The template containing default NVRAM variables (boot order, secure boot state).
  • nvram {}: Creates a private, writable copy of the NVRAM variables file for each virtual machine (e.g., ansible-control_VARS.fd), ensuring their boot states do not interfere with each other.

3. SCSI Bus Cloud-Init Workaround

This was the most complex technical hurdle of the VM deployment.

The Problem:

RHEL 10 has completely deprecated and removed legacy IDE controller drivers from its kernel. The standard Terraform libvirt_cloudinit_disk resource creates a .iso file and automatically mounts it as an ide-cdrom volume. Since the RHEL 10 kernel cannot load IDE drivers, the VM boots but fails to see the cloud-init volume, skipping all user setup and network configurations.

The Solution:

I bypassed the automatic attachment and configured a custom block:

  1. I created the cloud-init resource as a raw disk (.raw extension instead of .iso to bypass provider type validation).
  2. I attached it explicitly to the domain as a SCSI disk:
disk {
    volume_id   = libvirt_cloudinit_disk.commoninit[each.key].id
    scsi        = true
}
  • scsi = true: Forces QEMU to attach the configuration disk using the modern virtio-scsi driver. The RHEL 10 guest kernel loads this driver immediately, successfully mounts the drive at /dev/sda, and parses the configuration metadata on first boot.