Bicep CI/CD Handbook
Bicep CI/CD HandbookToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

Initial Setup

When setting up GitHub Actions within a repository for deployment to Azure there are a number of steps that need to be completed. Some common considerations include:

  • What environments are required?
  • How to structure the repository?
  • What credentials will be used?
  • How will the workflow be triggered?
  • How can I pre-validate my code?

Define environments

It’s common to want to be able to validate changes safely to confirm they have the desired effect before they are officially made to your production environment.

We recommend separating these environments to minimize the risk of accidentally mixing them up and deploying unvalidated changes to production.

Development

This environment exists to give developers a means of deploying and testing code that may be broken or partially working. We recommend optimizing for developer productivity by:

  • Supporting multiple copies of this environment, to allow developers to operate independently of one another.
  • Fully automating the setup and teardown of development environments.

Staging

This environment should serve as a final validation before you deploy to Production. It should again be separated from Production, but should represent the real environment as accurately as possible. We recommend:

  • Consider it as a “clean slate”. Only deploy changes if they have already been deemed “Production-ready” and merged to your main branch.
  • If possible, separate it from your Development environment. Ideally, it should be inaccessible to anyone with permissions to Development. If this is too complex, then at minimum, put process into place to avoid deploying unvalidated code to it.

Production

This is your live environment. Only code that has been merged to main and validated successfully in Staging should be deployed here.

Manual Approval

It’s common to want a manual approval stage in Production after running validation or What-If, and prior to deploying.

One way to achieve this is to create a separate environment named “Manual Approval (Production)”, and enable the deployment protection rule by:

  • Check the “Required reviewers” and add reviewers.
  • [Optional] Check the “Wait timer” and set a max waiting time for the approval.

A stage with this environment will not run until the reviewers approve the deployment.

Other options

There are other options for approvals out there, such as this Marketplace Action. However, this Action pauses a workflow with a worker actively running, whereas the deployment protection rule method waits for an approval before a worker is allocated, which is more cost effective. You should also exercise your own judgement when taking a dependency on any open-source solution.

Structure

When considering how to structure the infrastrucutre as code your repository, key considerations include:

  • Easy to understand — the structure should be easy to understand and navigate.
    • Consider mapping the structure to Azure to make it easier to understand when moving back and forth between the code and the Azure Portal.
  • Graular controls — the structure should allow for granular review and approval of changes.
    • GitHub supports code ownership and branch protection rules that can be used to control who can approve changes to specific directories.
    • Consider using separate directories for each environment.

For example:

  • / - Repository root
    • deployments - Directory for Azure deployments.
      • dev - Directory for deployments to the development environment.
        • rg-app-dev-001 - Directory for a deployment to a specific resource group.
          • deploy.bicep - The Bicep file for the deployment.
      • prod - Directory for deployments to the production environment.
        • rg-app-prod-001 - Directory for a deployment to a specific resource group.
          • deploy.bicep - The Bicep file for the deployment.

Connecting to Azure

See OpenID Connect for instructions on how to configure your GitHub Workflows to connect securely to Azure.

Workflow for validation

When developing Bicep deployments it is important to test the code before it is deployed to an environment. Performing tests on Bicep deployments can help to identify issues early and reduce the risk of later promblems.

A key DevOps concept called shifting-left, means that testing is performed as early as possible in the development process. By identifying issues early, the cost of fixing them is reduced. Cost is a generic term that can be applied to time, money, or risk. For example, if an issue is identified later in the process such as during deployment or in production:

  • There may be more rework required to fix the issue.
  • Elapsed time to fix the issue may affect the delivery schedule or cause downtime to a live service.

Bicep supports a number of different ways to validate code, including:

  • Bicep linter — is a static analysis feature of the Bicep langauge and CLI used during the development process to identify issues with the code.
    • Many of the issues identified by the linter are related to the syntax and structure of Bicep code.
    • Additionally, the Bicep linter will identify some security issues such as outputing a secret value in a deployment.
    • The linter will report error, warning, or informational issues. In most cases, rule severity can be configured to change the level of reporting by setting the level in bicepconfig.json.
  • PSRule for Azure — is a static analysis tool that can be used to test Bicep deployments. PSRule for Azure emulates how Azure Resource Manager (ARM) processes a deployment, then tests the resulting Azure resources.
    • Using PSRule for Azure, you can write your own tests, and/ or use a set of almost 400 built-in tests.
    • The built-in tests cover a wide range of scenarios including security, reliability, and best practices aligned to the Azure Well-Architected Framework.

If you use PSRule for Azure, running the Bicep linter separately is not required as any Bicep linter issues that generate an error will also be flagged by PSRule for Azure.

Consider using these tools in your workflow along with the following security tools to identify issues early:

Note: Bicep assertions and tests are currently in development.

Configure Workflow environment variables to pin to the same version tool versions across your workflow:

env:
  AZCLI_VERSION: 2.52.0
  BICEP_VERSION: 0.21.1
  PSRULE_BASELINE: Azure.GA_2023_06

Here is an example of a workflow job to test your Bicep deployment:

jobs:
  lint:
    name: 🧪 Lint
    runs-on: ubuntu-latest
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    - name: Install Bicep
      run: az bicep install --version v${{ env.BICEP_VERSION }}

    - name: Run PSRule and lint
      uses: microsoft/ps-rule@v2.9.0
      with:
        modules: PSRule.Rules.Azure
        baseline: ${{ env.PSRULE_BASELINE }}

To accompany this configure PSRule options within the ps-rule.yaml file at the root of the repository:

#
# PSRule configuration
#

# Please see the documentation for all configuration options:
# https://aka.ms/ps-rule/options
# https://aka.ms/ps-rule-azure/options

requires:
  PSRule: '@pre >=2.9.0'
  PSRule.Rules.Azure: '@pre >=1.29.0'

include:
  module:
    - PSRule.Rules.Azure

output:
  culture:
  - en-US

input:
  pathIgnore:
  - '**'
  - '!deployments/**/*.bicepparam'
  - '!deployments/**/deploy.bicep'

configuration:
  AZURE_BICEP_FILE_EXPANSION: true
  AZURE_BICEP_PARAMS_FILE_EXPANSION: true
  AZURE_RESOURCE_GROUP:
    tags:
      env: test

Dependabot

GitHub Dependabot is a GitHub feature that creates pull requests automatically to update dependencies in your repository. For example, if a new version of a GitHub Action is released, Dependabot will create a pull request to update the version in your workflow.

To enable Dependabot, configure a .github/dependabot.yml file in your repository:

#
# Dependabot configuration
#

# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/getting-started/dependabot-quickstart-guide

version: 2
updates:

# Maintain dependencies for GitHub Actions
- package-ecosystem: github-actions
  directory: '/'
  schedule:
    interval: daily