This post is also featured on the PostNL Engineering blog on Medium!
A common pattern is to invoke AWS actions from a GitHub Actions workflow. For example, a workflow can be used for AWS CDK deployments, invoking a Lambda function (e.g., for integration tests) or reading data from DynamoDB.
Using OpenID Connect (OIDC) such AWS actions can be invoked by an IAM role. The role trusts a specific GitHub repository or branch, giving granular control over how GitHub Actions can use AWS credentials. This solution uses temporary credentials, meaning that there is no need to store and rotate long-lived credentials.
This post demonstrates how to allow a specific GitHub repository branch to access AWS (with AWS CDK in TypeScript) and invoke actions on an AWS account (with GitHub Actions).
The code used in this post can be found on GitHub.
Create IAM OIDC Identity Provider
To establish a trust relationship between GitHub and AWS, create an IAM OIDC Identity Provider for GitHub.
const oidcProvider = new iam.CfnOIDCProvider(this, "GithubOidcProvider", {
url: "https://token.actions.githubusercontent.com",
clientIdList: ["sts.amazonaws.com"],
});
Create IAM Role
Next, the created identity provider is used to create an IAM role.
The trust policy of the role defines which specific GitHub repository branch is allowed to assume the role (using the pattern repo:OWNER/REPOSITORY:ref:refs/heads/BRANCH
).
const conditions: iam.Conditions = {
StringEquals: {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub":
"repo:mellevanderlinde/oidc-aws-github:ref:refs/heads/main",
},
};
const oidcRole = new iam.Role(this, "GithubOidcRole", {
roleName: "github-oidc-role",
assumedBy: new iam.WebIdentityPrincipal(oidcProvider.ref, conditions),
});
Alternatively, an entire GitHub repository can be allowed using a wildcard *
in the conditions
. For this, StringLike
is required instead of StringEquals
.
const conditions: iam.Conditions = {
StringEquals: {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
},
StringLike: {
"token.actions.githubusercontent.com:sub":
"repo:mellevanderlinde/oidc-aws-github:*",
},
};
For other patterns (e.g. allowing specific GitHub environments), see Example subject claims.
Attach Permissions to IAM Role
To invoke AWS actions, the OIDC role needs to have the correct permissions.
In this case the cdk diff
command will be run, requiring read only permissions.
Therefore, the OIDC role is granted permission to assume the lookup role (created when the AWS account is CDK bootstrapped).
const lookupRole = iam.Role.fromRoleName(
this,
"LookupRole",
`cdk-hnb659fds-lookup-role-${this.account}-${this.region}`,
);
lookupRole.grantAssumeRole(oidcRole);
Assume IAM Role from GitHub Actions Workflow
Next, the IAM role is assumed from a GitHub Actions workflow.
This is done with the action configure-aws-credentials, resulting in the following .github/workflows/oidc.yml
file.
Note that a dummy AWS account ID is used.
on: push
jobs:
oidc:
permissions:
id-token: write
contents: read
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Assume OIDC role
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: eu-west-1
role-to-assume: arn:aws:iam::012345678912:role/github-oidc-role
Invoke AWS Actions from GitHub Actions Workflow
The last step is to invoke AWS actions from the workflow. In this case, the cdk diff
command is run by adding the following step.
- name: Invoke AWS action
run: |
npm ci
npx cdk diff
This results in the following .github/workflows/oidc.yml
file.
on: push
jobs:
oidc:
permissions:
id-token: write
contents: read
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Assume OIDC role
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: eu-west-1
role-to-assume: arn:aws:iam::012345678912:role/github-oidc-role
- name: Invoke AWS action
run: |
npm ci
npx cdk diff
Conclusion
Using OpenID Connect, integrating AWS with GitHub Actions is relatively simple and secure. This can be useful for deployments, testing purposes or other cases where AWS actions need to be invoked.