Skip to main content

OpenTofu

Infraestrutura como Código

Infraestrutura como Código (IaC) refere-se à prática de utilizar arquivos de configuração armazenados em repositórios de código para descrever e gerenciar a infraestrutura de recursos computacionais, como servidores, redes, armazenamento, bancos de dados, acessos, e DNS.

Empresas que utilizam serviços de nuvem frequentemente recorrem ao Terraform para organizar e automatizar a configuração e o provisionamento de sua infraestrutura.

OpenTofu + S3 + Magalu Cloud

Este guia descreve como utilizar o OpenTofu, uma alternativa de código aberto ao Terraform, para criar e gerenciar recursos de Object Storage na Magalu Cloud.

Pré-requisitos

  • Sistema operacional Linux (por exemplo, Ubuntu)
  • OpenTofu na versão v1.7.1 ou superior

Projeto e Módulo Raíz

Inicie criando um diretório para o projeto e um módulo de configuração raiz:

mkdir tutorial-tofu
cd tutorial-tofu
echo "terraform {}" > main.tf
tofu validate

Embora esta configuração raiz seja válida, está vazia. Módulos do Terraform utilizam plugins chamados Providers, que são extensões instaláveis para permitir o uso de produtos de diferentes nuvens.

Neste tutorial, usaremos o provider hashicorp/aws (licença MPL v2.0), que permite a interação com fornecedores de Object Storage compatíveis com o padrão S3. Atualize o arquivo main.tf para o seguinte:

main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.50.0"
}
}
}

provider "aws" {
# Configuration options
}

Instale este plugin com o comando:

tofu init

Retornaremos ao bloco comentado acima após configurarmos um "profile aws" com credenciais de acesso no próximo passo.

Nota

Embora exista um provider de Terraform oficial da Magalu Cloud, não o utilizamos neste tutorial para demonstrar a compatibilidade da API com o padrão S3 e porque, na data de publicação, o provider da Magalu estava na versão 0.18.10, que suportava apenas buckets e não objetos.

Configuração das credenciais

Como o Object Storage da Magalu é compatível com S3, ele também utiliza autenticação via API Key. Você pode criar uma chave de API no console e Início > Object Storage > API Key, clicando em "Criar API Key"

A chave gerada possui duas informações essenciais: ID e Secret. No tutorial, essas variáveis serão configuradas em um arquivo de perfil da AWS, localizado em ~/.aws/credentials. Crie um perfil chamado tutorial-tofu substituindo os textos em caixa alta COLE_AQUI_O... com o ID e o SECRET da chave criada:

mkdir -p ~/.aws
cat << EOF >> ~/.aws/credentials
[tutorial-tofu]
aws_access_key_id = COLE_AQUI_O_ID
aws_secret_access_key = COLE_AQUI_O_SECRET

EOF

Para a Magalu Cloud, ajuste os campos endpoint_url e region. Neste exemplo, utilizaremos a região "Brasil - Nordeste 1", identificada como br-ne1:

cat << EOF >> ~/.aws/config
[profile tutorial-tofu]
endpoint_url = https://br-ne1.magaluobjects.com/
region = br-ne1

EOF

Se configurado corretamente, retorne ao módulo raiz do projeto e inclua o nome do perfil no campo profile do bloco de configuração do provider aws:

main.tf
provider "aws" {
profile = "tutorial-tofu"
skip_region_validation = true
skip_requesting_account_id = true
skip_credentials_validation = true
}

Os atributos adicionais (skip_region_validation, skip_requesting_account_id, skip_credentials_validation) são necessários, e detalhes podem ser consultados na documentação do plugin.

Primeiro Bucket

Agora, vamos criar o primeiro recurso: um bucket para armazenar objetos. Crie um novo módulo de configuração com o arquivo resources.tf:

resources.tf
resource "aws_s3_bucket" "first_bucket" {
bucket_prefix = "tutorial-bucket"
}
Nota

Utilizamos o campo bucket_prefix em vez de bucket para nomear o contêiner, pois, assim como na AWS, os nomes de buckets na Magalu Cloud são globais e devem ser únicos. Portanto, ao invés de criar um nome único manualmente, fornecemos apenas um prefixo e deixamos o provider gerar um nome único.

Execute tofu plan para planejar o provisionamento:

tofu plan

Este comando exibirá as mudanças necessárias para alcançar o estado descrito nas configurações. O plano listará a criação do bucket: "# aws_s3_bucket.first_bucket will be created"

Aplique o plano com:

tofu apply -compact-warnings

Confirme a aplicação do plano digitando yes quando solicitado. Ao final, a mensagem deve ser similar a:

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Verifique o estado do novo recurso com tofu state list, tofu state show e tofu show.

Você também pode verificar a criação do bucket através da listagem no console:

Certifique-se de selecionar a região "Brasil - Nordeste 1" se seu console estiver configurado para outra região:

Mais recursos: objetos

Agora que temos um bucket aws_s3_bucket criado, podemos adicionar dois arquivos dentro dele utilizando o recurso aws_s3_object. Atualize o arquivo resources.tf com::

resources.tf
resource "aws_s3_bucket" "first_bucket" {
bucket_prefix = "tutorial-bucket"
}

resource "aws_s3_object" "first_bucket_objects" {
for_each = tomap({
file1 = "./main.tf"
file2 = "./resources.tf"
})
bucket = aws_s3_bucket.first_bucket.id
key = each.value
source = each.value
}

O comando tofu plan mostrará:

Plan: 2 to add, 0 to change, 0 to destroy.

E o tofu apply:

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Os dois arquivos estarão agora na nuvem. O comando tofu state list deve mostrar 3 recursos:

$ tofu state list
aws_s3_bucket.first_bucket
aws_s3_object.first_bucket_objects["file1"]
aws_s3_object.first_bucket_objects["file2"]

Para ver o estado do segundo objeto, execute:

$ tofu state show 'aws_s3_object.first_bucket_objects["file2"]'
# aws_s3_object.first_bucket_objects["file2"]:
resource "aws_s3_object" "first_bucket_objects" {
arn = "arn::s3:::tutorial-bucket20240523203657781600000001/resources.tf"
bucket = "tutorial-bucket20240523203657781600000001"
bucket_key_enabled = false
content_type = "application/octet-stream"
etag = "01d689431df57be26625e8fb41ac04d2"
force_destroy = false
id = "./resources.tf"
key = "./resources.tf"
source = "./resources.tf"
storage_class = "STANDARD"
tags_all = {}
}

Desprovisionando Tudo

Para evitar surpresas na conta do cartão de crédito, é crucial saber como limpar todos os recursos criados. Para desprovisionar os recursos, execute:

tofu destroy
Destroy complete! Resources: 3 destroyed.

Configuração e Estado

O comando tofu state fornece informações sobre o estado atual da infraestrutura descrita no código. A declaração descreve a infraestrutura desejada, enquanto o "state" reflete a configuração atual.

Na minha configuração, declarei que a instalação deveria ter um bucket com um certo prefixo no nome e dois objetos dentro, com o caminho dos arquivos locais para cada objeto.

O estado vai ter muito mais informação do que isso, como por exemplo: o nome completo que o bucket recebeu, as etags de cada objeto e vários outros detalhes.

Um ls no diretório do projeto mostrará a existência de dois arquivos gerados que descrevem o estado atual e o estado anterior da instalação:

terraform.tfstate  terraform.tfstate.backup

Um less no arquivo atual, depois do destroy, mostrará que o estado atual não possui nenhum recurso, por exemplo:

$ less -F terraform.tfstate
{
"version": 4,
"terraform_version": "1.7.1",
"serial": 7,
"lineage": "51390cb1-af8e-d82b-99d6-1bb5da876657",
"outputs": {},
"resources": [],
"check_results": null
}

Compartilhando o estado com o time

Num cenário onde várias pessoas vão trabalhar na mesma infraestrutura, ter as configs e este estado apenas local na máquina de quem executou o plano por último não é o ideal. Ao mesmo tempo, por conter informações sensíveis, não é recomendado colocar os arquivos tfstate.* num repositório git, especialmente se este repositório for público.

Uma possível saída para coordenar este trabalho em grupo e compartilhar de maneira privada o estado da instalação é usar a funcionalidade de estado remoto, que suporta várias opções de onde armazenar este estado, implementados por diferentes backends.

Vamos então finalizar este tutorial com um exemplo de como configurar o OpenTofu para armazenar o estado remotamente num bucket de S3 da Magalu Cloud, utilizando o backend "s3".

Primeiro, vamos criar um bucket novo só para isso, num arquivo novo state-bucket.ft:

state-bucket.tf
resource "aws_s3_bucket" "state_bucket" {
bucket_prefix = "tutorial-state-bucket"
}

tofu apply

Agora vamos exibir na tela o nome do bucket gerado para copiar e usar na configuração do backend depois:

tofu state show aws_s3_bucket.state_bucket

Finalmente, de volta no main.tf adicionamos este bloco backend "s3" ao bloco terraform:

main.tf

terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.50.0"
}
}

backend "s3" {
bucket = "NOME_DO_SEU_BUCKET_DE_STATE_AQUI"
key = "the_state"
region = "br-ne1"
profile = "tutorial-tofu"
skip_region_validation = true
skip_requesting_account_id = true
skip_credentials_validation = true
skip_s3_checksum = true
}
}

provider "aws" {
profile = "tutorial-tofu"
skip_region_validation = true
skip_requesting_account_id = true
skip_credentials_validation = true
}

Para instalar o backend, execute:

tofu init
Nota

Num cenário mais real, o bucket de state talvez não viveria no mesmo projeto, ou teria alguma prevenção, como lifecycle { prevent_destroy = true }, ou seria criado manualmente. Eu criei junto aqui apenas para facilitar o tutorial.

(adaptado do post original em Egoísmo Duplicado)