Security

Keyless access to AWS in GitHub Actions with OIDC

With this blog, we will show you how to access your AWS environment without storing IAM credentials in GitHub by using OpenID Connect (OIDC).

What is OpenID Connect?

OpenID Connect has been around since 2014, and in reality, it’s a simplified identity layer on top of the OAuth 2.0 protocol. This gives your clients and applications an easy process to authenticate against an Authorization Server and get information about the end-users (in this case AWS) but in a process flow that’s easier for applications to do so. For a deeper look into OpenID Connect check out https://openid.net/connect/.

So using OpenID Connect we can remove the need to have keys stored in GitHub Actions, saving the headache of rotating the keys or the other headaches.

Setting up AWS OpenID Connect Identity Provider

The first step to this whole process is to configure AWS to allow GitHub to communicate with it via OpenID Connect. Here are a few different ways to do it.

We've got three different ways to do this AWS Console, AWS CLI, and Terraform.

AWS Console

Once you are logged into the AWS Console, head to IAM and select Identity providers:

AWS Console IAM sidebar with Identity providers selected, showing search results for identity providers

Select the Add provider button in the top right corner

IAM Identity providers page with the Add provider button highlighted in the top right

On the Add an Identity provider screen, you will want to select OpenID Connect as the Provider type, and then add the following information to the fields

  • Provider URL: https://token.actions.githubusercontent.com
  • Audience: https://github.com/(org name) (so in our case https://github.com/cloudquery)
Add an Identity provider form with OpenID Connect selected, GitHub Actions provider URL, and cloudquery audience configured

Make sure to select Get thumbprint to get the certificates, and then Add provider in the bottom right.

The next step is to add a role for the provider to assume when the GitHub Action accesses AWS. head to Roles within IAM:

AWS Console IAM sidebar with Roles selected, showing search results for roles

Select Create role in the top right.

IAM Roles page showing 104 roles with the Create role button in the top right

Now we need to identify our OpenID Connect provider. Select Web identity from the Trusted entity types, select the token.actions.githubusercontent.com from the Identity provider drop-down, and then select the Audience you added a moment ago. Then hit next.

IAM Create role wizard step 1: Web identity selected as trusted entity with GitHub Actions OIDC provider and cloudquery audience

The next step is to either assign or create the permissions the role will need, so what permissions do your GitHub Actions need to work within AWS. This is entirely up to your needs, so best to do reading around AWS Roles and service permissions.

IAM Create role wizard step 2: Add permissions page listing 747 available policies to attach

The final step in creating the Role is to name it and provide a relevant description. You can also add more permissions or even adapt the trusted entities ( which Identity providers have access to the role).

IAM Create role wizard step 3: Naming the role GitHub Action Role with trust policy JSON showing the OIDC federation configuration

AWS CLI

When using the AWS Console, the console itself will attempt to do some of the steps for you, however, when using the AWS CLI you’ll have an initial step to do yourself. This first step is getting the thumbprint for the GitHub certificates used to authenticate the requests, that process is an entire blog itself so I would suggest following the guide provided by AWS called Obtaining the thumbprint for an OpenID Connect Identity Provider.

Once you have the thumbprint, that should look something like 15E29108718111E59B3DAD31954647E3C344A231 we can get on to actually creating the Identity provider. The command to create the OpenID Connect Identity provider is as follows:

aws iam create-open-id-connect-provider --url https://token.actions.githubusercontent.com --client-id-list https://github.com/(org name) --thumbprint-list 15E29108718111E59B3DAD31954647E3C344A231

Using the IAM suite of commands we run create-open-id-connect-provider and provide it with a few pieces of information.

  • --url [https://token.actions.githubusercontent.com](https://token.actions.githubusercontent.com/) which is the provider URL
  • --client-id-list https://github.com/(org name) is the Audience in the console, and essentially is who can use it so https://github.com/cloudquery for us
  • --thumbprint-list 15E29108718111E59B3DAD31954647E3C344A231

This will give you a response object that looks like this:

{
"OpenIDConnectProviderArn": "arn:aws:iam::000000000000:oidc-provider/token.actions.githubusercontent.com"
}

Now the OpenID Connect Identity provider exists, you need to create a role for it to assume to access AWS services with a trust policy and permissions.

Let’s start with the trust policy githubactionawstrustpolicy.json

{
"Version": "2012-10-17",
"Statement": {
"Sid": "RoleForGitHubActions",
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::000000000000:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:github.com/(org name)"
}
}
}
}

This trust policy is allowing a connection from GitHub via the OpenID Connect we created earlier to assume the role, and to set this up we use the following command:

aws iam create-role --role-name GitHub-Action-Role --assume-role-policy-document /githubactionawstrustpolicy.json

Using the aws iam create-role command we are creating the GitHub-Action-Role and pointing it to our trust policy. And once this role exists, we need to give permissions to actually access AWS Services.

Here is an example permissions JSON in the form of permissionsforgithubactions.json

{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::example_bucket"
}
}

The permission we are granting here is that the GitHub Action may run ListBuckets on only the example_bucket S3 bucket.

To attach this to our new role you will need to run the command:

aws iam put-role-policy --role-name GitHub-Action-Role --policy-name Perms-Policy-For-GitHub-Actions --policy-document /permissionsforgithubactions.json

With this command, we’re attaching the Perms-Policy-For-GitHub-Actions to our GitHub-Action-Role with the ListBucket action.

Terraform

If you’re using Terraform already, it’s quite a straightforward terraform script to create an OpenID Connect Identity provider with its required role and permissions. Luckily, the Terraform registry contains a complete AWS provider to use. The script is as follows:

provider "aws" {}

resource "aws_iam_openid_connect_provider" "default" {
url = "https://token.actions.githubusercontent.com"

client_id_list = [
"https://github.com/(org name)",
]

thumbprint_list = [
"15E29108718111E59B3DAD31954647E3C344A231",
]
}

resource "aws_iam_role" "github_action_role" {
name = "github_actions_role"

# Terraform's "jsonencode" function converts a
# Terraform expression result to valid JSON syntax.
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRoleWithWebIdentity",
Effect = "Allow"
Sid = "RoleForGitHubActions",
Principal = {
Federated = aws_iam_openid_connect_provider.default.arn
}
Condition = {
StringLike = {
"token.actions.githubusercontent.com:sub" = "repo:github.com/(org name)"
}
}
},
]
})
}

resource "aws_iam_role_policy" "github_actions_policy" {
name = "github-actions-policy"
role = aws_iam_role.github_action_role.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"ec2:Describe*",
]
Effect = "Allow"
Resource = "*"
},
]
})
}

Let’s break this down, as we’re using AWS you will need to make sure you have provider "aws" {} configured to actually interact with the AWS account in question.

The next block is actually configuring the OpenID Connect Identity provider

resource "aws_iam_openid_connect_provider" "default" {
url = "https://token.actions.githubusercontent.com"

client_id_list = [
"https://github.com/(org name)",
]

thumbprint_list = [
"15E29108718111E59B3DAD31954647E3C344A231",
]
}

The individual parts of this are

  • url = “https://token.actions.githubusercontent.com” which is the provider URL
  • client_id_list = ["https://github.com/(org name)"] is the Audience in the AWS console, and essentially is who can use it so https://github.com/cloudquery for us
  • thumbprint_list = ["15E29108718111E59B3DAD31954647E3C344A231"] which is the certificates that OpenID Connect uses to authenticate. The process is an entire blog itself so I would suggest following the guide provided by AWS called Obtaining the thumbprint for an OpenID Connect Identity Provider.

Once we have an OIDC we will need to grant it a role to assume within AWS, the block for this is:

resource "aws_iam_role" "github_action_role" {
name = "github_actions_role"

# Terraform's "jsonencode" function converts a
# Terraform expression result to valid JSON syntax.
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRoleWithWebIdentity",
Effect = "Allow"
Sid = "RoleForGitHubActions",
Principal = {
Federated = aws_iam_openid_connect_provider.default.arn
}
Condition = {
StringLike = {
"token.actions.githubusercontent.com:sub" = "repo:github.com/(org name)"
}
}
},
]
})
}

Here we are simply naming the role github_actions_role and then giving it a role policy, once we have this role policy the final block is used to grant said role permissions:

resource "aws_iam_role_policy" "github_actions_policy" {
name = "github-actions-policy"
role = aws_iam_role.github_action_role.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"ec2:Describe*",
]
Effect = "Allow"
Resource = "*"
},
]
})
}

This permissions block is allowing the GitHub Action to Describe all EC2, which could be handy.

Setting up GitHub to use the OpenID Connect provider

Now that we have a working OpenID Connect provider within AWS, we need to add the configuration to GitHub for use in our GitHub Actions. To do this, we simply add another step to the desired yml workflow.

The relevant blocks look like so:

permissions:
id-token: write
contents: read # This is required for actions/checkout@v2

Adding the permissions to the job allows the action that gets the credentials from AWS to store them for use in further steps. The permission that is specifically required is id-token: write.

The next step is where the credential retrieving magic actually happens

    steps:
[…]
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: arn:aws:iam::000000000000:role/cq-provider-aws-github-action
aws-region: us-east-1
[…]

An AWS action already exists, so we point to aws-actions/configure-aws-credentials@v1 then within the with section we add the role we want it to assume (like arn:aws:iam::000000000000:role/GitHubActionRole), which AWS region it operates within, and we can optionally add the Role session name.

Summary

Now, no matter which way you’ve chosen to configure the AWS component your GitHub Actions are able to communicate without the need for stored keys and the access can be managed entirely on the AWS side of things.

Want to see CloudQuery in action? Schedule a demo with our team or check out the platform documentation to learn more.

Want help getting started? Join the CloudQuery community to connect with other users and experts, or message our team directly here if you have any questions.

Try CloudQuery yourself

Self-hosted, open source, and free to start. Or schedule a demo to see the managed version.

Related posts

AWS

Azure

GCP

Security

How Do You Run CloudQuery Without Static Credentials?

Workload identity federation for GCP and Azure, RDS IAM auth, and Lakebase OAuth let self-hosted CloudQuery syncs run without stored static credentials.

Ben Bernays

Ben Bernays

September 28, 2026

AWS

Security

Tutorials

Finding AWS Unrestricted Outbound Access in 2026: EC2, Lambda, and Beyond

Find EC2, Lambda, and ECS Fargate resources with unrestricted outbound access in AWS using SQL. Why egress controls still break attack chains in 2026.

Joe Karlsson

Joe Karlsson

May 5, 2026

AWS

Security

Tutorials

AWS IAM Identity Center with Google Workspace as an IdP: 2026 Step-by-Step Guide

Set up AWS IAM Identity Center with Google Workspace: SAML authentication, SCIM user provisioning, and group sync with CLI or ssosync. Updated for 2026.

Joe Karlsson

Joe Karlsson

May 5, 2026

GCP

Security

Tutorials

Creating Cross-Project Service Accounts in GCP (Console + gcloud CLI)

Create a GCP service account that reads across all your projects with one credential. Covers gcloud CLI, org-level vs.

Joe Karlsson

Joe Karlsson

May 5, 2026

AWS

Best Practices

Security

AWS CIS Benchmark in 2026: Automated Compliance with SQL-Based Policies

AWS CIS Benchmark hit v3.0 in 2024 with 40+ controls. Run automated compliance checks using SQL policies with exception management and multi-account views.

Joe Karlsson

Joe Karlsson

May 5, 2026

AWS

Cloud Infrastructure

Security

Tutorials

Integrate AbuseIPDB Threat Intelligence with CloudQuery

Enrich your cloud asset inventory with AbuseIPDB threat intelligence to identify public cloud IPs with known abuse reports.

Joe Karlsson

Joe Karlsson

April 20, 2026

AWS

Security

Tutorials

AWS IAM Identity Center (Formerly AWS SSO): The Complete 2026 Guide

AWS IAM Identity Center replaced AWS SSO in 2022. Learn how to set it up, configure permission sets, use the AWS CLI, and audit access with CloudQuery.

Joe Karlsson

Joe Karlsson

April 6, 2026

Cloud Infrastructure

Security

CWPP vs CNAPP: What Cloud Security Teams Need to Know in 2026

CWPP protects cloud workloads at runtime. CNAPP unifies CWPP, CSPM, and CIEM into one platform. Learn when each makes sense and how they compare.

Joe Karlsson

Joe Karlsson

April 6, 2026

Cloud Infrastructure

Security

What Is Attack Surface Management? A Technical Guide for Cloud Teams

Attack surface management finds exposed cloud assets before attackers do. Learn how ASM differs from CSPM and vulnerability management, with SQL examples.

Joe Karlsson

Joe Karlsson

April 6, 2026

Own your pipeline.
Own your data.