OIDC authentication for CI/CD in Terraform - Commands & Configuration
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
required_version = ">= 1.3.0"
}
provider "aws" {
region = "us-east-1"
}
resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}
resource "aws_iam_role" "ci_cd_role" {
name = "ci-cd-oidc-role"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
Federated = aws_iam_openid_connect_provider.github.arn
},
Action = "sts:AssumeRoleWithWebIdentity",
Condition = {
StringLike = {
"token.actions.githubusercontent.com:sub" = "repo:example-org/example-repo:ref:refs/heads/main"
}
}
}
]
})
}
resource "aws_iam_policy" "ci_cd_policy" {
name = "ci-cd-policy"
description = "Policy for CI/CD pipeline to access S3 bucket"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = ["s3:PutObject", "s3:GetObject"],
Resource = "arn:aws:s3:::example-bucket/*"
}
]
})
}
resource "aws_iam_role_policy_attachment" "attach_policy" {
role = aws_iam_role.ci_cd_role.name
policy_arn = aws_iam_policy.ci_cd_policy.arn
}
This Terraform file sets up OIDC authentication for a CI/CD pipeline using AWS.
terraform block defines the AWS provider and Terraform version.
aws_iam_openid_connect_provider registers GitHub's OIDC endpoint with AWS.
aws_iam_role creates a role that the CI/CD pipeline can assume using OIDC tokens from GitHub Actions, limited to the main branch of a specific repo.
aws_iam_policy defines permissions for the role, here allowing access to an S3 bucket.
aws_iam_role_policy_attachment attaches the policy to the role.
terraform init
terraform plan
terraform apply -auto-approve
-auto-approve - Automatically approves the apply without asking for confirmation.aws iam get-role --role-name ci-cd-oidc-role
--role-name - Specifies the exact IAM role to retrieve.If you remember nothing else from this pattern, remember: OIDC lets your CI/CD pipeline securely prove its identity to the cloud without storing long-term secrets.
Practice
Solution
Step 1: Understand OIDC purpose in CI/CD
OIDC provides a secure way for pipelines to prove identity without passwords.Step 2: Connect to Terraform usage
Terraform can create roles that trust OIDC tokens, avoiding password storage.Final Answer:
It allows secure authentication without storing passwords in the pipeline. -> Option DQuick Check:
OIDC = passwordless secure authentication [OK]
- Thinking OIDC automates deployment without approval
- Confusing OIDC with replacing Terraform
- Assuming OIDC disables access controls
Solution
Step 1: Identify correct OIDC provider ARN format
The OIDC provider ARN for GitHub Actions uses 'token.actions.githubusercontent.com'.Step 2: Check assume_role_policy structure
The policy must allow 'sts:AssumeRoleWithWebIdentity' with Principal as Federated and correct ARN.Final Answer:
The resource block with Federated principal using 'token.actions.githubusercontent.com' and 'sts:AssumeRoleWithWebIdentity' action. -> Option AQuick Check:
Correct OIDC ARN + sts:AssumeRoleWithWebIdentity = resource "aws_iam_role" "github_oidc_role" { assume_role_policy = jsonencode({ Version = "2012-10-17", Statement = [{ Effect = "Allow", Principal = { Federated = "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com" }, Action = "sts:AssumeRoleWithWebIdentity" }] }) } [OK]
- Using Service principal instead of Federated
- Wrong OIDC provider ARN format
- Using sts:AssumeRole instead of sts:AssumeRoleWithWebIdentity
condition = {
StringEquals = {
"token.actions.githubusercontent.com:sub" = "repo:myorg/myrepo:ref:refs/heads/main"
}
}Solution
Step 1: Understand the condition key
The condition uses 'token.actions.githubusercontent.com:sub' to specify the GitHub repo and branch.Step 2: Interpret the value
The value 'repo:myorg/myrepo:ref:refs/heads/main' restricts access to the main branch of that repo only.Final Answer:
Restricts role assumption to the main branch of myorg/myrepo only. -> Option BQuick Check:
Condition limits to specific repo and branch = Restricts role assumption to the main branch of myorg/myrepo only. [OK]
- Ignoring the branch restriction in condition
- Assuming condition allows all repos
- Confusing forks with original repo access
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/github.com" },
"Action": "sts:AssumeRoleWithWebIdentity"
}]
}
What is the most likely error?Solution
Step 1: Check the OIDC provider ARN
The ARN 'arn:aws:iam::123456789012:oidc-provider/github.com' is incorrect for GitHub Actions.Step 2: Identify correct ARN for GitHub Actions
The correct ARN uses 'token.actions.githubusercontent.com' as the OIDC provider host.Final Answer:
The OIDC provider ARN is incorrect; it should be 'token.actions.githubusercontent.com'. -> Option CQuick Check:
Wrong OIDC ARN causes auth failure = The OIDC provider ARN is incorrect; it should be 'token.actions.githubusercontent.com'. [OK]
- Using 'sts:AssumeRole' instead of 'sts:AssumeRoleWithWebIdentity'
- Confusing Federated with Service principal
- Ignoring ARN format for OIDC provider
Solution
Step 1: Understand the condition key and value
To restrict to the 'release' branch, the condition must match 'repo:myorg/myrepo:ref:refs/heads/release'.Step 2: Choose correct operator
'StringEquals' ensures exact match, so only 'release' branch is allowed.Final Answer:
The condition block with StringEquals matching 'repo:myorg/myrepo:ref:refs/heads/release'. -> Option AQuick Check:
Exact branch match uses StringEquals with correct ref [OK]
- Using StringLike with wildcard allowing all branches
- Checking audience (aud) instead of subject (sub)
- Using StringNotEquals which excludes main but allows others
Ready to prove it?
Pass the test and this pattern moves unknown
