Creating templates in Azure DevOps Pipelines

Building Azure DevOps YAML pipelines and continuously adding the same pipeline.yaml file? Over time; this file can grow and grow – copy and pasting the same job/task but changing variables? I recommend that you have a look at creating templates within your Azure DevOps pipelines to template common jobs/tasks that you can reuse within other pipelines!

Why create templates?

Some reasons into why I consider creating templates as part of my Azure DevOps pipelines:-

  • Reusability
  • Avoids duplication
  • Reduces complexity and size of creating a single pipeline
  • Set structure and pattern that I can follow throughout my pipelines
  • Used as a guide to create further stages
  • Saves time and allows me to create generic templates that I can use on multiple projects
  • Adding/removing a change in one file rather than a number of stages – changing the same config each time

I recommend checking out this blog post, Azure DevOps Pipelines – Keeping your pipelines DRY (Don’t Repeat Yourself) – it is following my thoughts and suggestions of keeping your pipelines DRY!

Azure DevOps Pipeline with no templates

First, lets have a look at an Azure DevOps Pipeline that doesn’t use templates, as you can see – one huge YAML file, I would not call this a clean approach. (GitHub URL)

Over time, this will grow – this is when I recommend to introduce templating. A template dedicated to each task!

name: $(BuildDefinitionName)_$(date:yyyyMMdd)$(rev:.r)

trigger: none

pr: none

variables:
  - group: azurefestivecalendar-production
  - name: backendServiceArm
    value: 'tamopstf2'
  - name: backendAzureRmResourceGroupName
    value: 'tamopstfstates'
  - name: backendAzureRmStorageAccountName
    value: 'tfstatedevops'
  - name: backendAzureRmContainerName
    value: 'azuredevopstemplates'
  - name: backendAzureRmKey
    value: 'terraform.tfstate'
  - name: environment
    value: 'production'

stages :
  - stage: validate
    jobs:
    - job: validate
      continueOnError: false
      steps:
      - task: TerraformInstaller@0
        displayName: 'install'
        inputs:
          terraformVersion: '0.13.3'
      - task: TerraformTaskV1@0
        displayName: 'init'
        inputs:
          provider: 'azurerm'
          command: 'init'
          backendServiceArm: 'tamopstf'
          backendAzureRmResourceGroupName: '${{ variables.backendAzureRmResourceGroupName}}'
          backendAzureRmStorageAccountName: '${{ variables.backendAzureRmStorageAccountName}}'
          backendAzureRmContainerName: '${{ variables.backendAzureRmContainerName }}'
          backendAzureRmKey: 'terraform.tfstate'
          workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'
      - task: TerraformTaskV1@0
        displayName: 'validate'
        inputs:
          provider: 'azurerm'
          command: 'validate'
          
  - stage: plan
    dependsOn: [validate]
    condition: succeeded('validate')
    jobs:
      - job: terraform_plan_develop
        steps:
              - checkout: self
              - task: TerraformInstaller@0
                displayName: 'install'
                inputs:
                  terraformVersion: '0.13.3'
              - task: TerraformTaskV1@0
                displayName: 'init'
                inputs:
                  provider: 'azurerm'
                  command: 'init'
                  backendServiceArm: 'tamopstf'
                  backendAzureRmResourceGroupName: '${{ variables.backendAzureRmResourceGroupName}}'
                  backendAzureRmStorageAccountName: '${{ variables.backendAzureRmStorageAccountName}}'
                  backendAzureRmContainerName: '${{ variables.backendAzureRmContainerName }}'
                  backendAzureRmKey: 'terraform.tfstate'
                  workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'
              - task: TerraformTaskV1@0
                displayName: 'plan'
                inputs:
                  provider: 'azurerm'
                  command: 'plan'
                  commandOptions: '-input=false -var-file="../$(Environment)/$(Environment).tfvars"'
                  environmentServiceNameAzureRM: 'tamopstf'
                  workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'

  - stage: apply
    dependsOn: [plan]
    condition: succeeded('plan')
    jobs:
      - job: terraform_apply_production
        steps:
              - checkout: self
              - task: TerraformInstaller@0
                displayName: 'install'
                inputs:
                  terraformVersion: '0.13.3'
              - task: TerraformTaskV1@0
                displayName: 'init'
                inputs:
                  provider: 'azurerm'
                  command: 'init'
                  backendServiceArm: 'tamopstf'
                  backendAzureRmResourceGroupName: '${{ variables.backendAzureRmResourceGroupName}}'
                  backendAzureRmStorageAccountName: '${{ variables.backendAzureRmStorageAccountName}}'
                  backendAzureRmContainerName: '${{ variables.backendAzureRmContainerName }}'
                  backendAzureRmKey: 'terraform.tfstate'
                  workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'
              - task: TerraformTaskV1@0
                displayName: 'plan'
                inputs:
                  provider: 'azurerm'
                  command: 'plan'
                  commandOptions: '-input=false -var-file="../$(Environment)/$(Environment).tfvars"'
                  environmentServiceNameAzureRM: 'tamopstf'
                  workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'
              - task: TerraformTaskV1@0
                displayName: 'apply'
                inputs:
                  provider: 'azurerm'
                  command: 'apply'
                  commandOptions: '-input=false -auto-approve -var-file="../$(Environment)/$(Environment).tfvars"'
                  environmentServiceNameAzureRM: 'tamopstf'
                  workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'

  - stage: test
    dependsOn: [apply]
    condition: succeeded('apply')
    jobs:
      - job: Inspec_Testing
        steps:
          - task: UseRubyVersion@0
            displayName: 'Install Ruby'
            inputs:
              versionSpec: '>= 2.5'
              addToPath: true
 
          - script: gem install inspec-bin
            displayName: 'Install inspec-bin'
                       
          - task: Bash@3
            displayName: 'Run inspec tests'
            inputs:
              targetType: inline
              script: |
                export AZURE_SUBSCRIPTION_ID=$(AZURE_SUBSCRIPTION_ID)
                export AZURE_CLIENT_ID=$(AZURE_CLIENT_ID)
                export AZURE_CLIENT_SECRET=$(AZURE_CLIENT_SECRET)
                export AZURE_TENANT_ID=$(AZURE_TENANT_ID)
                inspec exec ./azure-inspec-tests/ -t azure:// --chef-license=accept --reporter cli junit:inspectestresults.xml
 
          - task: PublishTestResults@2
            displayName: Publish inspec test results
            condition: succeededOrFailed()
            inputs:
              testResultsFiles: '**/inspectestresults.xml'
              mergeTestResults: true

  - stage: bash
    dependsOn: [apply]
    condition: succeeded('apply')
    jobs:
      - job: bash_echo
        steps:
              - task: Bash@3
                displayName: 'Echo Test'
                inputs:
                  targetType: inline
                  script: |
                    Echo "Test Script!"

  - stage: cli
    dependsOn: [apply]
    condition: succeeded('apply')
    jobs:
      - job: azcli_resourcegroup_create
        steps:
              - task: AzureCLI@2
                displayName: 'Deploy Resource Group'
                inputs:
                  azureSubscription: 'tamopstf'
                  scriptType: bash
                  scriptLocation: inlineScript
                  addSpnToEnvironment: true
                  inlineScript: |
                    #!/bin/bash
                    az group create -l uksouth -n bicep-rg 

Identifying jobs/tasks that can be templated

Lets identity common tasks within the pipeline:-

  • Terraform Init/Validate
  • Terraform Plan
  • Terraform Apply
  • Inspec Testing
  • Bash Script
  • Azure CLI

Current folder structure

Azure-DevOps-Pipelines-Using-Templates
    └── azure-inspec-tests
    └── pipelines
    └── production
    └── terraform

I will add a folder into pipelines – to store my templates

Azure-DevOps-Pipelines-Using-Templates
    └── azure-inspec-tests
    └── pipelines
         └── templates
    └── production
    └── terraform

How are templates referenced within Azure DevOps Pipelines?

Using – template reference , template below

stages :   
  - stage: terraform_validate
    jobs:
      - template: templates/terraform-validate.yaml

Moving pipeline from one file to using Azure DevOps Pipeline Templates

I will now show a template for each task identified:

  • Terraform Init/Validate
  • Terraform Plan
  • Terraform Apply
  • Inspec Testing
  • Bash Script
  • Azure CLI

Terraform Init/Validate

  jobs:
    - job: terraform_validate
      continueOnError: false
      steps:
      - task: TerraformInstaller@0
        displayName: 'install'
        inputs:
          terraformVersion: '${{ parameters.terraform_version }}'
      - task: TerraformTaskV1@0
        displayName: 'init'
        inputs:
          provider: 'azurerm'
          command: 'init'
          backendServiceArm: '${{ parameters.backendServiceArm }}'
          backendAzureRmResourceGroupName: '${{ parameters.backendAzureRmResourceGroupName }}'
          backendAzureRmStorageAccountName: '${{ parameters.backendAzureRmStorageAccountName }}'
          backendAzureRmContainerName: '${{ parameters.backendAzureRmContainerName }}'
          backendAzureRmKey: '${{ parameters.backendAzureRmKey }}' 
          workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'
      - task: TerraformTaskV1@0
        displayName: 'validate'
        inputs:
          provider: 'azurerm'
          command: 'validate'

Terraform Plan

  jobs:
    - job: terraform_plan
      steps:
        - task: TerraformInstaller@0
          displayName: 'install'
          inputs:
            terraformVersion: '${{ parameters.terraform_version }}'
        - task: TerraformTaskV1@0
          displayName: 'init'
          inputs:
            provider: 'azurerm'
            command: 'init'
            backendServiceArm: '${{ parameters.backendServiceArm }}'
            backendAzureRmResourceGroupName: '${{ parameters.backendAzureRmResourceGroupName }}'
            backendAzureRmStorageAccountName: '${{ parameters.backendAzureRmStorageAccountName }}'
            backendAzureRmContainerName: '${{ parameters.backendAzureRmContainerName }}'
            backendAzureRmKey: '${{ parameters.backendAzureRmKey }}' 
            workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'
        - task: TerraformTaskV1@0
          displayName: 'plan'
          inputs:
            provider: 'azurerm'
            command: 'plan'
            commandOptions: '-input=false -var-file="../vars/${{ parameters.environment }}/${{ parameters.environment }}.tfvars"'
            environmentServiceNameAzureRM: '${{ parameters.backendServiceArm }}'
            workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'

Terraform Apply

  jobs:
    - job: terraform_apply
      steps:
        - task: TerraformInstaller@0
          displayName: 'install'
          inputs:
            terraformVersion: '${{ parameters.terraform_version }}'
        - task: TerraformTaskV1@0
          displayName: 'init'
          inputs:
            provider: 'azurerm'
            command: 'init'
            backendServiceArm: '${{ parameters.backendServiceArm }}'
            backendAzureRmResourceGroupName: '${{ parameters.backendAzureRmResourceGroupName }}'
            backendAzureRmStorageAccountName: '${{ parameters.backendAzureRmStorageAccountName }}'
            backendAzureRmContainerName: '${{ parameters.backendAzureRmContainerName }}'
            backendAzureRmKey: '${{ parameters.backendAzureRmKey }}' 
            workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'
        - task: TerraformTaskV1@0
          displayName: 'plan'
          inputs:
            provider: 'azurerm'
            command: 'plan'
            commandOptions: '-input=false -var-file="../vars/${{ parameters.environment }}/${{ parameters.environment }}.tfvars"'
            environmentServiceNameAzureRM: '${{ parameters.backendServiceArm }}'
            workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'
        - task: TerraformTaskV1@0
          displayName: 'apply'
          inputs:
            provider: 'azurerm'
            command: 'apply'
            commandOptions: '-input=false -auto-approve -var-file="../vars/${{ parameters.environment }}/${{ parameters.environment }}.tfvars"'
            environmentServiceNameAzureRM: '${{ parameters.backendServiceArm }}'
            workingDirectory: '$(System.DefaultWorkingDirectory)/terraform/'

Inspec Testing

  jobs:
    - job: Inspec_Testing
      steps:
        - task: UseRubyVersion@0
          displayName: 'Install Ruby'
          inputs:
            versionSpec: '${{ parameters.ruby_versionspec }}'
            addToPath: true

        - script: gem install inspec-bin
          displayName: 'Install inspec-bin'
                      
        - task: Bash@3
          displayName: 'Run inspec tests'
          inputs:
            targetType: inline
            script: |
              export AZURE_SUBSCRIPTION_ID=$('${{ parameters.AZURE_SUBSCRIPTION_ID }}')
              export AZURE_CLIENT_ID=$('${{ parameters.AZURE_CLIENT_ID }}')
              export AZURE_CLIENT_SECRET=$('${{ parameters.AZURE_CLIENT_SECRET }}')
              export AZURE_TENANT_ID=$('${{ parameters.AZURE_TENANT_ID }}')
              inspec exec ./azure-inspec-tests/ -t azure:// --chef-license=accept --reporter cli junit:inspectestresults.xml

        - task: PublishTestResults@2
          displayName: Publish inspec test results
          condition: succeededOrFailed()
          inputs:
            testResultsFiles: '**/inspectestresults.xml'
            mergeTestResults: true

Bash Script

  jobs:
    - job: bash_echo
      steps:
        - task: Bash@3
          displayName: 'Echo Test'
          inputs:
            targetType: inline
            script: |
              Echo "${{ parameters.bash_input }}"

Azure CLI

  jobs:
    - job: azcli_resourcegroup_create
      steps:
            - task: AzureCLI@2
              displayName: 'Deploy Resource Group'
              inputs:
                azureSubscription: 'tamopstf'
                scriptType: bash
                scriptLocation: inlineScript
                addSpnToEnvironment: true
                inlineScript: |
                  #!/bin/bash
                  az group create -l uksouth -n ${{ parameters.resource_group }}

Awesome, all the jobs/tasks have now been templated, how does the main Azure DevOps pipeline now look?

name: $(BuildDefinitionName)_$(date:yyyyMMdd)$(rev:.r)

trigger: none

pr: none

variables:
  - group: azurefestivecalendar-production
  - name: backendServiceArm
    value: 'tamopstf2'
  - name: backendAzureRmResourceGroupName
    value: 'tamopstfstates'
  - name: backendAzureRmStorageAccountName
    value: 'tfstatedevops'
  - name: backendAzureRmContainerName
    value: 'azuredevopstemplates'
  - name: backendAzureRmKey
    value: 'terraform.tfstate'
  - name: environment
    value: 'production'
  - name: terraform_version
    value: '0.13.3'
  - name: ruby_versionspec
    value: '>= 2.5'

stages :   
  - stage: terraform_validate
    jobs:
      - template: templates/terraform-validate.yaml
        parameters:
          backendServiceArm: ${{ variables.backendServiceArm }}
          backendAzureRmResourceGroupName: ${{ variables.backendAzureRmResourceGroupName }}
          backendAzureRmStorageAccountName: ${{ variables.backendAzureRmStorageAccountName }}
          backendAzureRmContainerName: ${{ variables.backendAzureRmContainerName }}
          backendAzureRmKey: ${{ variables.backendAzureRmKey }}
          environment: ${{ variables.environment }}
          terraform_version: ${{ variables.terraform_version }}

  - stage: terraform_plan
    dependsOn: [terraform_validate]
    condition: succeeded('terraform_validate')
    jobs:
      - template: templates/terraform-plan.yaml
        parameters:
          backendServiceArm: ${{ variables.backendServiceArm }}
          backendAzureRmResourceGroupName: ${{ variables.backendAzureRmResourceGroupName }}
          backendAzureRmStorageAccountName: ${{ variables.backendAzureRmStorageAccountName }}
          backendAzureRmContainerName: ${{ variables.backendAzureRmContainerName }}
          backendAzureRmKey: ${{ variables.backendAzureRmKey }}
          environment: ${{ variables.environment }}
          terraform_version: ${{ variables.terraform_version }}

  - stage: terraform_apply
    dependsOn: [terraform_plan]
    condition: succeeded('terraform_plan')
    jobs:
      - template: templates/terraform-apply.yaml
        parameters:
          backendServiceArm: ${{ variables.backendServiceArm }}
          backendAzureRmResourceGroupName: ${{ variables.backendAzureRmResourceGroupName }}
          backendAzureRmStorageAccountName: ${{ variables.backendAzureRmStorageAccountName }}
          backendAzureRmContainerName: ${{ variables.backendAzureRmContainerName }}
          backendAzureRmKey: ${{ variables.backendAzureRmKey }}
          environment: ${{ variables.environment }}
          terraform_version: ${{ variables.terraform_version }}

  - stage: inspec_testing
    dependsOn: [terraform_apply]
    condition: succeeded('terraform_apply')
    jobs:
      - template: templates/inspec-testing.yaml
        parameters:
          ruby_versionspec: ${{ variables.backendServiceArm }}
          AZURE_SUBSCRIPTION_ID: $(AZURE_SUBSCRIPTION_ID)
          AZURE_CLIENT_ID: $(AZURE_CLIENT_ID)
          AZURE_CLIENT_SECRET: $(AZURE_CLIENT_SECRET)
          AZURE_TENANT_ID: $(AZURE_TENANT_ID)

  - stage: bash
    dependsOn: [terraform_apply]
    condition: succeeded('terraform_apply')
    jobs:
      - template: templates/bash-script.yaml
        parameters:
          bash_input: "Test Script!"

  - stage: cli
    dependsOn: [terraform_apply]
    condition: succeeded('terraform_apply')
    jobs:
      - template: templates/az-cli.yaml
        parameters:
          resource_group: "bicep-rg"

Notice the difference? The pipeline is now alot cleaner with no bloating!

I have created a number of generic templates that can now be used for the associated tasks within any additional pipelines – reusability! Awesome 🙂

Using this approach will assist you in reducing additional effort required creating new pipelines as you will have a number of tasks already templated!

Git Repository for code used

16 comments

  1. Hi Thomas,
    How are you doing? Hoping all is good. Much appreciated your effort and you’re my mentor and not only to me too many tech guys . Need your support /Suggestion on the inspec-azure azure devops pipeline.Getting the below error while running the pipeline . Would you please assist am i missing here anything?
    Thanks in advance. Please find my pipeline logs.
    2022-01-25T20:45:29.4482978Z ##[section]Starting: Run inspec tests
    2022-01-25T20:45:29.4571490Z ==============================================================================
    2022-01-25T20:45:29.4571771Z Task : Bash
    2022-01-25T20:45:29.4572012Z Description : Run a Bash script on macOS, Linux, or Windows
    2022-01-25T20:45:29.4572246Z Version : 3.198.0
    2022-01-25T20:45:29.4572451Z Author : Microsoft Corporation
    2022-01-25T20:45:29.4572742Z Help : https://docs.microsoft.com/azure/devops/pipelines/tasks/utility/bash
    2022-01-25T20:45:29.4573077Z ==============================================================================
    2022-01-25T20:45:29.5855046Z Generating script.
    2022-01-25T20:45:29.5885228Z ========================== Starting Command Output ===========================
    2022-01-25T20:45:29.5909067Z [command]/usr/bin/bash /home/vsts/work/_temp/ed9af8a9-4bae-41ba-8b01-765713ec80a5.sh
    2022-01-25T20:45:32.8402781Z +———————————————+
    2022-01-25T20:45:32.8403842Z ✔ 1 product license accepted.
    2022-01-25T20:45:32.8404893Z +———————————————+
    2022-01-25T20:45:35.3032554Z /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/activesupport-7.0.1/lib/active_support/xml_mini.rb:184:in `current_thread_backend’: uninitialized constant ActiveSupport::XmlMini::IsolatedExecutionState (NameError)
    2022-01-25T20:45:35.3033715Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/activesupport-7.0.1/lib/active_support/xml_mini.rb:103:in `backend=’
    2022-01-25T20:45:35.3034565Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/activesupport-7.0.1/lib/active_support/xml_mini.rb:201:in `’
    2022-01-25T20:45:35.3035438Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/activesupport-7.0.1/lib/active_support/xml_mini.rb:11:in `’
    2022-01-25T20:45:35.3036219Z from :85:in `require’
    2022-01-25T20:45:35.3037002Z from :85:in `require’
    2022-01-25T20:45:35.3037818Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/activesupport-7.0.1/lib/active_support/core_ext/hash/conversions.rb:3:in `’
    2022-01-25T20:45:35.3038613Z from :85:in `require’
    2022-01-25T20:45:35.3039350Z from :85:in `require’
    2022-01-25T20:45:35.3040155Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/activesupport-7.0.1/lib/active_support/core_ext/hash.rb:3:in `’
    2022-01-25T20:45:35.3040914Z from :85:in `require’
    2022-01-25T20:45:35.3041666Z from :85:in `require’
    2022-01-25T20:45:35.3042416Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/dsl_shared.rb:33:in `require’
    2022-01-25T20:45:35.3043014Z from libraries/backend/azure_require.rb:8:in `create’
    2022-01-25T20:45:35.3043674Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/dsl_shared.rb:47:in `eval’
    2022-01-25T20:45:35.3044418Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/dsl_shared.rb:47:in `require’
    2022-01-25T20:45:35.3045280Z from libraries/azure_backend.rb:1:in `create’
    2022-01-25T20:45:35.3045908Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/dsl_shared.rb:47:in `eval’
    2022-01-25T20:45:35.3046664Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/dsl_shared.rb:47:in `require’
    2022-01-25T20:45:35.3047426Z from libraries/azure_graph_generic_resource.rb:1:in `create’
    2022-01-25T20:45:35.3048097Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/dsl_shared.rb:47:in `eval’
    2022-01-25T20:45:35.3048835Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/dsl_shared.rb:47:in `require’
    2022-01-25T20:45:35.3049502Z from libraries/azure_active_directory_domain_service.rb:1:in `load_with_context’
    2022-01-25T20:45:35.3050232Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/profile_context.rb:170:in `instance_eval’
    2022-01-25T20:45:35.3051054Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/profile_context.rb:170:in `load_with_context’
    2022-01-25T20:45:35.3051874Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/profile_context.rb:159:in `load_library_file’
    2022-01-25T20:45:35.3052698Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/profile_context.rb:144:in `block in load_libraries’
    2022-01-25T20:45:35.3053504Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/profile_context.rb:143:in `each’
    2022-01-25T20:45:35.3054289Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/profile_context.rb:143:in `load_libraries’
    2022-01-25T20:45:35.3055082Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/profile.rb:376:in `load_libraries’
    2022-01-25T20:45:35.3055867Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/profile.rb:369:in `block in load_libraries’
    2022-01-25T20:45:35.3056636Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/profile.rb:350:in `each’
    2022-01-25T20:45:35.3057401Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/profile.rb:350:in `each_with_index’
    2022-01-25T20:45:35.3058171Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/profile.rb:350:in `load_libraries’
    2022-01-25T20:45:35.3058937Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/runner.rb:108:in `block in load’
    2022-01-25T20:45:35.3059669Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/runner.rb:102:in `each’
    2022-01-25T20:45:35.3060414Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/runner.rb:102:in `load’
    2022-01-25T20:45:35.3061132Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/runner.rb:136:in `run’
    2022-01-25T20:45:35.3061863Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/cli.rb:304:in `exec’
    2022-01-25T20:45:35.3062574Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/thor-1.2.1/lib/thor/command.rb:27:in `run’
    2022-01-25T20:45:35.3063290Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/thor-1.2.1/lib/thor/invocation.rb:127:in `invoke_command’
    2022-01-25T20:45:35.3064003Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/thor-1.2.1/lib/thor.rb:392:in `dispatch’
    2022-01-25T20:45:35.3067018Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/thor-1.2.1/lib/thor/base.rb:485:in `start’
    2022-01-25T20:45:35.3067937Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-core-4.52.9/lib/inspec/base_cli.rb:35:in `start’
    2022-01-25T20:45:35.3068660Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/inspec-bin-4.52.9/bin/inspec:11:in `’
    2022-01-25T20:45:35.3069272Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/bin/inspec:25:in `load’
    2022-01-25T20:45:35.3069792Z from /opt/hostedtoolcache/Ruby/3.0.3/x64/bin/inspec:25:in `’
    2022-01-25T20:45:35.3341227Z ##[error]Bash exited with code ‘1’.
    2022-01-25T20:45:35.3372393Z ##[section]Finishing: Run inspec tests

    1. Hi Rajesh,

      I’m good you? Thank you for the message!

      I believe you are having this known error, it arrived in a new update. I raised it here in this PR, hopefully this fix it soon
      https://github.com/inspec/inspec-azure/pull/605

      There is a fix to change locally or possibly if you are doing self hosted agent or even if you want to try using a bash script for the fix

      Thanks

      Thomas

      1. Thanks a lot for your update. Would you please share me bash script to resolve this issue via pipeline?
        By the way, now i’m your supporter.Happy to be a part of your milestones.Keep going and

      2. Hi Rajesh,

        Not something I’ve actually implemented currently as we use other various tools.

        If you check in that PR example, the local fix is referenced.if you map to the DevOps Pipeline

        Thank you for supporting and following me on my journey 🙂

        Feel free to give me a follow on Twitter/LinkedIn too!

        Thanks

        Thomas

  2. Good article, but there is *always* a flip side…. Imagine what heppens whn 100 pipelines use the same template just to save a little it of typing…. then someone edits that one template in a way that works for their pipeline, but not the other 99…

    Tradeoffs-Tradeoffs….

    [Personally/Professionally I tend to go with creating higher level custom Tasks or similar. At least this way they be revisioned

    1. Hi,

      Thanks for the comment, always an interesting discussion point – templating/what to template really does depend on your team, culture & how you’ve designed initital pipelines and where they are likely to grow etc in the year/2 and beyond.

      Whatever way you choose; the templating basis/fundamentals still apply

      Thanks

      Thomas

Leave a Reply