Introdução
Duas equipes precisam do mesmo projeto simples de armazenamento de relatórios, com nomes, responsáveis e textos de relatórios diferentes. Conclua a interface de um módulo fornecido, chame-o a partir de dois diretórios raiz independentes e amplie uma coleção estável de relatórios sem substituir os membros existentes.
Você deve compreender módulos locais, variáveis, saídas e endereços de recursos. A estrutura inicial dos recursos e as entradas dos chamadores são fornecidas; você define o contrato entre eles e os implanta neste novo ambiente, com ferramentas preparadas e sem precisar de uma conta pessoal da AWS.
Relação com certificações
| Certificação | Tarefa do exame | Prática |
|---|---|---|
| CloudOps Engineer – Associate (SOA-C03) | Tarefa 3.1, Habilidade 3.1.6 | Prática básica com Terraform: manter a infraestrutura por meio de alterações revisadas na configuração e no estado, preservando os recursos em uso. |
Visão geral do laboratório

Defina uma interface de módulo com tipos explícitos
Nesta etapa, conclua o contrato de entradas e saídas de um pequeno módulo de armazenamento reutilizável. Um módulo filho agrupa recursos por trás de uma interface; um módulo raiz fornece entradas concretas e tem seu próprio estado.
cd /home/labex/project/reusable-reports
cat modules/report-store/main.tf
cat dev/main.tf
A estrutura inicial dos recursos fornecida usa var.bucket_name, var.owner e var.reports. for_each cria uma instância de objeto por chave do mapa, usando endereços como aws_s3_object.reports["daily"]. Chaves nomeadas estáveis mantêm um objeto existente separado de um membro recém-adicionado. O recurso de versionamento do bucket é uma dependência explícita para que as gravações dos relatórios recebam identidades de versão.
Defina a interface. map(object({ body = string })) aceita uma coleção nomeada cujos membros devem fornecer, cada um, o texto de um relatório. Isso explicita as expectativas do módulo, em vez de depender de estruturas de entrada não documentadas:
cat > modules/report-store/variables.tf <<'HCL'
variable "bucket_name" {
type = string
description = "Unique bucket name supplied by this environment root."
}
variable "owner" {
type = string
description = "Owner tag supplied by the caller."
}
variable "reports" {
type = map(object({ body = string }))
description = "Stable report keys and their text content."
}
HCL
As saídas expõem informações úteis sobre os recursos sem exigir que o chamador acesse os detalhes internos do módulo. A expressão for abaixo constrói um mapa de cada chave para a chave do objeto correspondente:
cat > modules/report-store/outputs.tf <<'HCL'
output "bucket_id" {
value = aws_s3_bucket.store.id
}
output "object_keys" {
value = { for key, report in aws_s3_object.reports : key => report.key }
}
HCL
Inicialize o diretório raiz dev e verifique o contrato:
terraform -chdir=dev init
terraform -chdir=dev validate
terraform -chdir=dev plan
Espere uma configuração válida e 3 to add: o bucket, a configuração de versionamento e o objeto do relatório diário. Não aplique até verificar o nome do bucket, o responsável e o texto do relatório definidos pelo chamador. A referência financeira independente não está relacionada a este módulo e deve permanecer inalterada.
Chame o mesmo módulo a partir de dois diretórios raiz independentes
Nesta etapa, implante dev e preview por meio do mesmo módulo, com entradas diferentes. Os diretórios raiz compartilham os arquivos-fonte do módulo, não o estado. Seus diretórios de trabalho separados usam atualmente estados locais separados; for_each apenas separa recursos dentro de um estado.
Revise e aplique o plano salvo de dev:
terraform -chdir=dev plan -out=dev.tfplan
Espere 3 to add, 0 to change, 0 to destroy.
terraform -chdir=dev apply dev.tfplan
Leia o chamador de preview antes de inicializá-lo:
cat preview/main.tf
terraform -chdir=preview init
terraform -chdir=preview validate
terraform -chdir=preview plan -out=preview.tfplan
Suas entradas distintas selecionam module-preview-reports, o responsável preview e preview daily report. Espere outro plano de criação de três recursos, sem alterações em dev.
terraform -chdir=preview apply preview.tfplan
Inspecione as saídas e o endereço estável da instância em cada diretório raiz:
terraform -chdir=dev output
terraform -chdir=preview output
terraform -chdir=dev state list
terraform -chdir=preview state list
Os dois diretórios raiz expõem bucket_id e object_keys; cada um tem uma instância de relatório com a chave "daily", mas esses endereços pertencem a estados locais e buckets diferentes. Leia o conteúdo armazenado:
aws s3 \
cp s3://module-dev-reports/reports/daily.txt -
aws s3 \
cp s3://module-preview-reports/reports/daily.txt -
Espere dev daily report e preview daily report. O AWS View mostra dois armazenamentos independentes e a referência financeira. Exiba o VersionId atual do relatório diário de dev para compará-lo após ampliar a coleção:
aws s3api \
head-object \
--bucket module-dev-reports \
--key reports/daily.txt \
--query VersionId \
--output text
Adicione um membro estável à coleção sem substituir relatórios existentes
Nesta etapa, amplie apenas o mapa de dev. Alterar as chaves de uma coleção altera a identidade das instâncias de recursos; manter a chave daily e adicionar weekly deve preservar o objeto diário intacto.
Substitua o chamador de dev pelas mesmas entradas existentes, acrescidas de um novo membro:
cat > dev/main.tf <<'HCL'
module "report_store" {
source = "../modules/report-store"
bucket_name = "module-dev-reports"
owner = "dev"
reports = {
daily = { body = "dev daily report\n" }
weekly = { body = "dev weekly report\n" }
}
}
output "bucket_id" { value = module.report_store.bucket_id }
output "object_keys" { value = module.report_store.object_keys }
HCL
Revise a alteração:
terraform -chdir=dev plan -out=extra.tfplan
Espere 1 to add, 0 to change, 0 to destroy, adicionando apenas module.report_store.aws_s3_object.reports["weekly"]. Se o objeto diário existente ou o bucket for substituído, pare e compare as chaves e as entradas que deveriam permanecer inalteradas.
terraform -chdir=dev apply extra.tfplan
Compare o VersionId do relatório diário com a saída da etapa anterior:
aws s3api \
head-object \
--bucket module-dev-reports \
--key reports/daily.txt \
--query VersionId \
--output text
Ele deve corresponder à mesma versão original. Leia o novo membro e o relatório independente de preview:
aws s3 \
cp s3://module-dev-reports/reports/weekly.txt -
aws s3 \
cp s3://module-preview-reports/reports/daily.txt -
Espere dev weekly report e o conteúdo inalterado preview daily report. A verificação em segundo plano compara as versões nativas da primeira gravação, em vez de aceitar bytes idênticos em objetos substitutos.
terraform -chdir=dev output
terraform -chdir=dev plan
terraform -chdir=preview plan
O mapa de saída de dev contém as duas chaves; preview continua tendo apenas daily. Os dois diretórios raiz devem convergir com No changes. Mantenha os armazenamentos em funcionamento disponíveis. Este módulo define uma interface e uma coleção de recursos; escolher chaves separadas para o backend remoto é uma decisão adicional de isolamento de ambientes, abordada posteriormente.
Consulte as orientações sobre composição de módulos da HashiCorp para usar entradas e saídas de módulos como limites de interface.
Resumo
Você definiu uma interface de módulo com tipos explícitos, retornou saídas úteis a dois diretórios raiz independentes e adicionou um membro estável de for_each sem substituir os relatórios originais. Uma coleção de recursos é diferente de um limite de estado de ambiente.



