terraform: add libvirt cloud VM provisioning

This commit is contained in:
ahp
2026-10-18 16:42:30 +03:30
parent 3db2ca87cc
commit 0c8f857e30
5 changed files with 226 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/dmacvicar/libvirt" {
version = "0.9.9"
constraints = "~> 0.9.9"
hashes = [
"h1:ljmMoTuHWXJGf28PY5E0fX9BDQWNZfs8zX+xY80YBok=",
"zh:130c9df49bf2f69deb2af1ebbd25deb09b4ebda1022fcda980c197ffda800c1d",
"zh:38216229290a3450d53a8a2799175fd8de5c26581913e20c8848b75890343ea3",
"zh:45ad900f9c1866e24643b03ec4b0b716e012bd25d0c92bb4d0d47ca7531abec8",
"zh:6e77ca4b5fad91c30b7123fd1b40024dabf188e33509ed219904f7ff84c8fb1b",
"zh:70a9d5af99a60179c7d38032662f443c3d2ad8bddab11d535c545c5c44273b2d",
"zh:7de6b5272a89710f2490d3e7fdf7c495f7acaddfb127df8ebd86ef9a70508e3a",
"zh:8932f2eb1013b84f01008d59bb8a16b982ac9ac72f7de41a0f88b7fe41c352df",
"zh:8b6044884696c671610dfe752bec5507131f21757e848872d8624cfba7e9f6d6",
"zh:d891ec2c1a0ae55d44b158c7e629316caf02ab8a5c4018523865fdc2ae3299a3",
"zh:dd9bc604e2ac389da3bae463a3da2a1e06915b7952892c274ea0b54f2eb41300",
"zh:e18ca294c7ca2d35aa866d37c64d01f62bda2b2c261d8280dba0ab272517e47f",
"zh:ef96bb292d6df57c4975f9d94c09a2efd7d6aed470a0f23f1c56b69dc1ec0b14",
"zh:f7337de4d59dc06d578ab3393506fedfdc962b01cc24c78a3ff46b1311e5fdb7",
]
}
+13
View File
@@ -0,0 +1,13 @@
#cloud-config
hostname: ${hostname}
manage_etc_hosts: true
disable_root: true
ssh_pwauth: false
users:
- name: arch
groups: [wheel]
sudo: ["ALL=(ALL) NOPASSWD: ALL"]
shell: /bin/bash
lock_passwd: true
ssh_authorized_keys:
- ${ssh_public_key}
+129
View File
@@ -0,0 +1,129 @@
locals {
output_dir = abspath("${path.module}/../output")
cloud_images = try(sort(fileset(local.output_dir, "Parch-Linux-x86_64-cloudimg-*.qcow2")), [])
selected_image = var.image_path != "" ? pathexpand(var.image_path) : (
length(local.cloud_images) > 0 ? "${local.output_dir}/${local.cloud_images[length(local.cloud_images) - 1]}" : ""
)
ssh_public_key = trimspace(file(pathexpand(var.ssh_public_key_path)))
}
resource "libvirt_volume" "root" {
name = "${var.vm_name}.qcow2"
pool = var.libvirt_pool
target = { format = { type = "qcow2" } }
create = {
content = {
url = local.selected_image
}
}
lifecycle {
precondition {
condition = local.selected_image != "" && fileexists(local.selected_image)
error_message = "No Parch cloudimg QCOW2 found. Run 'make build' first or set -var image_path=/absolute/path/to/cloudimg.qcow2."
}
}
}
resource "libvirt_cloudinit_disk" "seed" {
name = "${var.vm_name}-cloudinit"
user_data = templatefile("${path.module}/cloud-init/user-data.yaml.tftpl", {
hostname = var.vm_name
ssh_public_key = jsonencode(local.ssh_public_key)
})
meta_data = yamlencode({
instance-id = var.vm_name
local-hostname = var.vm_name
})
}
resource "libvirt_volume" "cloudinit" {
name = "${var.vm_name}-cloudinit.iso"
pool = var.libvirt_pool
target = { format = { type = "iso" } }
create = {
content = {
url = libvirt_cloudinit_disk.seed.path
}
}
}
resource "libvirt_domain" "vm" {
name = var.vm_name
type = "kvm"
memory = var.memory_mib
memory_unit = "MiB"
vcpu = var.vcpu
features = {
acpi = true
apic = {}
}
cpu = {
mode = "host-passthrough"
}
os = {
type = "hvm"
type_arch = "x86_64"
type_machine = "q35"
boot_devices = [{ dev = "hd" }]
}
devices = {
disks = [
{
driver = { name = "qemu", type = "qcow2" }
source = { volume = { pool = libvirt_volume.root.pool, volume = libvirt_volume.root.name } }
target = { dev = "vda", bus = "virtio" }
},
{
device = "cdrom"
source = { volume = { pool = libvirt_volume.cloudinit.pool, volume = libvirt_volume.cloudinit.name } }
target = { dev = "sda", bus = "sata" }
}
]
interfaces = [
{
type = "network"
model = { type = "virtio" }
source = { network = { network = var.libvirt_network } }
}
]
serials = [{
type = "pty"
target = { type = "isa-serial", port = 0 }
}]
consoles = [{
type = "pty"
target = { type = "serial", port = 0 }
}]
channels = [{
source = { unix = {} }
target = { virt_io = { name = "org.qemu.guest_agent.0" } }
}]
rngs = [{
model = "virtio"
backend = { random = "/dev/urandom" }
}]
}
running = true
lifecycle {
replace_triggered_by = [libvirt_volume.root, libvirt_volume.cloudinit]
}
}
output "vm_name" {
value = libvirt_domain.vm.name
}
output "ssh_command" {
value = "make qemu-ssh (or: ssh -o IdentitiesOnly=yes -i ${trimsuffix(pathexpand(var.ssh_public_key_path), ".pub")} arch@<VM-IP>)"
}
+47
View File
@@ -0,0 +1,47 @@
variable "libvirt_uri" {
description = "Libvirt connection URI. Use qemu:///system for the system daemon."
type = string
default = "qemu:///system"
}
variable "libvirt_pool" {
description = "Existing libvirt storage pool for VM disks and cloud-init seed."
type = string
default = "default"
}
variable "libvirt_network" {
description = "Existing libvirt network that provides DHCP."
type = string
default = "default"
}
variable "vm_name" {
description = "Name for the Parch Linux VM."
type = string
default = "parch-cloud"
}
variable "image_path" {
description = "Optional path to a cloudimg QCOW2. Empty selects the newest output/Parch-Linux-x86_64-cloudimg-*.qcow2."
type = string
default = ""
}
variable "ssh_public_key_path" {
description = "Path to the public SSH key injected by cloud-init."
type = string
default = "~/.ssh/id_ed25519.pub"
}
variable "memory_mib" {
description = "VM memory in MiB."
type = number
default = 2048
}
variable "vcpu" {
description = "Number of virtual CPUs."
type = number
default = 2
}
+14
View File
@@ -0,0 +1,14 @@
terraform {
required_version = ">= 1.5.0"
required_providers {
libvirt = {
source = "dmacvicar/libvirt"
version = "~> 0.9.9"
}
}
}
provider "libvirt" {
uri = var.libvirt_uri
}