Use this construct to create an Amazon Cognito Identity Pool
that enables GitHub Actions OpenID Connect identities to request temporary AWS Credentials. This construct is for the Enhanced (Simplified) AuthFlow.
import * as iam from 'aws-cdk-lib/aws-iam';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as cdk from 'aws-cdk-lib';
import { ActionsIdentityPool, ClaimMapping } from '@catnekaise/actions-constructs';
const githubOrganization = 'catnekaise'; // Change this Value
const app = new cdk.App();
const stack = new cdk.Stack(app, 'ActionsIdentityPoolStack');
const openIdConnectProvider = iam.OpenIdConnectProvider
.fromOpenIdConnectProviderArn(stack, 'Provider', `arn:aws:iam::${stack.account}:oidc-provider/token.actions.githubusercontent.com`);
const pool = new ActionsIdentityPool(stack, 'Pool', {
openIdConnectProvider: openIdConnectProvider,
authenticatedRole: ActionsIdentityPoolAuthenticatedRoleBehaviour.CREATE,
claimMapping: ClaimMapping.fromClaims(GhaClaim.REPOSITORY,GhaClaim.ACTOR, GhaClaim.JOB_WORKFLOW_REF),
principalClaimRequirements: {
repository: {
condition: 'StringLike',
values: [`${githubOrganization}/*`],
},
},
});
const role = new iam.Role(stack, 'Role', {
assumedBy: pool.createPrincipalForPool(),
});
pool.assignRoleWhenClaimEquals(role, GhaClaim.REPOSITORY_OWNER, githubOrganization);
declare const bucket: s3.Bucket;
// permission granted at object prefix = /${aws:principalTag/repo}/cache/${aws:principalTag/jWorkRef}/*
bucket.grantReadWrite(role, pool.util.iamResourcePath.value(GhaClaim.REPOSITORY, 'cache', GhaClaim.JOB_WORKFLOW_REF, '*'));
The following workflow uses a re-usable action for authenticating with the ID Pool. Read here for more information about using Cognito Identity in GitHub Actions.
on:
workflow_dispatch:
jobs:
job1:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: "Get Credentials from Amazon Cognito Identity"
uses: catnekaise/cognito-idpool-auth@alpha
with:
cognito-identity-pool-id: "eu-west-1:11111111-example"
aws-region: "eu-west-1"
audience: "cognito-identity.amazonaws.com"
aws-account-id: "111111111111"
set-in-environment: true
- name: "STS Get Caller Identity"
run: |
aws sts get-caller-identity
- name: Upload text file to S3 bucket
run: |
date > info.txt
aws s3 cp info.txt s3://BUCKET_NAME/${{ github.repository }}/cache/${{ github.job_workflow_ref }}/info.txt
Import the existing Identity Provider or create the provider for the AWS Account in the stack. If using GitHub Enterprise Cloud with configured issuer, add enterpriseSlug
.
If no provider is passed in the props, construct will attempt to import an existing provider in the account.
import * as iam from 'aws-cdk-lib/aws-iam';
import { ActionsIdentityPool } from '@catnekaise/actions-constructs';
const openIdConnectProvider = new iam.OpenIdConnectProvider(stack, 'Provider', {
url: 'https://token.actions.githubusercontent.com',
clientIds: ['cognito-identity.amazonaws.com'],
});
const pool = new ActionsIdentityPool(stack, 'Pool', {
openIdConnectProvider: openIdConnectProvider,
authenticatedRole: ActionsIdentityPoolAuthenticatedRoleBehaviour.CREATE,
claimMapping: ClaimMapping.fromClaims(GhaClaim.REPOSITORY, GhaClaim.ACTOR, GhaClaim.JOB_WORKFLOW_REF, GhaClaim.ENVIRONMENT, GhaClaim.SHA, GhaClaim.RUNNER_ENVIRONMENT),
principalClaimRequirements: {
repository: {
condition: 'StringLike',
values: [`${githubOrganization}/*`],
},
},
});
Provide one or more GitHub Actions Claims and tag names will be the name abbreviations listed in the table below. Any claim not listed will have the same tag name as claim name.
A use case for tag name abbreviations is because of session limits.
const pool = new ActionsIdentityPool(stack, 'Pool', {
claimMapping: ClaimMapping.fromClaims(GhaClaim.REPOSITORY, GhaClaim.ENVIRONMENT, GhaClaim.ACTOR, GhaClaim.JOB_WORKFLOW_REF),
});
Claim | Tag Name |
---|---|
repository_owner | owner |
repository | repo |
runner_environment | runEnv |
job_workflow_ref | jWorkRef |
workflow_ref | workRef |
environment | env |
enterprise | ent |
run_number | run |
run_attempt | attempt |
run_id | runId |
repository_visibility | repoVis |
repository_owner_id | ownerId |
repository_id | repoId |
event_name | event |
actor_id | actorId |
Specify tag names for claims.
const pool = new ActionsIdentityPool(stack, 'Pool', {
claimMapping: ClaimMapping.fromClaimsWithTagName({
repository: 'repo',
job_workflow_ref: 'job',
}),
});
Use claim name as tag name.
const pool = new ActionsIdentityPool(stack, 'Pool', {
claimMapping: ClaimMapping.fromClaimsAsTagNames(GhaClaim.REPOSITORY, GhaClaim.ENVIRONMENT, GhaClaim.ACTOR, GhaClaim.JOB_WORKFLOW_REF),
});
ActionsIdentityPool
requires that there's at least one claim applied to a principal when using createPrincipalForPool
to create trust policies. These are added as conditions in the trust policy.
const pool = new ActionsIdentityPool(stack, 'Pool', {
principalClaimRequirements: {
repository: {
condition: 'StringLike',
values: ['catnekaise/*'],
},
},
});
// value of principalClaimRequirements set in ActionsIdentityPoolProps are used
const principal = pool.createPrincipalForPool();
const role = new iam.Role(stack, 'Role', {
assumedBy: principal,
});
const role2 = new iam.Role(stack, 'Role2', {
assumedBy: pool.createPrincipalForPool(({
// claims provided to principalClaimRequirements in ActionsIdentityPool constructor are NOT inherited/merged
repository: {
condition: 'StringLike',
values: ['catnekaise/*'],
},
environment: ['test'],
jobWorkflowRef: {
condition: 'StringLike',
values: [
'catnekaise/shared-workflows/*@refs/heads/main',
],
},
})),
});
pool.assignRoleWhenClaimEquals(role2, GhaClaim.ENVIRONMENT, 'test');
pool.assignRoleWhenClaimEquals(role, GhaClaim.REPOSITORY_OWNER, 'catnekaise');
It's possible to match against any claim even if not mapping the claim. If intending to assign a role based on the GitHub organization name, it's easier to user the claim repository_owner
equaling the name as seen above than matching startsWith on the repository
claim.
Cognito Identity can perform role selection by evaluating rules. This allows different roles to be assigned to different workflows based on rule configuration.
declare const pool: ActionsIdentityPool;
const principal = pool.createPrincipalForPool();
const role = new iam.Role(stack, 'Role', {
assumedBy: principal,
});
pool.assignRoleWhenClaimEquals(role, GhaClaim.REPOSITORY_OWNER, 'catnekaise');
pool.assignRoleWhenClaimStartsWith(role, GhaClaim.REPOSITORY, 'catnekaise/');
When rules are evaluated they are evaluated in the order they were created.
declare const pool: ActionsIdentityPool;
const role = new iam.Role(stack, 'Role', {
assumedBy: pool.createPrincipalForPool({
repository: {
condition: 'StringLike',
values: ['catnekaise/*'],
},
environment: ['dev', 'test', 'prod'],
}),
});
const generalRole = new iam.Role(stack, 'GeneralRole', {
assumedBy: pool.createPrincipalForPool(),
});
pool.assignRoleWhenClaimEquals(role, GhaClaim.ENVIRONMENT, 'dev'); // Rule 1
pool.assignRoleWhenClaimEquals(role, GhaClaim.ENVIRONMENT, 'test'); // Rule 2
pool.assignRoleWhenClaimEquals(generalRole, GhaClaim.REPOSITORY_OWNER, 'catnekaise'); // Rule 3
pool.assignRoleWhenClaimEquals(role, GhaClaim.ENVIRONMENT, 'prod'); // Rule 4
It's required to assign a role as the default authenticated role
when using role mappings for an Identity Pool. This has bearing if the Cognito Identity Pool role resolution is configured to resolve the default authenticated role
when no rule is matched.
- Set
authenticatedRole
touseFirstAssigned
and the first role provided toActionsIdentityPool
for role assignment is used as the default authenticated role. - Set
authenticatedRole
tocreate
andActionsIdentityPool
will create a role for this purpose.- Use
authenticatedRoleName
to provide the created role with a specific name.
- Use
- Optionally set
roleResolution
toEnhancedFlowRoleResolution.UseDefaultAuthenticatedRole
if the default authenticated role shall be used when no rule matched.
const pool = new ActionsIdentityPool(stack, 'Pool', {
authenticatedRole: ActionsIdentityPoolAuthenticatedRoleBehaviour.CREATE,
authenticatedRoleName: 'cognito-gha-default',
principalClaimRequirements: {
repository: {
condition: 'StringLike',
values: ['catnekaise/*'],
},
},
roleResolution: EnhancedFlowRoleResolution.DENY, // default
});
const defaultAuthRole: iam.Role | undefined = pool.defaultAuthenticatedRole;
https://docs.aws.amazon.com/cognito/latest/developerguide/role-trust-and-permissions.html
Provide authenticatedMethodReference
in constructor with value of either host
(default), authenticated
, or arn
to configure what value shall be used when getting principals from the pool.
If using any value other than authenticated
for the role that is configured as the Default Authenticated Role
, the Cognito Identity Console UI will display a large warning saying "Trust policy isn't secure for this identity pool". This is incorrect and is likely on a TODO list somewhere.
Nonetheless, when setting authenticatedRole
to create
, the construct will use authenticated
regardless of value provided in authenticatedMethodReference
. If using useFirstAssigned
, the amr can be provided when creating the principal as exampled below.
const pool = new ActionsIdentityPool(stack, 'Pool', {
authenticatedRole: ActionsIdentityPoolAuthenticatedRoleBehaviour.USE_FIRST_ASSIGNED,
authenticatedMethodReference: 'host',
});
const role = new iam.Role(stack, 'Role', {
assumedBy: pool.createPrincipalForPool(undefined, 'authenticated'),
});
pool.assignRoleWhenClaimEquals(role, GhaClaim.REPOSITORY_OWNER, 'catnekaise');
Option | Value |
---|---|
host (default) | token.actions.githubusercontent.com |
authenticated | authenticated |
arn | arn:aws:iam::111111111111:oidc-provider/token.actions.githubusercontent.com:OIDC:* |
When using GitHub Enterprise Cloud the issuer
claim can be configured to include the enterprise slug.
If having done so, either always provide the openIdConnectProvider to ActionsIdentityPool
or set context option @catnekaise/actions-identity-pool:enterpriseSlug
with value of enterprise slug.