This reusable GitHub Actions workflow is invoked by the Detect Changed main.tf Files workflow. It processes one main.tf file at a time, performing:
- Terraform environment setup and validation
- Security scanning and cost estimation
- Conditional
plan, apply or destroy actions based on CI mode
- Slack notifications for deployment status
This modular design ensures targeted, environment-specific deployments to Azure.
Purpose
- Isolate processing for each changed
main.tf file
- Automate complete Terraform lifecycle (format, validate, plan, apply/destroy)
- Integrate security and cost checks into the deployment pipeline
- Support multiple environments with isolated state management
- Provide deployment notifications via Slack integration
Workflow Jobs
(1) Trigger Conditions
| Aspect |
Description |
| WHAT |
Reusable workflow triggered via workflow_call from the detect workflow |
| HOW |
Receives tfvars_file input parameter and inherits secrets from calling repository |
| WHY |
Enables modular, isolated processing of individual infrastructure files |
| RESULT |
Complete Terraform validation and deployment pipeline for a single file |
(2) Azure Login with OIDC
| Aspect |
Description |
| WHAT |
Authenticates to Azure using OpenID Connect without storing static credentials |
| HOW |
Uses azure/login@v1 with client-id, tenant-id, and subscription-id from secrets |
| WHY |
Provides secure, credential-less authentication to Azure services |
| RESULT |
Authenticated session for Azure operations throughout the workflow |
(3) Checkout Repository
| Aspect |
Description |
| WHAT |
Downloads repository code with complete git history |
| HOW |
Uses actions/checkout@v4 with fetch-depth: 0 and ref: ${{ github.ref }} |
| WHY |
Provides access to Terraform files and modules for processing |
| RESULT |
Full repository context available for Terraform operations |
(4) Calculate Working File and Directory
| Aspect |
Description |
| WHAT |
Determines file paths and working directory for the target main.tf file |
| HOW |
Uses custom action calculate-file with tfvars_file input parameter |
| WHY |
Establishes context for subsequent Terraform operations |
| RESULT |
Outputs file_name, file_path, and dir for use in following steps |
(5) Read CI Mode
| Aspect |
Description |
| WHAT |
Determines deployment mode (plan, apply, destroy) from configuration |
| HOW |
Uses custom action read-ci-mode to parse mode from directory context |
| WHY |
Enables conditional execution of apply/destroy steps based on intended operation |
| RESULT |
Sets ci_mode output for controlling downstream deployment actions |
Each main.tf has a ci_mode block that is read by the workflow and used later to execute different actions based on plan, apply, ordestroy.
# Uncomment for CI/CD pipeline control
# ci_mode = "apply"
# --------------------------------
| Aspect |
Description |
| WHAT |
Configures Git authentication for accessing private Terraform modules |
| HOW |
Uses custom action with AZURE_DEPLOY_TO_MODULE_RO token for GitHub access |
| WHY |
Enables Terraform to download private modules during initialization |
| RESULT |
Git configured for secure access to private module repositories |
| Aspect |
Description |
| WHAT |
Caches Terraform provider binaries to improve execution speed |
| HOW |
Uses custom action cache-terraform-providers to store/retrieve provider downloads |
| WHY |
Reduces workflow execution time by avoiding repeated provider downloads |
| RESULT |
Faster Terraform initialization and reduced network usage |
| Aspect |
Description |
| WHAT |
Installs Terraform CLI in the workflow environment |
| HOW |
Uses HashiCorp's official setup-terraform@v3 action |
| WHY |
Provides Terraform binary required for all subsequent operations |
| RESULT |
Terraform CLI available for format, validate, plan, apply operations |
(9) Ensure TF Lockfile Exists
| Aspect |
Description |
| WHAT |
Ensures Terraform lockfile exists for provider version consistency |
| HOW |
Uses custom action ensure-tf-lockfile-exists with target directory |
| WHY |
Guarantees consistent provider versions across all workflow runs |
| RESULT |
Provider versions locked for reproducible infrastructure deployments |
| Aspect |
Description |
| WHAT |
Extracts key configuration values from the target Terraform file |
| HOW |
Uses custom action extract-tfvars to parse app_name, env, location variables |
| WHY |
Provides configuration context for backend setup and resource naming |
| RESULT |
Configuration variables available for subsequent workflow steps |
| Aspect |
Description |
| WHAT |
Generates Terraform backend configuration for remote state storage |
| HOW |
Uses custom action with Azure Blob Storage settings and extracted variables |
| WHY |
Ensures isolated state management per environment/application |
| RESULT |
Backend configuration enabling shared state storage in Azure |
| Aspect |
Description |
| WHAT |
Enforces consistent Terraform code formatting standards |
| HOW |
Uses custom action terraform-fmt to run terraform fmt on target directory |
| WHY |
Maintains code quality and consistency across infrastructure definitions |
| RESULT |
Formatted Terraform code meeting established style guidelines |
(13) Run TFLint
| Aspect |
Description |
| WHAT |
Performs static analysis and linting of Terraform code |
| HOW |
Uses custom action tflint to analyze code for best practices and errors |
| WHY |
Identifies potential issues and enforces Terraform best practices |
| RESULT |
Code quality validation with early detection of configuration problems |
| Aspect |
Description |
| WHAT |
Validates Terraform configuration syntax and internal consistency |
| HOW |
Uses custom action terraform-validate with GitHub token for module access |
| WHY |
Ensures configuration is syntactically correct and logically consistent |
| RESULT |
Verified Terraform configuration ready for planning |
| Aspect |
Description |
| WHAT |
Generates execution plan showing proposed infrastructure changes |
| HOW |
Uses custom action terraform-plan to create detailed change preview |
| WHY |
Provides visibility into proposed changes before applying them |
| RESULT |
Execution plan available for review and subsequent apply operations |
(16) Run Checkov Security Scan
| Aspect |
Description |
| WHAT |
Performs security analysis to detect misconfigurations and vulnerabilities |
| HOW |
Uses custom action checkov-security-scan to scan infrastructure code |
| WHY |
Identifies security risks before infrastructure deployment |
| RESULT |
Security validation ensuring compliance with best practices |
| Aspect |
Description |
| WHAT |
Estimates monthly costs of planned infrastructure changes |
| HOW |
Uses custom action terraform-cost-estimate with Infracost API integration |
| WHY |
Provides cost visibility before deploying infrastructure changes |
| RESULT |
Cost analysis helping with budget planning and cost optimization |
| Aspect |
Description |
| WHAT |
Applies Terraform changes to Azure infrastructure when CI mode is 'apply' |
| HOW |
Uses custom action terraform-apply conditionally based on ci_mode output |
| WHY |
Enables controlled deployment of infrastructure changes |
| RESULT |
Infrastructure deployed to Azure according to Terraform configuration |
| Aspect |
Description |
| WHAT |
Destroys Terraform-managed infrastructure when CI mode is 'destroy' |
| HOW |
Uses custom action terraform-destroy conditionally based on ci_mode output |
| WHY |
Enables controlled teardown of infrastructure for cleanup or testing |
| RESULT |
Infrastructure removed from Azure as specified by Terraform state |
(20) Slack Notifications
| Aspect |
Description |
| WHAT |
Sends deployment status notifications to Slack channels |
| HOW |
Uses custom action slack-notify for success, failure, and cancelled states |
| WHY |
Provides real-time visibility into deployment status for team awareness |
| RESULT |
Team notifications with deployment context and status information |
Concurrency Control
| Aspect |
Description |
| WHAT |
Concurrency managed by calling workflow, not at reusable workflow level |
| HOW |
Each matrix job processes different files, avoiding conflicts between parallel executions |
| WHY |
Prevents deadlocks between parent (caller) and child (reusable) workflow concurrency groups |
| RESULT |
Safe parallel processing with concurrency control handled upstream |
Security Considerations
- OIDC Authentication: Secure, credential-less authentication to Azure using OpenID Connect
- Scoped Secrets: Only required secrets passed from calling repository via
secrets: inherit
- Private Module Access:
AZURE_DEPLOY_TO_MODULE_RO token scoped for module repository access
- State Isolation: Backend configuration ensures environment-specific state separation
- Cross-Repository Actions: All actions referenced from centralized
.github repository with full paths
Key Components
| Component |
Description |
calculate-file |
Determines file paths and working directory context |
read-ci-mode |
Parses deployment mode (plan/apply/destroy) from configuration |
configure-git-private-modules |
Configures Git authentication for private Terraform modules |
cache-terraform-providers |
Caches Terraform provider binaries for performance |
ensure-tf-lockfile-exists |
Ensures provider lockfile exists for version consistency |
extract-tfvars |
Extracts configuration variables from Terraform files |
create-terraform-backend-config-file |
Generates backend config for Azure Blob Storage state |
terraform-fmt |
Enforces Terraform code formatting standards |
tflint |
Performs static analysis and linting |
terraform-validate |
Validates Terraform syntax and configuration |
terraform-plan |
Generates execution plan for infrastructure changes |
checkov-security-scan |
Runs security analysis using Checkov |
terraform-cost-estimate |
Estimates costs using Infracost integration |
terraform-apply |
Applies infrastructure changes to Azure |
terraform-destroy |
Destroys Terraform-managed infrastructure |
slack-notify |
Sends deployment notifications to Slack channels |