Deploying GitHub Enterprise Server on Azure the GitOps way
Table of Contents
- 0 - Introduction
- 1 - The infrastructure
- 2 - The Action workflows
- 3 - Putting it all together (a.k.a. the GitOps part)
- 4 - The end
0 - Introduction #
As a zen apprentice I like to free my mind as much as I can, specially regarding if/where/how my ressources are deployed in the cloud ☁️.
In this post I’ll share a simple way to do so, through the example of a GitHub Enterprise Server (GHES) instance that I use for testing purposes. This GHES instance is described as Terraform code and deployed in Azure using GitHub Actions workflows.
The code can be found in this repository which is based on Azure-Samples/terraform-github-actions, a sample repository that shows how to use GitHub Actions workflows to manage Azure infrastructure with Terraform.
1 - The infrastructure #
The Terraform code used to describe the target infrastructure - that is, the GHES related resources - is split into several files:
❯ tree -Pa '*.tf|*.tfvars|*.hcl' --prune
.
├── .terraform.lock.hcl
├── main.tf
├── outputs.tf
├── providers.tf
├── terraform.tfvars
└── variables.tf
Overall this is a typical file structure for a simple Terraform project:
main.tf
contains the main Terraform code, where Azure ressources (e.g. storage account, vnet, VM, etc.) are defined to be deployed;providers.tf
references plugins that allow Terraform to interact with cloud providers (here Azure);variables.tf
andterraform.tfvars
respectively defines and assign custom values to variables used inmain.tf
to customize the resources;outputs.tf
defines the outputs that the Terraform code will return regarding the deployed resources (e.g. Azure resource group, VM’s public IP, FQDN).
The special file .terraform.lock.hcl
is a lock file ensuring that the same versions of the plugins are used by all collaborators of the repo.
2 - The Action workflows #
The .github/workflows
directory contains the GitHub Actions workflows that will work with the Terraform code.
❯ tree -Pa '*.yml' --prune
.
└── .github
└── workflows
├── tf-drift.yml
├── tf-plan-apply.yml
└── tf-unit-tests.yml
Here are the main characteristics of these workflows:
Workflow | Triggering events | Description |
---|---|---|
tf-unit-tests.yml | push on any branch | Runs unit tests and analyze the Terraform code. |
tf-plan-apply.yml | pull_request or push to main branch | Builds the Terraform plan and applies it in Azure. |
tf-drift.yml | schedule or manual | Detects drifts between the desired and the current infrastructure. |
A nice thing is that the Terraform state is stored in an Azure storage account, so that it can be shared between the workflows to retrieve the currently deployed infrastructure. For more information, see the README of the repository.
Another noteworthy point is the use of the paths
keyword in the workflow files, allowing to trigger the workflows only if the Terraform files have been modified, e.g.:
on:
push:
paths:
- '**.tf'
- '**.tfvars'
3 - Putting it all together (a.k.a. the GitOps part) #
Combining both Terraform and GitHub Actions allows us to embrace the GitOps paradigm. Here is a typical process leveraging this approach:
Clone the repo and create a new branch to work on a new feature, or to update a Terraform variable that’ll in turn update the GHES deployment;
Once the Terraform code has been updated, push the changes to the remote repo and open a pull request towards the
main
branch;The
tf-unit-tests.yml
workflow is triggered to test the Terraform code;- In parallel, the
tf-plan-apply.yml
workflow is triggered to build the Terraform plan and to share it as a comment in the PR conversation, for review purposes; After all the PR required checks and reviews have passed, the PR can be merged into the
main
branch;- Once the PR merged, the
tf-plan-apply.yml
workflow is triggered again, and this time it applies the Terraform plan in Azure, effectively deploying the GHES resources;
At this point, my GitHub Enterprise Server instance is deployed in Azure and ready to be used (or well, to be set up if it’s the first boot :)
Now that the deployment is done, we want to ensure the infrastructure is always in line with the Terraform code. Indeed, unwanted changes might occur in the infrastructure along time, like the VM being deleted manually or the DNS name being updated. This is where the tf-drift.yml
workflow comes into play.
This workflow is triggered every day to detect drifts between the desired (i.e. the Terraform code) and the currently deployed infrastructure. If drifts are detected, the workflow will open a new issue, and I’ll receive a notification from GitHub:
4 - The end #
That’s it for today folks, I hope you enjoyed this post and that it will help you to get started with Terraform and GitHub Actions.
As usual, this is more like a PoC and many improvements can be done. Zen masters often say that “the journey is never ending”.
As a future work, I’d like to play with the Terraform provider for GitHub to store GHES configuration (e.g. organizations, policies, teams) as code, and write additional workflows to leverage it. I’ll also write another post to go further into the main
branch protection rules and the unit tests workflow, as it includes some nice code scanning features.
Oh and by the way, this blog post has been co-written with Copilot 🤖.
Gasshō! 🙏