Fase 1 — Build
A Fase 1 implanta uma zona DNS primária autoritativa no F5 Distributed Cloud com Terraform, com estado remoto armazenado no Azure Blob Storage. Esta página apresenta todos os arquivos do plano, explica as variáveis e segredos necessários, e cobre tanto a execução local quanto o pipeline do GitHub Actions.
O que você implanta
Seção intitulada “O que você implanta”- Uma
xcsh_dns_zonepara o seu domínio delegado, no namespacesystem, com um grupodemo-recordsde registros A (www,app,api). - Estado do Terraform armazenado em um contêiner do Azure Blob Storage, para que o mesmo estado seja compartilhado entre a sua máquina e o CI.
O plano
Seção intitulada “O plano”O plano está localizado em terraform/: um módulo raiz enxuto que conecta entradas a um módulo
dns-zone.
versions.tf
Seção intitulada “versions.tf”Fixa o Terraform e o provider. A restrição do provider é >= 3.62.0 — a versão em que os recursos
DNS exclusivos do sistema definem seu namespace automaticamente.
terraform { required_version = ">= 1.5"
required_providers { xcsh = { source = "f5-sales-demo/xcsh" # >= 3.62.0: the namespace attribute for system-only DNS resources defaults # to "system" (spec-driven), so it can be omitted. Locally the provider is # consumed via dev_overrides, which ignores this constraint. version = ">= 3.62.0" } }}providers.tf
Seção intitulada “providers.tf”O provider não recebe argumentos no código — ele se autentica a partir do ambiente, portanto nenhum segredo é confirmado no repositório.
# The xcsh provider authenticates from the environment — no secrets in code.# Export one of the following credential sets before running Terraform:## Token auth: XCSH_API_URL + XCSH_API_TOKEN# P12 auth: XCSH_API_URL + XCSH_P12_FILE + XCSH_P12_PASSWORD# PEM auth: XCSH_API_URL + XCSH_CERT + XCSH_KEY## See terraform/README.md for local dev setup (dev_overrides + env).provider "xcsh" {}backend.tf
Seção intitulada “backend.tf”Um backend azurerm parcial: nenhum valor específico de ambiente está fixado no código. As
coordenadas são fornecidas no momento do init (a partir de um arquivo local, ou de variáveis do
GitHub Actions no CI).
terraform { # Azure Blob Storage remote state, configured as a PARTIAL backend: # no environment-specific values are hardcoded here. Supply them at init time. # # CI: terraform init -backend-config="resource_group_name=$RG" ... # (values from GitHub Actions repository variables) # Local: terraform init -backend-config=backend.hcl (copy backend.hcl.example; gitignored) # # Auth is the storage account access key via the ARM_ACCESS_KEY environment # variable (never committed). Keyless auth (use_oidc / use_azuread_auth) is not # used: our Contributor-only RBAC cannot assign the "Storage Blob Data # Contributor" role those methods require. backend "azurerm" {}}variables.tf
Seção intitulada “variables.tf”domain é obrigatório (fornecido em tempo de execução, nunca fixado no código); a_records mapeia
cada nome de registro para seus endereços IPv4. Não há variável namespace — o provider fixa os
objetos DNS ao namespace system pela restrição da especificação da API, portanto nunca é
configurado aqui.
variable "domain" { description = "DNS zone FQDN (required; supplied via TF_VAR_domain / a GitHub variable / tfvars)." type = string}
variable "labels" { description = "Labels applied to managed DNS objects." type = map(string) default = { managed_by = "terraform" use_case = "dns" }}
variable "a_records" { description = "A records: record name (\"\" = apex) to list of IPv4 addresses." type = map(list(string)) default = { www = ["203.0.113.10"] app = ["203.0.113.20"] api = ["203.0.113.30"] }}main.tf e o módulo dns-zone
Seção intitulada “main.tf e o módulo dns-zone”O módulo raiz conecta as entradas a ./modules/dns-zone:
module "dns_zone" { source = "./modules/dns-zone"
domain = var.domain labels = var.labels a_records = var.a_records}O módulo cria a zona (o namespace é omitido — o provider o define como system por padrão). Um
bloco dynamic "rr_set" transforma o mapa a_records em um conjunto de registros por entrada:
resource "xcsh_dns_zone" "this" { name = var.domain labels = var.labels
primary { default_soa_parameters {}
rr_set_group { metadata { name = "demo-records" }
dynamic "rr_set" { for_each = var.a_records content { ttl = var.record_ttl a_record { name = rr_set.key values = rr_set.value } } } } }}O outputs.tf raiz reexporta o nome da zona e o identificador do F5 XC a partir do módulo.
Variáveis e segredos necessários
Seção intitulada “Variáveis e segredos necessários”Nada específico do ambiente está incorporado nos arquivos .tf. Tudo é fornecido em tempo de
execução — a partir de variáveis e segredos do GitHub Actions no CI, ou de arquivos locais
e variáveis de ambiente.
| Valor | Finalidade | Origem no CI | Origem local |
|---|---|---|---|
resource_group_name, storage_account_name, container_name, key | Coordenadas do backend azurerm | Variáveis do repositório TFSTATE_RESOURCE_GROUP, TFSTATE_STORAGE_ACCOUNT, TFSTATE_CONTAINER, TFSTATE_KEY (passadas via -backend-config) | backend.hcl (copie backend.hcl.example; ignorado pelo git) |
domain | Entrada do Terraform | Variável do repositório DNS_DOMAIN (como TF_VAR_domain) | terraform.tfvars (copie o exemplo) ou TF_VAR_domain |
ARM_ACCESS_KEY | Autenticação do backend azurerm (chave da conta de armazenamento) | Segredo do repositório | export ARM_ACCESS_KEY=... |
XCSH_API_URL, XCSH_API_TOKEN | Autenticação do provider xcsh | Segredos do repositório | export XCSH_API_URL=... XCSH_API_TOKEN=... |
Os dois arquivos de exemplo incluídos no repositório:
resource_group_name = "f5-sales-demo-tfstate"storage_account_name = "f5salesdemotfstate"container_name = "tfstate"key = "dns.tfstate"domain = "f5-sales-demo.com"
labels = { managed_by = "terraform" use_case = "dns"}
a_records = { www = ["203.0.113.10"] app = ["203.0.113.20"] api = ["203.0.113.30"]}Bootstrap do backend de estado no Azure
Seção intitulada “Bootstrap do backend de estado no Azure”O armazenamento do backend não pode armazenar seu próprio bootstrap, portanto crie-o uma vez, fora
do ciclo normal, com uma sessão autenticada da CLI do Azure. O repositório inclui
scripts/bootstrap-azure-state.sh:
az group create --name f5-sales-demo-tfstate --location eastus2 \ --tags managed_by=terraform use_case=dns purpose=tfstate
az storage account create --name f5salesdemotfstate --resource-group f5-sales-demo-tfstate \ --location eastus2 --sku Standard_LRS --kind StorageV2 \ --min-tls-version TLS1_2 --allow-blob-public-access false
# State safety: keep prior versions and allow recovery of deleted state blobs.az storage account blob-service-properties update \ --account-name f5salesdemotfstate --resource-group f5-sales-demo-tfstate \ --enable-versioning true \ --enable-delete-retention true --delete-retention-days 7 \ --enable-container-delete-retention true --container-delete-retention-days 7
KEY="$(az storage account keys list \ --account-name f5salesdemotfstate --resource-group f5-sales-demo-tfstate \ --query '[0].value' -o tsv)"
az storage container create --name tfstate \ --account-name f5salesdemotfstate --auth-mode key --account-key "$KEY"Em seguida, exporte a chave para execuções locais e defina-a (junto com as credenciais do provider) como segredos do GitHub:
export ARM_ACCESS_KEY="$KEY"gh secret set ARM_ACCESS_KEY -R f5-sales-demo/dnsgh secret set XCSH_API_URL -R f5-sales-demo/dnsgh secret set XCSH_API_TOKEN -R f5-sales-demo/dnsImplantar localmente
Seção intitulada “Implantar localmente”-
Configure as entradas. Copie os exemplos e preencha com seus valores:
Terminal window cp terraform/backend.hcl.example terraform/backend.hclcp terraform/terraform.tfvars.example terraform/terraform.tfvars -
Exporte as credenciais:
Terminal window export XCSH_API_URL="https://<tenant>.console.ves.volterra.io"export XCSH_API_TOKEN="<api-token>"export ARM_ACCESS_KEY="<storage-account-key>" -
Inicialize o backend com a configuração parcial:
Terminal window cd terraformterraform init -backend-config=backend.hcl -
Verifique a formatação e valide:
Terminal window terraform fmt -check -recursiveterraform validate -
Planeje e aplique:
Terminal window terraform planterraform apply
Uma aplicação bem-sucedida cria a zona e grava o estado no contêiner do Azure. Continue para Fase 2 — Validar para confirmar a resolução.
Integração contínua
Seção intitulada “Integração contínua”O workflow .github/workflows/terraform.yml executa um plan em cada pull request e um apply
no merge para main. Ele lê as coordenadas do backend e as entradas a partir das variáveis do
repositório, e as credenciais a partir dos segredos, injetando cada uma somente na etapa que a
necessita, e serializa as execuções em um grupo de concorrência para que dois applies nunca
concorram pelo mesmo blob de estado compartilhado.
name: Terraform
on: pull_request: branches: [main] paths: ['terraform/**', '.github/workflows/terraform.yml'] push: branches: [main] paths: ['terraform/**']
permissions: contents: read
concurrency: group: terraform-state cancel-in-progress: false
env: TF_IN_AUTOMATION: 'true'
jobs: terraform: name: ${{ github.event_name == 'push' && 'apply' || 'plan' }} runs-on: ubuntu-latest defaults: run: working-directory: terraform steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Setup Terraform uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
- name: Init env: ARM_ACCESS_KEY: ${{ secrets.ARM_ACCESS_KEY }} RESOURCE_GROUP: ${{ vars.TFSTATE_RESOURCE_GROUP }} STORAGE_ACCOUNT: ${{ vars.TFSTATE_STORAGE_ACCOUNT }} CONTAINER: ${{ vars.TFSTATE_CONTAINER }} STATE_KEY: ${{ vars.TFSTATE_KEY }} run: | terraform init -input=false \ -backend-config="resource_group_name=${RESOURCE_GROUP}" \ -backend-config="storage_account_name=${STORAGE_ACCOUNT}" \ -backend-config="container_name=${CONTAINER}" \ -backend-config="key=${STATE_KEY}"
- name: Format check run: terraform fmt -check -recursive
- name: Validate run: terraform validate -no-color
- name: Plan if: github.event_name == 'pull_request' env: ARM_ACCESS_KEY: ${{ secrets.ARM_ACCESS_KEY }} XCSH_API_URL: ${{ secrets.XCSH_API_URL }} XCSH_API_TOKEN: ${{ secrets.XCSH_API_TOKEN }} TF_VAR_domain: ${{ vars.DNS_DOMAIN }} run: terraform plan -input=false -no-color
- name: Apply if: github.event_name == 'push' env: ARM_ACCESS_KEY: ${{ secrets.ARM_ACCESS_KEY }} XCSH_API_URL: ${{ secrets.XCSH_API_URL }} XCSH_API_TOKEN: ${{ secrets.XCSH_API_TOKEN }} TF_VAR_domain: ${{ vars.DNS_DOMAIN }} run: terraform apply -input=false -auto-approve -no-colorAs actions estão fixadas em SHAs de commit (com comentários de versão), e os segredos têm escopo
limitado às etapas de init, plan e apply, em vez de todo o job.