Skip to main content

Command Palette

Search for a command to run...

Terraform + CI/CD: Provision AWS Infrastructure Automatically

Terraform + CI/CD: Provision AWS Infrastructure Automatically

Published
β€’6 min read
L

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:


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.com
    
  • Audience:

      sts.amazonaws.com
    
  • GitHub organization

      Enter the owner name
    
  • GitHub 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.