Skip to content

kewalaka/github-azure-iac-templates

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 

Repository files navigation

Multi-environment Infrastructure as Code CI/CD templates

This repository is a collection of GitHub Actions useful for deploying Terraform using OIDC authentication.

It supports multi-environment solutions (i.e. those that need to deploy similar code to dev, test, prod, etc), with optional support for commonly required checks such as linting and code security static analysis enabled by default.

Check the Optional Features section below to configure settings for:

  • Terraform Lint
  • Checkov infrastructure scanning (tfplan enriched)
  • Additional TFVARs at run time
  • Terraform backend options
  • Automatic creation of Terraform backend
  • Unlock private networking resource firewalls if not using runners

Usage

Below are manual instructions for getting started. If you're looking to automate this process, check out Az-Bootstrap.

GitHub repository settings

The following steps should be completed in the calling repository:

  1. Create Environments: Navigate to Settings -> Environments, create two for each target environment:

    • <env_name>-iac-plan (e.g., dev-iac-plan)
    • <env_name>-iac-apply (e.g., dev-iac-apply)
  2. Add Required Secrets: Add the following secrets to both the -plan and -apply environments you just created:

    • ARM_CLIENT_ID: Client ID for the User Assigned Managed Identity used for deployment.
    • ARM_SUBSCRIPTION_ID: Target Azure Subscription ID for resource deployment.
    • ARM_TENANT_ID: Azure Tenant ID.
  3. For Terraform only, create the following (also in both plan and apply environments):

    • TF_STATE_RESOURCE_GROUP_NAME: Resource group name containing the Terraform state storage account.
    • TF_STATE_STORAGE_ACCOUNT_NAME: Storage account name for Terraform state.

GitHub workflow

Create a workflow file (e.g., .github/workflows/deploy.yml) in your repository to call the deploy template.

The example below uses workflow_dispatch for manual triggering:

name: Terraform Deployment

on:
  workflow_dispatch:
    inputs:
      terraform_action:
        description: 'Terraform Action'
        default: apply
        type: choice
        options:
          - apply
          - destroy
          - plan
      target_environment:
        description: 'Select target environment'
        required: true
        type: choice
        default: dev
        options:  # options should match your configured environments (e.g., dev, test, prod)
          - dev
          - test
          - prod
      destroy_resources:
        description: 'Actually destroy resources?'
        type: boolean
        default: false

run-name: Terraform ${{ inputs.terraform_action }} (${{ inputs.target_environment }}) by @${{ github.actor }}

permissions:
  id-token: write # Required for OIDC authentication
  contents: read  # Required to checkout code
  pull-requests: write # Terraform Plan summaries can be written to PRs as comments
  security-events: write # Allow upload of sarif outputs

jobs:
  call-terraform-deploy:
    name: "Run terraform ${{ inputs.terraform_action }} for ${{ inputs.target_environment }}"
    uses: kewalaka/github-azure-iac-templates/.github/workflows/terraform-deploy-template.yml@main
    with:
      terraform_action: ${{ inputs.terraform_action }}
      environment_name_plan: "${{ inputs.target_environment }}-iac-plan"
      environment_name_apply: "${{ inputs.target_environment }}-iac-apply"
      tfvars_file: "./environments/${{ inputs.target_environment }}.terraform.tfvars"      
      destroy_resources: ${{ inputs.destroy_resources == true || inputs.terraform_action == 'destroy' }}
    secrets: inherit

Example Usage - Bicep Deployment Stacks

Create a workflow file (e.g., .github/workflows/deploy-bicep.yml) in your repository with the following content. This example uses workflow_dispatch for manual triggering:

name: Bicep Deployment Stacks

on:
  workflow_dispatch:
    inputs:
      bicep_action:
        description: 'Bicep Action'
        default: deploy
        type: choice
        options:
          - deploy
          - plan
      target_environment:
        description: 'Select environment'
        required: true
        type: choice
        default: dev
        options:  # options should match your configured environments (e.g., dev, test, prod)
          - dev
          - test
          - prod
      deployment_scope:
        description: 'Deployment scope'
        required: true
        type: choice
        default: resourceGroup
        options:
          - resourceGroup
          - subscription
          - managementGroup

run-name: Bicep ${{ inputs.bicep_action }} (${{ inputs.target_environment }}) by @${{ github.actor }}

permissions:
  id-token: write # Required for OIDC authentication
  contents: read  # Required to checkout code
  pull-requests: write # Required for PR commenting during plan

jobs:
  call-bicep-deploy:
    name: "Run bicep ${{ inputs.bicep_action }} for ${{ inputs.target_environment }}"
    uses: kewalaka/github-azure-iac-templates/.github/workflows/[email protected]
    with:
      bicep_action: ${{ inputs.bicep_action }}
      environment_name_plan: "${{ inputs.target_environment }}_plan"
      environment_name_apply: "${{ inputs.target_environment }}_apply"
      deployment_scope: ${{ inputs.deployment_scope }}
      deployment_stack_name: "${{ inputs.target_environment }}-stack"  # Optional: auto-generated if not provided
      root_iac_folder_relative_path: "./iac"
      parameters_file_path: "parameters/${{ inputs.target_environment }}.parameters.json"
      resource_group_name: "${{ inputs.deployment_scope == 'resourceGroup' && format('rg-{0}', inputs.target_environment) || '' }}"
      management_group_id: "${{ inputs.deployment_scope == 'managementGroup' && 'your-mg-id' || '' }}"
      location: "eastus"
      action_on_unmanage: "detachAll"  # Options: detachAll, deleteAll
      deny_settings_mode: "none"       # Options: none, denyDelete, denyWriteAndDelete
    secrets: inherit

Recommendation: Add Protection Rules

To prevent accidental deployments, configure protection rules on your -apply environments:

  1. Go to Repository Settings -> Environments -> <env_name>-iac-apply.
  2. Under Deployment protection rules, enable Required reviewers.
  3. Configure reviewers (users or teams) who must approve deployments to this environment.
  4. Save the protection rules.

Az-Bootstrap performs this step automatically by default.

Optional Variables (Bicep + Terraform)

The following section provides details of how to tune the configuration of the deployment templates.

Root folder

The default folder for IaC is ./iac. This can be modified using root_iac_folder_relative_path

Checkov (security scanning)

This is enabled by default, can be disabled using enable_checkov: false

Check out the actions README.md for more details.

Infracost (cost estimation)

Infracost is disabled by default, can be enabled using enable_infracost: true, and supplying the INFRACOST_API_KEY via GitHub secrets.

Secret Name Description
INFRACOST_API_KEY API key for Infracost. Sign up for free at infracost.io to get your API key.

Unlock private networking resource firewalls

It is possible to specify a list of resource firewalls to unlock during the pipeline run, however we recommend using self-hosted or managed runners instead of this feature:

Variable Name Description Default
EXTRA_FIREWALL_UNLOCKS Comma-separated list of additional storageaccountname or keyvaultname resources whose firewalls should be temporarily opened. (none)

Check out the actions README.md for more details.

Terraform-specific optional variables

TFLint, validate and format linting

This is enabled by default, can be disabled using enable_static_analysis_checks: false

TFLint can further be configured in the calling repository by placing a file .tflint.hcl in the IaC root.

Check out the actions README.md for more details.

Automatic Terraform backend

If the pipeline principal has sufficient permissions, it is possible to make the Terraform backend automatically. This action can also be used to check the backend is available.

This is disabled by default, can be enabled using deploy_backend: true

Check out the actions README.md for more details.

Terraform storage account configurable options

You can add these optional secrets to your environments (-plan and -apply) to customize behavior:

Secret Name Description Default
TF_STATE_SUBSCRIPTION_ID Subscription ID for the Terraform state storage, only required if it is not the same as the deployment subscription account. ARM_SUBSCRIPTION_ID
TF_STATE_STORAGE_CONTAINER_NAME Container name within the state storage account. tfstate
ARTIFACT_STORAGE_CONTAINER_NAME Container name for storing the Terraform plan artifact. tfartifact

Additional TFVARS at runtime

These are supplied using TF_VAR_ environment variables, using this:

Variable Name Description Default
EXTRA_TF_VARS Comma-separated key=value pairs passed as additional -var arguments to Terraform (e.g., containertag=<SHA>,subid=<GUID>) This should be used sparingly, only for variables that need to be computed by previous steps. (none)

Bicep-specific optional variables

Unlock private networking resource firewalls (Bicep)

For Bicep deployment stacks, you can customize stack behavior using the following parameters:

Parameter Name Description Default Options
deployment_stack_name Name for the deployment stack Auto-generated from repository name Any valid Azure resource name
action_on_unmanage What happens to resources no longer managed by the stack detachAll detachAll, deleteAll
deny_settings_mode Operations denied on stack-managed resources none none, denyDelete, denyWriteAndDelete

Note: Stack names are automatically generated based on the calling repository name if not explicitly provided. The what-if functionality uses standard Azure deployment commands since stacks don't support what-if operations directly.

Design notes

  • For Bicep, Azure deployment stacks are used across resource group, subscription, and management group scopes.
  • For Terraform, Azure Blob Storage is used for state and plan artifacts, to provide stronger RBAC than is available via GitHub packages.

Using Templates Across Repositories

To use these templates from another private repository within the same organization:

  1. Enable Access: In this template repository (github-azure-iac-templates), go to Settings -> Actions -> General. Under Access, ensure "Accessible from repositories in the <your_org_name> organization" is selected.
  2. Update uses Path: In the calling workflow of the other repository, update the uses: path to the full path of this template repository, pinning to a specific version tag (recommended):
  # Replace 'your-org-name' and use a real tag
  uses: your-org-name/github-azure-iac-templates/.github/workflows/[email protected]

(Reference: Managing access for Actions in an organization)

About

This repository is a collection of GitHub Actions useful for deploying Terraform or Bicep to Azure.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •