Terraform + CI/CD: Provision AWS Infrastructure Automatically
Terraform + CI/CD: Provision AWS Infrastructure Automatically
I build and deploy cloud-native applications using AWS and DevOps practices. I share practical tutorials on CI/CD pipelines, serverless architectures, and real project learnings, and Iβm exploring MLOps.
Introduction
In the previous blogs, we learned how to:
β
Host a static website using Amazon S3 and CloudFront
β
Automate application deployments using GitHub Actions
β
Provision infrastructure using Terraform
However, there is still one major gap.
In real-world DevOps and SRE environments, engineers do not run Terraform from their local machines.
Infrastructure deployments are automated through CI/CD pipelines.
Why?
Because automation provides visibility, control, repeatability, and team collaboration.
π Security Evolution β From Access Keys to OIDC
In earlier setups, we authenticated to AWS inside pipelines using long-lived access keys stored as GitHub Secrets.
This works, but in production it introduces risks:
β Secrets must be rotated
β Possible leakage
β Harder auditing
β Permanent credentials increase blast radius
Modern cloud teams instead use OIDC (OpenID Connect) to assume IAM roles dynamically.
With OIDC:
β
No long-term credentials
β
Short-lived temporary tokens
β
Automatic rotation
β
Better auditability
β
Industry-standard secure authentication
This is the approach used by mature DevOps organizations.
And in this blog, we are upgrading to this model π
In this blog, we will take the next step and build a CI/CD pipeline that automatically runs Terraform whenever code changes are pushed to GitHub.
This means:
π Git push β Infrastructure updated automatically.
Welcome to production-grade Infrastructure as Code automation π
This Blog Continues From
Blog-1: CI/CD Basics with GitHub Actions
Blog-2: Static Website Hosting using S3 + CloudFront
Blog-3: Application Deployment via GitHub Actions
Blog-4: Provisioning AWS Infrastructure using Terraform
What You Will Learn
In this blog, you will learn:
β
Why Terraform should run in CI/CD
β
Risks of running Terraform locally
β
How GitHub Actions can automate infrastructure
β
Terraform init, plan, apply inside pipeline
β
Secure authentication using GitHub Secrets
β
A real production-style workflow
Why Run Terraform in CI/CD Instead of Locally?
Many beginners run Terraform from laptops.
This works for practice β but fails in teams and production.
Problems with Local Terraform Execution
β No visibility for team
β No approval process
β No audit history
β Different people may run different versions
β Risk of accidental damage
β Hard to standardize
Benefits of CI/CD Automation
β
Centralized execution
β
Full history of deployments
β
Easy collaboration
β
Consistent process
β
Safer changes
β
Production-ready workflow
This is how companies manage infrastructure.
CI/CD Architecture Overview
Flow Explanation

Developer pushes Terraform code to GitHub
β
GitHub Actions pipeline starts
β
Terraform initializes providers
β
Terraform plans infrastructure changes
β
Terraform applies changes automatically
β
AWS infrastructure is created or updated
Prerequisites
Before starting, ensure you have:
Terraform code repository (from Blog-4)
AWS account
We are using the Role base access to AWS for security Production base
GitHub repository
Code
Step 1 β Configure OIDC Authentication Between GitHub and AWS
We must never store AWS credentials in code or long-lived secrets.
Instead, we use OIDC (OpenID Connect) so that GitHub Actions can assume an IAM role dynamically.
This provides:
β
short-lived credentials
β
automatic rotation
β
better auditability
β
zero secret storage
β
production-grade security
This is how modern DevOps teams authenticate.
βοΈ Configure OIDC for AWS
1οΈβ£ Create Identity Provider in AWS
In Amazon Web Services:
Go to IAM β Identity Providers
Provider type: OpenID Connect
Provider URL:
https://token.actions.githubusercontent.comAudience:
sts.amazonaws.comGitHub organization
Enter the owner nameGitHub repository - optional
Give the repo name
Next give the permission
Give the name of the role
Click on create
Steps
Go to:
Repository β Settings β Secrets and variables β Actions
Click New repository secret.
Add:
ACCOUNT_NUMBER
AWS_REGION
π What Changed vs Old Method?
Before:
Access key + secret key in GitHub
Now:
GitHub β OIDC β Assume Role β Temporary credentials
This is the industry standard.
These will be securely injected during the pipeline run.
Step 2: Create GitHub Actions Workflow for Terraform
Create this file in your repository:
.github/workflows/terraform.yml
Production-Ready Workflow Example
name: Iac CI/CD Pipeline
on:
push:
branches:
- main
paths:
- 's3-cdn-modules/**'
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
concurrency:
group: terraform
cancel-in-progress: false
jobs:
terraform:
runs-on: ubuntu-latest
outputs:
s3_bucket_name: ${{ steps.get_bucket.outputs.s3_bucket_name }}
cloudfront_domain_name: ${{ steps.get_cf.outputs.cloudfront_domain_name }}
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: latest
terraform_wrapper: false
- name: configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ secrets.ACCOUNT_NUMBER }}:role/github-action-cicd-role
aws-region: ${{ secrets.AWS_REGION }}
- name: Initialize Terraform
run: terraform -chdir=s3-cdn-modules init
- name: Validate Terraform configuration
run: terraform -chdir=s3-cdn-modules validate
- name: Terraform Format Check
run: terraform -chdir=s3-cdn-modules fmt -check
- name: Plan Terraform changes
run: terraform -chdir=s3-cdn-modules plan -out=tfplan
- name: Apply Terraform changes
if: github.ref == 'refs/heads/main'
run: terraform -chdir=s3-cdn-modules apply -auto-approve tfplan
- name: get bucket output
id: get_bucket
run: |
S3_Bucket_NAME=$(terraform -chdir=s3-cdn-modules output -raw s3_bucket_name)
echo s3_bucket_name=$S3_Bucket_NAME >> $GITHUB_OUTPUT
- name: get cloudfront output
id: get_cf
run: |
CLOUDFRONT_DOMAIN_NAME=$(terraform -chdir=s3-cdn-modules output -raw cloudfront_domain_name)
echo cloudfront_domain_name=$CLOUDFRONT_DOMAIN_NAME >> $GITHUB_OUTPUT
Deploy_to_s3:
runs-on: ubuntu-latest
needs: terraform
environment: production
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ secrets.ACCOUNT_NUMBER }}:role/github-action-cicd-role
aws-region: ${{ secrets.AWS_REGION }}
- name: Sync files to S3
run: |
aws s3 sync . s3://${{ needs.terraform.outputs.s3_bucket_name }} \
--exclude "*" \
--include "index.html" \
--include "style.css" \
--include "script.js" \
--delete
- name: Invalidate CloudFront cache
run: |
aws cloudfront create-invalidation \
--distribution-id $(terraform -chdir=s3-cdn-modules output -raw cloudfront_distribution_id) \
--paths "/*"
Workflow Explanation
Checkout Code
Downloads Terraform configuration into the runner.
Setup Terraform
Installs Terraform binary.
Configure AWS Credentials
Authenticates securely using GitHub Secrets.
Terraform Init
Downloads required providers and prepares backend.
Terraform Plan
Shows what changes will happen.
Terraform Apply
Creates or updates AWS resources.
Deploy to AWS S3
File will copy to S3 bucket
What Happens After Push?
Once code is pushed to the main branch:
β The CI/CD pipeline triggers automatically in GitHub Actions
β Terraform initializes and validates the configuration
β Infrastructure changes are planned and applied
β The pipeline becomes the single, trusted path for modifying cloud resources
You now have fully automated, production-style infrastructure delivery π
β Deployment Result
After a successful run:
β
S3 bucket is created or updated
β
CloudFront distribution is configured
β
IAM permissions are applied
β
Infrastructure is provisioned entirely from code
β
No manual console work is required

β Conclusion
In this blog, we moved from manual or local Terraform execution to a production-grade automation model.
By integrating Terraform with CI/CD and using a remote backend, deployments become:
β Consistent
β Secure
β Auditable
β Collaborative
β Repeatable
Infrastructure is now delivered through Git workflows β the same way modern engineering teams ship applications.
This significantly reduces human error and increases reliability at scale.
π§Ή Clean-Up Best Practice
After testing or learning, always remember:
π Destroy unused resources to avoid unnecessary cost.
terraform destroy
Production teams automate cleanup for temporary environments to maintain financial control.
π Whatβs Next?
So far, we automated infrastructure for a single environment.
In real systems, organizations manage:
Development
Staging
Production
Each environment has different configurations, approvals, and safety controls.
In the next blog, we will design a multi-environment Terraform strategy used in real enterprises.