GitHub Actions to AWS OIDC Integration Setup
Securely deploy to AWS from GitHub Actions using OpenID Connect (OIDC), eliminating the need for long-lived AWS credentials.
✅ Step 1: Create IAM Role for GitHub (via AWS CLI)
Run the following AWS CLI commands to manually bootstrap the IAM role and OIDC provider:
aws iam create-open-id-connect-provider \ --url https://token.actions.githubusercontent.com \ --client-id-list sts.amazonaws.com \ --thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1 aws iam create-role \ --role-name github-oidc-role \ --assume-role-policy-document file://trust-policy.json aws iam attach-role-policy \ --role-name github-oidc-role \ --policy-arn arn:aws:iam::aws:policy/AdministratorAccess Note: Replace
AdministratorAccesswith a least-privilege policy if possible.
✅ Step 2: Folder Structure (Bootstrap Setup)
Minimal layout for bootstrapping (no remote backend):
my-terraform-project/ ├── .github/ │ └── workflows/ │ └── deploy.yml ├── main.tf ├── .gitignore └── README.md Example
.gitignore:.terraform/
Note: This is a bootstrap setup and does not use a remote backend. For production, it's recommended to configure a remote backend such as an S3 bucket.
✅ Step 3: Minimal Terraform Configuration (main.tf)
This main.tf contains only a minimal resource for testing. Terraform is not responsible for creating the IAM role in this bootstrap scenario.
provider "aws" { region = var.aws_region } variable "aws_region" { description = "AWS region" default = "ap-southeast-2" } variable "aws_account_id" { description = "AWS Account ID" } variable "github_owner" { description = "GitHub organization or username" } variable "repo_name" { description = "GitHub repository name" } # Minimal test resource data "aws_caller_identity" "current" {} resource "aws_s3_bucket" "test_bucket" { bucket = "test-bucket-${data.aws_caller_identity.current.account_id}" acl = "private" tags = { Name = "Test Bucket" Env = "Bootstrap" } } ✅ Step 4: Configure GitHub Secrets and Variables
Go to Repo → Settings → Secrets and variables → Actions, and set:
- Secrets:
-
AWS_ACCOUNT_ID: Your 12-digit AWS account ID (e.g.123456789012)
Variables:
-
AWS_REGION: AWS region to deploy resources (e.g.ap-southeast-2) -
TERRAFORM_VERSION: Terraform version to use (e.g.1.12.1) -
OIDC_ROLE_NAME: IAM role name that GitHub Actions will assume (must match the name inmain.tf, e.g.github-oidc-role)
✅ Step 5: GitHub Actions Workflows
🔹 plan.yml – for Terraform Plan (on PR)
name: Terraform Plan permissions: id-token: write contents: read concurrency: group: terraform-plan-${{ github.head_ref || github.ref }} cancel-in-progress: true on: pull_request: branches: [main] env: AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }} AWS_REGION: ${{ vars.AWS_REGION }} TERRAFORM_VERSION: ${{ vars.TERRAFORM_VERSION }} OIDC_ROLE_NAME: ${{ vars.OIDC_ROLE_NAME }} jobs: terraform: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Configure AWS credentials via OIDC uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/${{ env.OIDC_ROLE_NAME }} aws-region: ${{ env.AWS_REGION }} - name: Setup Terraform uses: hashicorp/setup-terraform@v3 with: terraform_version: ${{ env.TERRAFORM_VERSION }} - name: Terraform Init run: terraform init - name: Terraform Format run: terraform fmt -check continue-on-error: true - name: Terraform Validate run: terraform validate - name: Terraform Plan run: terraform plan 🔹 deploy.yml – for Terraform Apply
name: Terraform Apply permissions: id-token: write contents: write concurrency: group: terraform-apply-${{ github.head_ref || github.ref }} cancel-in-progress: true on: push: branches: [main] env: AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }} AWS_REGION: ${{ vars.AWS_REGION }} TERRAFORM_VERSION: ${{ vars.TERRAFORM_VERSION }} OIDC_ROLE_NAME: ${{ vars.OIDC_ROLE_NAME }} jobs: terraform: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Configure AWS credentials via OIDC uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/${{ env.OIDC_ROLE_NAME }} aws-region: ${{ env.AWS_REGION }} - name: Setup Terraform uses: hashicorp/setup-terraform@v3 with: terraform_version: ${{ env.TERRAFORM_VERSION }} - name: Terraform Init run: terraform init - name: Terraform Format run: terraform fmt -check continue-on-error: true - name: Terraform Validate run: terraform validate - name: Terraform Plan run: terraform plan - name: Terraform Apply id: apply run: terraform apply -auto-approve continue-on-error: true - name: Commit and force push if changed if: always() run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" if ! git diff --quiet || ! git diff --staged --quiet; then git add . git commit -m "Update terraform state and lock file" git push --force fi - name: Fail if Terraform Apply errored if: steps.apply.outcome == 'failure' run: exit 1 ✅ Summary
| Component | Status | Notes |
|---|---|---|
| OIDC Provider | ✅ | One-time setup per AWS account |
| IAM Role | ✅ | Defined inline in main.tf |
| Policy Permissions | ✅ | Start with AdministratorAccess, scope down later |
| GitHub Workflow | ✅ | Uses configure-aws-credentials for OIDC login |
| No Secrets Required | ✅ | Secure, short-lived tokens – no static credentials |
You’re now ready to securely run terraform apply from GitHub Actions using short-lived OIDC credentials with a minimal bootstrap setup.
Top comments (0)