Skip to content

MonolithProjects/terraform-libvirt-vm

Folders and files

NameName
Last commit message
Last commit date
Mar 30, 2022
Apr 4, 2024
Feb 5, 2023
May 22, 2024
Feb 5, 2023
Mar 25, 2022
Mar 30, 2022
Dec 19, 2020
May 22, 2024
Feb 5, 2023
May 22, 2024
Feb 5, 2023
Feb 5, 2023
May 22, 2024

Repository files navigation

Libvirt VM Terraform module

GitHub Actions License Terraform

Terraform module for KVM/Libvirt Virtual Machine. This module will create a KVM Virtual Machine(s), configure it using Cloud Init and test the ssh connection. This module is using dmacvicar/libvirt Terraform provider.

What it provides

  • creates one or more VMs
  • one NIC per domain, connected to the network using the bridge interface
  • setup network interface using DHCP or static configuration
  • cloud_init VM(s) configuration (Ubuntu+Netplan complient)
  • optionally add multiple extra disks
  • test the ssh connection

Tested on

  • Ubuntu 20.04 TLS Cloud Image
  • Ubuntu 22.04 TLS Cloud Image

Requirements

Name Version
terraform >= 1.0
libvirt >= 0.7.0

Modules

No modules.

Resources

Name Type
libvirt_cloudinit_disk.commoninit resource
libvirt_domain.virt-machine resource
libvirt_volume.base-volume-qcow2 resource
libvirt_volume.volume-qcow2 resource

Inputs

Name Description Type Default Required
additional_disk_ids List of volume ids list(string) [] no
autostart Autostart the domain bool true no
base_pool_name Name of base OS image string null no
base_volume_name Name of base OS image string null no
bridge Bridge interface string "virbr0" no
cpu_mode CPU mode string "host-passthrough" no
dhcp Use DHCP or Static IP settings bool false no
graphics Graphics type (can be 'spice' or 'vnc') string spice no
index_start From where the indexig start number 1 no
ip_address List of IP addresses list(string)
[
"192.168.123.101"
]
no
ip_gateway IP addresses of a gateway string "192.168.123.1" no
ip_nameserver IP addresses of a nameserver string "192.168.123.1" no
local_admin Admin user without ssh access string "" no
local_admin_passwd Local admin user password string "password_example" no
memory RAM in MB string "1024" no
os_img_url URL to the OS image string "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img" no
pool Storage pool name string "default" no
runcmd Extra commands to be run with cloud init list(string)
[
"[ systemctl, daemon-reload ]",
"[ systemctl, enable, qemu-guest-agent ]",
"[ systemctl, start, qemu-guest-agent ]",
"[ systemctl, restart, systemd-networkd ]"
]
no
share_filesystem n/a
object({
source = string
target = string
readonly = bool
mode = string
})
{
"mode": null,
"readonly": false,
"source": null,
"target": null
}
no
ssh_admin Admin user with ssh access string "ssh-admin" no
ssh_keys List of public ssh keys list(string) [] no
ssh_private_key Private key for SSH connection test (either path to file or key content) string null no
system_volume System Volume size (GB) number 10 no
time_zone Time Zone string "UTC" no
vcpu Number of vCPUs number 1 no
vm_count Number of VMs number 1 no
vm_hostname_prefix VM hostname prefix string "vm" no
xml_override With these variables you can: Enable hugepages; Set USB controllers; Attach USB devices
object({
hugepages = bool
usb_controllers = list(object({
model = string
}))
usb_devices = list(object({
vendor = string
product = string
}))
pci_devices_passthrough = list(object({
src_domain = string
src_bus = string
src_slot = string
src_func = string
dst_domain = string
dst_bus = string
dst_slot = string
dst_func = string
}))
})
{
"hugepages": false,
"usb_controllers": [
{
"model": "piix3-uhci"
}
],
"usb_devices": []
"pci_devices_passthrough": []
}
no
bastion_host ssh bastion host string null no
bastion_user ssh user on bastion host string null no
bastion_ssh_private_key ssh private key for bastion host (either path to file or key content) string null no

Outputs

Name Description
ip_address n/a
name n/a

Example

Example with enable HugePages, Attached USB device, changed USB controller model... :

terraform {
  required_version = ">= 0.13"
    required_providers {
      libvirt = {
        source  = "dmacvicar/libvirt"
      }
    }
}

resource "tls_private_key" "ecdsa-p384-bastion" {
  algorithm   = "ECDSA"
  ecdsa_curve = "P384"
}

provider "libvirt" {
  uri = "qemu+ssh://hero@192.168.165.100/system"
}

module "vm" {
  source  = "MonolithProjects/vm/libvirt"
  version = "1.8.0"

  vm_hostname_prefix = "server"
  vm_count    = 3
  memory      = "2048"
  vcpu        = 1
  pool        = "terra_pool"
  system_volume = 20
  dhcp        = true
  local_admin = "local-admin"
  ssh_admin   = "ci-user"
  ssh_private_key = "~/.ssh/id_ed25519"
  local_admin_passwd = "$6$rounds=4096$xxxxxxxxHASHEDxxxPASSWORD"
  ssh_keys    = [
    "ssh-ed25519 AAAAxxxxxxxxxxxxSSHxxxKEY example",
    ]
  bastion_host = "10.0.0.1"
  bastion_user = "admin"
  bastion_ssh_private_key = tls_private_key.ecdsa-p384-bastion.private_key_pem
  time_zone   = "CET"
  os_img_url  = "file:///home/myuser/ubuntu-20.04-server-cloudimg-amd64.img"
  xml_override = {
      hugepages = true,
      usb_controllers = [
        {
          model = "qemu-xhci"
        }
      ],
      usb_devices = [
        {
          vendor = "0x0bc2",
          product = "0xab28"
        }
      ]
      pci_devices_passthrough = [
        {
          src_domain = "0x0000",
          src_bus    = "0xc1",
          src_slot   = "0x00",
          src_func   = "0x0",
          dst_domain = "0x0000",
          dst_bus    = "0x00",
          dst_slot   = "0x08"
          dst_func   = "0x0"
        },
        {
          src_domain = "0x0000",
          src_bus    = "0xc1",
          src_slot   = "0x00",
          src_func   = "0x1",
          dst_domain = "0x0000",
          dst_bus    = "0x00",
          dst_slot   = "0x09"
          dst_func   = "0x0"
        }
      ]      
    }
}

output "ip_addresses" {
  value = module.nodes
}

Static IP settings... :

terraform {
  required_version = ">= 0.13"
    required_providers {
      libvirt = {
        source  = "dmacvicar/libvirt"
        version = "0.6.3"
      }
    }
}

provider "libvirt" {
  uri = "qemu+ssh://hero@192.168.165.100/system"
}

module "vm" {
  source  = "MonolithProjects/vm/libvirt"
  version = "1.8.0"

  vm_hostname_prefix = "server"
  vm_count    = 3
  memory      = "2048"
  vcpu        = 1
  pool        = "terra_pool"
  system_volume = 20
  share_filesystem = {
    source = "/tmp"
    target = "tmp"
    readonly = false
  }

  dhcp        = false
  ip_address  = [
                  "192.168.165.151",
                  "192.168.165.152",
                  "192.168.165.153"
                ]
  ip_gateway  = "192.168.165.254"
  ip_nameserver = "192.168.165.104"

  local_admin = "local-admin"
  ssh_admin   = "ci-user"
  ssh_private_key = "~/.ssh/id_ed25519"
  local_admin_passwd = "$6$rounds=4096$xxxxxxxxHASHEDxxxPASSWORD"
  ssh_keys    = [
    "ssh-ed25519 AAAAxxxxxxxxxxxxSSHxxxKEY example",
    ]
  time_zone   = "CET"
  os_img_url  = "file:///home/myuser/ubuntu-20.04-server-cloudimg-amd64.img"
}

output "outputs" {
  value = module.nodes
}

The shared directory from the example can be mounted inside the VM with command sudo mount -t 9p -o trans=virtio,version=9p2000.L,rw tmp /host/tmp

Create a VM with an extra disk

# Creates a 50GB extra-data-disk within vms pool
resource "libvirt_volume" "data_volume" {
  pool = "vms"
  name  = "extra-data-disk.qcow2"
  format = "qcow2"
  size = 1024*1024*1024*50
}

module "vm" {
  source  = "MonolithProjects/vm/libvirt"
  version = "1.8.0"

  vm_hostname_prefix = "data-server"
  base_volume_name = "debian-11-base.qcow2"
  base_pool_name = "linked-images"
  vm_count    = 1
  bridge =        "bridge-dmz"
  memory      = "4096"
  vcpu        = 4
  pool        = "vms"
  system_volume = 25
  additional_disk_ids = [ libvirt_volume.data_volume.id ]
  dhcp        = true
  ssh_admin   = "admin"
  ssh_keys    = [
    chomp(file("~/.ssh/id_rsa.pub"))
    ]
  time_zone   = "America/Argentina/Buenos_Aires"
}

output "ip_addresses" {
  value = module.vm
}

Module output example

output_data = {
  "ip_address" = [
    "192.168.165.151",
    "192.168.165.152",
    "192.168.165.153",
  ]
  "name" = [
    "server01",
    "server02",
    "server03",
  ]
}

License

MIT

Author Information

Created in 2020 by Michal Muransky