The Infrastructure Hub -- Parte 1
Tu Infraestructura No Tiene Catálogo
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-v2es la última versión, o alguien creóplatform-terraform-newpara 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
Paso 2: Crear un módulo de Terraform con metadata de catálogo
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:
| Module | Cloud | Owner | Lifecycle |
|---|---|---|---|
| tf-azurerm-resource-group | Azure | team-platform | production |
| tf-azurerm-vnet | Azure | team-platform | production |
| tf-azurerm-aks | Azure | team-platform | experimental |
| tf-scaleway-kapsule | Scaleway | team-platform | production |
| tf-aws-vpc | AWS | team-legacy | deprecated |
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:
- Elige 3 módulos de Terraform de tu organización
- Crea un
catalog-info.yamlpara cada uno - Regístralos en Backstage (o en una instancia local)
- 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.
Loading comments...