Skip to content

Instantly share code, notes, and snippets.

@jbutz
Last active June 9, 2023 12:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbutz/f21c73de8e5d23c25691149b30a2cb4b to your computer and use it in GitHub Desktop.
Save jbutz/f21c73de8e5d23c25691149b30a2cb4b to your computer and use it in GitHub Desktop.
AWS CDK v2 GitHub OIDC Construct & GitHub Workflow
import { Arn, CfnOutput, Stack } from 'aws-cdk-lib';
import {
Effect,
OpenIdConnectPrincipal,
OpenIdConnectProvider,
PolicyDocument,
PolicyStatement,
Role,
} from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';
export type GitHubCredentialsProps = {
githubUsername: string;
githubRepoName: string;
/**
* @default "*"
*/
githubSubjectRef?: string;
/**
* @default true
*/
createIamOidcProvider?: boolean;
/**
* @default true
*/
enableLookupRole?: boolean;
/**
* @default true
*/
enableFilePublishingRole?: boolean;
/**
* @default true
*/
enableContainerImagePublishingRole?: boolean;
};
export class GitHubCredentials extends Construct {
constructor(
scope: Construct,
id: string,
readonly props: GitHubCredentialsProps
) {
super(scope, id);
const oidcDeployProvider =
props.createIamOidcProvider !== false
? new OpenIdConnectProvider(this, 'oidc', {
url: 'https://token.actions.githubusercontent.com',
clientIds: ['sts.amazonaws.com'],
thumbprints: ['6938fd4d98bab03faadb97b34396831e3780aea1'],
})
: OpenIdConnectProvider.fromOpenIdConnectProviderArn(
this,
'oidc',
Arn.format(
{
service: 'iam',
region: '',
resource: 'oidc-provider',
resourceName:
'token.actions.githubusercontent.com',
},
Stack.of(this)
)
);
const deploymentRole = new Role(this, 'role', {
assumedBy: new OpenIdConnectPrincipal(oidcDeployProvider, {
StringEquals: {
'token.actions.githubusercontent.com:aud':
'sts.amazonaws.com',
'token.actions.githubusercontent.com:sub': `repo:${
props.githubUsername
}/${props.githubRepoName}:${props.githubSubjectRef || '*'}`,
},
}),
inlinePolicies: {
CDK: new PolicyDocument({
statements: [
new PolicyStatement({
effect: Effect.ALLOW,
actions: ['sts:AssumeRole'],
resources: [
this.generateCdkRoleArn('deploy'),
...(props.enableLookupRole !== false
? [this.generateCdkRoleArn('lookup')]
: []),
...(props.enableFilePublishingRole !== false
? [
this.generateCdkRoleArn(
'file-publishing'
),
]
: []),
...(props.enableContainerImagePublishingRole !==
false
? [
this.generateCdkRoleArn(
'image-publishing'
),
]
: []),
],
}),
],
}),
},
});
new CfnOutput(this, 'deploymentArn', {
value: deploymentRole.roleArn,
});
}
private generateCdkRoleArn(nameFragment: string) {
const stack = Stack.of(this);
return Arn.format(
{
region: '',
service: 'iam',
resource: 'role',
resourceName: `cdk-${stack.synthesizer.bootstrapQualifier}-${nameFragment}-role-${stack.account}-${stack.region}`,
},
stack
);
}
}
name: Node.js CI
on:
push:
branches: ['main']
pull_request:
branches: ['main']
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js 18
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'npm'
- run: npm ci
- run: npm run build --if-present
- run: npm run test --if-present
- uses: actions/upload-artifact@v3
with:
name: build
path: dist/
retention-days: 1
if-no-files-found: error
deploy:
needs: build
if: github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v3
- name: Use Node.js 18
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'npm'
- run: npm ci
- uses: actions/download-artifact@v3
with:
name: build
path: dist/
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-region: us-east-1
role-to-assume: arn:aws:iam::000000000000:role/SampleRole
- run: npm run deploy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment