Deploy to Azure infrastructure with GitHub Actions

In this guide, we'll cover how to utilize CI/CD and Infrastructure as Code (IaC) to deploy to Azure with GitHub Actions in an automated and repeatable fashion.

This article is an architecture overview and presents a structured solution for designing an application on Azure that's scalable, secure, resilient, and highly available. To see more real world examples of cloud architectures and solution ideas, browse Azure architectures.

Benefits of using IaC and automation for deployments

There are many ways to deploy to Azure including the Azure portal, CLI, API, and more. For this guide, we'll use IaC and CI/CD automation. Benefits of this approach include:

  • Declarative: When you define your infrastructure and deployment process in code, it can be versioned and reviewed using the standard software development lifecycle. IaC also helps prevent any drift in your configuration.

  • Consistency: Following an IaC process ensures that the whole organization follows a standard, well-established method to deploy infrastructure that incorporates best practices and is hardened to meet your security needs. Any improvements made to the central templates can easily be applied across the organization.

  • Security: Centrally managed templates can be hardened and approved by a cloud operations or security team to meet internal standards.

  • Self-Service: Teams can be empowered to deploy their own infrastructure by utilizing centrally managed templates.

  • Improved Productivity: By utilizing standard templates, teams can quickly provision new environments without needing to worry about all the implementation details.

Additional information can be found under repeatable infrastructure in the Azure Architecture Center or what is infrastructure as code in the DevOps Resource Center.

Architecture

Architecture overview of using CI/CD to deploy Azure

Dataflow

  1. Create a new branch and check in the needed IaC code modifications.
  2. Create a Pull Request (PR) in GitHub once you're ready to merge your changes into your environment.
  3. A GitHub Actions workflow will trigger to ensure your code is well formatted, internally consistent, and produces secure infrastructure. In addition, a Terraform Plan or Bicep what-if analysis will run to generate a preview of the changes that will happen in your Azure environment.
  4. Once appropriately reviewed, the PR can be merged into your main branch.
  5. Another GitHub Actions workflow will trigger from the main branch and execute the changes using your IaC provider.
  6. (exclusive to Terraform) A regularly scheduled GitHub Action workflow should also run to look for any configuration drift in your environment and create a new issue if changes are detected.

Prerequisites

Use Bicep

  1. Create GitHub Environments

    The workflows utilize GitHub environments and secrets to store the Azure identity information and set up an approval process for deployments. Create an environment named production by following these instructions. On the production environment, set up a protection rule and add any required approvers you want that need to sign off on production deployments. You can also limit the environment to your main branch. Detailed instructions can be found here.

  2. Set up Azure Identity:

    An Azure Active Directory application is required that has permissions to deploy within your Azure subscription. Create a single application and give it the appropriate read/write permissions in your Azure subscription. Next set up the federated credentials to allow the GitHub to utilize the identity using OpenID Connect (OIDC). See the Azure documentation for detailed instructions. Three federated credentials will need to be added:

    • Set Entity Type to Environment and use the production environment name.
    • Set Entity Type to Pull Request.
    • Set Entity Type to Branch and use the main branch name.
  3. Add GitHub secrets

    Note

    While none of the data about the Azure identities contain any secrets or credentials, we still utilize GitHub secrets as a convenient means to parameterize the identity information per environment.

    Create the following secrets on the repository using the Azure identity:

    • AZURE_CLIENT_ID : The application (client) ID of the app registration in Azure
    • AZURE_TENANT_ID : The tenant ID of Azure Active Directory where the app registration is defined.
    • AZURE_SUBSCRIPTION_ID : The subscription ID where the app registration is defined.

    Instructions to add the secrets to the repository can be found here.

Use Terraform

  1. Configure Terraform State Location

    Terraform utilizes a state file to store information about the current state of your managed infrastructure and associated configuration. This file will need to be persisted between different runs of the workflow. The recommended approach is to store this file within an Azure Storage Account or other similar remote backend. Normally, this storage would be provisioned manually or via a separate workflow. The Terraform backend block will need updated with your selected storage location (see here for documentation).

  2. Create GitHub environment

    The workflows utilize GitHub environments and secrets to store the Azure identity information and set up an approval process for deployments. Create an environment named production by following these instructions. On the production environment set up a protection rule and add any required approvers you want that need to sign off on production deployments. You can also limit the environment to your main branch. Detailed instructions can be found here.

  3. Set up Azure Identity:

    An Azure Active Directory application is required that has permissions to deploy within your Azure subscription. Create a separate application for a read-only and read/write accounts and give them the appropriate permissions in your Azure subscription. In addition, both roles will also need at least Reader and Data Access permissions to the storage account where the Terraform state from step 1 resides. Next, set up the federated credentials to allow GitHub to utilize the identity using OpenID Connect (OIDC). See the Azure documentation for detailed instructions.

    For the read/write identity create one federated credential as follows:

    • Set Entity Type to Environment and use the production environment name.

    For the read-only identity create two federated credentials as follows:

    • Set Entity Type to Pull Request.
    • Set Entity Type to Branch and use the main branch name.
  4. Add GitHub secrets

    Note

    While none of the data about the Azure identities contain any secrets or credentials, we still utilize GitHub secrets as a convenient means to parameterize the identity information per environment.

    Create the following secrets on the repository using the read-only identity:

    • AZURE_CLIENT_ID : The application (client) ID of the app registration in Azure
    • AZURE_TENANT_ID : The tenant ID of Azure Active Directory where the app registration is defined.
    • AZURE_SUBSCRIPTION_ID : The subscription ID where the app registration is defined.

    Instructions to add the secrets to the repository can be found here.

    Create another secret on the production environment using the read-write identity:

    • AZURE_CLIENT_ID : The application (client) ID of the app registration in Azure

    Instructions to add the secrets to the environment can be found here. The environment secret will override the repository secret when doing the deploy step to the production environment when elevated read/write permissions are required.

Deploy with GitHub Actions

Use Bicep

There are two main workflows included in the reference architecture:

  1. Bicep Unit Tests

    This workflow runs on every commit and is composed of a set of unit tests on the infrastructure code. It runs bicep build to compile the bicep to an ARM template. This ensures there are no formatting errors. Next it performs a validate to ensure the template is deployable. Lastly, checkov, an open source static code analysis tool for IaC, will run to detect security and compliance issues. If the repository is utilizing GitHub Advanced Security (GHAS), the results will be uploaded to GitHub.

  2. Bicep What-If / Deploy

    This workflow runs on every pull request and on each commit to the main branch. The what-if stage of the workflow is used to understand the impact of the IaC changes on the Azure environment by running what-if. This report is then attached to the PR for easy review. The deploy stage runs after the what-if analysis when the workflow is triggered by a push to the main branch. This stage will deploy the template to Azure after a manual review has signed off.

Use Terraform

There are three main workflows included in the reference architecture:

  1. Terraform Unit Tests

    This workflow runs on every commit and is composed of a set of unit tests on the infrastructure code. It runs terraform fmt to ensure the code is properly linted and follows terraform best practices. Next it performs terraform validate to check that the code is syntactically correct and internally consistent. Lastly, checkov, an open source static code analysis tool for IaC, will run to detect security and compliance issues. If the repository is utilizing GitHub Advanced Security (GHAS), the results will be uploaded to GitHub.

  2. Terraform Plan / Apply

    This workflow runs on every pull request and on each commit to the main branch. The plan stage of the workflow is used to understand the impact of the IaC changes on the Azure environment by running terraform plan. This report is then attached to the PR for easy review. The apply stage runs after the plan when the workflow is triggered by a push to the main branch. This stage will take the plan document and apply the changes after a manual review has signed off if there are any pending changes to the environment.

  3. Terraform Drift Detection

    This workflow runs on a periodic basis to scan your environment for any configuration drift or changes made outside of Terraform. If any drift is detected, a GitHub Issue is raised to alert the maintainers of the project.