La rama main de Contoso ya está protegida por directivas, pero una de ellas apunta al vacío: la compilación con validación exige que un pipeline dé el visto bueno antes de fusionar, y ese pipeline todavía no existe. Sin él, las demás directivas comprueban formalidades —hay dos aprobaciones, hay un elemento de trabajo vinculado— pero nadie ha verificado lo más elemental: que el código compila y que las pruebas pasan.

Esta lección construye ese verificador. Azure Pipelines ejecuta, en cada cambio propuesto y en cada fusión, la misma secuencia repetible de restaurar, compilar, probar, analizar y empaquetar. El resultado es un artefacto —el paquete exacto que después se desplegará— y un veredicto binario en pocos minutos. Aquí nos ocupamos solo de construir y verificar; llevar ese artefacto a Azure es la lección siguiente.

Contenido

  1. Qué es la integración continua y qué problema elimina
  2. Agentes, grupos y sus costes reales
  3. Pipelines YAML frente a los clásicos por interfaz
  4. Anatomía de un azure-pipelines.yml, línea a línea
  5. Las tareas imprescindibles y la caché de dependencias
  6. Variables, grupos de variables y Key Vault
  7. Plantillas de pipeline reutilizables
  8. Validación de solicitudes de cambio y matriz de trabajos
  9. Artefactos, diagnóstico de fallos e insignia de estado
  10. Errores Comunes y Consejos
  11. Ejercicios
  12. Conclusión

  1. Qué es la integración continua y qué problema elimina

La integración continua consiste en que cada cambio se integre con el trabajo de todos y se verifique automáticamente, varias veces al día. No es «tener un servidor de compilación»: es una disciplina en la que el código de todo el mundo se junta constantemente en lugar de acumularse en rincones separados.

El problema que elimina tiene nombre: el infierno de la integración. Diego trabaja tres semanas en el mapa de cabina mientras otro compañero refactoriza el modelo de tarifas; cada uno prueba en su portátil y todo funciona. Al juntarlo aparecen conflictos, incompatibilidades de firma y un fallo que nadie sabe atribuir, justo el viernes de la entrega. El coste de integrar crece de forma no lineal con el tiempo que se tarda en hacerlo, y de ahí la conclusión contraintuitiva: si integrar duele, hazlo más veces.

Requisito Qué significa Cómo lo cumple Contoso
Un repositorio compartido Una única fuente de verdad Azure Repos (05-02)
Compilación automática Nadie compila a mano para validar Este pipeline
Pruebas automáticas El veredicto es objetivo, no una opinión Pruebas unitarias con cobertura
Realimentación rápida Menos de 10 minutos Caché, paralelismo, pruebas rápidas

La regla cultural que lo sostiene: si la compilación de main se rompe, arreglarla es la prioridad número uno del equipo. Una compilación rota que se tolera durante días convierte el pipeline en ruido que todo el mundo ignora.

  1. Agentes, grupos y sus costes reales

Un agente es la máquina que ejecuta los pasos. Hay dos modalidades, y elegir mal cuesta dinero o directamente impide desplegar:

Agentes hospedados por Microsoft Agentes autohospedados
Quién los mantiene Microsoft Tú
Estado entre ejecuciones Máquina nueva y limpia siempre Persiste: caché rápida, pero también basura
Software preinstalado Catálogo amplio (SDK, Docker, Node) Lo que instales
Acceso a vnet-contoso-pro No Sí
Tiempo máximo por trabajo 60 min (gratuito) / 360 min (de pago) Sin límite
Coste Minutos gratuitos y después por trabajo paralelo La VM más su mantenimiento

Aviso de coste real: un proyecto privado tiene 1 trabajo paralelo hospedado con 1.800 minutos al mes gratuitos. Agotados, las ejecuciones se encolan hasta el mes siguiente o hay que comprar trabajos paralelos adicionales (unos 40 $/mes cada uno). Los proyectos públicos tienen 10 trabajos paralelos gratuitos e ilimitados en minutos, lo que tienta a hacer público un repositorio: no lo hagas por ahorrar. Con agentes autohospedados, el primer trabajo paralelo es gratuito y los siguientes cuestan unos 15 $/mes, pero pagas la máquina virtual y su mantenimiento.

Contoso necesita ambos. El pipeline de compilación (esta lección) va en agentes hospedados: no toca nada privado y aprovecha imágenes siempre actualizadas. El pipeline de despliegue (05-04) que actúa contra sql-contoso-reservas-pro necesita estar dentro de vnet-contoso-pro, porque ese servidor ya solo es accesible por pe-sql-reservas desde que lo cerramos en 02-05: ahí hace falta un agente autohospedado en snet-gestion, agrupado en el grupo pool-contoso-privado.

# Instalar un agente autohospedado en una VM Linux dentro de snet-gestion
mkdir ~/agente && cd ~/agente
curl -O https://vstsagentpackage.azureedge.net/agent/3.243.1/vsts-agent-linux-x64-3.243.1.tar.gz
tar zxvf vsts-agent-linux-x64-3.243.1.tar.gz
./config.sh --unattended --url https://dev.azure.com/contoso-airlines \
  --auth pat --token $PAT_AGENTE --acceptTeeEula \
  --pool pool-contoso-privado --agent agente-contoso-01
sudo ./svc.sh install && sudo ./svc.sh start   # Servicio, para sobrevivir a reinicios

La VM del agente debe autenticarse contra Azure con identidad administrada, no con credenciales guardadas: el mismo criterio del módulo 4.

  1. Pipelines YAML frente a los clásicos por interfaz

YAML Clásico (interfaz gráfica)
Dónde vive la definición En el repositorio, junto al código En la base de datos de Azure DevOps
Versionado y reversión Con Git: historia y ramas No versionado
Revisión En la solicitud de cambios, como el código Nadie revisa un clic
Reutilización Plantillas Grupos de tareas
Estado Recomendado Heredado, sin novedades

El argumento decisivo es el primero: si el pipeline vive en el repositorio, un cambio en la forma de compilar pasa por la misma revisión que un cambio de código, queda registrado y se puede revertir. Con el clásico, alguien modifica un paso un martes por la tarde, la compilación empieza a comportarse distinto y no hay historia que consultar. Contoso usa YAML exclusivamente.

  1. Anatomía de un azure-pipelines.yml, línea a línea

Este es el pipeline contoso-reservas-ci, en la raíz del repositorio:

# Numero de compilacion de cada ejecucion; disponible como $(Build.BuildNumber).
# Que incluya la version es lo que permitira versionar paquetes en 05-05.
name: 2.5.$(Date:yyyyMMdd)$(Rev:.r)

trigger:                    # Que ENVIOS lanzan el pipeline
  branches:
    include: [ main, releases/* ]
  paths:
    exclude: [ docs/*, README.md ]   # No gastar minutos por cambios de documentacion

pr:                         # Que SOLICITUDES de cambio lo lanzan: engancha con
  branches:                 # la directiva de compilacion con validacion de 05-02
    include: [ main ]

schedules:                  # Compilacion nocturna: detecta roturas que no vienen
  - cron: "0 2 * * *"       # de tu codigo (una dependencia actualizada, otra imagen)
    displayName: Compilacion nocturna
    branches: { include: [ main ] }
    always: true            # Ejecutar aunque no haya habido cambios

pool:
  vmImage: ubuntu-latest    # Agente hospedado por Microsoft

variables:
  - group: vg-contoso-reservas-comun    # Grupo de variables compartido (apartado 6)
  - name: rutaSolucion
    value: 'src/Contoso.Reservas.sln'

stages:
  - stage: compilacion
    jobs:
      - job: compilar
        timeoutInMinutes: 20            # Corta la ejecucion si algo se cuelga
        steps:
          - checkout: self
            fetchDepth: 1               # Clon superficial: mas rapido
          - task: UseDotNet@2           # No dependas de lo que traiga la imagen
            inputs: { packageType: sdk, version: '8.0.x' }
          - script: dotnet restore $(rutaSolucion)
            displayName: Restaurar dependencias
          - script: dotnet build $(rutaSolucion) -c Release --no-restore -warnaserror
            displayName: Compilar
          - script: |
              dotnet test $(rutaSolucion) -c Release --no-build \
                --logger trx --collect:"XPlat Code Coverage" \
                --results-directory $(Agent.TempDirectory)/pruebas
            displayName: Ejecutar pruebas unitarias
          - task: PublishTestResults@2
            condition: succeededOrFailed()      # Publicar tambien si fallaron:
            inputs:                             # es cuando mas interesa verlos
              testResultsFormat: VSTest
              testResultsFiles: '$(Agent.TempDirectory)/pruebas/**/*.trx'
              failTaskOnFailedTests: true       # Sin esto el pipeline queda verde
          - task: PublishCodeCoverageResults@2  # aunque las pruebas fallen
            condition: succeededOrFailed()
            inputs:
              summaryFileLocation: '$(Agent.TempDirectory)/pruebas/**/coverage.cobertura.xml'
          - script: |
              dotnet publish src/Contoso.Reservas.Web/Contoso.Reservas.Web.csproj \
                -c Release --no-build -o $(Build.ArtifactStagingDirectory)/web
            displayName: Preparar el paquete desplegable
          - publish: $(Build.ArtifactStagingDirectory)/web
            artifact: web-reservas       # Lo que 05-04 tomara tal cual

Sobre la estructura: trigger y pr son los dos desencadenadores de envío (trigger: none desactiva el arranque automático, típico en pipelines de despliegue). La jerarquía stages → jobs → steps significa que una etapa es una fase lógica y puede llevar aprobaciones, un trabajo se ejecuta entero en un agente y varios pueden ir en paralelo, y un paso es una acción individual. La diferencia entre task y script es que una tarea es un componente reutilizable con entradas nombradas y versión (@2) mientras que un script es una orden de shell: usa tareas cuando aporten algo real —publicar resultados, autenticarse— y scripts cuando el comando sea claro, porque son más legibles y portables. Y condition: succeededOrFailed() fuerza un paso aunque el anterior haya fallado.

  1. Las tareas imprescindibles y la caché de dependencias

Restaurar siempre de forma explícita, sin depender de lo que «ya estaba» en la máquina. Compilar con -warnaserror, que convierte las advertencias en errores; sin ello se acumulan por cientos y dejan de leerse. Probar publicando resultados y cobertura, con failTaskOnFailedTests: true —y ojo con convertir la cobertura en objetivo: exigir un 90 % produce pruebas que recorren código sin comprobar nada—. Publicar el artefacto, que consagra el principio de construir una vez, desplegar muchas: el binario validado en desarrollo es exactamente el mismo bit a bit que llegará a producción; recompilar por entorno introduce diferencias imposibles de rastrear.

Falta el análisis estático, que busca fallos y vulnerabilidades antes de que existan en ejecución, y la caché, que baja el tiempo de restauración de minutos a segundos:

          # La clave de la cache se deriva del sistema operativo y del resumen de
          # los ficheros de proyecto: si cambia una dependencia, cambia la clave y
          # no se reutiliza una cache obsoleta. Una clave fija sirve basura eterna.
          - task: Cache@2
            inputs:
              key: 'nuget | "$(Agent.OS)" | **/*.csproj'
              restoreKeys: 'nuget | "$(Agent.OS)"'
              path: $(NUGET_PACKAGES)

          - script: |
              dotnet format --verify-no-changes            # Estilo acordado
              dotnet list package --vulnerable --include-transitive
            displayName: Analisis de estilo y dependencias vulnerables

Nunca caches resultados de compilación, solo dependencias descargadas.

  1. Variables, grupos de variables y Key Vault

Dónde Para qué Visibilidad
variables en el YAML Valores no sensibles del pipeline Público en el repositorio
Grupo de variables en la biblioteca Valores compartidos por varios pipelines Proyecto, con permisos
Grupo vinculado a Key Vault Secretos Nunca se ven; se resuelven al ejecutar

Un secreto no se escribe jamás en el YAML, que está en el repositorio y en cada clon; ya vimos en 05-02 lo que cuesta un secreto confirmado. La forma correcta es vincular un grupo de variables a kv-contoso-pro:

# Grupo normal, para valores no sensibles
az pipelines variable-group create --name vg-contoso-reservas-comun \
  --variables entorno=comun proyecto=contoso-reservas centro-coste=CC-1042 \
  --authorize false

# Grupo vinculado a kv-contoso-pro: los VALORES se quedan en el almacen y
# la conexion de servicio solo necesita el rol "Usuario de secretos de Key Vault"
az pipelines variable-group create --name vg-contoso-secretos-pro \
  --variables pasarela-pago-clave="" token-meteo="" --authorize false
variables:
  - group: vg-contoso-secretos-pro     # Sus valores se leen de kv-contoso-pro

steps:
  - script: ./scripts/prueba-integracion.sh
    env:
      TOKEN_METEO: $(token-meteo)      # Los secretos NO se inyectan solos en el
                                       # entorno: hay que mapearlos a proposito

Tres detalles que evitan disgustos. Las variables secretas no se exponen automáticamente como variables de entorno; el mapeo explícito con env: es una protección deliberada. Azure Pipelines enmascara los secretos en los registros, pero no es infalible: si tu script imprime el valor transformado (en base64, por ejemplo), el enmascarado no lo reconoce. Y --authorize false obliga a autorizar qué pipelines pueden usar el grupo, que es el mínimo privilegio aplicado a los secretos de compilación: el pipeline de una aplicación de pruebas no tiene por qué poder leer la clave de la pasarela de pago.

  1. Plantillas de pipeline reutilizables

Contoso tiene cuatro repositorios que compilan igual. Copiar el YAML en cada uno son cuatro sitios que actualizar cuando cambie el SDK. Las plantillas lo resuelven. En contoso-infra, fichero plantillas/compilar-dotnet.yml:

parameters:                       # Tipados: si falta uno obligatorio, ni arranca
  - { name: rutaSolucion,    type: string }
  - { name: nombreArtefacto, type: string }
  - { name: versionSdk,      type: string,  default: '8.0.x' }
  - { name: ejecutarPruebas, type: boolean, default: true }

steps:
  - task: UseDotNet@2
    inputs: { packageType: sdk, version: ${{ parameters.versionSdk }} }
  - script: dotnet restore ${{ parameters.rutaSolucion }}
  - script: dotnet build ${{ parameters.rutaSolucion }} -c Release --no-restore -warnaserror

  # Condicion en tiempo de expansion: si ejecutarPruebas es false, estos pasos
  # ni siquiera existen en el pipeline generado
  - ${{ if eq(parameters.ejecutarPruebas, true) }}:
    - script: dotnet test ${{ parameters.rutaSolucion }} -c Release --no-build --logger trx
    - task: PublishTestResults@2
      condition: succeededOrFailed()
      inputs: { testResultsFormat: VSTest, testResultsFiles: '**/*.trx', failTaskOnFailedTests: true }

  - publish: $(Build.ArtifactStagingDirectory)
    artifact: ${{ parameters.nombreArtefacto }}

Uso desde el repositorio contoso-api-disponibilidad:

resources:
  repositories:
    - repository: plantillas
      type: git
      name: contoso-reservas/contoso-infra   # proyecto/repositorio
      ref: refs/tags/plantillas-v1.2         # ANCLADO a una etiqueta, no a main

steps:
  - template: plantillas/compilar-dotnet.yml@plantillas
    parameters:
      rutaSolucion: 'src/Contoso.Api.sln'
      nombreArtefacto: 'api-disponibilidad'

El ref anclado a una etiqueta evita que un cambio en la plantilla rompa a la vez los cuatro pipelines: la actualización se hace subiendo la etiqueta, deliberadamente. Y existe una variante más estricta, extends, en la que el pipeline hijo no puede añadir pasos arbitrarios, solo rellenar los huecos previstos:

extends:
  template: plantillas/pipeline-seguro.yml@plantillas
  parameters:
    pasosCompilacion:
      - script: dotnet build src/Contoso.Reservas.sln -c Release

extends es el mecanismo con el que un equipo de plataforma garantiza que ningún pipeline se salte el análisis de seguridad, por mucho que alguien edite su YAML. Es el equivalente en la entrega a las políticas de 04-06: no se confía en la buena voluntad, se hace estructuralmente imposible.

  1. Validación de solicitudes de cambio y matriz de trabajos

Con el desencadenador pr definido y la directiva de rama de 05-02 apuntando a este pipeline, el ciclo se cierra:

graph LR
    A["Diego envía<br/>feature/1842"] --> B["Abre solicitud<br/>de cambios"]
    B --> C["Desencadenador pr:<br/>se ejecuta el CI"]
    C --> D{"¿Compila y<br/>pasan pruebas?"}
    D -->|No| E["Estado rojo:<br/>fusión bloqueada"]
    D -->|Sí| F["Verde + 2 aprobaciones<br/>+ elemento vinculado"]
    F --> G["Squash sobre main"]
    G --> H["Desencadenador trigger:<br/>CI sobre main"]
    H --> I["Artefacto web-reservas<br/>listo para 05-04"]

Cuando hay que verificar en varias configuraciones, la matriz genera un trabajo por combinación:

  - job: compatibilidad
    strategy:
      matrix:
        linux_net8:   { imagenAgente: 'ubuntu-latest',  versionSdk: '8.0.x' }
        windows_net8: { imagenAgente: 'windows-latest', versionSdk: '8.0.x' }
        linux_net9:   { imagenAgente: 'ubuntu-latest',  versionSdk: '9.0.x' }
      maxParallel: 2        # Limitado por los trabajos paralelos contratados
    pool:
      vmImage: $(imagenAgente)
    steps:
      - task: UseDotNet@2
        inputs: { packageType: sdk, version: $(versionSdk) }
      - script: dotnet test src/Contoso.Reservas.sln

maxParallel importa por dinero: con un solo trabajo paralelo contratado, una matriz de nueve combinaciones no va nueve veces más rápido, va en fila y consume nueve veces los minutos.

  1. Artefactos, diagnóstico de fallos e insignia de estado

Artefactos de canalización (publish/download) Artefactos de compilación (PublishBuildArtifacts@1)
Rendimiento Mucho más rápido, con deduplicación Lento con muchos ficheros
Almacenamiento Optimizado Copia completa
Estado Recomendado Heredado
- publish: $(Build.ArtifactStagingDirectory)/web    # Publicar en la etapa de compilacion
  artifact: web-reservas
- download: current                                 # Recuperarlo en otra etapa
  artifact: web-reservas

Cuando un pipeline falla, el orden de diagnóstico que ahorra horas: (1) leer el registro del paso que falló, no el resumen —el error real suele estar 30 líneas por encima de la última línea roja—; (2) activar los registros de diagnóstico poniendo la variable system.debug a true al relanzar, que muestra la resolución de variables y las rutas reales, donde suele estar el problema; (3) reproducir en local ejecutando exactamente los comandos del YAML, porque el 80 % de los fallos «solo del pipeline» son diferencias de entorno —una variable que en tu portátil existe, un fichero ignorado por .gitignore que en tu máquina sí está, o mayúsculas de un nombre que en Linux importan y en Windows no—; y (4) comprobar permisos si el fallo menciona autorización: la conexión de servicio o el grupo de variables probablemente no están autorizados para este pipeline.

Y la insignia de estado en el README.md, que da visibilidad inmediata de si main está sano:

[![Estado](https://dev.azure.com/contoso-airlines/contoso-reservas/_apis/build/status/contoso-reservas-ci?branchName=main)](https://dev.azure.com/contoso-airlines/contoso-reservas/_build/latest?definitionId=12&branchName=main)

Errores Comunes y Consejos

  • Compilar en cada entorno. Rompe el principio de construir una vez y desplegar muchas: el binario de producción deja de ser el que se probó. Compila una vez, publica el artefacto y reutilízalo.
  • Escribir secretos en el YAML. Están en el repositorio y en cada clon. Grupo de variables vinculado a kv-contoso-pro, siempre.
  • Pipelines de 40 minutos. Si el veredicto tarda más que un café, la gente deja de esperarlo y sigue trabajando sobre código no verificado. Divide, cachea y paraleliza hasta bajar de 10 minutos.
  • Tolerar una compilación rota en main, o convivir con pruebas inestables. En cuanto el rojo es normal, el pipeline deja de significar algo y el equipo aprende a relanzar sin mirar.
  • continueOnError: true por comodidad. Convierte un paso en decorativo. Úsalo solo mientras ajustas el umbral de una herramienta nueva, y con fecha de caducidad.
  • Plantillas apuntando a main. Un cambio rompe simultáneamente todos los pipelines. Ancla a etiqueta y versiona las plantillas.
  • Consejo: pon timeoutInMinutes en todos los trabajos. Un trabajo colgado consume minutos facturables hasta el límite de 60 o 360.
  • Consejo: excluye docs/* y README.md del desencadenador. Es un minuto de trabajo que ahorra minutos de agente todos los meses.

Ejercicios

Ejercicio 1: leer un pipeline y detectar sus problemas

Un compañero propone este pipeline para Contoso Millas:

trigger:
  branches: { include: ['*'] }
pool: { vmImage: ubuntu-latest }
variables:
  claveApi: 'ak_millas_9f2b7c1d4e'
steps:
  - script: dotnet build src/Millas.sln
  - script: dotnet test src/Millas.sln
    continueOnError: true
  - script: dotnet publish src/Millas.Web -o salida
  1. Identifica al menos cinco problemas.
  2. Enumera los cambios concretos que harías para corregirlos.
  3. ¿Cuál es un incidente de seguridad y qué hay que hacer además de corregir el fichero?

Ejercicio 2: agentes y coste

Contoso añade tres pipelines: uno compila la web, otro la API y otro ejecuta pruebas de integración contra sql-contoso-reservas-dev, que tiene punto privado. Los tres se ejecutan unas 20 veces al día, 12 minutos cada uno.

  1. ¿Qué tipo de agente necesita cada pipeline y por qué?
  2. Estima el consumo mensual de minutos hospedados y di si cabe en el nivel gratuito.
  3. Propón dos formas de reducir el gasto sin renunciar a la verificación.

Ejercicio 3: diseñar la reutilización

Los cuatro repositorios compilan .NET casi igual, pero contoso-infra no tiene pruebas unitarias (son plantillas de Bicep) y contoso-modelos publica un paquete en vez de una aplicación web. Seguridad exige que ningún pipeline pueda saltarse el análisis de dependencias vulnerables.

  1. ¿Usarías template o extends? Justifícalo respecto al requisito de seguridad.
  2. Esboza los parámetros de la plantilla.
  3. ¿Cómo evitas que actualizarla rompa los cuatro pipelines a la vez?

Soluciones

Solución 1:

  1. (a) Secreto en el YAML: claveApi está en el repositorio. (b) trigger sobre todas las ramas, que compila cada rama de trabajo y quema minutos. (c) Falta el desencadenador pr, así que la directiva de compilación con validación no tiene nada a lo que engancharse. (d) continueOnError: true en las pruebas: el pipeline pasa aunque fallen. (e) No se publican resultados ni cobertura. (f) No se publica artefacto alguno: no hay nada que desplegar después. (g) Falta UseDotNet fijando la versión del SDK, así que se depende de lo que traiga la imagen del agente. (h) Sin timeoutInMinutes ni displayName legibles.
  2. trigger limitado a main más pr: [main]; UseDotNet@2 con 8.0.x; dotnet restore explícito; compilación con -warnaserror; dotnet test con --logger trx --collect:"XPlat Code Coverage"; PublishTestResults@2 con failTaskOnFailedTests: true y condition: succeededOrFailed(); publicación de cobertura; dotnet publish a $(Build.ArtifactStagingDirectory) seguido de - publish: con nombre de artefacto; la clave sustituida por un grupo de variables vinculado a kv-contoso-pro y mapeada con env:; y timeoutInMinutes: 20.
  3. El secreto. Además de sacarlo del fichero hay que rotarlo: está en la historia de Git y en los registros de todas las ejecuciones anteriores. Después, guardar el valor nuevo en el almacén y revisar si se usó indebidamente. Igual que en 05-02, borrar no basta.

Solución 2:

  1. Web y API, agentes hospedados: no necesitan acceso a nada privado y se benefician de una máquina limpia y mantenida. Las pruebas de integración, agente autohospedado en pool-contoso-privado dentro de snet-gestion, porque sql-contoso-reservas-dev solo es accesible por su punto privado y un agente hospedado no tiene ruta hasta él. La alternativa serían agentes de conjunto de escalado gestionados dentro de la red virtual.
  2. Los dos pipelines hospedados suman 2 × 20 × 12 = 480 minutos al día, unos 10.500 al mes con 22 días laborables. El nivel gratuito son 1.800, así que se supera con creces; y con un solo trabajo paralelo las ejecuciones además se encolarían. Habría que contratar trabajos paralelos adicionales (~40 $/mes cada uno).
  3. (a) Caché de dependencias y clon superficial, para bajar los 12 minutos a la mitad. (b) Excluir rutas irrelevantes del desencadenador y no compilar en cada envío a ramas de trabajo, sino en la solicitud de cambios. (c) Reservar la matriz completa y las pruebas lentas para la compilación nocturna, dejando en el ciclo rápido solo lo imprescindible. (d) Comparar el coste de una VM de agente autohospedado con el de los trabajos paralelos, que a este volumen puede salir a favor de la VM.

Solución 3:

  1. extends. Con template el pipeline hijo incluye la plantilla pero puede añadir pasos, reordenarlos o sencillamente no llamarla; con extends está obligado a construirse sobre ella y solo puede rellenar los huecos previstos, de modo que el análisis de dependencias no se puede omitir editando el YAML. Es la diferencia entre una recomendación y una regla, la misma lógica de las políticas de 04-06.
  2. rutaSolucion (string, obligatorio), nombreArtefacto (string), versionSdk (string, por defecto 8.0.x), ejecutarPruebas (boolean, false para contoso-infra), tipoSalida (web o paquete, para distinguir contoso-modelos) y pasosAdicionales (stepList) como único hueco de extensión.
  3. Anclando la referencia del repositorio de plantillas a una etiqueta (ref: refs/tags/plantillas-v1.2) y versionando las plantillas con versionado semántico. Cada repositorio sube su etiqueta cuando le conviene, con lo que un cambio incompatible se prueba en uno antes de propagarse. La plantilla vive en contoso-infra, con sus directivas de rama y la revisión obligatoria de Contoso-Infraestructura.

Conclusión

La rama main de Contoso ya no solo está protegida: está verificada. Entiendes la integración continua como disciplina —integrar a menudo porque integrar tarde duele de forma no lineal— con sus cuatro requisitos y la regla cultural que la sostiene: una compilación rota es la prioridad número uno. Sabes elegir entre agentes hospedados, limpios y mantenidos por Microsoft, y agentes autohospedados, imprescindibles cuando hay que alcanzar recursos que solo existen tras un punto privado dentro de vnet-contoso-pro; y conoces los números reales: 1.800 minutos y un trabajo paralelo gratuitos por proyecto privado, y unos 40 $ al mes por cada trabajo paralelo adicional.

Has escrito el azure-pipelines.yml de contoso-reservas-ci entendiendo cada pieza: trigger con exclusión de rutas, pr para enganchar con la directiva de compilación con validación de 05-02, schedules para la compilación nocturna que detecta lo que se rompe sin que tú toques nada, y la jerarquía stages → jobs → steps con la diferencia entre task y script. Dentro has puesto lo imprescindible —restaurar, compilar con -warnaserror, probar publicando resultados y cobertura con failTaskOnFailedTests, analizar dependencias vulnerables y publicar el artefacto—, lo has acelerado con una caché cuya clave se deriva del manifiesto, y lo has hecho reutilizable con plantillas, distinguiendo template de extends: solo extends convierte el análisis de seguridad en algo que ningún equipo puede omitir. Y has mantenido intacta la regla del módulo 4: ningún secreto en el YAML, todos en un grupo de variables vinculado a kv-contoso-pro, mapeados explícitamente y autorizados solo a los pipelines que los necesitan.

El resultado de todo esto es un fichero: el artefacto web-reservas, compilado una sola vez, probado y esperando. Pero sigue esperando: nadie lo ha llevado a app-contoso-reservas-pro, y el despliegue del viernes por la noche continúa existiendo tal cual. En la próxima lección, Despliegue continuo con entornos y aprobaciones, ese artefacto viajará. Crearás los entornos desarrollo, preproduccion y produccion con su registro de qué versión hay en cada uno, pondrás la aprobación manual de Marta Ríos y las ventanas que impiden desplegar un viernes por la tarde, compararás las estrategias de despliegue y ejecutarás el azul-verde real de Contoso con el intercambio de la ranura preproduccion que ya conoces del módulo 2, con migraciones de base de datos compatibles hacia atrás y una reversión que funciona de verdad.

Curso de Azure

Módulo 1: Introducción a Azure

Módulo 2: Servicios principales de Azure

Módulo 3: Bases de datos de Azure

Módulo 4: Seguridad en Azure

Módulo 5: Azure DevOps

Módulo 6: Servicios avanzados de Azure

Módulo 7: Monitoreo y gestión

Módulo 8: Gestión y optimización de costos

Módulo 9: Estudios de caso y mejores prácticas

© Copyright 2026. Todos los derechos reservados