GitHub Actions and AWS can securely authenticate using OpenID Connect (OIDC) without needing to store long-lived AWS credentials as GitHub secrets.
Here’s a look at how this works in practice. Imagine you have a workflow that needs to deploy an application to an EC2 instance.
name: Deploy to AWS
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }}
role-arn: arn:aws:iam::123456789012:role/GitHubActionsDeployRole
role-session-name: GitHubActionsSession
aws-region: us-east-1
- name: Deploy to EC2
run: |
echo "Deploying application..."
# Your deployment commands here, e.g., using aws ssm send-command
aws ssm send-command \
--instance-ids i-0abcdef1234567890 \
--document-name "AWS-RunShellScript" \
--parameters 'commands=echo "Hello from GitHub Actions!" > /tmp/hello.txt' \
--comment "Deploying application via GitHub Actions" \
--output text
This workflow uses the aws-actions/configure-aws-credentials action. The key parameters here are role-arn and role-session-name. Instead of providing direct AWS access keys, you’re telling the action which IAM role to assume. The role-arn is the Amazon Resource Name of the IAM role that GitHub Actions will assume. The role-session-name is a unique identifier for this specific session, useful for auditing.
The magic happens behind the scenes. When this step runs, GitHub Actions generates an OIDC token. This token is a JSON Web Token (JWT) that contains claims about the repository, the commit, and the workflow run. This OIDC token is then exchanged with AWS Security Token Service (STS) for temporary AWS credentials. These temporary credentials are then configured for subsequent steps in your workflow.
The IAM role (arn:aws:iam::123456789012:role/GitHubActionsDeployRole in the example) needs to be configured in AWS to trust your GitHub repository for OIDC authentication. This trust relationship is established in the IAM role’s trust policy.
Here’s what that trust policy might look like in AWS IAM:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:sub": "repo:your-github-username/your-repo-name:ref:refs/heads/main"
}
}
}
]
}
This policy states that the IAM role can be assumed (sts:AssumeRoleWithWebIdentity) by a principal that is federated through the GitHub OIDC provider (arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com). The Condition block is crucial: it restricts which specific GitHub repositories and branches can assume this role. repo:your-github-username/your-repo-name:ref:refs/heads/main ensures that only pushes to the main branch of your specific repository can trigger this trust. You can make this condition more general if needed, for example, to allow any branch by removing the :ref:refs/heads/main part, or allow any repository within an organization.
The aws-actions/configure-aws-credentials action simplifies this by allowing you to specify the role-arn and it handles the OIDC token generation and exchange. This is a significant security improvement over storing static AWS access keys and secret keys in GitHub secrets, as OIDC tokens are short-lived and tied to specific workflow runs.
The role-session-name parameter in the configure-aws-credentials action is not just for naming; it’s actually used in the sts:AssumeRoleWithWebIdentity call and appears in AWS CloudTrail logs, making it easier to track which specific workflow run or job is performing actions in your AWS account.
When you use the aws ssm send-command example, the credentials obtained via OIDC are automatically used by the AWS CLI. The IAM role you’ve configured must have permissions to perform actions like ssm:SendCommand on the target EC2 instances.
The most surprising thing about OIDC authentication in this context is how it fundamentally shifts the security model from "secrets in secrets managers" to "identity and trust established at the point of interaction." Instead of a machine having a long-term identity (AWS keys), the identity is ephemeral and granted based on the context of the request (the GitHub Actions workflow run).
The next logical step after securely deploying is managing your deployed resources. You’ll likely want to look into how to integrate with AWS Systems Manager Parameter Store or Secrets Manager to securely inject application secrets into your deployed applications.