0

I'm running a YAML pipeline in Azure DevOps on a Windows agent (windows-latest). I'm trying to run a what-if deployment using the Azure CLI with the following step:

  • task: AzureCLI@2 displayName: 'What if Bicep file' inputs: azureSubscription: '$(azureServiceConnection)' scriptType: 'ps' scriptLocation: 'inlineScript' inlineScript: | az deployment group what-if --resource-group rg-inbound-integration-dev --mode Incremental --template-file Global/infra/main.bicep --parameters Global/infra/main-dev.bicepparam

But the task fails with the following error: ERROR: [WinError 2] The system cannot find the file specified

I used the AzureCLI@2 task to run a what-if deployment using az deployment group what-if in a YAML pipeline. Initially, I set scriptType: bash, which caused the [WinError 2] error. I then tried switching to ps and pscore and modified the inlineScript to be a single line to avoid multi-line parsing issues. I confirmed that the Bicep file and parameters file exist and are correctly referenced. I also attempted to run the same command using a powershell task instead of AzureCLI@2, but the same error persisted. The az CLI is available and works in other steps and pipelines, so it's not a missing tool issue globally on the agent.

I used scriptType: bash in the AzureCLI@2 task because the Azure CLI what-if examples in official Microsoft documentation and other community samples typically use Bash syntax (e.g., multi-line az deployment group what-if commands). I expected the Azure DevOps windows-latest agent to support Bash through the Git installation (which usually includes Git Bash at C:\Program Files\Git\bin\bash.exe).

Since Bash is often preinstalled and usable on Windows agents, I assumed this would work seamlessly. However, it turns out the agent failed to locate the necessary files or interpreter, causing the [WinError 2] error. This helped me realize that even though Bash is sometimes present, its availability or integration with AzureCLI@2 may not be consistent across all agents or contexts.

I expected the Azure CLI what-if command to run and output the planned changes from the Bicep file deployment to the specified resource group — similar to how az deployment group create would run, but in "dry-run" mode to preview changes.

This is my yaml code

variables:
  dotNetVersion: '8.0.x'
  buildConfiguration: 'Release'
  includeCoverageReport: true
  azureServiceConnection: 'Dynamics-ERP-DEV (4ce4b7e8-cc95-40ed-9567-46843a458d4b)'
  location: 'West Europe'
 # sonarProjectKey: '<to be determined>'

pool:
  vmImage: 'windows-latest'

trigger:
  paths:
    include:
    - Global/*
    - Common/*

jobs:
  - job: DotNetBuild
    steps:
      #- task: SonarCloudPrepare@1
      #  condition: ne('${{ variables.sonarProjectKey }}', '')
      #  inputs:
      #    SonarCloud: 'SonarCloud Xebia'
      #    organization: 'Xebia'
      #    scannerMode: 'MSBuild'
      #    projectKey: '${{ variables.sonarProjectKey }}'
      #    extraProperties: |
      #      sonar.cs.opencover.reportsPaths=$(Agent.TempDirectory)/**/coverage.opencover.xml

      - task: UseDotNet@2
        inputs:
          version: '${{ variables.dotNetVersion }}'
          includePreviewVersions: True      

      - task: DotNetCoreCLI@2
        displayName: 'Build solution'
        inputs:
          command: 'build'
          arguments: '--configuration ${{ variables.buildConfiguration }}'
          workingDirectory: $(System.DefaultWorkingDirectory)/Global
      - task: DotNetCoreCLI@2
        displayName: 'Run tests'
        inputs:
          command: 'test'
          arguments: '--no-build --configuration $(buildConfiguration) --settings ./coveragerunsettings.runsettings'
          workingDirectory: $(System.DefaultWorkingDirectory)/Global

      - script: |
          cat $(Agent.TempDirectory)/*/coverage.cobertura.xml
        displayName: 'Show Cobertura file'  

      #- task: SonarCloudAnalyze@1
      #  condition: ne('${{ variables.sonarProjectKey }}', '')

      #- task: SonarCloudPublish@1
      #  condition: ne('${{ variables.sonarProjectKey }}', '')
      #  inputs:
      #    pollingTimeoutSec: '300'

      - task: DotNetCoreCLI@2
        displayName: 'Publish Employee Import Azure Function'
        condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
        inputs:
          command: publish
          arguments: '--configuration Release --output $(Build.ArtifactStagingDirectory)/EmployeeImport'
          projects: '**/Middleware.FnO.EmployeeInfo.csproj'
          zipAfterPublish: true    
          publishWebProjects: false
          modifyOutputPath: false

      - task: DotNetCoreCLI@2
        displayName: 'Publish D&B Integration Azure Function'
        condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
        inputs:
          command: publish
          arguments: '--configuration Release --output $(Build.ArtifactStagingDirectory)/DnBIntegration'
          projects: '**/Middleware.FnO.DnB.csproj'
          zipAfterPublish: true    
          publishWebProjects: false
          modifyOutputPath: false

      #- task: DotNetCoreCLI@2
      #  displayName: 'Publish NonWorkingTime Azure Function'
      #  condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
      #  inputs:
      #    command: publish
      #    arguments: '--configuration Release --output $(Build.ArtifactStagingDirectory)/NonWorkingTime'
      #    projects: '**/Middleware.NonWorkingTime.csproj'
      #    zipAfterPublish: true    
      #    publishWebProjects: false
      #    modifyOutputPath: false
      
      - task: PublishTestResults@2
        displayName: 'Publish test results'
        inputs:
          testResultsFormat: 'VSTest'
          testResultsFiles: '*.trx'
          searchFolder: '$(Agent.TempDirectory)'
          buildConfiguration: '${{ variables.buildConfiguration }}'

      - task: PublishCodeCoverageResults@2
        displayName: 'Publish coverage report'
        condition: eq(${{ variables.includeCoverageReport }}, 'true')
        inputs:
          codeCoverageTool: 'Cobertura'
          summaryFileLocation: '$(Agent.TempDirectory)/*/coverage.cobertura.xml'
          reportDirectory: '$(Agent.TempDirectory)/coveragereport'
      
      - script: |
          dotnet tool install -g dotnet-reportgenerator-globaltool
        displayName: 'Install ReportGenerator Tool'  
      - script: |
          reportgenerator -h
        displayName: 'Install ReportGenerator Tool'  

      - script: |
          reportgenerator -reports:$(Agent.TempDirectory)/*/coverage.cobertura.xml -targetdir:$(Build.SourcesDirectory)/CodeCoverage -reporttypes:HtmlInline_AzurePipelines;Cobertura -filefilters:-**/obj/**
        displayName: 'Generate Corbertura Report'

      - task: PublishBuildArtifacts@1
        condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
        displayName: 'Publish EmployeeImport dotnet artifacts'
        inputs:
          pathToPublish: '$(Build.ArtifactStagingDirectory)/EmployeeImport'
          artifactName: 'EmployeeImport' 
              
      - task: PublishBuildArtifacts@1
        condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
        displayName: 'Publish DnbIntegration dotnet artifacts'
        inputs:
          pathToPublish: '$(Build.ArtifactStagingDirectory)/DnBIntegration'
          artifactName: 'DnBIntegration'      

  - job: BicepPreparation
    steps:
      - script: |
          az bicep build --file Global/infra/main.bicep
        displayName: 'Lint Bicep file'      
      - task: AzureResourceManagerTemplateDeployment@3
        displayName: 'Validate Bicep file'    
        inputs:
          deploymentScope: 'Resource Group'
          azureResourceManagerConnection: '$(azureServiceConnection)'
          location: '$(location)'
          action: 'Create Or Update Resource Group'
          csmFile: 'Global/infra/main.bicep'          
          csmParametersFile: 'Global/infra/main-dev.bicepparam'
          resourceGroupName: 'rg-inbound-integration-dev'
          deploymentMode: 'Validation' 
      
      - task: AzureCLI@2
        displayName: 'What if Bicep file'
        inputs:
          azureSubscription: '$(azureServiceConnection)'
          scriptType: 'bash'
          scriptLocation: 'inlineScript'
          inlineScript: |
            az deployment group what-if \
              --resource-group rg-inbound-integration-dev \
              --mode Incremental \
              --template-file Global/infra/main.bicep \
              --parameters 'Global/infra/main-dev.bicepparam'

      - task: AzureResourceManagerTemplateDeployment@3
        displayName: 'Validate Bicep file for TEST'    
        inputs:
          deploymentScope: 'Resource Group'
          azureResourceManagerConnection: '$(azureServiceConnection)'
          location: '$(location)'
          action: 'Create Or Update Resource Group'
          csmFile: 'Global/infra/main.bicep'          
          csmParametersFile: 'Global/infra/main-test.bicepparam'
          resourceGroupName: 'rg-inbound-integration-test'
          deploymentMode: 'Validation' 
      - task: AzureCLI@2
        displayName: 'What if Bicep file for TEST'
        inputs:
          azureSubscription: '$(azureServiceConnection)'
          scriptType: 'bash'
          scriptLocation: 'inlineScript'
          inlineScript: |
            az deployment group what-if \
              --resource-group rg-inbound-integration-test \
              --mode Incremental \
              --template-file Global/infra/main.bicep \
              --parameters 'Global/infra/main-test.bicepparam'
      
      
      - task: ArchiveFiles@2
        displayName: 'Archive files'
        condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
        inputs:
          rootFolderOrFile: '$(System.DefaultWorkingDirectory)/Global/infra/'
          includeRootFolder: false
          archiveType: zip
          archiveFile: $(Build.ArtifactStagingDirectory)/infra_$(Build.BuildId).zip
          replaceExistingArchive: true
      - task: ArchiveFiles@2
        displayName: 'Archive common files'
        condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
        inputs:
          rootFolderOrFile: '$(System.DefaultWorkingDirectory)/Common/infra/'
          includeRootFolder: false
          archiveType: zip
          archiveFile: $(Build.ArtifactStagingDirectory)/infra_common_$(Build.BuildId).zip
          replaceExistingArchive: true
      
      - publish: $(Build.ArtifactStagingDirectory)/infra_$(Build.BuildId).zip
        condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
        artifact: infra

      - publish: $(Build.ArtifactStagingDirectory)/infra_common_$(Build.BuildId).zip
        condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
        artifact: infra_common

2 Answers 2

0

az deployment group what-if fails with WinError 2

The issue you're facing is due to scriptType: bash in AzureCLI@2 as a Windows agent doesn't guarantee that Bash is available in the environment, this takes place even if Git Bash is installed.

The preferred method for this case is to use Linux Agent.

Try to switch the pool for your Bicep-related job to ubuntu-latest

- job: BicepPreparation
  pool:
    vmImage: 'ubuntu-latest'
  steps:
    - task: AzureCLI@2
      displayName: 'What if Bicep file'
      inputs:
        azureSubscription: '$(azureServiceConnection)'
        scriptType: 'bash'
        scriptLocation: 'inlineScript'
        inlineScript: |
          az deployment group what-if \
            --resource-group rg-inbound-integration-dev \
            --mode Incremental \
            --template-file Global/infra/main.bicep \
            --parameters '@Global/infra/main-dev.bicepparam'

Make sure you use @ in front of the parameters file, It's important while using CLI.

Refer:

https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/deploy-what-if?tabs=azure-powershell%2CCLI

Sign up to request clarification or add additional context in comments.

Comments

0

I am also getting this error.. I am also using scriptType: 'pscore' . I will make an update and report back. If pscore isn't available in a windows agent, I would think that is a bug. In my templates i use this so that it can work in both windows or linux agent pools.

edit: i know i posted this as an asnwer, if anyone feels it should be a comment to the ubuntu answer.. let me know.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.