- Overview
- Getting Started
- Example of provisioned resources
- Contents of this repository
- Technical Details
- Acknowledgements
- Contributing
This repository provides a comprehensive, modular infrastructure-as-code solution for deploying and managing Azure resources with using Terraform and CI/CD workflows. This is designed to support both Azure DevOps and GitHub-based CI/CD workflows, enabling organizations to automate the provisioning of secure, scalable, and policy-compliant cloud environments using infrastructure as code, versioned with Git. The project Git repository which is provisined by this module is designed to be used in enterprise environments and follows Azure and Terraform best practices.
Key features include:
- Modular Terraform Architecture: Reusable modules for common Azure and DevOps patterns.
- Support for Azure DevOps and GitHub: Provisioning of projects, repositories, pipelines, and self-hosted agents/runners.
- Secure State and Secret Management: Uses Azure Storage and Key Vault for managing Terraform state and sensitive information.
- Enterprise-Ready: Implements best practices for secure closed network, identity, and repository policy enforcement for deployment task executions.
- Extensible and Customizable: Easily adaptable to various organizational requirements and cloud governance models.
The infra/terraform directory contains all core infrastructure code, organized for clarity and scalability. This structure allows teams to bootstrap foundational resources, deploy landing zones, and manage project-specific DevOps resources in a consistent and automated manner.
Note
Currently we only support GitHub projects.
We are planning to support Azure DevOps projects in the near future.
The architecture including Azure network is shown in the diagram below. This diagram shows a configuration where a private virtual network is enabled and a GitHub self-hosted runners are hosted in Azure Container App jobs and execute CI/CD workflow jobs in an event-driven manner using KEDA scaling.
It also shows a configuration where a project for a Microsoft Dev Box is deployed so that developers can work for Azure projects using a secure development environment. The Dev Box virtual desktop is connected to a private virtual network, so it can securely access Terraform state management files and deploy and manage Azure resources.
To provision DevOps CI/CD environment that automates the deployment of Azure resources, please perform the following steps after making the necessary preparations for each of steps.
Please prepare the following components and permissions:
- Azure CLI
- Entra ID tenant to manage Azure Subscriptions
- Azure Subscriptions (total of 5)
- Azure Subscriptions to deploy DevOps resources
- Azure Subscriptions to be used in the project (4)
- Subscriptions used by developers for feature development work
- Subscriptions for development
- Subscriptions for staging
- Subscriptions for production deployment
OwnersorContributorspermissions for the above Azure Subscriptions- Permissions for the user who will be using this document
- GitHub PAT (Personal Access Token)
- Token for provisioning GitHub project resources (token with the following permissions)
repoworkflowadmin:orguser: read:useruser: user:emaildelete_repo
- Token for GitHub self-hosted runner
repoadmin:org
- Token for provisioning GitHub project resources (token with the following permissions)
Note
All Azure Subscriptions must be prepared under the same Entra ID tenant.
Note
The above explanation about GitHub PAT refer to classic personal access tokens. You can also use fine-grained access tokens which are still in beta to provide more granular permissions. These docs will be updated to reflect this in the future.
Please log in to your Entra ID using Azure CLI.
az login --tenant <Tenant_Id>Note
Please register a list of resource providers for all subscriptions you have prepared.
The Terraform IaC code prepared in this repository requires that you need to register the necessary resource providers in advance. Also, in your future Azure projects, it may cause an error when running terraform plan with the allocated user-assigned identity.
To register resource providers, you can use the script in the following folder to register the specified resource providers in bulk.
cd $ProjectRoot/infra/terraform/_setup_subscriptions
./register_rps.sh -s <your_subscription_id>Please use the bootstrap module (infra/terraform/_bootstrap to provision the foundation resources (Azure Blob Storage and Key Vault) for the DevOps environment.
cd $ProjectRoot/infra/terraform/_bootstrapFor provisioning the bootstrap resources, refer to the sample parameters file (infra/terraform/_bootstrap/terraform.tfvars.sample).
cp terraform.tfvars.sample terraform.tfvarsThe following parameters can be specified:
| Parameter | Type | Optional | Description |
|---|---|---|---|
target_subscription_id |
string | No | Azure subscription Id for deploying DevOps Landing Zone (bootstrap resources) |
naming_suffix |
list(string) | Yes | Suffix for resource naming (default: ["alz", "devops", "bootstrap"]) |
location |
string | No | Location in where DevOps resources are deployed |
tags |
map(string) | Yes | Resource tags (default: {} [empty map]) |
tfstate_container_name |
string | Yes | Blob container name for Terraform state files (*.tfstate) for Bootstrap and DevOps resources (default: tfstate) |
enable_user_assigned_identity |
bool | Yes | Whether to enable user-assigned identity for Bootstrap resources (default: false) |
enable_storage_customer_managed_key |
bool | Yes | Whether to enable customer-managed keys (CMK) for Blob Storage (tfstate) (default: true) |
customer_managed_key_policy |
object | Yes | Policy for customer-managed key (default: RSA 4096 bit, require renewal in 90 days) |
bootstrap_config_filename |
string | Yes | Filename to save bootstrap config file (default: ./bootstrap.config.json) |
tfbackend_config_template_filename |
string | Yes | Template configurations of azurerm remote backend for Terraform state management files (default: ./devops.azurerm.tfbackend) |
Run Terraform for Bootstrap resource provisioning.
First, you will manage the Terraform state management file locally.
terraform init
terraform plan
terraform applyAfter the provisioning above is complete, the following files will be generated.
backend.tfbootstrap.config.json(or the file name specified inbootstrap_config_filename)devops.azurerm.tfbackend(or the file name specified intfbackend_config_template_filename)
These files store the configuration information for the bootstrap resources and will be used in the next step.
You can also manage the Terraform state management file for this bootstrap resource provisioning itself in the azurerm remote backend by running the following command:
terraform init -migrate-stateAfter the Bootstrap resource provisioning above is complete, the next step is to provision the DevOps Landing Zone resources.
cd $ProjectRoot/infra/terraform/devops/lzFor provisioning the DevOps Landing Zone resources, refer to the sample parameters file (infra/terraform/devops/lz/terraform.tfvars.sample).
Note
All sensitive values (such as PATs) which are used in this step are managed in Bootstrap's Azure Key Vault.
cp terraform.tfvars.sample terraform.tfvarsNote
In this step, you will use provisioned Bootstrap resources, so be sure to specify the JSON file (specified in the parameter bootstrap_config_filename) that contains the configuration information of the bootstrap resources. If you are using the default value in the previous step, there is no need to change the parameter in this step.
The parameters that can be specified are as follows.
| Parameter | Type | Optional | Description |
|---|---|---|---|
target_subscription_id |
string | No | Azure Subscription Id for deploying DevOps Landing Zone (DevOps landing zone resources) |
naming_suffix |
list(string) | Yes | Suffix for resource naming (default: ["alz", "devops"]) |
location |
string | No | Location in where DevOps resources are deployed |
tags |
map(string) | Yes | Resource tags (default: {} [empty map]) |
enable_self_hosted_agents |
bool | Yes | Whether to enable self-hosted agents/runners (default: true) |
enable_private_network |
bool | Yes | Whether to enable the private virtual network where the self-hosted agents/runners run (default: true) |
enable_github |
bool | Yes | Whether to enable GitHub for this DevOps resource (default: true) |
enable_devbox |
bool | Yes | Enable Microsoft Dev Box for this DevOps resources (default true) |
github_organization_name |
string | No | GitHub organization name |
github_personal_access_token |
string | No | GitHub personal access token to be used for provisioning GitHub resources |
github_personal_access_token_for_runners |
string | No | GitHub personal access token to be used for GitHub self-hosted runners |
vnet_address_prefix |
string | No | Virtual network address prefix for the private virtual network |
vnet_private_endpoint_subnet_address_prefix |
string | No | Private endpoint subnet address prefix for the private virtual network |
vnet_gateway_subnet_address_prefix |
string | No | Gateway subnet address prefix for the private virtual network |
vnet_container_app_subnet_address_prefix |
string | No | ACA subnet address prefix for the private virtual network |
vnet_container_instance_subnet_address_prefix |
string | No | ACI subnet address prefix for the private virtual network |
vnet_devbox_subnet_address_prefix |
string | No | DevBox subnet address prefix for Private Virtual Network |
vnet_private_endpoint_subnet_name |
string | Yes | Private endpoint subnet name for the private virtual network (default: private-endpoints) |
vnet_container_app_subnet_name |
string | Yes | ACA subnet name for the private virtual network (default: container-apps) |
vnet_container_instance_subnet_name |
string | Yes | ACI subnet name for the private virtual network (default: container-instances) |
vnet_devbox_subnet_name |
string | Yes | DevBox subnet name for Private Virtual NetworkAdd commentMore actions (default: devbox) |
devbox_definitions_image_list |
list(string) | Yes | A list of Dev Box VM Image for Dev Box Definitions (default: ["galleries/default/images/microsoftwindowsdesktop_windows-ent-cpc_win11-24h2-ent-cpc"]) |
devbox_definitions_sku_list |
list(string) | Yes | A list of Dev Box VM SKU (default: all SKUs supported by default) |
enable_agents_environment_zone_redundancy |
bool | Yes | Whether to enable zone redundancy for ACA (default: true) |
bootstrap_config_filename |
string | Yes | Filename to load bootstrap config file to (default: ../../_bootstrap/bootstrap.config.json) |
Run Terraform for DevOps resource provisioning.
You will manage the Terraform state file with the azurerm remote backend with using template configurations of Terraform azurerm remote backend.
terraform init -backend-config ../../_bootstrap/devops.azurerm.tfbackend -backend-config key=devopslz.terraform.tfstate
terraform plan
terraform applyAfter the DevOps Landing Zone resources provisioning above is complete, the next step is to provision individual resources for each project (Git repositories, CI/CD pipelines, user-assigned identities, environments for running containers for self-hosted agents/runners, and so on).
This step can be run on a per-project basis, using the shared resources in the DevOps Landing Zone provisioned in the previous step.
cd $ProjectRoot/infra/terraform/devops/project_github
export project_name="<your-project-name>"Note
Currently we only support GitHub projects.
We are planning to support Azure DevOps projects in the future.
For provisioning project resources, please refer to the sample parameters file (infra/terraform/devops/project_github/terraform.tfvars.sample).
cp terraform.tfvars.sample terraform.tfvarsNote
In this step, you will use provisioned Bootstrap resources, so be sure to specify the JSON file (specified in the parameter bootstrap_config_filename) that contains the configuration information of the bootstrap resources. If you are using the default value in the previous step, there is no need to change the parameter in this step.
Note
In this step, you will use the provisioned DevOps Landing Zone resources, so you will use the Terraform state management file which was generated when provisioning the DevOps Landing Zone resources. For this reason, be sure to specify in the devops_tfstate_key parameter the key parameter of the azurerm remote backend specified in the provisioning execution command in step 2. If you are executing as specified in this document, there is no problem with the default value.
The parameters that can be specified are as follows.
| Parameter | Type | Optional | Description |
|---|---|---|---|
target_subscription_id |
string | No | Azure Subscription Id for provisioning DevOps Project resources |
project_name |
string | No | Project name |
location |
string | No | Location in where DevOps resources are deployed |
tags |
map(string) | Yes | Resource tags (default: {} [empty map]) |
subscriptions |
map(object) | No | A map type list of Azure Subscriptions to be used in the DevOps Project |
use_templates_repository |
bool | Yes | Whether to use a template repository with your DevOps Project (default: true) |
use_runner_group |
bool | Yes | Whether to use GitHub Runner Group with your DevOps Project (default: false) |
use_self_hosted_runners |
bool | Yes | Whether to use GitHub Self-Hosted Runners in your DevOps project (default: true) |
self_hosted_runners_type |
string | Yes | Compute type of GitHub Self-Hosted Runners (Options: "aca" or "aci") (default: aca) |
use_devbox |
bool | Yes | Whether to use Microsoft Dev Box; The devops/lz module must be provisioned with true for enable_devbox option (default: true) |
devbox_maximum_dev_boxes_per_user |
number | Yes | When specified, limits the maximum number of Dev Boxes a single user can create across all pools in the project (default: 2) |
devbox_local_administrator_enabled |
bool | Yes | Specifies whether owners of Dev Boxes in the Dev Center Project Pool are added as local administrators on the Dev Box (default: true) |
devbox_stop_on_disconnect_grace_period_minutes |
number | Yes | The specified time in minutes to wait before stopping a Dev Center Dev Box once disconnect is detected. Possible values are between 60 and 480 (default: 60) |
bootstrap_config_filename |
string | Yes | Filename to load bootstrap config file to (default: ../../_bootstrap/bootstrap.config.json) |
devops_tfstate_key |
string | Yes | Terraform state management filename for DevOps Landing Zone resources (azurerm remote backend key) (default: devopslz.terraform.tfstate) |
Run Terraform to run DevOps Project resource provisioning.
You will manage the Terraform state file with the azurerm remote backend with using template configurations of Terraform azurerm remote backend.
terraform init -backend-config ../../_bootstrap/devops.azurerm.tfbackend -backend-config key=${project_name}.terraform.tfstate
terraform plan
terraform applyThe document explaines an example of provisioned resources.
infra/
└── terraform/
├── _bootstrap/
├── _setup_subscriptions/
├── devops/
│ ├── lz/
│ └── project_github/
└── modules/
This folder contains Terraform code and configuration files for bootstrapping the foundational Azure resources required for state management and secure secret storage, such as:
- Storage accounts for Terraform state
- Key Vaults for secrets
This folder contains environment-specific and project-specific Terraform configurations for provisioning DevOps resources. Key subfolders include:
lz/: Landing Zone resources, including networking, identity, and self-hosted agents/runners infrastructure for both Azure DevOps and GitHub Actions.project_github/: Project-level resources for GitHub-based CI/CD, including repository, runner, and workflow setup.
A collection of reusable Terraform modules for common infrastructure patterns, including:
aca_env/,aca_event_job/,aca_manual_job/: Modules for Azure Container Apps environments and jobs.aci/: Azure Container Instances.azure_devops/,azure_devops_agent_aca/,azure_devops_agent_aci/,azure_devops_pipelines/: Modules for Azure DevOps projects, agents, and pipelines.github/,github_runner_aca/,github_runner_aci/,github_workflows/: Modules for GitHub repositories, self-hosted runners (on ACA/ACI), and workflow automation.resource_providers/: Registration and management of Azure resource providers.
Note
Terraform azurerm_resource_provider_registration does not provide a module for loading already registered Azure resource providers. Registration and un-registration of the Azure resource providers are likely conflicting between multiple Terraform project deployments, and Terraform state management for resource providers is also difficult, so it should not be used.
This document provides technical details about the workflow architecture using GitHub Actions that is provisioned in this project repository.
This project is insipred by the Azure Landing Zone Accelerator project. The Azure Landing Zone Accelerator project is focusing on the DevOps resources only for Azure Landing Zone deployment, while this project is focusing on a generic Azure deployment project. Thanks to Jared Holgate and contributors and team members of Azure Landing Zone Accelerator project.
Contributions are welcome! Please submit a pull request or open an issue for any suggestions or improvements.




