0
0
Terraformcloud~10 mins

OIDC authentication for CI/CD in Terraform - Commands & Configuration

Choose your learning style9 modes available
Introduction
CI/CD pipelines need a safe way to access cloud resources without using long-lived passwords. OIDC authentication lets pipelines prove who they are using short-lived tokens, making access safer and easier to manage.
When your CI/CD pipeline needs to deploy infrastructure to a cloud provider securely.
When you want to avoid storing static cloud credentials in your pipeline configuration.
When you want to use your cloud provider's identity system to control pipeline permissions.
When you want to automatically rotate credentials without manual intervention.
When you want to improve security by using temporary tokens instead of permanent keys.
Config File - main.tf
main.tf
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.

Commands
Initializes Terraform, downloads the AWS provider plugin, and prepares the working directory.
Terminal
terraform init
Expected OutputExpected
Initializing the backend... Initializing provider plugins... - Finding hashicorp/aws versions matching "~> 4.0"... - Installing hashicorp/aws v4.60.0... - Installed hashicorp/aws v4.60.0 (signed by HashiCorp) Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure.
Shows the changes Terraform will make to create the OIDC provider, IAM role, policy, and attachment.
Terminal
terraform plan
Expected OutputExpected
Terraform used the selected providers to generate an execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_iam_openid_connect_provider.github will be created + resource "aws_iam_openid_connect_provider" "github" { + arn = (known after apply) + client_id_list = ["sts.amazonaws.com"] + id = (known after apply) + thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"] + url = "https://token.actions.githubusercontent.com" } # aws_iam_role.ci_cd_role will be created + resource "aws_iam_role" "ci_cd_role" { + arn = (known after apply) + assume_role_policy = jsonencode(...) + id = (known after apply) + name = "ci-cd-oidc-role" } # aws_iam_policy.ci_cd_policy will be created + resource "aws_iam_policy" "ci_cd_policy" { + arn = (known after apply) + description = "Policy for CI/CD pipeline to access S3 bucket" + id = (known after apply) + name = "ci-cd-policy" + policy = jsonencode(...) } # aws_iam_role_policy_attachment.attach_policy will be created + resource "aws_iam_role_policy_attachment" "attach_policy" { + id = (known after apply) + policy_arn = (known after apply) + role = "ci-cd-oidc-role" } Plan: 4 to add, 0 to change, 0 to destroy.
Applies the planned changes to create the OIDC provider, IAM role, policy, and attach the policy to the role.
Terminal
terraform apply -auto-approve
Expected OutputExpected
aws_iam_openid_connect_provider.github: Creating... aws_iam_role.ci_cd_role: Creating... aws_iam_policy.ci_cd_policy: Creating... aws_iam_openid_connect_provider.github: Creation complete after 2s [id=arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com] aws_iam_role.ci_cd_role: Creation complete after 3s [id=ci-cd-oidc-role] aws_iam_policy.ci_cd_policy: Creation complete after 2s [id=arn:aws:iam::123456789012:policy/ci-cd-policy] aws_iam_role_policy_attachment.attach_policy: Creating... aws_iam_role_policy_attachment.attach_policy: Creation complete after 1s [id=ci-cd-oidc-role/arn:aws:iam::123456789012:policy/ci-cd-policy] Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
-auto-approve - Automatically approves the apply without asking for confirmation.
Verifies the IAM role was created and shows its details.
Terminal
aws iam get-role --role-name ci-cd-oidc-role
Expected OutputExpected
{ "Role": { "Path": "/", "RoleName": "ci-cd-oidc-role", "RoleId": "AROAJEXAMPLEID", "Arn": "arn:aws:iam::123456789012:role/ci-cd-oidc-role", "CreateDate": "2024-06-01T12:00:00Z", "AssumeRolePolicyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Federated\":\"arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com\"},\"Action\":\"sts:AssumeRoleWithWebIdentity\",\"Condition\":{\"StringLike\":{\"token.actions.githubusercontent.com:sub\":\"repo:example-org/example-repo:ref:refs/heads/main\"}}}]}" } }
--role-name - Specifies the exact IAM role to retrieve.
Key Concept

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.

Common Mistakes
Using incorrect or missing thumbprint for the OIDC provider.
AWS rejects the OIDC provider registration if the thumbprint does not match the provider's SSL certificate, causing authentication failures.
Always verify and use the correct thumbprint for the OIDC provider URL before applying Terraform.
Not specifying the correct 'sub' condition in the IAM role trust policy.
Without the correct 'sub' condition, any token from the OIDC provider could assume the role, which is a security risk or the role might not be assumable by the intended pipeline.
Set the 'sub' condition to match your specific repository and branch to restrict role assumption.
Attaching overly broad IAM policies to the role.
This grants more permissions than needed, increasing security risks if the pipeline is compromised.
Grant only the minimum permissions the pipeline needs, following the principle of least privilege.
Summary
Initialize Terraform to prepare the working directory and download providers.
Plan the Terraform deployment to see what resources will be created.
Apply the Terraform configuration to create the OIDC provider, IAM role, and policies.
Verify the IAM role exists and has the correct trust policy for OIDC authentication.