The Infrastructure Hub -- Parte 1

Tu Infraestructura No Tiene Catálogo

#platform-engineering #backstage #terraform #infrastructure #catalog

El Problema

Abre el GitHub de tu organización. Busca “terraform”. Cuenta los repositorios.

Vas a encontrar algo así: terraform-azure-networking, infra-modules, tf-modules-v2, legacy-terraform, platform-terraform-new. Cinco repos. Tres convenciones de nombres. Dos de ellos tienen un README que dice “WIP”. Uno no se ha actualizado en 14 meses.

Ahora intenta responder a estas preguntas:

  • ¿Qué módulo debería usar para crear una Virtual Network?
  • ¿tf-modules-v2 es la última versión, o alguien creó platform-terraform-new para reemplazarlo?
  • ¿Quién es responsable del módulo de storage account? ¿Puedo cambiarlo, o voy a romper el entorno de alguien?
  • ¿El módulo de AKS funciona con Azure Provider 4.x, o sigue en 3.x?
  • ¿Hay un módulo para Scaleway, o solo tenemos Azure?

Nadie lo sabe. Ni siquiera la persona que escribió la mitad de esos módulos. Porque no hay catálogo. No hay un sitio único donde puedas ver todos los módulos de infraestructura que tiene tu organización, quién es responsable, en qué versión están, a qué cloud apuntan y si siguen mantenidos.

Este es el mismo problema que la serie AI-Native IDP resolvió para los servicios. Un catálogo de servicios que se queda desactualizado no sirve para nada. Un catálogo de infraestructura que ni existe es peor — porque los errores de infraestructura son caros y lentos de arreglar.

Y si trabajas en una empresa de servicios gestionados — gestionas infraestructura para 10, 20, 50 clientes — multiplica el problema por el número de clientes. Cada cliente tiene módulos diferentes, convenciones diferentes, procesos de aprobación diferentes. Llevar la cuenta de todo eso en tu cabeza no escala.

La Solución

Backstage ya tiene un Software Catalog. Lo usamos para servicios, APIs y librerías. Pero una entidad de Backstage puede ser cualquier cosa — incluyendo un módulo de Terraform.

La idea: registrar cada módulo de infraestructura como un Component en el catálogo con type: terraform-module. Añadir metadata: a qué cloud provider apunta, qué versión del Terraform provider necesita, quién es responsable, qué inputs espera. Y después usar las funciones que Backstage ya trae — búsqueda, filtros, ownership, TechDocs — para que el catálogo sea útil de verdad.

Para un equipo de DevOps, esto es un índice donde puedes buscar todos tus módulos. Para un proveedor de servicios gestionados (MSP), se convierte en un catálogo multi-tenant: la infraestructura de cada cliente es visible, organizada y trazable.

La entrada del catálogo para un módulo de Terraform tiene este aspecto:

# catalog-info.yaml (in the module repo)
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: tf-azurerm-vnet
  title: Azure Virtual Network Module
  description: "Creates a VNet with subnets, NSGs, and optional peering. Supports hub-spoke topology."
  tags:
    - terraform
    - azure
    - networking
  annotations:
    github.com/project-slug: victorZKov/tf-azurerm-vnet
    backstage.io/techdocs-ref: dir:.
  links:
    - url: https://registry.terraform.io/providers/hashicorp/azurerm/latest
      title: Azure Provider
spec:
  type: terraform-module
  lifecycle: production
  owner: team-platform
  system: infrastructure
  providesApis:
    - tf-azurerm-vnet-api

Y la entidad API documenta la interfaz del módulo — inputs y outputs:

apiVersion: backstage.io/v1alpha1
kind: API
metadata:
  name: tf-azurerm-vnet-api
  title: tf-azurerm-vnet Interface
  description: "Inputs and outputs for the Azure VNet module"
spec:
  type: terraform
  lifecycle: production
  owner: team-platform
  definition: |
    inputs:
      - name: resource_group_name (string, required)
      - name: location (string, default: "westeurope")
      - name: address_space (list(string), required)
      - name: subnets (map(object), required)
      - name: tags (map(string), required)
    outputs:
      - name: vnet_id (string)
      - name: subnet_ids (map(string))

Ejecución

Construimos sobre la instancia de Backstage de la serie IDP. Mismo proyecto Forge, mismo servicio de AI, misma autenticación. Añadimos entidades de infraestructura al catálogo.

Paso 1: Definir el sistema de infraestructura

Creamos una entidad de tipo system que agrupa todos los módulos de infraestructura:

# catalog/infrastructure.yaml
apiVersion: backstage.io/v1alpha1
kind: System
metadata:
  name: infrastructure
  title: Infrastructure Modules
  description: "Terraform modules, deployment patterns, and environment definitions"
  tags:
    - terraform
    - infrastructure
spec:
  owner: team-platform

Vamos a crear un módulo real. Un módulo simple de Azure resource group — básico, pero muestra el patrón.

# modules/tf-azurerm-resource-group/main.tf
terraform {
  required_version = ">= 1.8"

  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.0"
    }
  }
}

variable "name" {
  type        = string
  description = "Resource group name"
}

variable "location" {
  type        = string
  default     = "westeurope"
  description = "Azure region"
}

variable "tags" {
  type        = map(string)
  description = "Resource tags"
}

resource "azurerm_resource_group" "this" {
  name     = var.name
  location = var.location
  tags     = var.tags
}

output "name" {
  value = azurerm_resource_group.this.name
}

output "id" {
  value = azurerm_resource_group.this.id
}

output "location" {
  value = azurerm_resource_group.this.location
}

Ahora la entrada del catálogo:

# modules/tf-azurerm-resource-group/catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: tf-azurerm-resource-group
  title: Azure Resource Group Module
  description: "Creates an Azure resource group with standard tags"
  tags:
    - terraform
    - azure
    - foundation
  annotations:
    github.com/project-slug: victorZKov/forge
    backstage.io/techdocs-ref: dir:.
spec:
  type: terraform-module
  lifecycle: production
  owner: team-platform
  system: infrastructure

Paso 3: Registrar módulos en Backstage

Añade las ubicaciones de los módulos a app-config.yaml:

catalog:
  locations:
    # Infrastructure modules
    - type: file
      target: ../../modules/tf-azurerm-resource-group/catalog-info.yaml
      rules:
        - allow: [Component, API]

    # Infrastructure system
    - type: file
      target: ../../catalog/infrastructure.yaml
      rules:
        - allow: [System]

Cuando Backstage arranca, los módulos aparecen en el catálogo junto a tus servicios. Filtra por type: terraform-module para ver solo los componentes de infraestructura.

Paso 4: Un template de Scaffolder para nuevos módulos

Cada nuevo módulo de Terraform debería empezar con la misma estructura: main.tf, variables.tf, outputs.tf, catalog-info.yaml, README.md, y un test básico. Creamos un template de Backstage para esto:

# templates/terraform-module/template.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: terraform-module
  title: New Terraform Module
  description: Create a new Terraform module with standard structure, catalog metadata, and documentation.
  tags:
    - terraform
    - infrastructure
    - recommended
spec:
  owner: team-platform
  type: terraform-module

  parameters:
    - title: Module Details
      required:
        - name
        - cloud
        - description
        - owner
      properties:
        name:
          title: Module Name
          type: string
          pattern: "^tf-[a-z]+-[a-z-]+$"
          ui:placeholder: "tf-azurerm-storage-account"
          ui:help: "Format: tf-{provider}-{resource}"
        cloud:
          title: Cloud Provider
          type: string
          enum:
            - azurerm
            - aws
            - scaleway
            - google
          enumNames:
            - Azure
            - AWS
            - Scaleway
            - Google Cloud
        description:
          title: Description
          type: string
          ui:widget: textarea
        owner:
          title: Owner
          type: string
          ui:field: OwnerPicker

  steps:
    - id: fetch
      name: Generate Module Structure
      action: fetch:template
      input:
        url: ./skeleton
        values:
          name: ${{ parameters.name }}
          cloud: ${{ parameters.cloud }}
          description: ${{ parameters.description }}
          owner: ${{ parameters.owner }}

    - id: publish
      name: Publish to GitHub
      action: publish:github
      input:
        allowedHosts: ["github.com"]
        repoUrl: github.com?owner=victorZKov&repo=${{ parameters.name }}
        description: ${{ parameters.description }}
        defaultBranch: main

    - id: register
      name: Register in Catalog
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
        catalogInfoPath: /catalog-info.yaml

  output:
    links:
      - title: Repository
        url: ${{ steps.publish.output.remoteUrl }}
      - title: Open in Catalog
        icon: catalog
        entityRef: ${{ steps.register.output.entityRef }}

El directorio skeleton tiene la estructura estándar del módulo:

templates/terraform-module/skeleton/
├── main.tf              # Provider config + resources
├── variables.tf         # All input variables
├── outputs.tf           # All outputs
├── versions.tf          # Required providers and versions
├── catalog-info.yaml    # Backstage metadata
└── README.md            # Auto-generated from variables

Un desarrollador va a Backstage, hace clic en “Create”, elige “New Terraform Module”, rellena el nombre, el cloud provider y la descripción. Obtiene un repo con la estructura correcta, ya registrado en el catálogo.

Paso 5: Usar el enricher de AI para módulos

El enricher del catálogo del artículo 2 ya lee código y actualiza la metadata del catálogo. Funciona también con módulos de Terraform — lee main.tf, variables.tf y outputs.tf, y genera descripciones y tags precisos.

El enricher ya busca estos archivos:

const targetFiles = tree.tree.filter(
  f => f.path === 'Program.cs' ||
       f.path === 'package.json' ||
       f.path.endsWith('.csproj') ||
       f.path === 'Dockerfile' ||
       f.path === 'appsettings.json' ||
       f.path === 'app-config.yaml',
);

Lo extendemos para que también recoja archivos de Terraform:

const targetFiles = tree.tree.filter(
  f => f.path === 'Program.cs' ||
       f.path === 'package.json' ||
       f.path.endsWith('.csproj') ||
       f.path === 'Dockerfile' ||
       f.path === 'appsettings.json' ||
       f.path === 'app-config.yaml' ||
       f.path === 'main.tf' ||
       f.path === 'variables.tf' ||
       f.path === 'outputs.tf' ||
       f.path === 'versions.tf',
);

Ahora la AI lee tu código de Terraform y mantiene el catálogo actualizado. Si alguien añade una nueva variable o output, el enricher lo detecta y propone una actualización.

Cómo se ve

Abre Backstage. Ve al catálogo. Filtra por type: terraform-module. Ves esto:

ModuleCloudOwnerLifecycle
tf-azurerm-resource-groupAzureteam-platformproduction
tf-azurerm-vnetAzureteam-platformproduction
tf-azurerm-aksAzureteam-platformexperimental
tf-scaleway-kapsuleScalewayteam-platformproduction
tf-aws-vpcAWSteam-legacydeprecated

Haz clic en cualquier módulo. Ves la descripción, los inputs, los outputs, quién es responsable, cuándo se actualizó por última vez, y links al código fuente. Si TechDocs está configurado, ves la documentación renderizada directamente en Backstage.

Se acabó buscar por repos de GitHub. Se acabó adivinar qué módulo es el correcto. Se acabó el “¿esto sigue mantenido?” — el campo lifecycle te lo dice.

Para un MSP que gestiona múltiples clientes, añade un tag client a cada módulo. Filtra por cliente. Ve exactamente qué infraestructura usa cada uno.

Template

Aquí tienes el template de catalog-info.yaml para cualquier módulo de Terraform:

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: ${{ name }}
  title: ${{ title }}
  description: "${{ description }}"
  tags:
    - terraform
    - ${{ cloud }}
    - ${{ category }}  # networking, compute, storage, database, security
  annotations:
    github.com/project-slug: ${{ org }}/${{ name }}
    backstage.io/techdocs-ref: dir:.
spec:
  type: terraform-module
  lifecycle: ${{ lifecycle }}  # experimental, production, deprecated
  owner: ${{ owner }}
  system: infrastructure

Reto

Antes del próximo artículo:

  1. Elige 3 módulos de Terraform de tu organización
  2. Crea un catalog-info.yaml para cada uno
  3. Regístralos en Backstage (o en una instancia local)
  4. Prueba a filtrar por cloud provider, por owner, por lifecycle

En el próximo artículo, construimos Golden Path Terraform Modules — templates estándar de módulos para Azure, Scaleway, AWS y GCP con testing, documentación y versionado incluidos. El scaffolder los genera desde Backstage con la estructura correcta desde el día uno.


Si esta serie te ayuda, considera invitarme a un café.

Este es el artículo 1 de la serie Infrastructure Hub. Siguiente: Golden Path Terraform Modules — templates estándar para cada cloud.

Comments

Loading comments...