Unleashing the Power of GitFlow: GitHub AT-AT v1.0.7 Refines Terraform Automation
The GitHub AT-AT project has undergone significant enhancements, especially in its support for GitFlow. This iteration is a substantial update from the previous version, providing better modularity and flexibility in how environments are managed. The new design splits the Terraform state backend setup to accommodate multiple environments — making it simpler to manage both non-production and production resources. This article will walk you through the new version of the AT-AT and its improved GitFlow integration, helping you understand the structure, configurations, and benefits of the latest changes.
Getting Started with the GitHub AT-AT The GitHub AT-AT is now published under the “Azure Terraformer” organization on the Terraform Registry. You can access it through the Terraform Registry, where it provides essential Terraform modules for automating cloud infrastructure deployments.
The AT-AT simplifies the process of setting up Terraform workflows, such as provisioning Azure resources via GitHub Actions. It provides a flexible, modular setup that allows you to implement multiple environments in a streamlined way.
https://registry.terraform.io/namespaces/Azure-Terraformer
You can also follow me on GitHub:
https://github.com/Azure-Terraformer
The GitHub AT-AT is just one collection of modules that I will publish here.
Module Dependency Graph
- Azure-Terraformer/atat/github v1.0.7
- azure-dual-backend-core
- Azure-Terraformer/terraform-backend/azurerm v1.0.4
- azure-dual-backend-app
- Azure-Terraformer/github-credential/azuread v1.0.10
- Azure-Terraformer/atat-application-environment/github v1.0.5
- Azure-Terraformer/codebase-terraform-azure-application/github v1.0.3
- Azure-Terraformer/azure-credential/github v1.0.0
- Azure-Terraformer/azure-backend/github v1.0.1
- Azure-Terraformer/action-azure-application/github v1.0.23
- Azure-Terraformer/action-azure-login-test/github v1.0.0
- atat: The GitHub AT-AT Root Module
- azure-dual-backend-core: sub-module of the GitHub AT-AT Root Module focused on provisioning dual Terraform State backends to Azure in a Non-PROD / PROD setup.
- azure-dual-backend-app: sub-module of the GitHub AT-AT Root Module focused on provisioning an application’s development and operational environment working end-to-end using GitHub and Entra ID.
- github-credential/azuread: Provisions an Entra ID application, Service Principal and Federated Identity Credential allowing GitHub to impersonate an Entra ID identity when running GitHub Actions that need to talk to Azure.
- atat-application-environment: Provisions the entire “Azure Terraform Starter Project” to a new GitHub repository. Requires Terraform State Backends provisioned to Azure and identities and credentials provisioned to Entra ID.
- codebase-terraform-azure-application: The “Azure Terraform Starter Project” source code. Setups up a GitHub Repository with a simple Azure Terraform project that provisions a simple Azure Resource Group with nothing in it — a blank canvas.
- azure-credential/github: Configures GitHub environment with the needed GitHub Environment Variables to authenticate with Azure. Used by the GitHub Actions Workflow YAML files that are provisioned to the GitHub repository.
- azure-backend/github: Configures GitHub environment with needed GitHub Environment Variables to target a Terraform State Backend hosted on an Azure Storage account. Used by the GitHub Actions Workflow YAML files that are provisioned to the GitHub repository.
- action-azure-application/github: Opinionated implementation of a Terraform CI/CD pipeline that provides three modes of execution — manual, on pull request, and on push.
- action-azure-login/github: A simple GitHub Action Workflow that can be used to test and very GitHub integration with Entra ID allowing GitHub Actions to use Entra ID OIDC authentication to authenticate with Azure.
azure-dual-backend-core
This module provisions to backends: one for Non-PROD and one for PROD, recognizing that many enterprises and organizations often have a multiplicity of environments in each category — not just one ‘dev’ and one ‘prod’ — admittadely an oversimplification of the original version.
module "backend_nonprod" {
providers = {
azurerm = azurerm.nonprod
}
source = "Azure-Terraformer/terraform-backend/azurerm"
version = "1.0.4"
name = var.name
location = var.location
tags = {
application_name = var.name
environment_name = "nonprod"
}
}
Here is a screenshot of the pair of storage accounts that have been setup by this module when it is used with the “stand-alone” example:
I also provide a “core” example that is intended to provision a pair of resuable storage accounts that can be the Terraform State Backend for many applications within the same subscription. These storage accounts deploy with a more friendly resource group name of “rg-terraform-state”.
azure-dual-backend-app
This module assumes you have Terraform State backends provisioned and it will provision everything that is needed in GitHub (i.e. the repo, the code, the actions, etc.) and in Entra (the Application, Service Principal, Federated Identity Credential). I briefly waffled between creating shared Entra ID Applications but felt it would make it easier to enhance security if each application had its own Entra ID application setup to control access to its codebase. This might be a little bit confusing because the word “Application” is so overloaded here. First we need to recognize what the real application is. That is the application we want to deploy to Azure. We create logical representations of that application to group configuration across different platforms to help organize and prevent tight coupling with other applications. Entra ID has its own canonized entities that it manages for this logical representation of our real application. So let’s break down the difference between each representation of the application vs. the real application, shall we?
Many different faces of our “application”
- The “Real” Application: the code, configuration, and infrastructure we want to run on Azure. This could be a Web Site, a REST API, or a Minecraft server. This application is likely composed of source code (if we have it), infrastructure-as-code configuration, and other artifacts that are used in the provisioning and deployment process.
- The “Azure” Application: we need a place to host our application so end users whether they be humans on smartphones, in web browsers, or other machines that need to access our application through an API interface.
- The “Terraform State” Application: in order to identify our application in Terraform State files we use naming conventions on the Terraform State filenames via the “azurerm” backend’s “key” attribute. We concatenate a string token for the “application_name” with a string token for the “environment_name” to produce a unique key within the Terraform State backend so that we can have a isolated Terraform State file for each of the application’s environments.
- The “Entra ID” Application: in order to allow GitHub Actions workflows access to our Azure subscriptions we need to setup an Entra ID application that can be used by the GitHub Actions to impersonate an Entra ID identity. We grant this identity the necessary access within our Azure subscriptions to provision the environment (via Terraform) and deploy the code and configuration (via Terraform — or any other toolset needed). This Entra ID Application needs to have a Service Principal which is ultimately the “door” that GitHub uses to access Entra ID to authenticate — the lock is the Federated Identity Credential which allows GitHub, under very specific circumstances (e.g. specific GitHub Organization, specific GitHub repository, specific GitHub Environment) to impersonate the Entra ID identity when it communicates with Azure and provisions things to the Azure Subscriptions.
- The “GitHub” Application: we need to store the code, configuration, and any artifacts needed to deploy our application in source control. Therefore the GitHub repository acts as a 1:1 mapping to our application. Since we use the same source code to provision multiple environments — maybe source code stored on different branches — but the same source code from the same source code repository is deployed. GitHub’s “Environments” help us align the different version of our code and configuration with the target Terraform State file we want to use when provisioning via Terraform.
After we run the GitHub AT-AT and provision the repository and all the bits and pieces we need we’ll get a few things showing up in GitHub.
Entra ID Applications
As you can see we setup a unique Entra ID Application / Service Principal / Federated Identity Credential per environment for our application. This will allow us the most granular control over RBAC, if we want it, and since this process is already automated by the GitHub AT-AT, its not really that much more work to do it the right way.
This also means that each Entra ID application will have its own Federated Identity Credential that grants it access to the GitHub Actions Workflow but only for a specific GitHub environment.
Each Federated Identity Credential is tied to a specific set of GitHub configuration settings:
- Organization
- Repository
- Entity Type (in this case environment)
- Entity Name (in this case ‘test’)
GitHub Source Code
This is where it all starts from with regards to our application. Of course, as we know, with the GitHub AT-AT there is a lot going on below the surface that is automated that gets us to this point.
Source code with our sample Terraform code and the GitHub Actions workflows which of course are stored in the .github\workflows directory but also manifest under the actions tab. GitHub Actions Workflows
1. Plan on PR
Because I setup three environments we see three instances of the “Pull Request” Terraform Plan Workflow which runs a Terraform Plan when submitting a Pull Request on a feature branch.
2. Apply on Push
You’ll also get three instances of the “Push” Terraform Apply Workflow which runs Terraform Apply when a commit is merged into a target branch.
Here we can see why we need multiple files. Each file needs to have a trigger with the correct branch and target environment.
You can see that when we configure the environments map we are supplying the values that ultimately drive the configuration of these GitHub Actions Workflows.
environments = {
dev = {
subscription_id = var.azure_dev_subscription
branch_name = "develop"
backend = var.nonprod_backend
}
test = {
subscription_id = var.azure_dev_subscription
branch_name = "release"
backend = var.nonprod_backend
}
prod = {
subscription_id = var.azure_prod_subscription
branch_name = "main"
backend = var.prod_backend
}
}
3. Manual Trigger
There is also the Manual Trigger which uses the oddly named “workflow dispatch” trigger.
Ultimately, this is what you get when you manually provision all three environments:
After the deployments of all three environments we’ll see two Terraform State files in the ‘Non-PROD’ Storage Account:
and one more in the PROD Storage Account:
The “tfplan” container will likely be empty because we only use compiled plans in the “Apply on Push” workflows.
Conclusion
In summary, the Azure-Terraformer/atat/github v1.0.7 module provides the high-level orchestration, with various submodules handling specific functions such as backend provisioning, credentials management, GitHub workflow automation, and Azure application management. Each module is layered upon the previous one, creating a robust solution for deploying and managing infrastructure with Terraform, integrated with GitHub and Azure. By using this modular approach, the solution is scalable, flexible, and easy to extend as your environment grows and evolves.