Securely Deploy to AWS

With OIDC and IAM Roles and Github Action
https://token.actions.githubusercontent.com
sts.amazonaws.com
Create a bucket
Create an S3 bucket to store our Terraform states.
Create your AWS IAM Role and policies
For permission set create poliicy
1. Create Policy for access bucket for state file
Copy{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::YOUR_BUCKET/*",
"arn:aws:s3:::YOUR_BUCKET"
]
}
]
}
Create on more policy with requied permission to iam role to create infra on aws
Add this both policy to role
Step 2: Select trusted entity
Copy{
"Version": "2008-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::YOUR_ACCOUNT_NUMBER:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:YOUR_GITHUB_USERNAME/YOUR_REPO_NAME:*"
}
}
}
]
}
You will need to modify:
YOUR_ACCOUNT_NUMBER (AWS)
YOUR_GITHUB_USERNAME
YOUR_REPO_NAME
Go to ssh to get “YOUR_GITHUB_USERNAME/YOUR_REPO_NAME”
Terraform code
create your terraform code and also provider block
Provider Block
terraform {
required_version = ">= 1.5.3, < 1.10.0" # Supports Terraform v1.5.3 and higher, but under 1.10.0
required_providers {
aws = {
source = "hashicorp/aws"
#version = ">= 5.73.0" # Use AWS provider version 5.73.0 or newer
version = ">= 5.0"
}
}
# Adding Backend as S3 for Remote State Storage
backend "s3" {
bucket = "BUCKETNAME"
key = "terraform/BUCKET-FOLDER/terraform.tfstate"
region = "ap-south-1"
# For State Locking
dynamodb_table = "TABLE-NAME"
}
}
# Provider Block
provider "aws" {
region = var.aws_region
#profile = "default"
}
GitHub Secrets
Before creating the GitHub Actions, let’s add some Secrets inside our repository.
Left side » Secrets » Actions
Click on: New repository secret
secreat for aws role
- AWS_ROLE : ARN OF YOUR ROLE
GitHub Action
Let’s create our GitHub Action, for this, we need to create a file in our GitHub repository :
.github/workflows/deploy.yml
Copyname: "Terraform Action" # Name of the workflow
# Trigger the workflow
on:
push:
paths:
- 'FOLDER/**' # Trigger only if files in FOLDER folder are modified
branches:
[ "BRANCH-NAME" ]
# "clem-devops/v1.1" # Trigger only on pushes to the devops v1.1 branch
pull_request:
paths:
- 'FOLDER/**' # Trigger only if PR modifies files in FOLDER folder
# Set permissions required for the workflow
permissions:
id-token: write # Required for AWS OIDC connection
contents: read # Required to checkout the repository
pull-requests: write # Required to comment on PRs
# Environment variables shared across the workflow
env:
TF_LOG: INFO # Enable Terraform logging
AWS_REGION: ap-south-1 # Specify AWS region
jobs:
deploy:
runs-on: ubuntu-latest # Run the job on the latest Ubuntu environment
defaults:
run:
shell: bash
working-directory: ./FOLDER # Change working directory for FOLDER module
steps:
- name: Checkout repository
uses: actions/checkout@v3 # Check out the code from the repository
- name: Configure AWS credentials using OIDC
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: ${{ secrets.AWS_ROLE_DEV }} # Role to assume for AWS access
aws-region: ${{ env.AWS_REGION }} # AWS region
role-session-name: GitHub-OIDC-TERRAFORM # Session name for identification
- name: Clear Terraform Cache
run: |
rm -rf ~/.terraform.d/plugin-cache # Clear Terraform plugin cache
rm -rf .terraform
rm -f .terraform.lock.hcl
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.5.3 # Specify Terraform version
- name: Terraform Format Check
id: fmt
run: terraform fmt -check # Check if Terraform files are formatted
continue-on-error: true # Continue even if formatting errors are found
- name: Terraform Init
id: init
run: terraform init # Initialize Terraform configuration
- name: Terraform Validate
id: validate
run: terraform validate -no-color # Validate Terraform files
- name: Terraform Plan
id: plan
run: terraform plan -no-color # Generate execution plan
if: github.event_name == 'pull_request'
continue-on-error: true
- uses: actions/github-script@v6
if: github.event_name == 'pull_request'
env:
PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
#### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
#### Terraform Validation 🤖\`${{ steps.validate.outcome }}\`
<details><summary>Validation Output</summary>
\`\`\`\n
${{ steps.validate.outputs.stdout }}
\`\`\`
</details>
#### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`\n
${process.env.PLAN}
\`\`\`
</details>
*Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
- name: Terraform Plan Status
if: steps.plan.outcome == 'failure'
run: exit 1 # Fail the workflow if the plan step fails
- name: Terraform Apply
if: github.ref == 'refs/heads/BRANCH-NAME' && github.event_name == 'push'
run: terraform apply -auto-approve -input=false # Apply changes if on BRANCH-NAME branch
Subscribe to my newsletter
Read articles from Varish Ansari directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Varish Ansari
Varish Ansari
As a DevOps and Cloud Engineer, I design and manage scalable AWS cloud infrastructures, implement Infrastructure as Code using Terraform, and automate CI/CD pipelines with GitHub Actions. I also work with Docker and Docker Swarm and ECS for containerized deployments, optimize cloud security using AWS WAF, Security Hub, and GuardDuty, and enhance monitoring and observability with CloudWatch, Prometheus, and Grafana. My focus is on automation, scalability, and security to ensure efficient and resilient cloud environments