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:
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
| Argumento | Variável de ambiente | O que é |
|---|---|---|
token | CODA_TOKEN | Token da conta de serviço (sensível). Sem ele o plano falha dizendo as duas formas de informá-lo |
endpoint | CODA_API_URL | Endereço da API, sem /api/v1 |
ca_file | CODA_CA_FILE | CA 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:
| Recurso | Escopo exigido |
|---|---|
data.coda_organization, data.coda_regions | orgs:read |
coda_project, data.coda_project | projects:write (leitura: projects:read) |
coda_environment | environments:write |
coda_application | apps:write e deployments:write |
coda_config_variable | config:write |
coda_secret | secrets:write |
coda_domain, coda_route | domains: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 é 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,stagingeproduction: 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 comonginx:latestfalha na validação do plano. Criar ou mudar a imagem faz um deploy e o apply só termina quando ele chega aRUNNING(até otimeouts, 15 minutos por padrão). Se o deploy falha, o apply mostra o motivo edeployed_imageregistra a imagem que continua no ar. Mudar sóinstancesescala sem novo deploy. Porta, CPU, memória, comando, argumentos e região mudam no lugar (update in-place): o provider envia umPATCHcom oIf-Matchda versão guardada no estado (atributoversion) e espera o novo deploy, com o mesmo digest, chegar aRUNNING. Se alguém alterou a aplicação pelo portal, pela CLI ou pela API depois do últimorefresh, a API responde 412 e o apply falha pedindotofu refresh; depois do refresh o plano mostra a diferença real e o apply passa. Mudarname,slugouhealth_check_pathainda substitui a aplicação. - Variáveis e segredos. Veja abaixo.
- Domínio e rota.
coda_domainexpõeverification_record_nameeverification_record_valuepara um recurso de DNS no mesmo plano; comwait_for_verification = trueo apply espera a verificação.coda_routeexige 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 umaIdempotency-Keyigual 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-Keynas 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 responde422 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 refreshe 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.