ARTICLES · 16 May 2026
How I Secured an ECS Fargate Deployment With OIDC and ECR — No Static Keys
A GitHub Actions to AWS ECS Fargate deployment walkthrough using OIDC federation, Amazon ECR, and short-lived credentials.
Originally published at hashnode.
Introduction
Most AWS CI/CD tutorials hand you a AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY and call it a day. That’s a security failure waiting to happen. Static keys get leaked in logs, committed to repos, and rotated too late.
I built a full deployment pipeline for a real DevOps challenge — GitHub Actions → ECR → ECS Fargate — with zero static credentials. Here’s exactly how.
The Problem With Static Keys
When you store AWS credentials as GitHub secrets and reference them in your pipeline, you’re creating long-lived credentials that:
Never expire unless you manually rotate them
Have blast radius if leaked
Violate AWS security best practices
Will fail a real security audit
OIDC federation eliminates this entirely.
How OIDC Federation Works
GitHub Actions requests a short-lived token from GitHub’s OIDC provider. AWS is configured to trust that provider. When the pipeline runs, AWS exchanges the GitHub token for a temporary STS credential scoped to an IAM role — valid for the duration of the job only.
No secret stored. No rotation needed. No leak vector.
Step 1 — Configure the OIDC Provider in AWS
bash
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1
Step 2 — Create the IAM Role
Trust policy (trust-policy.json):
{ “Version”: “2012-10-17”, “Statement”: [ { “Effect”: “Allow”, “Principal”: { “Federated”: “arn:aws:iam::YOUR_ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com” }, “Action”: “sts:AssumeRoleWithWebIdentity”, “Condition”: { “StringLike”: { “token.actions.githubusercontent.com:sub”: “repo:YOUR_GITHUB_ORG/YOUR_REPO:*” } } } ] }
bashaws iam create-role
--role-name github-actions-ecr-ecs
--assume-role-policy-document file://trust-policy.json Attach permissions: bashaws iam attach-role-policy
--role-name github-actions-ecr-ecs
--policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser
aws iam attach-role-policy
--role-name github-actions-ecr-ecs
--policy-arn arn:aws:iam::aws:policy/AmazonECS_FullAccess
Step 3 — GitHub Actions Pipeline yamlname: Deploy to ECS Fargate
on: push: branches: [main]
permissions: id-token: write contents: read
jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Configure AWS credentials via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::YOUR_ACCOUNT_ID:role/github-actions-ecr-ecs
aws-region: us-east-1
- name: Login to ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/myapp:$IMAGE_TAG .
docker push $ECR_REGISTRY/myapp:$IMAGE_TAG
- name: Deploy to ECS
run: |
aws ecs update-service \
--cluster my-cluster \
--service my-service \
--force-new-deployment
Key security wins:
Credentials live for job duration only — minutes, not months No secret stored in GitHub at all Repo-scoped: the role can only be assumed by your specific repository Full audit trail in CloudTrail
Live repo: github.com/EdwinJdevops/damolak-challenge
I’m Edwin Jonathan — a 17-year-old self-taught DevOps Engineer building from Lagos, Nigeria. No degree, no shortcuts — just real infrastructure, real pipelines, and real results.
Follow the journey: 🔗 GitHub: github.com/EdwinJdevops ✍️ Hashnode: edwinjonathand-devops.hashnode.dev 💼 Open to remote DevOps/Cloud roles globally