Skip to content

Libvirt (Qemu) Terraform

Create workdir and add files:

  • provider.tf
Terraform
# see https://github.com/hashicorp/terraform
terraform {
  required_version = ">1.1.3"
  required_providers {
    # see https://registry.terraform.io/providers/hashicorp/random
    random = {
      source = "hashicorp/random"
      version = "3.1.0"
    }
    # see https://registry.terraform.io/providers/hashicorp/template
    template = {
      source = "hashicorp/template"
      version = "2.2.0"
    }
    # see https://registry.terraform.io/providers/dmacvicar/libvirt
    # see https://github.com/dmacvicar/terraform-provider-libvirt
    libvirt = {
      source = "dmacvicar/libvirt"
      version = "0.6.11"
    }
  }
}

provider "libvirt" {
  ## Configuration options
  uri = "qemu:///system"
  # uri = "qemu+ssh://[email protected]:42315/system"

}
  • libvirt.tf
Terraform
resource "libvirt_volume" "ubuntu-terra" {
  name = "ubuntu-terra.qcow2"
  pool = "kvm-disks" # List storage pools using virsh pool-list
#  source = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
  source = "./jammy-server-cloudimg-amd64.img"  # faster (but you have to download img)
  format = "qcow2"
}

# Give a Disk size
resource "libvirt_volume" "ubuntu-terra_resized" {
  name           = "disk"
  base_volume_id = "${libvirt_volume.ubuntu-terra.id}"
  pool           = "kvm-disks"
  size           = 30361393152
}

# Use CloudInit ISO to add ssh-key to the instance
resource "libvirt_cloudinit_disk" "commoninit" {
  name = "ubuntu-terra-commoninit.iso"
  pool = "kvm-disks"
  user_data = data.template_file.user_data.rendered
  network_config = data.template_file.network_config.rendered
}

# ssh key and users - in cloudinit
data "template_file" "user_data" {
  template = file("${path.module}/cloud_init.cfg")
  vars = {
    hostname = "ubuntu-terra"
    fqdn = "ubuntu-terra.urdomain.com"
  }
}

data "template_file" "network_config" {
  template = file("${path.module}/network_config_dhcp.cfg")
}

# Define KVM domain to create
resource "libvirt_domain" "ubuntu-terra" {
  name   = "ubuntu-terra"
  memory = "2048"
  vcpu   = 2

  network_interface {
    network_name = "default" # List networks with virsh net-list
      bridge = "virbr0"
      addresses = ["192.168.122.200"]
  }

  disk {
    volume_id = "${libvirt_volume.ubuntu-terra_resized.id}"
  }

  console {
    type = "pty"
    target_type = "serial"
    target_port = "0"
  }

  graphics {
    type = "spice"
    listen_type = "address"
    autoport = true
  }

  cloudinit = libvirt_cloudinit_disk.commoninit.id

}

# # Output Server IP
 output "ip" {
   value = "${libvirt_domain.ubuntu-terra.network_interface.0.addresses.0}"
 }
  • cloud_init.cfg
INI
#cloud-config
hostname: ubuntu-terra
fqdn: urdomain.com # optional
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("/home/user/.ssh/id_ed25519.pub")}  # add your user
# only cert auth via ssh (console access can still login)
ssh_pwauth: false
disable_root: false
chpasswd:
  list: |
     ubuntu:linux
  expire: False
packages:
  - qemu-guest-agent
  - nano
  - cmatrix
  - net-tools
  - ncdu
  - wireguard
  - resolvconf
# add packages

# test of writing content
write_files:
  - content: |
      The quick brown fox jumped
      over the lazy dog
    path: /root/test.txt
# written to /var/log/cloud-init-output.log
final_message: "The system is finally up, after $UPTIME seconds"
  • network.cfg (check what ethernet nic you VM gets and change it)
INI
version: 2
ethernets:
  ens3:
     dhcp4: true