Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions entregas/aula-06/6325149/entrega.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Entrega — Aula 06: Terraform Modules

**Aluno:** Gabriel Reis Cunha
**RA:** 6325149
**Data:** 17/09/2026

## Repositório

- URL: https://github.com/gabrielreis354/unifaat-devops-portfolio
- Pasta do projeto: [`aula-06/`](https://github.com/gabrielreis354/unifaat-devops-portfolio/tree/main/aula-06)
- Spec (SDD): [`specs/002-aula06-terraform-modules/`](https://github.com/gabrielreis354/unifaat-devops-portfolio/tree/main/specs/002-aula06-terraform-modules)

## Evidências

- [x] Módulo VPC com `for_each` para subnets dinâmicas
- [x] Módulo Security Group genérico (regras como lista de objetos)
- [x] Módulo EC2 reutilizável
- [x] Módulo RDS reutilizável
- [x] Composição entre módulos (output de um alimenta input de outro)
- [x] Dois ambientes (dev + staging) usando os mesmos módulos
- [x] `terraform validate` e `terraform plan` sem erros nos dois ambientes
- [x] README documentando cada módulo (inputs, outputs, exemplo)

## Evidência do `terraform plan` (ambiente dev)

```
$ terraform plan

# module.api_server.aws_instance.this will be created
# module.api_sg.aws_security_group.this will be created
# module.api_sg.aws_security_group_rule.egress[0] will be created
# module.api_sg.aws_security_group_rule.ingress[0] will be created
# module.api_sg.aws_security_group_rule.ingress[1] will be created
# module.database.aws_db_instance.main will be created
# module.database.aws_db_subnet_group.main will be created
# module.rds_sg.aws_security_group.this will be created
# module.rds_sg.aws_security_group_rule.egress[0] will be created
# module.rds_sg.aws_security_group_rule.ingress[0] will be created
# module.vpc.aws_internet_gateway.main will be created
# module.vpc.aws_route_table.public will be created
# module.vpc.aws_route_table_association.public["public-1"] will be created
# module.vpc.aws_route_table_association.public["public-2"] will be created
# module.vpc.aws_subnet.this["private-1"] will be created
# module.vpc.aws_subnet.this["private-2"] will be created
# module.vpc.aws_subnet.this["public-1"] will be created
# module.vpc.aws_subnet.this["public-2"] will be created
# module.vpc.aws_vpc.main will be created

Plan: 19 to add, 0 to change, 0 to destroy.
```

O ambiente `staging` produz o mesmo conjunto de 19 recursos, com CIDRs e
nomes próprios (`10.1.0.0/16`, `technova-staging-*`), sem colisão com `dev`.

## Evidência do comportamento seletivo do `for_each`

Removendo `"private-2"` do mapa `subnets` (ambiente dev) e rodando `plan`
novamente, **apenas aquela subnet** deixa de aparecer no plano — as demais
(`public-1`, `public-2`, `private-1`) mantêm as mesmas chaves nomeadas, sem
qualquer reindexação:

```
$ terraform plan # com "private-2" removida
Plan: 18 to add, 0 to change, 0 to destroy.
```

(mudança revertida antes do commit final — as 4 subnets voltaram ao mapa.)

## Evidência de `apply` real (smoke test do ambiente dev)

Apesar de não ser exigido pelo TF.md, rodei `terraform apply` real no
ambiente `dev` para confirmar que os módulos funcionam de ponta a ponta, com
`destroy` imediato após a evidência:

```
$ terraform apply
...
Apply complete! Resources: 19 added, 0 changed, 0 destroyed.

Outputs:
api_sg_id = "sg-0d496fab03c893eb7"
db_endpoint = "technova-dev-db.ch0dmfn54vrt.us-east-1.rds.amazonaws.com:5432"
ec2_public_ip = "52.90.99.133"
rds_sg_id = "sg-05fb0c5a18823aea3"
vpc_id = "vpc-000ba4578e0f3b85e"

$ terraform plan
No changes. Your infrastructure matches the configuration.

$ aws rds describe-db-instances --db-instance-identifier technova-dev-db \
--query "DBInstances[0].{PubliclyAccessible:PubliclyAccessible,MultiAZ:MultiAZ,Encrypted:StorageEncrypted}"
Encrypted=true MultiAZ=false PubliclyAccessible=false

$ terraform destroy
Destroy complete! Resources: 19 destroyed.
```

**Achado real:** a conta do AWS Academy Learner Lab rotacionou entre a
aula-05 e hoje — a key pair `technova-key` não existia na conta nova
(`InvalidKeyPair.NotFound` na criação do EC2). Resolvido com `aws ec2
import-key-pair` reaproveitando a chave pública já gerada. Confirma que o
módulo `ec2` trata a key pair como pré-requisito externo (não a cria), como
documentado no `README.md` — e vira nota prática: **se a conta do lab
rotacionar, reimportar a chave antes do apply.**

`environments/staging` permanece validado só por `terraform validate` +
`plan` (mesmos módulos que `dev`; o TF.md desaconselha aplicar os dois
ambientes ao mesmo tempo, para não dobrar o consumo do Learner Lab).

## Nota sobre o módulo RDS

Nenhum dos dois laboratórios desta aula cobre um módulo RDS. Ele foi
desenhado a partir do `aula-05-rds/rds.tf`, já validado com `terraform
apply` real na aula-05 (RDS PostgreSQL criado, testado via `psql` e
destruído). Detalhes da decisão em
[`specs/002-aula06-terraform-modules/plan.md`](https://github.com/gabrielreis354/unifaat-devops-portfolio/blob/main/specs/002-aula06-terraform-modules/plan.md).

## Sobre `terraform apply`

Conforme o `TF.md` desta aula, `terraform apply` **não é obrigatório** — a
avaliação usa `terraform validate` e `terraform plan` limpos nos dois
ambientes (o que `staging` cumpre). Ainda assim, apliquei de verdade o
ambiente `dev` como smoke test (seção acima) para confirmar que os módulos
funcionam de ponta a ponta, e destruí tudo imediatamente após capturar a
evidência — nenhum recurso ficou ativo na AWS.
116 changes: 116 additions & 0 deletions entregas/aula-06/6325149/trabalho-em-aula.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Trabalho em Aula — Aula 06: Módulos Terraform

**Aluno:** Gabriel Reis Cunha
**RA:** 6325149
**Data:** 17/09/2026

## Parte 1 — Identificação de Duplicação

1. **Blocos de recursos duplicados entre dev e staging (7 pares, 14 recursos):**
- `aws_vpc` (`dev` / `staging`)
- `aws_subnet` público 1 (`dev_public_1` / `staging_public_1`)
- `aws_subnet` público 2 (`dev_public_2` / `staging_public_2`)
- `aws_internet_gateway` (`dev` / `staging`)
- `aws_security_group` da API (`dev_api` / `staging_api`)
- `aws_security_group` do RDS (`dev_rds` / `staging_rds`)
- `aws_instance` da API (`dev_api` / `staging_api`)

2. **O que muda entre dev e staging:** só um punhado de valores —
o CIDR da VPC (`10.0.0.0/16` → `10.1.0.0/16`), os CIDRs das duas subnets
(`10.0.1.0/24`/`10.0.2.0/24` → `10.1.1.0/24`/`10.1.2.0/24`), a tag
`Environment` (`dev` → `staging`) e o prefixo usado em todos os nomes/tags
(`technova-dev-*` → `technova-staging-*`). Toda a **estrutura** — quantidade
de subnets, regras de ingress/egress dos Security Groups, AMI, tipo de
instância, referência ao key pair — é idêntica byte a byte.

3. **Módulos que eu criaria (mínimo 3, uso 4):**
- `modules/vpc` — VPC + subnets + Internet Gateway + route table
- `modules/security-group` — SG genérico, reutilizável tanto para a API
quanto para o RDS (e qualquer SG futuro)
- `modules/ec2` — instância EC2 parametrizável
- `modules/rds` — RDS PostgreSQL parametrizável (não aparece no trecho
de código analisado, mas é parte natural da arquitetura da TechNova das
aulas anteriores e está no diagrama de referência do exercício)

4. **Variáveis (inputs) de cada módulo:**
- **vpc:** `vpc_cidr`, `project_name`, `environment`, `subnets`
(`map(object)` com `cidr`, `az`, `type`)
- **security-group:** `name`, `vpc_id`, `ingress_rules` (`list(object)`),
`environment`, `project_name`
- **ec2:** `instance_name`, `instance_type`, `ami_id`, `subnet_id`,
`security_group_ids`, `key_name`
- **rds:** `db_name`, `db_username`, `db_password` (sensitive),
`subnet_ids`, `security_group_ids`, `instance_class`, `environment`,
`project_name`

5. **Outputs de cada módulo:**
- **vpc:** `vpc_id`, `public_subnet_ids`, `private_subnet_ids`
- **security-group:** `sg_id`
- **ec2:** `instance_id`, `public_ip`, `private_ip`
- **rds:** `db_endpoint`, `db_name`, `db_port`

6. **Linhas para um ambiente de produção — código atual vs. módulos:**
O `main.tf` atual tem ~180 linhas para 2 ambientes (~90 linhas por
ambiente). Sem módulos, produção seria **mais uma cópia inteira**: +90
linhas, chegando a ~270 linhas totais, todas quase idênticas e todas
precisando ser mantidas em sincronia manualmente. Com módulos, os 4
módulos são escritos **uma única vez** (a lógica fica concentrada neles);
um ambiente novo é só um bloco de chamadas `module { ... }` com valores
diferentes — algo como **20-30 linhas**, não 90. Produção deixa de ser
"copiar e adaptar 90 linhas" e vira "adicionar um bloco pequeno".

## Parte 2 — Design de Módulos (Diagrama de Dependências)

```
┌──────────────────┐
│ modules/vpc │
│ │
│ out: vpc_id │
│ out: public_ids[] │
│ out: private_ids[] │
└─────────┬──────────┘
│ vpc_id
┌───────────────┼───────────────┐
▼ ▼
┌────────────────────┐ ┌────────────────────┐
│ modules/security- │ │ modules/security- │
│ group (api) │ │ group (rds) │
│ out: sg_id │ │ out: sg_id │
└──────────┬───────────┘ └──────────┬───────────┘
│ sg_id │ sg_id
public_subnet_ids private_subnet_ids
│ │
▼ ▼
┌────────────────────┐ ┌────────────────────┐
│ modules/ec2 │ │ modules/rds │
│ (subnet_id + │ │ (subnet_ids + │
│ security_group_ids) │ │ security_group_ids) │
└────────────────────┘ └────────────────────┘
```

- **Qual módulo deve ser criado primeiro, e por quê:** o `vpc`. Todos os
outros módulos precisam de `vpc_id` (Security Groups) ou dos IDs das
subnets (EC2 e RDS) que só existem depois da VPC ser criada — é a base de
toda a dependência.
- **Os módulos de Security Group dependem de qual output da VPC:**
`vpc_id` — é o único dado que o `aws_security_group` precisa da VPC para
saber onde aplicar as regras.
- **De quantos outros módulos o módulo EC2 depende:** 2 — do `vpc` (para o
`subnet_id` da subnet pública) e do `security-group` (para
`security_group_ids`).
- **O que acontece com os outros módulos se a VPC for destruída:** o
Terraform enxerga a dependência implícita via referência aos outputs
(`module.vpc.vpc_id`, `module.vpc.public_subnet_ids`), então ele tentaria
destruir primeiro tudo que depende da VPC (SGs, EC2, RDS) antes de poder
destruir a própria VPC. Se alguém tentasse remover só a VPC do state fora
de ordem, os outros módulos ficariam órfãos — outputs como `vpc_id`
deixariam de existir e o próximo `plan` mostraria erro ou tentativa de
recriação em cascata.
- **Vantagem de um módulo genérico de Security Group em vez de "api-sg" e
"rds-sg" separados:** com um único módulo parametrizado por
`ingress_rules` (lista de objetos), a mesma lógica de `aws_security_group`
é escrita uma vez e reutilizada para qualquer finalidade — API, RDS, e
amanhã um bastion host ou um load balancer, bastando passar regras
diferentes. Dois módulos hardcoded (`api-sg`, `rds-sg`) duplicariam a
mesma estrutura de recurso só mudando as regras, reproduzindo exatamente o
problema de duplicação que o exercício pede para eliminar.
Loading