Exercise - Add lint and validate stages to your pipeline

Completed

You've spoken to your team and have decided to further automate your deployments by using a pipeline. You want to build more confidence in what you deploy.

In this exercise, you'll add validation stages to your pipeline. You'll then run the linter and preflight validation before each deployment.

During the process, you'll:

  • Update your existing pipeline to add two new stages to lint and validate your Bicep code.
  • Run your pipeline.
  • Fix any issues that your pipeline detects.

Update your pipeline to prepare for stages

First, you need to update your pipeline file to define a stage. Azure Pipelines automatically creates a single stage for you, but because you'll add more stages soon, you need to update your pipeline to explicitly define stages.

  1. In Visual Studio Code, open the azure-pipelines.yml file in the deploy folder.

  2. Remove everything in the file from line 14 to the bottom of the file. Be sure to also remove the jobs: line.

  3. At the bottom of the file, add the following code:

    
    - stage: Lint
      jobs:
      - job: Deploy
        steps:
          - task: AzureResourceManagerTemplateDeployment@3
            name: Deploy
            displayName: Deploy to Azure
            inputs:
              connectedServiceName: $(ServiceConnectionName)
              deploymentName: $(Build.BuildNumber)
              ___location: $(DeploymentDefaultLocation)
              resourceGroupName: $(ResourceGroupName)
              csmFile: deploy/main.bicep
              overrideParameters: >
                -environmentType $(EnvironmentType)
    

    Tip

    YAML files are sensitive to indentation. Whether you type or paste this code, make sure your indentation is correct. Later in this exercise, you'll see the complete YAML pipeline definition so that you can verify that your file matches.

  4. Replace the pool configuration on line 7 with this line:

    pool: Default
    

Add lint and validation stages to your pipeline

  1. Below the stages: line, add a lint stage:

    jobs:
    - job: LintCode
      displayName: Lint code
      steps:
        - script: |
            az bicep build --file deploy/main.bicep
          name: LintBicepCode
          displayName: Run Bicep linter
    

    This stage defines a single step that runs the az bicep build command to lint the Bicep file.

  2. Below the lines that you just added, add a validation stage:

    jobs:
    - job: ValidateBicepCode
      displayName: Validate Bicep code
      steps:
        - task: AzureResourceManagerTemplateDeployment@3
          name: RunPreflightValidation
          displayName: Run preflight validation
          inputs:
            connectedServiceName: $(ServiceConnectionName)
            ___location: $(deploymentDefaultLocation)
            deploymentMode: Validation
            resourceGroupName: $(ResourceGroupName)
            csmFile: deploy/main.bicep
            overrideParameters: >
              -environmentType $(EnvironmentType)
    

    This stage defines a single step that runs the preflight validation. Notice that this step includes a reference to your service connection. That reference is needed because the preflight validation process requires communication with Azure.

    Your pipeline definition now has three stages. The first runs the linter on your Bicep file, the second performs a preflight validation, and the third performs the deployment to Azure.

  3. Save the file.

Configure the linter

By default, the Bicep linter provides a warning when it detects a problem in a file. Azure Pipelines doesn't treat linter warnings as problems that should stop your pipeline. To customize this behavior, create a bicepconfig.json file that reconfigures the linter.

  1. Add a new file in the deploy folder and name it bicepconfig.json.

    Screenshot of Visual Studio Code Explorer. The new file is shown in the deploy folder.

  2. Copy the following code into the file:

    {
      "analyzers": {
        "core": {
          "enabled": true,
          "verbose": true,
          "rules": {
            "adminusername-should-not-be-literal": {
              "level": "error"
            },
            "max-outputs": {
              "level": "error"
            },
            "max-params": {
              "level": "error"
            },
            "max-resources": {
              "level": "error"
            },
            "max-variables": {
              "level": "error"
            },
            "no-hardcoded-env-urls": {
              "level": "error"
            },
            "no-unnecessary-dependson": {
              "level": "error"
            },
            "no-unused-params": {
              "level": "error"
            },
            "no-unused-vars": {
              "level": "error"
            },
            "outputs-should-not-contain-secrets": {
              "level": "error"
            },
            "prefer-interpolation": {
              "level": "error"
            },
            "secure-parameter-default": {
              "level": "error"
            },
            "simplify-interpolation": {
              "level": "error"
            },
            "protect-commandtoexecute-secrets": {
              "level": "error"
            },
            "use-stable-vm-image": {
              "level": "error"
            }
          }
        }
      }
    }
    
  3. Save the file.

Verify and commit your pipeline definition

  1. Verify that your azure-pipelines.yml file looks like the following file:

    trigger:
      batch: true
      branches:
        include:
        - main
    
    pool: Default
    
    variables:
      - name: deploymentDefaultLocation
        value: westus3
    
    stages:
    
    - stage: Lint
      jobs:
      - job: LintCode
        displayName: Lint code
        steps:
          - script: |
              az bicep build --file deploy/main.bicep
            name: LintBicepCode
            displayName: Run Bicep linter
    
    - stage: Validate
      jobs:
      - job: ValidateBicepCode
        displayName: Validate Bicep code
        steps:
          - task: AzureResourceManagerTemplateDeployment@3
            name: RunPreflightValidation
            displayName: Run preflight validation
            inputs:
              connectedServiceName: $(ServiceConnectionName)
              ___location: $(deploymentDefaultLocation)
              deploymentMode: Validation
              resourceGroupName: $(ResourceGroupName)
              csmFile: deploy/main.bicep
              overrideParameters: >
                -environmentType $(EnvironmentType)
    
    - stage: Deploy
      jobs:
      - job: Deploy
        steps:
          - task: AzureResourceManagerTemplateDeployment@3
            name: Deploy
            displayName: Deploy to Azure
            inputs:
              connectedServiceName: $(ServiceConnectionName)
              deploymentName: $(Build.BuildNumber)
              ___location: $(DeploymentDefaultLocation)
              resourceGroupName: $(ResourceGroupName)
              csmFile: deploy/main.bicep
              overrideParameters: >
                -environmentType $(EnvironmentType)
    

    If it doesn't look the same, update it to match this example, and then save it.

  2. Commit and push your changes to your Git repository by running the following commands in the Visual Studio Code terminal:

    git add .
    git commit -m "Add lint and validation stages"
    git push
    

    Immediately after you push, Azure Pipelines starts a new pipeline run.

View the pipeline run

  1. In Azure DevOps, go to Pipelines.

  2. Select the most recent run of your pipeline.

    Screenshot of Azure DevOps. The link to the latest pipeline run is highlighted.

    If the pipeline is still running, wait until it's finished. Although Azure Pipelines automatically updates the page with the latest status, it's a good idea to refresh your page occasionally.

  3. Notice that the pipeline run now shows the three stages that you defined in the YAML file. Also notice that the Lint stage failed.

    Screenshot of a pipeline run in Azure DevOps. The Lint stage is reporting a failure.

  4. Select the Lint stage to see more details.

    Screenshot of a pipeline run in Azure DevOps. The name of the Lint stage is highlighted.

  5. Select the Run Bicep linter step to view the pipeline log.

    Screenshot of the pipeline log for the Lint stage. The step for running a Bicep linter is highlighted.

    Notice that the error displayed is similar to this one:

    Error no-unused-params: Parameter "storageAccountNameParam" is declared but never used.

    This error indicates that the linter found a rule violation in your Bicep file.

Fix the linter error

Now that you've identified the problem, you can fix it in your Bicep file.

  1. In Visual Studio Code, open the main.bicep file in the deploy folder.

  2. Notice that the Bicep linter has also detected that the storageAccountNameParam parameter isn't used. Visual Studio Code indicates the unused parameter with a wavy line. Normally, the line would be yellow to indicate a warning. But because you customized the bicepconfig.json file, the linter treats the code as an error and displays the line in red. Here's the line of code:

    param storageAccountNameParam string = uniqueString(resourceGroup().id)
    
  3. Delete the storageAccountNameParam parameter.

  4. Save the file.

  5. Commit and push your changes to your Git repository by running the following commands in the Visual Studio Code terminal:

    git add .
    git commit -m "Remove unused parameter"
    git push
    

    Once again, Azure Pipelines automatically triggers a new run of your pipeline.

View the pipeline run again

  1. In Azure DevOps, go to your pipeline.

  2. Select the most recent run.

    Wait until the pipeline run completes. Although Azure Pipelines automatically updates the page with the latest status, it's a good idea to refresh the page occasionally.

  3. Notice that the Lint stage finished successfully, but now the Validate stage has failed.

    Screenshot of the pipeline run. The Lint stage reports success and the Validate stage reports failure.

  4. Select the Validate stage to see more details.

  5. Select the Run preflight validation step to view the pipeline log.

    Screenshot of the pipeline log for the Validate stage. The step for running preflight validation is highlighted.

    Notice that the error displayed in the log includes the following message:

    mystorageresourceNameSuffix is not a valid storage account name. Storage account name must be between 3 and 24 characters in length and use numbers and lower-case letters only.

    This error indicates that the storage account name isn't valid.

Fix the validation error

You found another problem in the Bicep file. Here, you'll fix the problem.

  1. In Visual Studio Code, open the main.bicep file in the deploy folder.

  2. Look at the definition of the storageAccountName variable:

    var appServiceAppName = 'toy-website-${resourceNameSuffix}'
    var appServicePlanName = 'toy-website'
    var applicationInsightsName = 'toywebsite'
    var logAnalyticsWorkspaceName = 'workspace-${resourceNameSuffix}'
    var storageAccountName = 'mystorageresourceNameSuffix'
    

    The string interpolation hasn't been configured correctly.

  3. Update the storageAccountName variable to correctly use string interpolation:

    var storageAccountName = 'mystorage${resourceNameSuffix}'
    
  4. Save the file.

  5. Commit and push the changes to your Git repository by running the following commands in the Visual Studio Code terminal:

    git add .
    git commit -m "Fix string interpolation"
    git push
    

View the successful pipeline run

  1. In Azure DevOps, go to your pipeline.

  2. Select the most recent run.

    Wait until the pipeline run completes. Although Azure Pipelines automatically updates the page with the latest status, it's a good idea to refresh the page occasionally.

    Note

    If you get an error stating that you need to register the Microsoft.Insights resource provider, see Resolve errors for resource provider registration.

  3. Notice that all three stages of the pipeline have finished successfully:

    Screenshot of the pipeline run in Azure DevOps, with all three stages reporting success.

You now have a pipeline that successfully detects errors in your Bicep code early in your deployment process and then deploys to Azure if there are no errors.