Post

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 subnets
  • lower() → transforma strings em minúsculas
  • format() → 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 mapas
  • depends_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.

Esta postagem está licenciada sob CC BY 4.0 pelo autor.

Trending Tags