Post

Guia Avançado: Template Debian 13 com UEFI, LVM, Packer e Terraform (Parte 2/2)

Domine a criação de imagens Debian 13 com suporte a UEFI e LVM usando Packer. Provisione VMs modernas com Terraform e virt-install de forma automatizada.

Guia Avançado: Template Debian 13 com UEFI, LVM, Packer e Terraform (Parte 2/2)

Introdução

Na Parte 1, Guia Avançado: Template Debian 13 com UEFI, LVM, Packer e Terraform (Parte 1/2), criamos uma imagem base do Debian 13 com suporte a UEFI. Agora, vamos utilizá-la para provisionar VMs com virt-install e Terraform, aplicando as configurações necessárias para um boot UEFI bem-sucedido.


1. Provisionando com virt-install

Esta abordagem é excelente para automação via scripts shell e para quem prefere ferramentas nativas do ecossistema Libvirt.

1.1. Preparação do Ambiente

Primeiro, vamos criar um diretório de trabalho e copiar a imagem base para o diretório de imagens do KVM, renomeando-a para a nossa nova VM.

1
2
3
4
5
6
7
# Crie um diretório para os arquivos de configuração da VM
mkdir -p ~/Workspace/libvirt/packer/debian13-uefi
cd ~/Workspace/libvirt/packer/debian13-uefi

# Copie a imagem base para o diretório de imagens de VMs ativas
# É uma boa prática não usar o template diretamente
cp ~/kvm/templates/debian-13-uefi.qcow2 ~/kvm/images/debian-trixie-uefi.qcow2 

1.2. Configuração do cloud-init

O cloud-init utiliza arquivos de metadados para configurar a VM no primeiro boot. Vamos criar os três arquivos necessários: user-data, meta-data e network-config.

user-data (Configuração do Usuário): Define o usuário, senha, grupos e chaves SSH autorizadas.

1
2
3
4
5
6
7
8
9
10
11
12
13
cat << EOF > user-data
#cloud-config
# Define o usuário 'gean', concede privilégios de sudo sem senha e
# adiciona a chave SSH pública para acesso remoto.
users:
  - name: gean
    sudo: ALL=(ALL) NOPASSWD:ALL
    groups: users, sudo
    shell: /bin/bash
    lock_passwd: true
    ssh_authorized_keys:
      - $(cat ~/.ssh/kvm.pub)
EOF

meta-data (Identidade da Instância): Define o hostname e o ID da instância.

1
2
3
4
cat << EOF > meta-data
instance-id: debian-trixie
local-hostname: debian-trixie
EOF

network-config (Configuração de Rede): Instrui a VM a obter um endereço IP via DHCP.

1
2
3
4
5
6
cat << EOF > network-config
version: 2
ethernets:
  enp1s0:
    dhcp4: true
EOF

1.3. Criação da ISO de cloud-init

Agora, vamos empacotar esses três arquivos em uma imagem ISO, que será anexada à VM como um CD-ROM.

1
2
# O 'genisoimage' cria um ISO chamado cidata.iso com os arquivos de configuração
genisoimage -output cidata.iso -volid cidata -joliet -rock user-data meta-data network-config

1.4. Criação da VM com virt-install

Com a imagem e a ISO prontas, podemos criar a VM. A principal diferença aqui é a adição da flag --boot uefi.

1
2
3
4
5
6
7
8
9
10
11
12
13
virt-install \
  --name debian-trixie \
  --memory 2048 \
  --vcpus 2 \
  --machine q35 \
  --boot uefi \
  --os-variant debian13 \
  --network=default,model=virtio \
  --disk path=/home/gean/kvm/images/debian-trixie-uefi.qcow2,bus=scsi \
  --disk path=cidata.iso,device=cdrom,bus=scsi \
  --graphics spice \
  --noautoconsole \
  --import

1.5. Verificação e Acesso

Após alguns instantes, a VM estará em execução. Verifique seu status e obtenha seu endereço IP para acessá-la via SSH.

1
2
3
4
~/Workspace/libvirt/packer/debian13-uefi  → virsh list
 Id   Name            State
-------------------------------
 17   debian-trixie   running
1
2
3
4
~/Workspace/libvirt/packer/debian13-uefi  → virsh domifaddr debian-trixie
 Name       MAC address          Protocol     Address
-------------------------------------------------------------------------------
 vnet16     52:54:00:fa:5b:40    ipv4         192.168.122.188/24
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
~/Workspace/libvirt/packer/debian13-uefi  → ssh -i ~/.ssh/kvm gean@192.168.122.188
The authenticity of host '192.168.122.188 (192.168.122.188)' can't be established.
ED25519 key fingerprint is SHA256:fZgHsaXZUwR5gsEXVX8ZcmzdQXEaUlxIMoLGY2ZppJs.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.122.188' (ED25519) to the list of known hosts.
Linux debian-trixie 6.12.41+deb13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.41-1 (2025-08-12) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
gean@debian-trixie:~$ 
gean@debian-trixie:~$ sudo poweroff
1
2
# Para remover a VM e suas variáveis de boot salvas
virsh undefine --domain debian-trixie-uefi --nvram

2. Provisionando com Terraform

Esta abordagem utiliza Infraestrutura como Código para um gerenciamento mais robusto, ideal para ambientes complexos e times que colaboram no gerenciamento da infraestrutura.

2.1. Estrutura do Projeto Terraform

Crie um novo diretório para o seu projeto Terraform.

1
2
~  → mkdir -p ~/Workspace/terraform/providers/libvirt/debian13-trixie-uefi
~  → cd ~/Workspace/terraform/providers/libvirt/debian13-trixie-uefi

2.2. Arquivos de Configuração (cloud-init)

Assim como no método anterior, crie os arquivos user-data, meta-data e network-config no diretório do projeto. Você pode usar os mesmos conteúdos da seção 1.2.

2.3. main.tf (Configuração do Terraform)

Este arquivo define todos os recursos que o Terraform irá gerenciar: a cópia da imagem, a ISO do cloud-init e a própria VM.

No Terraform, em vez de uma simples flag, precisamos definir explicitamente os caminhos do firmware e o bloco nvram.

2.2. main.tf (Configuração do Terraform para UEFI)

O recurso libvirt_domain é modificado para incluir as diretivas firmware e nvram.

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
terraform {
  required_providers {
    libvirt = {
      source = "dmacvicar/libvirt"
    }
  }
}

provider "libvirt" {
  uri = "qemu:///system"
}

resource "libvirt_volume" "os_image" {
  name   = "debpacker.qcow2"
  pool   = "default"
  source = "/home/gean/kvm/templates/debian-13-uefi.qcow2"
  format = "qcow2"
}

# Criar a ISO do cloud-init usando genisoimage
resource "null_resource" "create_cloud_init_iso" {
  triggers = {
    user_data      = filemd5("${path.module}/user-data")
    meta_data      = filemd5("${path.module}/meta-data")
    network-config = filemd5("${path.module}/network-config")
  }

  provisioner "local-exec" {
    command = "genisoimage -output ${path.module}/cloud-init.iso -volid cidata -joliet -rock ${path.module}/user-data ${path.module}/meta-data ${path.module}/network-config"
  }
}

# Volume para a ISO do cloud-init
resource "libvirt_volume" "cloud_init_iso" {
  name   = "cloud-init.iso"
  pool   = "default"
  source = "${path.module}/cloud-init.iso"
  format = "raw"

  depends_on = [null_resource.create_cloud_init_iso]
}

resource "libvirt_domain" "debpacker" {
  name     = "debpacker"
  memory   = "2048"
  vcpu     = 2
  machine  = "q35"
  firmware = "/usr/share/OVMF/OVMF_CODE_4M.fd"

  cpu {
    mode = "host-passthrough"
  }

  network_interface {
    network_name   = "default"
    wait_for_lease = true
  }

  disk {
    volume_id = libvirt_volume.os_image.id
    scsi      = true
  }

  disk {
    volume_id = libvirt_volume.cloud_init_iso.id
    scsi      = true
  }

  nvram {
    template = "/usr/share/OVMF/OVMF_VARS_4M.fd"
  }

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

  graphics {
    type        = "spice"
    listen_type = "none"
  }

  depends_on = [libvirt_volume.cloud_init_iso]
}

output "ip" {
  value = libvirt_domain.debpacker.network_interface[0].addresses[0]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cat << EOF > user-data
#cloud-config
manage_etc_hosts: true
users:
  - name: gean
    gecos: "Gean Martins"
    sudo: ALL=(ALL) NOPASSWD:ALL
    groups: users, sudo
    shell: /bin/bash
    lock_passwd: true
    ssh_authorized_keys:
      - $(cat ~/.ssh/kvm.pub)
      
runcmd:
  - echo '127.0.1.1 debian-trixie' >> /etc/hosts
EOF
1
2
3
4
5
6
cat << EOF > network-config
version: 2
ethernets:
  enp1s0:
    dhcp4: true
EOF
1
2
3
4
cat << EOF > meta-data
instance-id: debian-trixie
local-hostname: debian-trixie
EOF

2.4. Execução do Terraform

Com os arquivos prontos, siga o fluxo de trabalho padrão do Terraform.

1
2
3
4
5
6
7
8
# Inicializa o projeto (baixa o provedor libvirt)
terraform init

# Valida a sintaxe dos arquivos
terraform validate

# (Opcional) Formata o código para o padrão
terraform fmt
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
# Gera e exibe um plano de execução
~/Workspace/terraform/providers/libvirt/debian13-trixie-uefi  → terraform plan

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_domain.debpacker will be created
  + resource "libvirt_domain" "debpacker" {
      + arch        = (known after apply)
      + autostart   = (known after apply)
      + emulator    = (known after apply)
      + firmware    = "/usr/share/OVMF/OVMF_CODE_4M.fd"
      + fw_cfg_name = "opt/com.coreos/config"
      + id          = (known after apply)
      + machine     = "q35"
      + memory      = 2048
      + name        = "debpacker"
      + 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"
        }

      + cpu {
          + mode = "host-passthrough"
        }

      + disk {
          + scsi      = true
          + volume_id = (known after apply)
          + wwn       = (known after apply)
        }
      + disk {
          + scsi      = true
          + volume_id = (known after apply)
          + wwn       = (known after apply)
        }

      + graphics {
          + autoport       = true
          + listen_address = "127.0.0.1"
          + listen_type    = "none"
          + 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
        }

      + nvram {
          + file     = (known after apply)
          + template = "/usr/share/OVMF/OVMF_VARS_4M.fd"
        }
    }

  # libvirt_volume.cloud_init_iso will be created
  + resource "libvirt_volume" "cloud_init_iso" {
      + format = "raw"
      + id     = (known after apply)
      + name   = "cloud-init.iso"
      + pool   = "default"
      + size   = (known after apply)
      + source = "./cloud-init.iso"
    }

  # libvirt_volume.os_image will be created
  + resource "libvirt_volume" "os_image" {
      + format = "qcow2"
      + id     = (known after apply)
      + name   = "debpacker.qcow2"
      + pool   = "default"
      + size   = (known after apply)
      + source = "/home/gean/kvm/templates/debian-13-uefi.qcow2"
    }

  # null_resource.create_cloud_init_iso will be created
  + resource "null_resource" "create_cloud_init_iso" {
      + id       = (known after apply)
      + triggers = {
          + "meta_data"      = "b967d84df1b8a9b5973d980208fd8b36"
          + "network-config" = "739856b4831a24a6333014e36aec1070"
          + "user_data"      = "385a9c90c260f0f54daf4c28963bffcc"
        }
    }

Plan: 4 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + ip = (known after apply)

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
1
2
# Aplica o plano para criar os recursos
~/Workspace/terraform/providers/libvirt/debian13-trixie-uefi  → terraform apply

Após a confirmação, o Terraform provisionará a VM e, ao final, exibirá o endereço IP dela.

1
2
3
4
5
6
[...]
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Outputs:

ip = "192.168.122.70"

2.5. Acesso e Destruição

Acesse a VM usando o IP fornecido pelo terraform apply.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
~/Workspace/terraform/providers/libvirt/debian13-trixie-uefi  → ssh -i ~/.ssh/kvm gean@192.168.122.70
The authenticity of host '192.168.122.70 (192.168.122.70)' can't be established.
ED25519 key fingerprint is SHA256:J46FKkKxaoFqbzitE29KJMShJPeYXpGR3QDwD1qNCEM.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.122.70' (ED25519) to the list of known hosts.
Linux debian-trixie 6.12.41+deb13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.41-1 (2025-08-12) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
gean@debian-trixie:~$

Quando não precisar mais da VM, você pode destruí-la facilmente com um único comando, removendo todos os recursos gerenciados pelo Terraform (a VM e os volumes).

1
terraform destroy

4. Conclusão

Este guia demonstrou as adaptações necessárias para construir um fluxo de automação de imagens com suporte a UEFI. As principais mudanças estão no particionamento (adição da partição ESP) e na configuração do bootloader e do firmware da VM.

Ao dominar ambas as abordagens (BIOS e UEFI), você estará preparado para criar templates de VM compatíveis com uma vasta gama de requisitos de hardware e sistemas operacionais.

This post is licensed under CC BY 4.0 by the author.