Phase 1: Host Preparation
1. Install KVM and Libvirt tools
(Commands for Ubuntu/Debian)
1
2
| sudo apt update
sudo apt install -y qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virtinst libvirt-daemon virt-manager
|
2. Install ISO generation utility
Terraform uses this to generate the Cloud-Init ISO that injects your SSH keys.
1
| sudo apt install -y genisoimage
|
3. Permissions
Add your user to the libvirt and kvm groups so Terraform can run without sudo.
1
2
| sudo usermod -aG libvirt $USER
sudo usermod -aG kvm $USER
|
Phase 2: Project Setup
Create a clean directory for the project and download a cloud-native image. We use Cloud Images (qcow2) rather than installer ISOs because they support automatic configuration on boot.
1
2
3
4
5
| mkdir tf-libvirt-lab
cd tf-libvirt-lab
# Download Ubuntu 22.04 Cloud Image
wget https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64.img
|
Create a file named main.tf.
Copy and paste the following configuration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
| terraform {
required_providers {
libvirt = {
source = "dmacvicar/libvirt"
version = "0.7.1"
}
}
}
# Connect to the local system QEMU
provider "libvirt" {
uri = "qemu:///system"
}
resource "libvirt_network" "lab_net" {
name = "lab_net"
mode = "nat"
domain = "lab.local"
addresses = ["10.10.10.0/24"]
dhcp {
enabled = true
}
dns {
enabled = true
}
}
resource "libvirt_pool" "lab_pool" {
name = "lab_pool"
type = "dir"
# Use a path your user has write access to, or standard libvirt path
path = "/var/lib/libvirt/images/terraform-lab"
}
# The Base Image (The template we downloaded)
resource "libvirt_volume" "os_base" {
name = "ubuntu-base.qcow2"
pool = libvirt_pool.lab_pool.name
source = "./ubuntu-22.04-server-cloudimg-amd64.img"
format = "qcow2"
}
# The VM Disk (Cloned from base, resized to 10GB)
resource "libvirt_volume" "vm_disk" {
name = "vm-disk-1.qcow2"
base_volume_id = libvirt_volume.os_base.id
pool = libvirt_pool.lab_pool.name
size = 10737418240 # 10GB
}
data "template_file" "user_data" {
template = <<EOF
#cloud-config
hostname: terraform-vm
fqdn: terraform-vm.lab.local
manage_etc_hosts: true
users:
- name: ubuntu
sudo: ALL=(ALL) NOPASSWD:ALL
groups: users, admin
home: /home/ubuntu
shell: /bin/bash
lock_passwd: false
ssh-authorized-keys:
- ${file("~/.ssh/id_rsa.pub")}
ssh_pwauth: true
disable_root: false
chpasswd:
list: |
ubuntu:password
expire: False
package_update: true
packages:
- qemu-guest-agent
runcmd:
- [ systemctl, start, qemu-guest-agent ]
EOF
}
resource "libvirt_cloudinit_disk" "commoninit" {
name = "commoninit.iso"
pool = libvirt_pool.lab_pool.name
user_data = data.template_file.user_data.rendered
}
resource "libvirt_domain" "vm_01" {
name = "ubuntu-terraform-01"
memory = "2048"
vcpu = 2
cloudinit = libvirt_cloudinit_disk.commoninit.id
network_interface {
network_id = libvirt_network.lab_net.id
wait_for_lease = true
}
console {
type = "pty"
target_port = "0"
target_type = "serial"
}
disk {
volume_id = libvirt_volume.vm_disk.id
}
graphics {
type = "spice"
listen_type = "address"
autoport = true
}
}
# Output the IP so we know where to SSH
output "ip" {
value = libvirt_domain.vm_01.network_interface[0].addresses
}
|
Phase 4: Execution
1. SSH Key Verification
Ensure you have a public key at ~/.ssh/id_rsa.pub. If not, generate one:
1
| ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N ""
|
2. Initialize Terraform
This downloads the dmacvicar/libvirt provider.
3. Review the Plan
4. Apply
Build the infrastructure.
Phase 5: Verification
Once the apply is complete, Terraform will output the IP address.
1. Test Connection
2. Inspect in KVM
Open virt-manager. You will see the VM running, the storage pool created, and the network active.
3. Cleanup
To tear down the environment: