Cómo reutilizar jobs en las pipelines de Azure DevOps

Ayer compartí contigo cómo puedes reutilizar workflows de GitHub Actions dentro de otros, e incluso en otros repos, con el objetivo de poder centralizar su mantenimiento y poder facilitar su uso en toda la organización, o en tus repos personales. Hoy quiero mostrarte lo mismo pero en Azure DevOps con su sistema de plantillas.

Este ejemplo está basado en mi vídeo sobre la búsqueda de vulnerabilidades en tu código en Terraform, el cuál puedes ver directamente desde aquí 👇🏻

Ejemplo de plantilla

Las plantillas en Azure DevOps pueden hacerse de diferentes objetos, o lo que es lo mismo a diferentes niveles de lo que forma una pipeline. En mi caso me interesa hacerlas de los jobs, que sería el nivel más alto y lo más parecido a lo que hice con GitHub Actions. Siguiendo con el mismo ejemplo, así sería la plantilla del job de Snyk:

parameters:
  - name: SNYK_TOKEN # name of the parameter; required
    type: string # data type of the parameter; required
  - name: working_directory
    type: string
    default: "."
jobs:
  - job: Snyk
    steps:
      - task: CmdLine@2
        displayName: "Install Snyc CLI"
        inputs:
          script: |
            curl --compressed https://static.snyk.io/cli/latest/snyk-linux -o snyk
            chmod +x ./snyk
            mv ./snyk /usr/local/bin/
      - task: CmdLine@2
        displayName: "Scan Terraform files"
        inputs:
          script: snyk config set api=${{ parameters.SNYK_TOKEN }}
      - task: CmdLine@2
        displayName: "Scan Terraform files"
        inputs:
          script: |
            snyk iac test ${{ parameters.working_directory }} --sarif
            snyk iac test ${{ parameters.working_directory }} --sarif > $(Build.ArtifactStagingDirectory)/snyk-iac.sarif || true
      - task: PublishBuildArtifacts@1
        inputs:
          PathtoPublish: "$(Build.ArtifactStagingDirectory)/snyk-iac.sarif"
          ArtifactName: "CodeAnalysisLogs"
          publishLocation: "Container"

En ella, como ves, defino una sección llamada parameters (que sería igual a la de inputs de GitHub Actions) donde indico cuáles son los valores que necesito para que este job se pueda ejecutar. Por otro lado, tengo el array de jobs y en este ejemplo solamente tengo uno, que es el encargado de instalar el CLI de Snyk, escanear el directorio pasado como parámetro y subir el resultado a un artefacto llamado CodeAnalysis.

Uso de la plantilla en el mismo proyecto

Si quisiera usar la plantilla dentro del mismo proyecto de Azure DevOps podría hacerlo de la siguiente manera:

name: IaC scans
trigger:
  branches:
    include:
      - main
  paths:
    exclude:
      - .github/**
      - README.md
pr:
  branches:
    include:
      - main
  paths:
    exclude:
      - .github/**
      - README.md
pool:
  vmImage: ubuntu-latest
jobs:
  - template: "/.ado/templates/snyk.yml"
    parameters:
      SNYK_TOKEN: $(SNYK_TOKEN)
  - template: "/.ado/templates/checkov.yml"
  - template: "/.ado/templates/terrascan.yml"
  - template: "/.ado/templates/trivy.yml"

Aquí como ves tengo una pipeline completa donde, en el apartado jobs, utilizo la propiedad template por cada una de las plantillas que quiero utilizar, que en este caso las tengo alojadas en /.ado/templates. En esta pipeline no he necesitado hacer uso del parámetro working_directory, por lo que tomará el valor por defecto establecido en las plantillas.

Usar la plantilla en otros proyectos de Azure DevOps

Lo habitual cuando usas este mecanismo es que las plantillas las uses fuera del proyecto en el que se encuentran. Para hacer esto la sintaxis cambia a lo siguiente:

resources:
  repositories:
    - repository: scan-tf-vulnerabilities #Identifier
      type: git #git for Azure Repos, github for GitHub
      name: 'Terraform scanners/scan-tf-vulnerabilities' #ProjectName/repositoryName
trigger:
- main
pool:
  vmImage: ubuntu-latest
jobs:
- template: /.ado/templates/snyk.yml@scan-tf-vulnerabilities
  parameters:
    SNYK_TOKEN: $(SNYK_TOKEN)
    working_directory: "terraform"
- template: /.ado/templates/checkov.yml@scan-tf-vulnerabilities
  parameters:
    working_directory: "terraform"
- template: /.ado/templates/terrascan.yml@scan-tf-vulnerabilities
  parameters:
    working_directory: "terraform"
- template: /.ado/templates/trivy.yml@scan-tf-vulnerabilities
  parameters:
    working_directory: "terraform"

Defino en la parte superior el apartado resources, donde puedo decir de dónde vienen estas plantillas, y en el apartado de los jobs utilizo la ruta donde está la plantilla seguido del nombre del repositorio que le he dado en el apartado resources, para poder dirigirme a él.

El resultado de ejecutar esto será como el siguiente:

Plantillas de jobs reutilizados en una pipeline en otro proyecto de Azure DevOps
Plantillas de jobs reutilizados en una pipeline en otro proyecto de Azure DevOps

¡Saludos!