Fase 1 — Construcción
La Fase 1 despliega una zona DNS primaria autoritativa en F5 Distributed Cloud con Terraform, respaldada por estado remoto en Azure Blob Storage. Esta página muestra cada archivo del plan, explica las variables y secretos requeridos, y cubre tanto una ejecución local como la canalización de GitHub Actions.
Qué se despliega
Sección titulada «Qué se despliega»- Una
xcsh_dns_zonepara su dominio delegado, en el namespacesystem, con un grupodemo-recordsde registros A (www,app,api). - Estado de Terraform almacenado en un contenedor de Azure Blob Storage, de modo que el mismo estado se comparte entre su máquina y CI.
El plan
Sección titulada «El plan»El plan se encuentra bajo terraform/: un módulo raíz delgado que conecta las entradas con un módulo dns-zone.
versions.tf
Sección titulada «versions.tf»Fija Terraform y el proveedor. La restricción del proveedor es >= 3.62.0 — la versión donde
los recursos DNS solo del sistema establecen su namespace automáticamente por defecto.
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
Sección titulada «providers.tf»El proveedor no toma argumentos en el código — se autentica desde el entorno, por lo que no se confirman secretos.
# 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
Sección titulada «backend.tf»Un backend azurerm parcial: no se codifican valores específicos del entorno. Las coordenadas se
proporcionan en el momento de init (desde un archivo local, o desde variables de GitHub Actions en 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
Sección titulada «variables.tf»domain es obligatorio (se proporciona en tiempo de ejecución, nunca codificado); a_records asigna
cada nombre de registro a sus direcciones IPv4. No existe una variable namespace — el proveedor fija
los objetos DNS al namespace system según la restricción de la especificación de la API, por lo que
nunca se configura aquí.
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 y el módulo dns-zone
Sección titulada «main.tf y el módulo dns-zone»El módulo raíz conecta las entradas con ./modules/dns-zone:
module "dns_zone" { source = "./modules/dns-zone"
domain = var.domain labels = var.labels a_records = var.a_records}El módulo crea la zona (el namespace se omite — el proveedor lo establece por defecto en system). Un
bloque dynamic "rr_set" convierte el mapa a_records en un 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 } } } } }}El archivo outputs.tf raíz reexporta el nombre de la zona y el identificador de F5 XC desde el módulo.
Variables y secretos requeridos
Sección titulada «Variables y secretos requeridos»Nada específico del entorno está integrado en los archivos .tf. Todo se proporciona en tiempo de
ejecución — desde variables y secretos de GitHub Actions en CI, o desde archivos locales y
variables de entorno.
| Valor | Propósito | Fuente en CI | Fuente local |
|---|---|---|---|
resource_group_name, storage_account_name, container_name, key | Coordenadas del backend azurerm | Variables del repositorio TFSTATE_RESOURCE_GROUP, TFSTATE_STORAGE_ACCOUNT, TFSTATE_CONTAINER, TFSTATE_KEY (pasadas mediante -backend-config) | backend.hcl (copiar backend.hcl.example; ignorado por git) |
domain | Entrada de Terraform | Variable del repositorio DNS_DOMAIN (como TF_VAR_domain) | terraform.tfvars (copiar el ejemplo) o TF_VAR_domain |
ARM_ACCESS_KEY | Autenticación del backend azurerm (clave de cuenta de almacenamiento) | Secreto del repositorio | export ARM_ACCESS_KEY=... |
XCSH_API_URL, XCSH_API_TOKEN | Autenticación del proveedor xcsh | Secretos del repositorio | export XCSH_API_URL=... XCSH_API_TOKEN=... |
Los dos archivos de ejemplo que se incluyen en el repositorio:
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"]}Inicializar el backend de estado en Azure
Sección titulada «Inicializar el backend de estado en Azure»El almacenamiento del backend no puede almacenar su propia inicialización, por lo que debe crearse una
vez, fuera de banda, con una sesión autenticada de Azure CLI. El repositorio incluye 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"Luego exporte la clave para ejecuciones locales y configúrela (junto con las credenciales del proveedor) como secretos de 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/dnsDespliegue local
Sección titulada «Despliegue local»-
Configure las entradas. Copie los ejemplos y complete sus valores:
Ventana de terminal cp terraform/backend.hcl.example terraform/backend.hclcp terraform/terraform.tfvars.example terraform/terraform.tfvars -
Exporte las credenciales:
Ventana de terminal export XCSH_API_URL="https://<tenant>.console.ves.volterra.io"export XCSH_API_TOKEN="<api-token>"export ARM_ACCESS_KEY="<storage-account-key>" -
Inicialice el backend con la configuración parcial:
Ventana de terminal cd terraformterraform init -backend-config=backend.hcl -
Verifique el formato y valide:
Ventana de terminal terraform fmt -check -recursiveterraform validate -
Planifique y aplique:
Ventana de terminal terraform planterraform apply
Una aplicación exitosa crea la zona y escribe el estado en el contenedor de Azure. Continúe a Fase 2 — Validación para confirmar que resuelve correctamente.
Integración continua
Sección titulada «Integración continua»El flujo de trabajo .github/workflows/terraform.yml ejecuta un plan en cada solicitud de
extracción y un apply al fusionar con main. Lee las coordenadas del backend y las entradas desde
las variables del repositorio, y las credenciales desde los secretos, inyectando cada una únicamente
en el paso que la necesita, y serializa las ejecuciones en un grupo de concurrencia para que dos
aplicaciones nunca compitan en el blob de estado compartido.
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-colorLas acciones están fijadas a SHAs de confirmación (con comentarios de versión), y los secretos están
limitados a los pasos de init, plan y apply en lugar de todo el trabajo.