Table of Contents

Terraform libvirt - Criando VM para estudos

Introdução

Os seguintes scripts Terraform foram desenvolvidos para automatizar a criação e configuração de máquinas virtuais usando o provedor libvirt. O libvirt é uma API de virtualização de código aberto que oferece suporte a uma variedade de hipervisores, como QEMU/KVM, Xen e VirtualBox.

Objetivo

O objetivo desses scripts é provisionar uma máquina virtual Ubuntu com base em uma imagem pré-existente, configurar o CloudInit para personalizar a inicialização da máquina virtual e atribuir uma chave SSH para acesso remoto seguro.

Componentes

Os scripts Terraform consistem em vários arquivos:

  1. provider.tf: Este arquivo configura o provedor libvirt.
  2. volume.tf: Aqui são definidos os volumes necessários para a máquina virtual, incluindo o volume para a imagem do sistema operacional e o volume para o disco da máquina virtual.
  3. cloudinit.tf: Neste arquivo, são configuradas as opções do CloudInit, permitindo a personalização da inicialização da máquina virtual, como a configuração de usuários, grupos e pacotes.
  4. domain.tf: Aqui é definido o domínio da máquina virtual, especificando detalhes como nome, memória, CPU, interface de rede e configurações de disco.
  5. variables.tf: Este arquivo define as variáveis necessárias para os scripts, como configurações da máquina virtual e caminhos para arquivos.
  6. outputs.tf: Aqui são definidas as saídas dos scripts, como o endereço IP da máquina virtual.

Utilização

Para utilizar esses scripts, você pode personalizar as configurações da máquina virtual no arquivo terraform.tfvars e executar o comando terraform apply para provisionar a máquina virtual de acordo com suas especificações.

Sem redimencionar o disco

main.tf
#main.tf 
terraform {
  required_providers {
    libvirt = {
      source = "dmacvicar/libvirt"
    }
  }
}
provider.tf
#provider.tf 
provider "libvirt" {
  uri = "qemu:///system"
}
variables.tf
#variables.tf 
variable "vm_config" {
  description = "Configuration for the virtual machine"
  type        = map(any)
}
volume.tf
#volume.tf
resource "libvirt_volume" "os_image" {
  name           = var.vm_config["os_image_name"]
  pool           = var.vm_config["storage_pool"]
  source         = var.vm_config["os_image_url"]
  format         = "qcow2"
}
cloudinit.tf
#cloudinit.tf 
data "template_file" "user_data" {
  template = file(var.vm_config["cloud_init_file"])
}
 
resource "libvirt_cloudinit_disk" "cloudinit_resized" {
  name      = "cloudinit_resized.iso"
  user_data = data.template_file.user_data.rendered
  pool      = var.vm_config["storage_pool"]
}
domain.tf
#domain.tf 
resource "libvirt_domain" "virtual_machine" {
  name   = var.vm_config["name"]
  memory = var.vm_config["memory"]
  vcpu   = var.vm_config["cpu"]
 
  cpu {
    mode = "host-passthrough"
  }
 
  cloudinit = libvirt_cloudinit_disk.cloudinit_resized.id
 
  network_interface {
    network_name   = var.vm_config["network_name"]
    wait_for_lease = true
  }
 
  console {
    type        = "pty"
    target_port = "0"
    target_type = "serial"
  }
 
  console {
    type        = "pty"
    target_type = "virtio"
    target_port = "1"
  }
 
  disk {
    volume_id = libvirt_volume.os_image.id
  }
 
  graphics {
    type        = "spice"
    listen_type = "address"
    autoport    = true
  }
}
outputs.tf
#outputs.tf 
output "ip" {
  value = libvirt_domain.virtual_machine.network_interface[0].addresses[0]
}
ubuntu-cloud-init.yml
#ubuntu-cloud-init.yml 
#cloud-config
 
## Modifying root password
chpasswd:
  list: |
    root:root
  expire: False
 
## Enable direct root access
disable_root: false
 
## Allow ssh password authentication
ssh_pwauth: true
 
## Setup users and ssh keys
users:
  - name: gke-suporte
    gecos: GKE Suporte
    groups: users, admin
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    lock_passwd: true
    ssh_authorized_keys:
      - ${file("~/.ssh/id_ed25519.pub")}
  - name: eks-suporte
    gecos: EKS Suporte
    groups: users, admin
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    lock_passwd: true
    ssh_authorized_keys:
      - ${file("~/.ssh/id_ed25519.pub")}
  - name: eks-projeto
    gecos: EKS Projeto
    groups: users, admin
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    lock_passwd: true
    ssh_authorized_keys:
      - ${file("~/.ssh/id_ed25519.pub")}
  - name: root
    ssh_authorized_keys:
      - ${file("~/.ssh/id_ed25519.pub")}
 
## Install packages
package_update: true
package_upgrade: true
packages:
  - qemu-guest-agent
  - vm
  - git
  - python
main.tf
#terraform.tfvars 
vm_config = {
  name            = "ubuntu"
  cpu             = 2
  memory          = 2048
  disksize        = 32
  storage_pool    = "default"
  os_image_name   = "ubuntu.qcow2"
  network_name    = "default"
  cloud_init_file = "ubuntu-cloud-init.yml"
  os_image_url    = "/var/lib/libvirt/templates/jammy-server-cloudimg-amd64.img"
}

Redimencionando o diso

main.tf
#main.tf 
terraform {
  required_providers {
    libvirt = {
      source = "dmacvicar/libvirt"
    }
  }
}
provider.tf
#provider.tf 
provider "libvirt" {
  uri = "qemu:///system"
}
variables.tf
#variables.tf 
variable "vm_config" {
  description = "Configuration for the virtual machine"
  type        = map(any)
}
volume.tf
#volume.tf 
resource "libvirt_volume" "os_image" {
  name   = var.vm_config["os_image_name"]
  pool   = var.vm_config["storage_pool"]
  source = var.vm_config["os_image_url"]
  format = "qcow2"
}
 
resource "libvirt_volume" "disk_resized" {
  name           = "disk.qcow2"
  base_volume_id = libvirt_volume.os_image.id
  pool           = var.vm_config["storage_pool"]
  size           = var.vm_config["disksize"] * 1024 * 1024 * 1024
}
cloudinit.tf
#cloudinit.tf 
data "template_file" "user_data" {
  template = file(var.vm_config["cloud_init_file"])
}
 
resource "libvirt_cloudinit_disk" "cloudinit_resized" {
  name      = "cloudinit_resized.iso"
  user_data = data.template_file.user_data.rendered
  pool      = var.vm_config["storage_pool"]
}
domain.tf
#domain.tf 
resource "libvirt_domain" "virtual_machine" {
  name   = var.vm_config["name"]
  memory = var.vm_config["memory"]
  vcpu   = var.vm_config["cpu"]
 
  cpu {
    mode = "host-passthrough"
  }
 
  cloudinit = libvirt_cloudinit_disk.cloudinit_resized.id
 
  network_interface {
    network_name   = var.vm_config["network_name"]
    wait_for_lease = true
  }
 
  console {
    type        = "pty"
    target_port = "0"
    target_type = "serial"
  }
 
  console {
    type        = "pty"
    target_type = "virtio"
    target_port = "1"
  }
 
  disk {
    volume_id = libvirt_volume.disk_resized.id
  }
 
  graphics {
    type        = "spice"
    listen_type = "address"
    autoport    = true
  }
}
outputs.tf
#outputs.tf 
output "ip" {
  value = libvirt_domain.virtual_machine.network_interface[0].addresses[0]
}
ubuntu-cloud-init.yml
#ubuntu-cloud-init.yml 
#cloud-config
 
## Modifying root password
chpasswd:
  list: |
    root:root
  expire: False
 
## Enable direct root access
disable_root: false
 
## Allow ssh password authentication
ssh_pwauth: true
 
## Setup users and ssh keys
users:
  - name: gke-suporte
    gecos: GKE Suporte
    groups: users, admin
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    lock_passwd: true
    ssh_authorized_keys:
      - ${file("~/.ssh/id_ed25519.pub")}
  - name: eks-suporte
    gecos: EKS Suporte
    groups: users, admin
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    lock_passwd: true
    ssh_authorized_keys:
      - ${file("~/.ssh/id_ed25519.pub")}
  - name: eks-projeto
    gecos: EKS Projeto
    groups: users, admin
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    lock_passwd: true
    ssh_authorized_keys:
      - ${file("~/.ssh/id_ed25519.pub")}
  - name: root
    ssh_authorized_keys:
      - ${file("~/.ssh/id_ed25519.pub")}
 
## Install packages
package_update: true
package_upgrade: true
packages:
  - qemu-guest-agent
  - vm
  - git
  - python
main.tf
#terraform.tfvars 
vm_config = {
  name            = "ubuntu"
  cpu             = 2
  memory          = 2048
  disksize        = 32
  storage_pool    = "default"
  os_image_name   = "ubuntu.qcow2"
  network_name    = "default"
  cloud_init_file = "ubuntu-cloud-init.yml"
  os_image_url    = "/var/lib/libvirt/templates/jammy-server-cloudimg-amd64.img"
}
$ terraform apply
data.template_file.user_data: Reading...
data.template_file.user_data: Read complete after 0s [id=bc718697370c1302222f8c6d6ff150f5996a70c7a12d75f916f9f8de2a6ba8d4]
 
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 
Terraform will perform the following actions:
 
  # libvirt_cloudinit_disk.cloudinit_resized will be created
  + resource "libvirt_cloudinit_disk" "cloudinit_resized" {
      + id        = (known after apply)
      + name      = "cloudinit_resized.iso"
      + pool      = "default"
      + user_data = <<-EOT
            #cloud-config
 
            ## Modifying root password
            chpasswd:
              list: |
                root:root
              expire: False
 
            ## Enable direct root access
            disable_root: false
 
            ## Allow ssh password authentication
            ssh_pwauth: true
 
            ## Setup users and ssh keys
            users:
              - name: gke-suporte
                gecos: GKE Suporte
                groups: users, admin
                shell: /bin/bash
                sudo: ALL=(ALL) NOPASSWD:ALL
                lock_passwd: true
                ssh_authorized_keys:
                  - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAwHB1kIi15h/qdqbk7T3hk+rBKLylzr33cEIdTiO4ZO gean@inspiron
 
              - name: eks-suporte
                gecos: EKS Suporte
                groups: users, admin
                shell: /bin/bash
                sudo: ALL=(ALL) NOPASSWD:ALL
                lock_passwd: true
                ssh_authorized_keys:
                  - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAwHB1kIi15h/qdqbk7T3hk+rBKLylzr33cEIdTiO4ZO gean@inspiron
 
              - name: eks-projeto
                gecos: EKS Projeto
                groups: users, admin
                shell: /bin/bash
                sudo: ALL=(ALL) NOPASSWD:ALL
                lock_passwd: true
                ssh_authorized_keys:
                  - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAwHB1kIi15h/qdqbk7T3hk+rBKLylzr33cEIdTiO4ZO gean@inspiron
 
              - name: root
                ssh_authorized_keys:
                  - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAwHB1kIi15h/qdqbk7T3hk+rBKLylzr33cEIdTiO4ZO gean@inspiron
 
 
            ## Install packages
            package_update: true
            package_upgrade: true
            packages:
              - qemu-guest-agent
              - vm
              - git
              - python
        EOT
    }
 
  # libvirt_domain.virtual_machine will be created
  + resource "libvirt_domain" "virtual_machine" {
      + arch        = (known after apply)
      + autostart   = (known after apply)
      + cloudinit   = (known after apply)
      + emulator    = (known after apply)
      + fw_cfg_name = "opt/com.coreos/config"
      + id          = (known after apply)
      + machine     = (known after apply)
      + memory      = 2048
      + name        = "ubuntu"
      + qemu_agent  = false
      + running     = true
      + type        = "kvm"
      + vcpu        = 2
 
      + console {
          + source_host    = "127.0.0.1"
          + source_service = "0"
          + target_port    = "0"
          + target_type    = "serial"
          + type           = "pty"
        }
      + console {
          + source_host    = "127.0.0.1"
          + source_service = "0"
          + target_port    = "1"
          + target_type    = "virtio"
          + type           = "pty"
        }
 
      + cpu {
          + mode = "host-passthrough"
        }
 
      + disk {
          + scsi      = false
          + volume_id = (known after apply)
        }
 
      + graphics {
          + autoport       = true
          + listen_address = "127.0.0.1"
          + listen_type    = "address"
          + type           = "spice"
        }
 
      + network_interface {
          + addresses      = (known after apply)
          + hostname       = (known after apply)
          + mac            = (known after apply)
          + network_id     = (known after apply)
          + network_name   = "default"
          + wait_for_lease = true
        }
    }
 
  # libvirt_volume.disk_resized will be created
  + resource "libvirt_volume" "disk_resized" {
      + base_volume_id = (known after apply)
      + format         = (known after apply)
      + id             = (known after apply)
      + name           = "disk"
      + pool           = "default"
      + size           = 34359738368
    }
 
  # libvirt_volume.os_image will be created
  + resource "libvirt_volume" "os_image" {
      + format = "qcow2"
      + id     = (known after apply)
      + name   = "ubuntu.qcow2"
      + pool   = "default"
      + size   = (known after apply)
      + source = "/var/lib/libvirt/templates/jammy-server-cloudimg-amd64.img"
    }
 
Plan: 4 to add, 0 to change, 0 to destroy.
 
Changes to Outputs:
  + ip = (known after apply)
 
Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.
 
  Enter a value: yes
libvirt_cloudinit_disk.cloudinit_resized: Creating...
libvirt_volume.os_image: Creating...
libvirt_volume.os_image: Creation complete after 0s [id=/var/lib/libvirt/images/ubuntu.qcow2]
libvirt_volume.disk_resized: Creating...
libvirt_cloudinit_disk.cloudinit_resized: Creation complete after 0s [id=/var/lib/libvirt/images/cloudinit_resized.iso;961426d4-803b-4390-a9c8-6ca3fed6402f]
libvirt_volume.disk_resized: Creation complete after 0s [id=/var/lib/libvirt/images/disk]
libvirt_domain.virtual_machine: Creating...
libvirt_domain.virtual_machine: Still creating... [10s elapsed]
libvirt_domain.virtual_machine: Creation complete after 15s [id=f9fd0383-54cb-4cff-8ddc-05637c58140a]
 
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
 
Outputs:
 
ip = "192.168.122.10"