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.
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.