Hands-On Terraform - Criando uma VPC
Desafio: Provisionando uma VPC com Terraform
O desafio consistia em provisionar uma VPC utilizando Terraform.
Dessa forma, são trabalhados conceitos de rede como Subnets (públicas e privadas), Internet Gateway, NAT Gateway, Tabelas de Rotas e Endereços IP.
Ao final do desafio, você será capaz de desenvolver um projeto básico de VPC com seus principais componentes.
Pré-requisitos
Criar uma rede básica na AWS com os seguintes recursos:
- 1 VPC
- 2 Subnets privadas
- 2 Subnets públicas
- 2 NAT Gateways
- 1 Internet Gateway
Por que utilizar o Terraform?
Diferente de ferramentas nativas como o CloudFormation (AWS), o Terraform funciona em múltiplos providers.
Ele elimina a necessidade de criar recursos manualmente no console e permite que um mesmo código crie múltiplos ambientes idênticos (dica: Terraform Workspaces).
Além disso, garante previsibilidade, mantendo um state do que foi criado, o que permite auditoria e rastreabilidade dos recursos.
Como foi pensada a resolução do desafio
Para deixar o código mais dinâmico e reutilizável, utilizei Built-in Functions e Meta-Arguments.
O que são Built-in Functions?
São funções já prontas do Terraform, que permitem manipular dados, strings, listas, mapas, números, entre outros.
Exemplos utilizados neste código:
cidrsubnet()
→ calcula automaticamente o CIDR das subnetslower()
→ transforma strings em minúsculasformat()
→ formata strings dinamicamente
O que são Meta-Arguments?
São argumentos que controlam o comportamento dos recursos, evitando repetição de código e permitindo criar múltiplos recursos dinamicamente.
Exemplos do código:
for_each
→ cria múltiplos recursos a partir de listas ou mapasdepends_on
→ garante a ordem correta de criação de recursos
Resolução e criação da VPC
O que é VPC? A VPC (Virtual Private Cloud) é como se fosse a sua rede privada dentro da AWS. É nela que você define o espaço de endereçamento (CIDR), cria subnets, controla tabelas de rota e gerencia a comunicação entre os recursos. Em resumo: é a base da infraestrutura em nuvem, onde todo o resto vai rodar.
vpc.tf
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
108
// Criando VPC
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr_block
tags = {
Name = "vpc-project-elven"
terraformed = "true"
}
}
// Subnets Públicas
resource "aws_subnet" "public" {
for_each = { for idx, name in local.subnet_public : idx => name } # Cria o recurso com base em uma lista
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr_block, 8, each.key) # Calcula automaticamente o CIDR
map_public_ip_on_launch = true
tags = {
Name = lower(format("subnet-%s", each.value)) # Formata o nome da subnet
}
}
// Subnets Privadas
resource "aws_subnet" "priv" {
for_each = { for idx, name in local.subnet_priv : idx => name }
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr_block, 8, each.key + length(local.subnet_public))
map_public_ip_on_launch = false
tags = {
Name = lower(format("subnet-%s", each.value))
}
}
// Internet Gateway
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "igw"
terraformed = "true"
}
}
// Tabela de Rotas Públicas
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
}
resource "aws_route" "internet_access" {
route_table_id = aws_route_table.public.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
resource "aws_route_table_association" "public" {
for_each = aws_subnet.public # Associa cada subnet à tabela de rota
subnet_id = each.value.id
route_table_id = aws_route_table.public.id
}
// EIP para NAT Gateways
resource "aws_eip" "nat" {
for_each = aws_subnet.public # Cria um EIP por subnet pública
domain = "vpc"
tags = {
Name = "nat-eip-${each.key}"
terraformed = "true"
}
}
// NAT Gateway
resource "aws_nat_gateway" "ngw" {
for_each = aws_subnet.public
subnet_id = each.value.id
allocation_id = aws_eip.nat[each.key].id
tags = {
Name = "nat-gw-${each.key}"
terraformed = "true"
}
depends_on = [aws_internet_gateway.igw] # Garante que o IGW seja criado antes do NAT Gateway
}
// Tabela de Rotas Privadas
resource "aws_route_table" "priv" {
for_each = aws_subnet.public
vpc_id = aws_vpc.main.id
tags = {
Name = "rtb-priv-${each.key}"
terraformed = "true"
}
}
// Rotas Privadas
resource "aws_route" "priv" {
for_each = aws_route_table.priv
route_table_id = each.value.id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.ngw[each.key].id
}
// Associações de Subnets Privadas às Tabelas de Rotas
resource "aws_route_table_association" "priv" {
for_each = aws_subnet.priv
subnet_id = each.value.id
route_table_id = aws_route_table.priv[each.key % length(aws_nat_gateway.ngw)].id
}
Criação do módulo Locals
O que são Locals? Os locals servem para simplificar a lógica dentro do Terraform. É como se fossem variáveis internas, calculadas ou organizadas para facilitar o uso dentro do próprio código. Em vez de repetir listas e valores, você centraliza em locals e usa em vários pontos. Isso deixa o código mais limpo e fácil de manter.
locals.tf
1
2
3
4
5
6
7
8
9
10
11
12
locals {
subnet_public = [
"public-1a",
"public-1c"
]
subnet_priv = [
"priv-1a",
"priv-1c"
]
}
Criação das Variables
O que são Variables? As variables no Terraform são formas de deixar o código mais flexível e reutilizável. Em vez de “fixar” valores, você cria variáveis para region, CIDR, nomes, tamanhos de bloco e por aí vai. Assim, com o mesmo código, você consegue subir ambientes diferentes só mudando os valores das variáveis.
variables.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
variable "region" {
description = "Região da AWS"
default = "us-east-1"
}
variable "vpc_cidr_block" {
type = string
description = "Bloco CIDR /16 para VLSM"
default = "10.100.0.0/16"
}
variable "aws_subnet" {
type = number
description = "Tamanho do bloco /24"
default = 24
}
Criação do Main
O que é Main? O main.tf é onde você geralmente concentra a definição do provider e o “chamado” dos recursos principais. É como o ponto de entrada do projeto: você diz “vou usar AWS” e define o que precisa. Os outros arquivos (variables, locals, vpc, etc.) complementam, mas o main é quem amarra tudo.
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Definindo provedor
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.region
}
Finalização do Desafio
Este desafio faz parte do Programa SRE Advanced da Elven Works.
O código foi desenvolvido seguindo boas práticas do Terraform, garantindo modularidade, reusabilidade e previsibilidade.
- Repositório oficial da Elven: Minha Primeira VPC com Terraform
- Meu repositório com o código deste desafio: Terraform VPC no GitLab