Bird
Raised Fist0
Terraformcloud~3 mins

Why OIDC authentication for CI/CD in Terraform? - Purpose & Use Cases

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
The Big Idea

Discover how to ditch secret keys and make your CI/CD pipeline both safer and easier!

The Scenario

Imagine you have a CI/CD pipeline that needs to access cloud resources securely. Without automation, you manually create and manage long-lived credentials or keys for each pipeline run.

The Problem

This manual approach is slow and risky. Keys can be leaked or forgotten to rotate, causing security holes. It's also a hassle to update credentials every time permissions change.

The Solution

OIDC authentication lets your CI/CD pipeline prove its identity dynamically using short-lived tokens. This removes the need for stored secrets and automates secure access to cloud resources.

Before vs After
Before
aws_access_key = "OLD_KEY"
aws_secret_key = "OLD_SECRET"
After
provider "aws" {
  region = "us-east-1"
}
What It Enables

It enables secure, automatic, and scalable access to cloud resources without managing static secrets.

Real Life Example

A developer pushes code to GitHub. The GitHub Actions workflow uses OIDC to get a temporary token and deploys the app to AWS without storing any AWS keys in the repo.

Key Takeaways

Manual credential management is slow and risky.

OIDC provides short-lived, automatic authentication for CI/CD.

This improves security and simplifies pipeline setup.

Practice

(1/5)
1. What is the main benefit of using OIDC authentication in CI/CD pipelines with Terraform?
easy
A. It disables all access restrictions for faster builds.
B. It automatically deploys code without any manual approval.
C. It replaces Terraform with a different infrastructure tool.
D. It allows secure authentication without storing passwords in the pipeline.

Solution

  1. Step 1: Understand OIDC purpose in CI/CD

    OIDC provides a secure way for pipelines to prove identity without passwords.
  2. Step 2: Connect to Terraform usage

    Terraform can create roles that trust OIDC tokens, avoiding password storage.
  3. Final Answer:

    It allows secure authentication without storing passwords in the pipeline. -> Option D
  4. Quick Check:

    OIDC = passwordless secure authentication [OK]
Hint: OIDC means no passwords needed in CI/CD [OK]
Common Mistakes:
  • Thinking OIDC automates deployment without approval
  • Confusing OIDC with replacing Terraform
  • Assuming OIDC disables access controls
2. Which Terraform resource block correctly defines an IAM role trusting GitHub Actions OIDC provider?
easy
A. 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" }] }) }
B. resource "aws_iam_role" "github_oidc_role" { assume_role_policy = jsonencode({ Version = "2012-10-17", Statement = [{ Effect = "Allow", Principal = { Service = "github.com" }, Action = "sts:AssumeRole" }] }) }
C. resource "aws_iam_role" "github_oidc_role" { assume_role_policy = jsonencode({ Version = "2012-10-17", Statement = [{ Effect = "Allow", Principal = { AWS = "arn:aws:iam::123456789012:root" }, Action = "sts:AssumeRoleWithWebIdentity" }] }) }
D. 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/github.com" }, Action = "sts:AssumeRoleWithWebIdentity" }] }) }

Solution

  1. Step 1: Identify correct OIDC provider ARN format

    The OIDC provider ARN for GitHub Actions uses 'token.actions.githubusercontent.com'.
  2. Step 2: Check assume_role_policy structure

    The policy must allow 'sts:AssumeRoleWithWebIdentity' with Principal as Federated and correct ARN.
  3. Final Answer:

    The resource block with Federated principal using 'token.actions.githubusercontent.com' and 'sts:AssumeRoleWithWebIdentity' action. -> Option A
  4. Quick 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]
Hint: OIDC provider ARN uses token.actions.githubusercontent.com [OK]
Common Mistakes:
  • Using Service principal instead of Federated
  • Wrong OIDC provider ARN format
  • Using sts:AssumeRole instead of sts:AssumeRoleWithWebIdentity
3. Given this Terraform snippet for an IAM role trust policy, what is the effect of the condition block?
condition = {
  StringEquals = {
    "token.actions.githubusercontent.com:sub" = "repo:myorg/myrepo:ref:refs/heads/main"
  }
}
medium
A. Allows any GitHub repo to assume the role.
B. Restricts role assumption to the main branch of myorg/myrepo only.
C. Blocks all role assumptions from GitHub Actions.
D. Allows only forks of myorg/myrepo to assume the role.

Solution

  1. Step 1: Understand the condition key

    The condition uses 'token.actions.githubusercontent.com:sub' to specify the GitHub repo and branch.
  2. Step 2: Interpret the value

    The value 'repo:myorg/myrepo:ref:refs/heads/main' restricts access to the main branch of that repo only.
  3. Final Answer:

    Restricts role assumption to the main branch of myorg/myrepo only. -> Option B
  4. Quick Check:

    Condition limits to specific repo and branch = Restricts role assumption to the main branch of myorg/myrepo only. [OK]
Hint: Condition with repo and ref limits branch access [OK]
Common Mistakes:
  • Ignoring the branch restriction in condition
  • Assuming condition allows all repos
  • Confusing forks with original repo access
4. You wrote this Terraform assume_role_policy but your CI/CD pipeline fails to authenticate:
{
  "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?
medium
A. The Principal should use 'Service' instead of 'Federated'.
B. The Action should be 'sts:AssumeRole' instead of 'sts:AssumeRoleWithWebIdentity'.
C. The OIDC provider ARN is incorrect; it should be 'token.actions.githubusercontent.com'.
D. The Version date is invalid and must be updated.

Solution

  1. Step 1: Check the OIDC provider ARN

    The ARN 'arn:aws:iam::123456789012:oidc-provider/github.com' is incorrect for GitHub Actions.
  2. Step 2: Identify correct ARN for GitHub Actions

    The correct ARN uses 'token.actions.githubusercontent.com' as the OIDC provider host.
  3. Final Answer:

    The OIDC provider ARN is incorrect; it should be 'token.actions.githubusercontent.com'. -> Option C
  4. Quick Check:

    Wrong OIDC ARN causes auth failure = The OIDC provider ARN is incorrect; it should be 'token.actions.githubusercontent.com'. [OK]
Hint: Check OIDC ARN spelling carefully for GitHub Actions [OK]
Common Mistakes:
  • Using 'sts:AssumeRole' instead of 'sts:AssumeRoleWithWebIdentity'
  • Confusing Federated with Service principal
  • Ignoring ARN format for OIDC provider
5. You want to create a Terraform IAM role for GitHub Actions that only allows workflows from the 'release' branch of 'myorg/myrepo' to assume it. Which condition block correctly enforces this restriction?
hard
A. { "StringEquals": { "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:ref:refs/heads/release" } }
B. { "StringLike": { "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:ref:refs/heads/*" } }
C. { "StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" } }
D. { "StringNotEquals": { "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:ref:refs/heads/main" } }

Solution

  1. 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'.
  2. Step 2: Choose correct operator

    'StringEquals' ensures exact match, so only 'release' branch is allowed.
  3. Final Answer:

    The condition block with StringEquals matching 'repo:myorg/myrepo:ref:refs/heads/release'. -> Option A
  4. Quick Check:

    Exact branch match uses StringEquals with correct ref [OK]
Hint: Use StringEquals with exact repo and branch ref [OK]
Common Mistakes:
  • Using StringLike with wildcard allowing all branches
  • Checking audience (aud) instead of subject (sub)
  • Using StringNotEquals which excludes main but allows others