Infraestructura como código: el cambio que falta en tu pull request
Este artículo forma parte del pilar Azure, DevOps y operación.
El pipeline terminó en verde. La aplicación ya está desplegada. Alguien intenta guardar un documento y aparece el detalle que faltaba: nadie creó la cuenta de Azure Storage que necesita la nueva funcionalidad.
Es un escenario fácil de reconocer cuando automatizamos la aplicación, pero seguimos preparando su entorno a mano. El código pasó por un pull request, pruebas y un despliegue. La infraestructura quedó en una tarea que decía «recordar configurar esto en Azure».
Si una funcionalidad depende de un recurso o de una configuración, ese cambio también forma parte de su entrega. Por eso creo que deberíamos conversar sobre infraestructura como código desde que empezamos a trabajar con CI/CD, junto con la compilación y las pruebas.
La entrega continua puede abarcar una API escrita en C#, una aplicación Python o una aplicación móvil que llega a Google Play. También puede abarcar los recursos que permiten que esas aplicaciones funcionen. En Azure tenemos herramientas para llevar esa parte al mismo proceso.
El cambio que quedó fuera del pull request
Pensemos en una aplicación que incorpora la carga de documentos. En desarrollo alguien crea el almacenamiento desde el portal de Azure. Prueba la funcionalidad, funciona y abre el pull request con el código.
Quien lo revisa puede comprobar cómo se guarda el archivo y cómo se manejan los errores. Pero la cuenta de almacenamiento, sus opciones y el acceso que necesitará la aplicación no aparecen en el cambio. Al preparar otro ambiente hay que reconstruir esas decisiones preguntando o recorriendo pantallas.
Lo mismo ocurre con el escalado de un servicio o una asignación de permisos pendiente. La lista de pasos manuales empieza a crecer alrededor del despliegue. Si uno se olvida, podemos terminar investigando la aplicación cuando lo que falta es preparar su entorno.
Los cambios temporales complican más la situación. Se modifica una opción para resolver una urgencia, queda funcionando y después nadie recuerda si debía conservarse. El siguiente ambiente se prepara con otro valor. Ya tenemos diferencias que el repositorio no explica.
La infraestructura como código permite expresar el estado que queremos mantener en archivos versionados. Así podemos revisar qué recurso necesita una funcionalidad, qué propiedades cambian y por qué, antes de llevarlo a un ambiente. La memoria de quien hizo clic deja de ser el único registro de esa decisión.
En Azure, empezaría con Bicep
Terraform permite definir infraestructura mediante archivos declarativos y llevarlos a un flujo de revisión y despliegue. Si un equipo ya trabaja con él, la idea de este artículo se aplica igual.
Para un desarrollo nuevo centrado en Azure, mi recomendación es Bicep. Es un lenguaje declarativo con tipos: describimos los recursos y sus propiedades, y Azure Resource Manager se encarga del despliegue.
Conviene precisar los nombres. ARM es Azure Resource Manager, el servicio que administra los recursos. Las plantillas ARM se escriben en JSON; Bicep ofrece otra sintaxis y se compila a esas plantillas. La documentación de Bicep explica esa relación.
Yo evitaría escribir las plantillas JSON a mano en un desarrollo nuevo porque Bicep resulta más conciso y facilita revisar la definición. Esa preferencia tiene un motivo bastante práctico: si esperamos que la infraestructura pase por un pull request, nos conviene que el equipo pueda leerla y discutirla.
El Azure Storage que necesita la aplicación
Volvamos a la carga de documentos. Junto al código de la funcionalidad podríamos agregar este archivo:
param location string = resourceGroup().locationparam storageAccountName string = 'docs${uniqueString(resourceGroup().id)}'
resource documentsStorage 'Microsoft.Storage/storageAccounts@2023-05-01' = { name: storageAccountName location: location kind: 'StorageV2' sku: { name: 'Standard_LRS' } properties: { supportsHttpsTrafficOnly: true minimumTlsVersion: 'TLS1_2' allowBlobPublicAccess: false }}
output storageAccountId string = documentsStorage.idEs un ejemplo ilustrativo, contrastado con la referencia del recurso, con API 2023-05-01. No lo he compilado ni desplegado para este artículo.
Aquí ya hay decisiones que antes podían quedar escondidas en el portal: el tipo de cuenta, la redundancia Standard_LRS, el uso obligatorio de HTTPS, TLS mínimo 1.2 y la desactivación del acceso anónimo a blobs. El nombre se deriva del identificador del resource group para mantenerlo estable al repetir el despliegue sobre ese grupo.
El ejemplo crea la cuenta; todavía faltan el contenedor de documentos, la identidad y los permisos que utilizará la aplicación, su configuración de conexión y las reglas de red que requiera el entorno. Desactivar el acceso anónimo a blobs no equivale a restringir el acceso de red. Esas decisiones también deben quedar explícitas al completar la funcionalidad.
En el pull request ya podemos preguntar si esa redundancia sirve para el ambiente, quién va a acceder y qué configuración recibirá la aplicación. La revisión tiene algo concreto sobre lo que trabajar, además del código que sube archivos.
Llevar el archivo al pipeline
Guardar Bicep en Git nos da seguimiento. Para reducir los pasos olvidados también hay que ejecutarlo como parte de la entrega.
Supongamos un resource group existente llamado rg-documentos-dev, una identidad del pipeline autenticada en la suscripción correcta y permisos para desplegar. Con Azure CLI 2.75.0 o posterior, el flujo puede mostrar primero los cambios previstos:
az deployment group what-if --resource-group rg-documentos-dev --template-file infra/main.bicepLa operación what-if consulta el entorno y anticipa cambios sin aplicarlos. Sirve para revisarlos, aunque tiene limitaciones y puede mostrar diferencias que no terminarán siendo cambios efectivos. No la trataría como una prueba de que la aplicación funcionará.
Después de la revisión y de la aprobación que corresponda al ambiente, el paso que aplica la infraestructura sería:
az deployment group create --name documentos-infra --resource-group rg-documentos-dev --template-file infra/main.bicep --mode IncrementalEstos comandos ilustran los pasos del pipeline; no se han ejecutado aquí. El resultado esperado del segundo es una cuenta creada con la configuración declarada, o la actualización de esa misma cuenta al volver a desplegarla.
Para esta funcionalidad, ordenaría la entrega así: validar el cambio, revisar la infraestructura prevista, aplicarla y, si termina correctamente, desplegar la aplicación con su configuración. Después comprobaría una carga de documentos con la identidad real de la aplicación. Si falla la preparación del recurso, la nueva versión no debería continuar como si ya tuviera su dependencia disponible.
Un mismo pull request permite revisar juntos los cambios; el pipeline tiene que coordinar su aplicación. No se convierten en una operación atómica por estar en el mismo repositorio. Si el despliegue de la aplicación falla después, la cuenta puede haber quedado creada y habrá que decidir cómo continuar.
Tampoco usaría esta definición mínima para adoptar a ciegas una cuenta existente. En el modo incremental de ARM, las propiedades omitidas de un recurso que se vuelve a desplegar pueden restablecerse a sus valores por defecto. Antes de incorporarlo, hay que recoger la configuración que queremos conservar.
Qué hacemos con los cambios urgentes
Puede seguir siendo necesario intervenir en el portal durante un incidente. El problema aparece cuando esa intervención se queda allí y el archivo describe otra cosa.
Después de resolver la urgencia, el equipo debe decidir si conserva el cambio. Si lo conserva, lo incorpora a Bicep y lo revisa; si era temporal, prepara su retirada. La próxima comparación con what-if puede ayudar a ver diferencias, pero el archivo no vigila Azure por estar guardado en Git: hace falta ejecutar la comparación y atender sus resultados.
Empezaría por el recurso que ya necesita la siguiente funcionalidad, con un alcance claro y una revisión de su configuración. Intentar trasladar toda la suscripción de golpe puede convertir una mejora concreta en un proyecto difícil de terminar.
Para mí, pensar en DevOps también exige mirar esa parte del despliegue. Si el código necesita almacenamiento para guardar documentos, entregar la funcionalidad incluye preparar ese almacenamiento y comprobar que puede utilizarlo. Esa dependencia merece el mismo seguimiento que el código que la consume.
