Este documento descreve como usar Terraform para provisionar máquinas virtuais em um ambiente KVM/LIBVIRT, incluindo configuração do provider, volumes com backing store, cloud-init e domínio libvirt.
Adicionar o repositório oficial da HashiCorp e instalar o Terraform:
wget -O- https://apt.releases.hashicorp.com/gpg \
| sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" \
| sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update
sudo apt install terraform
Validar binário e versão:
which terraform
# /usr/bin/terraform
terraform --version
# Terraform v1.14.5
# on linux_amd64
Habilitar autocomplete para o shell:
terraform -install-autocomplete
A estrutura sugerida para o módulo é:
terraform/
├── provider.tf
├── volumes.tf
├── cloudinit.tf
├── domain.tf
└── user-data.yaml
terraform {
required_version = "~> 1.14"
required_providers {
libvirt = {
source = "dmacvicar/libvirt"
version = "~> 0.9"
}
}
}
provider "libvirt" {
uri = "qemu:///system"
}
# volumes.tf
resource "libvirt_volume" "os_volume" {
name = "vm-tf.qcow2"
pool = "default"
target = {
format = {
type = "qcow2"
}
}
capacity = 16 * 1024 * 1024 * 1024
backing_store = {
path = "/datastore/templates/debian-trixie-base.qcow2"
format = {
type = "qcow2"
}
}
}
# volumes.tf
resource "libvirt_volume" "os_base" {
name = "os_base.qcow2"
pool = "default"
target = {
format = {
type = "qcow2"
}
}
create = {
content = {
url = "/datastore/templates/debian-trixie-base.qcow2"
}
}
}
resource "libvirt_volume" "os_volume" {
name = "vm-tf.qcow2"
pool = "default"
target = {
format = {
type = "qcow2"
}
}
capacity = 16 * 1024 * 1024 * 1024
backing_store = {
path = libvirt_volume.os_base.path
format = {
type = "qcow2"
}
}
}
# cloudinit.tf
resource "libvirt_cloudinit_disk" "common_init" {
name = "common_init.iso"
user_data = templatefile("${path.module}/user-data.yaml", {
ssh_key = trimspace(file("~/.ssh/id_rsa.pub"))
hostname = "vm-tf"
})
meta_data = yamlencode({
instance-id = "vm-tf"
local-hostname = "vm-tf"
})
}
resource "libvirt_volume" "common_init_volume" {
name = "vm-tf_init.iso"
pool = "default"
create = {
content = {
url = libvirt_cloudinit_disk.common_init.path
}
}
}
# domain.tf
resource "libvirt_domain" "domain" {
name = "vm-tf"
title = "VM Terraform"
description = "VM implementada via Terraform"
memory = 2048
memory_unit = "MiB"
vcpu = 2
type = "kvm"
cpu = {
mode = "host-passthrough"
}
features = {
acpi = true
}
os = {
type = "hvm"
type_arch = "x86_64"
type_machine = "q35"
firmware = "efi"
}
devices = {
disks = [
{
source = {
volume = {
pool = libvirt_volume.os_volume.pool
volume = libvirt_volume.os_volume.name
}
}
target = { dev = "vda", bus = "virtio" }
driver = {
type = "qcow2"
}
},
{
device = "cdrom"
source = {
volume = {
pool = libvirt_volume.common_init_volume.pool
volume = libvirt_volume.common_init_volume.name
}
}
target = { dev = "sda", bus = "sata" }
}
]
interfaces = [
{
type = "network"
model = {
type = "virtio"
}
source = {
network = {
network = "default"
}
}
}
]
graphics = [
{
vnc = {
auto_port = true
listen = "127.0.0.1"
}
}
]
}
running = true
}
#cloud-config
manage_etc_hosts: true
hostname: ${hostname}
users:
- name: gean
gecos: "Gean Martins"
sudo: "ALL=(ALL) NOPASSWD:ALL"
groups: users, sudo
shell: /bin/bash
lock_passwd: true
ssh_authorized_keys:
- ${ssh_key}
Dentro do diretório do módulo Terraform:
terraform init
terraform validate
terraform fmt
terraform plan
terraform apply
Comandos úteis para inspeção de estado:
terraform state list
terraform show