Greetings to my fellow Technology Advocates and Specialists.
This Blog post is a follow-up to my previous post - Break Terraform State Lease Using Azure DevOps
In this Session, I will demonstrate how to Break Terraform State Lease Using Github Actions.
AUTOMATION OBJECTIVE:-
Validate If Resource Group Exists.
Validate If Storage Account Exists.
Validate If Storage Account Container Exists.
Validate If Terraform State File Exists in the Specified Storage Account Container.
If any One of the above validation DOES NOT PASS , Pipeline will Fail immediately.
If All of the above validation is SUCCESSFUL , Pipeline will then check the Terraform Blob State.
If Terraform Blob State is == AVAILABLE, Pipeline fails with Exit Code 1.
If Terraform Blob State is == LEASED, Pipeline executes successfully by breaking the Terraform State Lease.
If Terraform Blob State is == BROKEN, Pipeline Still executes successfully without altering the present state.
IMPORTANT TO NOTE:-
There is No way to find the Blob Lease State before executing the az storage blob lease break command.
Refer the Link for more Information: az cli blob lease break
Azure Subscription.
Service Principal with Required RBAC ( Contributor ) applied on Subscription or Resource Group(s).
GitHub Repository with Mandatory Repository Secrets and Optionally Environments Configured.
Microsoft DevLabs Terraform Extension Installed in Local System (VS Code Extension).
REPOSITORY SECRETS FORMAT:-
Below is how you configure Repository Secret named AZURE_CREDENTIALS
{
"clientId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"clientSecret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"subscriptionId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"tenantId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
Enter fullscreen mode
Exit fullscreen mode
CODE REPOSITORY:-
BREAK TERRAFORM STATE LEASE USING GITHUB ACTIONS
BREAK TERRAFORM STATE LEASE USING GITHUB ACTIONS
Greetings to my fellow Technology Advocates and Specialists.
This Blog post is a follow-up to my previous post - Break Terraform State Lease Using Azure DevOps
In this Session, I will demonstrate how to Break Terraform State Lease Using Github Actions.
AUTOMATION OBJECTIVE:-
Validate If Resource Group Exists.
Validate If Storage Account Exists.
Validate If Storage Account Container Exists.
Validate If Terraform State File Exists in the Specified Storage Account Container.
If any One of the above validation DOES NOT PASS , Pipeline will Fail immediately.
If All of the above validation is SUCCESSFUL , Pipeline will then check the Terraform Blob State.
If Terraform Blob State is == AVAILABLE, Pipeline fails with Exit Code 1.
If Terraform Blob State is == LEASED, Pipeline executes successfully by breaking the Terraform State Lease.
If Terraform Blob State is == BROKEN, Pipeline Still executes successfully
…
HOW DOES MY CODE PLACEHOLDER LOOKS LIKE:-
GITHUB ACTIONS YAML WORKFLOW (gh-actions-tf-break-lease-v1.0.yml):-
name: 'Break Terraform Lease'
on: [workflow_dispatch]
env:
SUBSCRIPTIONID: '210e66cb-55cf-424e-8daa-6cad804ab604'
RGNAME: 'tfpipeline-rg'
STORAGEACCOUNTNAME: 'tfpipelinesa'
STORAGEACCOUNTCONTAINERNAME: 'terraform'
TFSTATEFILENAME: 'TF-LEASE/BreakLease.tfstate'
jobs:
build-and-deploy:
runs-on: windows-latest
environment: dev
steps:
- name: 'Azure CLI Login'
uses: azure/login@v1
with:
creds: '${{ secrets.AZURE_CREDENTIALS }}'
- name: 'Break TF State'
run: |
az --version
az account set --subscription ${{ env.SUBSCRIPTIONID }}
az account show
$i = az group exists -n ${{ env.RGNAME }}
if ($i -eq "true") {
echo "#####################################################"
echo "Resource Group ${{ env.RGNAME }} exists!!!"
echo "#####################################################"
$j = az storage account check-name --name ${{ env.STORAGEACCOUNTNAME }} --query "reason" --out tsv
if ($j -eq "AlreadyExists") {
echo "#####################################################"
echo "Storage Account ${{ env.STORAGEACCOUNTNAME }} exists!!!"
echo "#####################################################"
$k = az storage container exists --account-name ${{ env.STORAGEACCOUNTNAME }} --account-key $(az storage account keys list -g ${{ env.RGNAME }} -n ${{ env.STORAGEACCOUNTNAME }} --query [0].value -o tsv) --name ${{ env.STORAGEACCOUNTCONTAINERNAME }} --query "exists" --out tsv
if ($k -eq "true") {
echo "#####################################################"
echo "Storage Account Container ${{ env.STORAGEACCOUNTCONTAINERNAME }} exists!!!"
echo "#####################################################"
$l = az storage blob exists --account-name ${{ env.STORAGEACCOUNTNAME }} --account-key $(az storage account keys list -g ${{ env.RGNAME }} -n ${{ env.STORAGEACCOUNTNAME }} --query [0].value -o tsv) --container-name ${{ env.STORAGEACCOUNTCONTAINERNAME }} --name ${{ env.TFSTATEFILENAME }} --query "exists" --out tsv
if ($l -eq "true") {
echo "#####################################################"
echo "Terraform State Blob ${{ env.TFSTATEFILENAME }} exists!!!"
echo "#####################################################"
az storage blob lease break --account-name ${{ env.STORAGEACCOUNTNAME }} --account-key $(az storage account keys list -g ${{ env.RGNAME }} -n ${{ env.STORAGEACCOUNTNAME }} --query [0].value -o tsv) --container-name ${{ env.STORAGEACCOUNTCONTAINERNAME }} --blob-name ${{ env.TFSTATEFILENAME }}
}
else {
echo "#####################################################"
echo "Terraform State Blob ${{ env.TFSTATEFILENAME }} DOES NOT EXISTS in ${{ env.STORAGEACCOUNTCONTAINERNAME }}"
echo "#####################################################"
exit 1
}
}
else {
echo "#####################################################"
echo "Storage Account Container ${{ env.STORAGEACCOUNTCONTAINERNAME }} DOES NOT EXISTS in STORAGE ACCOUNT ${{ env.STORAGEACCOUNTNAME }}!!!"
echo "#####################################################"
exit 1
}
}
else {
echo "#####################################################"
echo "Storage Account ${{ env.STORAGEACCOUNTNAME }} DOES NOT EXISTS!!!"
echo "#####################################################"
exit 1
}
}
else {
echo "#####################################################"
echo "Resource Group ${{ env.RGNAME }} DOES NOT EXISTS!!!"
echo "#####################################################"
exit 1
}
Enter fullscreen mode
Exit fullscreen mode
Now, let me explain each part of GitHub Actions YAML Workflow for better understanding.
BELOW FOLLOWS WORKFLOW TRIGGER CODE SNIPPET:-
on: [workflow_dispatch]
Enter fullscreen mode
Exit fullscreen mode
This indicates that GitHub Actions Workflow needs to be triggered manually.
BELOW FOLLOWS WORKFLOW VARIABLES CODE SNIPPET:-
env:
SUBSCRIPTIONID: '210e66cb-55cf-424e-8daa-6cad804ab604'
RGNAME: 'tfpipeline-rg'
STORAGEACCOUNTNAME: 'tfpipelinesa'
STORAGEACCOUNTCONTAINERNAME: 'terraform'
TFSTATEFILENAME: 'TF-LEASE/BreakLease.tfstate'
Enter fullscreen mode
Exit fullscreen mode
NOTE:-
Please feel free to change the values of the variables.
The entire workflow is build using variables. No Values are Hardcoded.
In GitHub Actions, the variables are designed to work in such a way that the Declared Variables are Mapped to Environmental Variables .
BELOW FOLLOWS RUNNER AND ENVIRONMENT CODE SNIPPET:-
runs-on: windows-latest
environment: dev
Enter fullscreen mode
Exit fullscreen mode
NOTE:-
RUNNER in GitHub Actions = BUILD AGENT in Azure DevOps.
ENVIRONMENT = Workflow Environment where Approval Gate is Configured.
BELOW FOLLOWS AZURE CLI LOGIN CODE SNIPPET USING GITHUB ACTIONS FOR AZURE CLI:-
- name: 'Azure CLI Login'
uses: azure/login@v1
with:
creds: '${{ secrets.AZURE_CREDENTIALS }}'
Enter fullscreen mode
Exit fullscreen mode
BELOW FOLLOWS BREAK TERRAFORM STATE LEASE CODE SNIPPET :-
- name: 'Break TF State'
run: |
az --version
az account set --subscription ${{ env.SUBSCRIPTIONID }}
az account show
$i = az group exists -n ${{ env.RGNAME }}
if ($i -eq "true") {
echo "#####################################################"
echo "Resource Group ${{ env.RGNAME }} exists!!!"
echo "#####################################################"
$j = az storage account check-name --name ${{ env.STORAGEACCOUNTNAME }} --query "reason" --out tsv
if ($j -eq "AlreadyExists") {
echo "#####################################################"
echo "Storage Account ${{ env.STORAGEACCOUNTNAME }} exists!!!"
echo "#####################################################"
$k = az storage container exists --account-name ${{ env.STORAGEACCOUNTNAME }} --account-key $(az storage account keys list -g ${{ env.RGNAME }} -n ${{ env.STORAGEACCOUNTNAME }} --query [0].value -o tsv) --name ${{ env.STORAGEACCOUNTCONTAINERNAME }} --query "exists" --out tsv
if ($k -eq "true") {
echo "#####################################################"
echo "Storage Account Container ${{ env.STORAGEACCOUNTCONTAINERNAME }} exists!!!"
echo "#####################################################"
$l = az storage blob exists --account-name ${{ env.STORAGEACCOUNTNAME }} --account-key $(az storage account keys list -g ${{ env.RGNAME }} -n ${{ env.STORAGEACCOUNTNAME }} --query [0].value -o tsv) --container-name ${{ env.STORAGEACCOUNTCONTAINERNAME }} --name ${{ env.TFSTATEFILENAME }} --query "exists" --out tsv
if ($l -eq "true") {
echo "#####################################################"
echo "Terraform State Blob ${{ env.TFSTATEFILENAME }} exists!!!"
echo "#####################################################"
az storage blob lease break --account-name ${{ env.STORAGEACCOUNTNAME }} --account-key $(az storage account keys list -g ${{ env.RGNAME }} -n ${{ env.STORAGEACCOUNTNAME }} --query [0].value -o tsv) --container-name ${{ env.STORAGEACCOUNTCONTAINERNAME }} --blob-name ${{ env.TFSTATEFILENAME }}
}
else {
echo "#####################################################"
echo "Terraform State Blob ${{ env.TFSTATEFILENAME }} DOES NOT EXISTS in ${{ env.STORAGEACCOUNTCONTAINERNAME }}"
echo "#####################################################"
exit 1
}
}
else {
echo "#####################################################"
echo "Storage Account Container ${{ env.STORAGEACCOUNTCONTAINERNAME }} DOES NOT EXISTS in STORAGE ACCOUNT ${{ env.STORAGEACCOUNTNAME }}!!!"
echo "#####################################################"
exit 1
}
}
else {
echo "#####################################################"
echo "Storage Account ${{ env.STORAGEACCOUNTNAME }} DOES NOT EXISTS!!!"
echo "#####################################################"
exit 1
}
}
else {
echo "#####################################################"
echo "Resource Group ${{ env.RGNAME }} DOES NOT EXISTS!!!"
echo "#####################################################"
exit 1
}
Enter fullscreen mode
Exit fullscreen mode
NOTE:-
Az CLI commands are used with RUN action, without the need of GitHub Actions for Azure CLI (azure/login@v1)
OBJECTIVE OF TERRAFORM CODE SNIPPET:-
Create a Resource Group and User Assigned System Managed Identity.
The Purpose of the Terraform Code Snippet is to Reproduce the Issue, by Locking Terraform State File .
terraform {
required_version = ">= 1.2.3"
backend "azurerm" {
resource_group_name = "tfpipeline-rg"
storage_account_name = "tfpipelinesa"
container_name = "terraform"
key = "TF-LEASE/BreakLease.tfstate"
}
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.2"
}
}
}
provider "azurerm" {
features {}
skip_provider_registration = true
}
Enter fullscreen mode
Exit fullscreen mode
## Azure Resource Group:-
resource "azurerm_resource_group" "rg" {
name = var.rg-name
location = var.rg-location
}
## Azure User Assigned Managed Identities:-
resource "azurerm_user_assigned_identity" "az-usr-mid" {
name = var.usr-mid-name
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
depends_on = [azurerm_resource_group.rg]
}
Enter fullscreen mode
Exit fullscreen mode
TERRAFORM (variables.tf):-
variable "rg-name" {
type = string
description = "Name of the Resource Group"
}
variable "rg-location" {
type = string
description = "Resource Group Location"
}
variable "usr-mid-name" {
type = string
description = "Name of the User Assigned Managed Identity"
}
Enter fullscreen mode
Exit fullscreen mode
TERRAFORM (usrmid.tfvars):-
rg-name = "AMTest100"
rg-location = "West Europe"
usr-mid-name = "AMUSRMID100"
Enter fullscreen mode
Exit fullscreen mode
HOW TO LOCK TERRAFORM STATE FILE:-
Run the Terraform Apply Command manually in your local System as mentioned below:-
terraform apply --var-file="usrmid.tfvars"
Enter fullscreen mode
Exit fullscreen mode
When Prompted for "Yes", Press Control + C to terminate the Execution.
Next time, when you Re-Execute the Command, It will Inform the User that Terraform State File is in Locked State.
NOW ITS TIME TO TEST !!!...
TEST CASE #1: TERRAFORM BLOB STATE == AVAILABLE:-
DESIRED OUTPUT: PIPELINE WILL FAIL WITH EXIT CODE 1.
TERRAFORM BLOB STATE:-
WORKFLOW RUN:-
TEST CASE #2: TERRAFORM BLOB STATE == LEASED:-
DESIRED OUTPUT: PIPELINE EXECUTES SUCCESSFULLY BREAKING TERRAFORM BLOB STATE LEASE.
TERRAFORM BLOB STATE:-
WORKFLOW RUN:-
TEST CASE #3: TERRAFORM BLOB STATE == BROKEN:-
DESIRED OUTPUT: PIPELINE EXECUTES SUCCESSFULLY WITHOUT ANY ALTERATIONS TO THE CURRENT STATE.
TERRAFORM BLOB STATE:-
WORKFLOW RUN:-
OVERALL GITHUB ACTIONS WORKFLOW RUNS:-
Hope You Enjoyed the Session!!!
Stay Safe | Keep Learning | Spread Knowledge
Top comments (0)