Pular para o conteúdo principal

OpenTofu e Terraform

O provider coda-cloud/coda gerencia o Coda Cloud como código: projetos, ambientes, aplicações implantadas, variáveis, segredos, domínios e rotas. Ele funciona igual no OpenTofu (1.11 ou mais novo, o caminho principal) e no Terraform, tem plano fiel, importação, detecção de drift e deixa os valores de segredos fora do state.

O provider fala com a mesma API pública do portal e da CLI, com o token de uma conta de serviço: ele nunca tem mais acesso do que a conta e toda alteração fica na auditoria, com o canal "Conta de serviço" e o provider como cliente ("via OpenTofu provider 0.1.0").

Instalar e configurar​

Declare o provider e a organização. O token vem da variável de ambiente CODA_TOKEN e nunca do código:

main.tf
terraform {
required_providers {
coda = {
source = "coda-cloud/coda"
version = ">= 0.1.0"
}
}
}

provider "coda" {
# endpoint = "https://api.<domínio>" # padrão: a API pública; também CODA_API_URL
}

data "coda_organization" "this" {
slug = "acme"
}
export CODA_TOKEN=atl_sat_...
tofu init
tofu plan
ArgumentoVariável de ambienteO que é
tokenCODA_TOKENToken da conta de serviço (sensível). Sem ele o plano falha dizendo as duas formas de informá-lo
endpointCODA_API_URLEndereço da API, sem /api/v1
ca_fileCODA_CA_FILECA extra em PEM, para ambientes privados

Um token revogado, expirado ou inválido falha o plano com "Credencial inválida, expirada ou revogada" e nada é alterado. Se a versão do provider for mais antiga que a mínima aceita pela plataforma, o plano falha pedindo tofu init -upgrade.

Conta de serviço e escopos​

Crie uma conta de serviço com papel Admin (o provider precisa criar projetos) e emita um token com os escopos do que o código gerencia:

RecursoEscopo exigido
data.coda_organization, data.coda_regionsorgs:read
coda_project, data.coda_projectprojects:write (leitura: projects:read)
coda_environmentenvironments:write
coda_applicationapps:write e deployments:write
coda_config_variableconfig:write
coda_secretsecrets:write
coda_domain, coda_routedomains:write

Quando falta um escopo, o apply falha nomeando o recurso e o escopo exigido (apps:write, por exemplo) e o recurso não entra no state. Um token restrito a um projeto, ou de outra organização, vê o que está fora dele como não encontrado, sem revelar que existe.

Arquivar um projeto

Arquivar um projeto é ação de Owner e nenhuma conta de serviço é Owner. Por isso o tofu destroy de um coda_project com token de conta de serviço falha com a mensagem do papel exigido. Desligue deletion_protection e arquive pelo portal, pela CLI ou com o token pessoal de um Owner.

Recursos​

resource "coda_project" "web" {
organization_id = data.coda_organization.this.id
slug = "web"
name = "Web"
tags = { team = "core" }
deletion_protection = true
}

resource "coda_environment" "production" {
organization_id = data.coda_organization.this.id
project_id = coda_project.web.id
name = "production"
}

resource "coda_application" "api" {
organization_id = data.coda_organization.this.id
project_id = coda_project.web.id
environment_id = coda_environment.production.id
name = "api"
image = "registry.example.com/acme/api@sha256:<64 hex>"
instances = 2
}
  • Projeto. O slug é imutável. A remoção arquiva o projeto e, com deletion_protection = true (o padrão), é recusada no plano, antes de qualquer chamada à API.
  • Ambiente. Todo projeto nasce com development, staging e production: declarar um deles adota o existente. A API recusa remover um ambiente que ainda tem aplicações.
  • Aplicação. A imagem é sempre por digest sha256; uma tag como nginx:latest falha na validação do plano. Criar ou mudar a imagem faz um deploy e o apply só termina quando ele chega a RUNNING (até o timeouts, 15 minutos por padrão). Se o deploy falha, o apply mostra o motivo e deployed_image registra a imagem que continua no ar. Mudar só instances escala sem novo deploy. Porta, CPU, memória, comando, argumentos e região mudam no lugar (update in-place): o provider envia um PATCH com o If-Match da versão guardada no estado (atributo version) e espera o novo deploy, com o mesmo digest, chegar a RUNNING. Se alguém alterou a aplicação pelo portal, pela CLI ou pela API depois do último refresh, a API responde 412 e o apply falha pedindo tofu refresh; depois do refresh o plano mostra a diferença real e o apply passa. Mudar name, slug ou health_check_path ainda substitui a aplicação.
  • Variáveis e segredos. Veja abaixo.
  • Domínio e rota. coda_domain expõe verification_record_name e verification_record_value para um recurso de DNS no mesmo plano; com wait_for_verification = true o apply espera a verificação. coda_route exige um domínio verificado.

Cada página de recursos e fontes de dados traz todos os argumentos, exemplos e o comando de importação.

Variáveis e segredos​

coda_config_variable guarda valores não secretos. Se alguém mudar LOG_LEVEL pelo portal, o tofu plan mostra a diferença entre o valor do portal e o declarado.

coda_secret recebe o valor só por atributo write-only, que não entra no state, no plano nem nos logs:

variable "db_password" {
type = string
sensitive = true
ephemeral = true
}

resource "coda_secret" "db_password" {
organization_id = data.coda_organization.this.id
project_id = coda_project.web.id
environment_id = coda_environment.production.id
application_id = coda_application.api.id
name = "DB_PASSWORD"
value_wo = var.db_password
value_wo_version = 1
}

Para rotacionar, informe o novo valor e suba value_wo_version (para 2); sem mudar a versão, nada acontece. Atributos write-only exigem OpenTofu ou Terraform 1.11 ou mais novo: numa versão anterior a validação falha informando a versão mínima, porque o provider nunca grava um segredo no state como alternativa.

Importar o que já existe​

Todo recurso se importa pelo GUID ou pelo caminho de slugs <organização>/<projeto>[/<ambiente>[/<aplicação>[/<nome>]]], inclusive por blocos import:

import {
to = coda_application.api
id = "acme/web/production/api"
}

Depois do apply, o plano seguinte está vazio quando a configuração corresponde ao recurso.

Exportar o projeto como HCL​

No portal, a aba Infraestrutura como código do projeto (visível para quem tem papel Developer ou mais no projeto) mostra o HCL do que o projeto tem hoje, com Copiar e Baixar main.tf. A API oferece o mesmo arquivo em GET /api/v1/orgs/{org}/projects/{projeto}/iac/export (escopo projects:read).

O arquivo traz o bloco provider, um recurso e um bloco import para cada item, e cada segredo como uma variable sensível e efêmera ligada a value_wo, sem o valor. O projeto sai com deletion_protection = true. Itens que o provider ainda não gerencia aparecem como comentário (# não suportado pelo provider ainda: ...) e não quebram o plano.

tofu apply # adota tudo; informe cada segredo com TF_VAR_<nome>
tofu plan # vazio

Concorrência, repetições e erros​

  • Repetições automáticas. O provider repete respostas 429 (respeitando Retry-After), 502, 503 e 504 e falhas de rede, até 5 vezes com espera exponencial. As criações levam uma Idempotency-Key igual em todas as tentativas, então uma resposta perdida nunca cria o recurso duas vezes. Erros 4xx de validação nunca são repetidos.
  • Chave de idempotência na API. Quem chama a API diretamente pode mandar Idempotency-Key nas criações de projeto, ambiente, aplicação, deploy, variável, segredo, domínio e rota: repetir a mesma chave, com o mesmo corpo e a mesma credencial, em até 24 horas devolve a resposta original; o mesmo valor com outro corpo responde 422 idempotency_key.mismatch.
  • Diagnósticos. 401, 403 (com os escopos), 404, 409/422 (com o código estável da API) e 412 viram mensagens que dizem o que fazer. Um 412 (a versão do estado ficou velha porque alguém alterou o recurso fora do OpenTofu) pede tofu refresh e um novo plano, sem sobrescrever a alteração alheia.

Versões e compatibilidade​

A API publica a janela de versões em GET /api/v1/meta/clients (cli e provider, cada um com min e latest). O provider confere na configuração: abaixo do mínimo é erro, abaixo da última é aviso.

O provider é testado contra o OpenTofu. O protocolo é o mesmo do Terraform, então o provider também roda nele, mas os testes automáticos não executam o binário do Terraform (licença BSL).

Release e verificação​

Cada versão é publicada no OpenTofu Registry com SHA256SUMS assinado por uma chave GPG dedicada ao provider. O tofu init confere a assinatura. A chave pública e o procedimento de rotação estão no deploy/README.md do repositório.