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
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:
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.
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
:
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
:
resource "aws_s3_bucket" "first_bucket" {
bucket_prefix = "tutorial-bucket"
}
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::
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
:
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
:
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
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.