{"id":21029432,"url":"https://github.com/catnekaise/actions-constructs","last_synced_at":"2025-08-25T02:11:24.046Z","repository":{"id":203299556,"uuid":"709306198","full_name":"catnekaise/actions-constructs","owner":"catnekaise","description":"AWS CDK Constructs for integrating GitHub Actions and AWS.","archived":false,"fork":false,"pushed_at":"2024-12-02T12:54:13.000Z","size":786,"stargazers_count":4,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-19T00:14:36.409Z","etag":null,"topics":["abac","aws","cdk","cognito-identity","github-actions","iam","openid-connect"],"latest_commit_sha":null,"homepage":"https://catnekaise.github.io/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/catnekaise.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-24T13:03:31.000Z","updated_at":"2024-10-03T05:43:46.000Z","dependencies_parsed_at":null,"dependency_job_id":"c2eca0ed-c753-4d11-a5d1-56adae2d417f","html_url":"https://github.com/catnekaise/actions-constructs","commit_stats":null,"previous_names":["catnekaise/actions-constructs"],"tags_count":40,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catnekaise%2Factions-constructs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catnekaise%2Factions-constructs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catnekaise%2Factions-constructs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catnekaise%2Factions-constructs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/catnekaise","download_url":"https://codeload.github.com/catnekaise/actions-constructs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254330721,"owners_count":22053034,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["abac","aws","cdk","cognito-identity","github-actions","iam","openid-connect"],"created_at":"2024-11-19T12:12:25.445Z","updated_at":"2025-05-15T11:31:54.384Z","avatar_url":"https://github.com/catnekaise.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![npm (scoped)](https://img.shields.io/npm/v/@catnekaise/actions-constructs?style=flat-square)](https://www.npmjs.com/package/@catnekaise/actions-constructs)\n[![Nuget](https://img.shields.io/nuget/v/Catnekaise.CDK.ActionsConstructs?style=flat-square)](https://www.nuget.org/packages/Catnekaise.CDK.ActionsConstructs/)\n[![PyPI](https://img.shields.io/pypi/v/catnekaise.actions-constructs?style=flat-square)](https://pypi.org/project/catnekaise.actions-constructs/)\n[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/catnekaise/actions-constructs?sort=semver\u0026style=flat-square)](https://github.com/catnekaise/actions-constructs/releases)\n\n# Actions Constructs\nAWS CDK Constructs for integrating GitHub Actions and AWS.\n\n### Table of Contents\n\n* [\"Legacy\" Constructs](#legacy-constructs)\n* [ActionsIdentityPoolV2](#actionsidentitypoolv2)\n    * [Usage](#usage)\n    * [Usage - GitHub Actions](#usage---github-actions)\n    * [OpenID Connect Provider](#openid-connect-provider)\n        * [Audience](#audience)\n    * [Mapped Claims](#mapped-claims)\n        * [Custom Tag Names](#custom-tag-names)\n        * [Library Tag Abbreviations](#library-tag-abbreviations)\n    * [Enhanced AuthFlow](#enhanced-authflow)\n* [ActionsIdentityPolicyUtility](#actionsidentitypolicyutility)\n    * [Philosophy](#philosophy)\n    * [Constraints](#constraints)\n        * [Example](#example)\n            * [Regular Usage](#regular-usage)\n            * [With Policy Utility](#with-policy-utility)\n    * [Policy Constraining](#policy-constraining)\n    * [Policy Variable and PrincipalTagConditionKey](#policy-variable-and-principaltagconditionkey)\n    * [ActionsIdentityConstraints](#actionsidentityconstraints)\n        * [Constraints and CDK](#constraints-and-cdk)\n    * [Resource Paths](#resource-paths)\n        * [ToString](#tostring)\n        * [More Examples](#more-examples)\n    * [Role-Chaining (Cross Account)](#role-chaining-cross-account)\n    * [Role Chaining in Workflows](#role-chaining-in-workflows)\n    * [Passing Claims](#passing-claims)\n        * [Trust Policy](#trust-policy)\n* [Entrypoint Account](#entrypoint-account)\n    * [Grant Organization Role Chain](#grant-organization-role-chain)\n        * [Policy Statement](#policy-statement)\n* [Additional Roles](#additional-roles)\n    * [Same Stack](#same-stack-)\n    * [Separate Stack](#separate-stack)\n\n### \"Legacy\" Constructs\nWill continue to be supported.\n\n- [ActionsIdentityPool](./docs/actions-identity-pool/actions-identity-pool.md)\n- [ActionsIdentityPoolBasic](./docs/actions-identity-pool/actions-identity-pool-basic.md)\n\n# ActionsIdentityPoolV2\n\u003e Detailed explanation of the use-case can be found [here](https://catnekaise.github.io/github-actions-abac-aws/cognito-identity/).\n\nUse this to create a Cognito Identity Pool that allows GitHub Actions to authenticate and receive temporary AWS credentials that has session/principal tags corresponding with the GitHub Actions claims. This enables attribute-based access control (ABAC) to AWS resources from GitHub Actions.\n\n## Usage\n\n```typescript\nimport * as iam from 'aws-cdk-lib/aws-iam';\nimport * as cdk from 'aws-cdk-lib';\nimport {\n  ActionsIdentityPoolV2,\n  ActionsIdentityMappedClaims,\n  GitHubActionsClaimConstraint,\n} from '@catnekaise/actions-constructs';\n\nconst app = new cdk.App();\nconst stack = new cdk.Stack(app, 'CatnekaiseActionsIdentityExampleStack');\n\n// More details about OIDC Provider further below.\nconst openIdConnectProvider = iam.OpenIdConnectProvider\n        .fromOpenIdConnectProviderArn(stack, 'Provider', `arn:aws:iam::${stack.account}:oidc-provider/token.actions.githubusercontent.com`);\n\nconst pool = new ActionsIdentityPoolV2(stack, 'Pool', {\n  authenticatedRoleConstraints: [\n    // change value to the name(s) of your GitHub organization(s) that shall be allowed to authenticate\n    GitHubActionsClaimConstraint.repoOwners('catnekaise'),\n  ],\n  mappedClaims: ActionsIdentityMappedClaims.create(GhaClaim.REPOSITORY, GhaClaim.ACTOR, GhaClaim.RUNNER_ENVIRONMENT),\n  authenticatedRoleName: 'GhaCognito', // Optional\n  openIdConnectProvider,\n});\n```\n\n## Usage - GitHub Actions\nIf having used the example above you can test it by using the workflow below. Make sure to update the values to match your environment.\n\nAlso, make sure the [OIDC Provider Audience](#audience) has been configured with whichever audience is used below.\n\n```yaml\non:\n  workflow_dispatch:\njobs:\n  job1:\n    runs-on: ubuntu-latest\n    permissions:\n      id-token: write\n      contents: read\n    steps:\n      - name: \"Get Credentials from Amazon Cognito Identity\"\n        uses: catnekaise/cognito-idpool-auth@v1\n        with:\n          # Change values below to match your setup\n          auth-flow: \"basic\"\n          cognito-identity-pool-id: \"eu-west-1:11111111-example\"\n          aws-role-arn: \"arn:aws:iam::111111111111:role/GhaCognito\"\n          aws-region: \"eu-west-1\"\n          audience: \"cognito-identity.amazonaws.com\"\n          aws-account-id: \"111111111111\"\n          set-in-environment: true\n      \n      - name: \"STS Get Caller Identity\"\n        run: |\n          aws sts get-caller-identity\n```\n\n## OpenID Connect Provider\n\n- If no OIDC Provider is defined in the props given to ActionsIdentityPoolV2 it will try to import the provider from the AWS Account where the pool is being created.\n- If the OIDC Provider does not exist in the target account, the construct cannot be deployed.\n- If using GitHub Enterprise with a customized issuer, then import/create that provider and explicitly define it in the props.\n\n```typescript\nconst stack = new cdk.Stack(new cdk.App(), 'CatnekaiseActionsIdentityExampleStack');\n\n// Example for creating the OIDC Provider in the same stack as the Identity Pool\nconst openIdConnectProvider = new iam.OpenIdConnectProvider(stack, 'Provider', {\n  url: 'https://token.actions.githubusercontent.com',\n  // Keep sts.amazonaws.com if also using account to assume roles directly via STS\n  clientIds: ['cognito-identity.amazonaws.com', 'sts.amazonaws.com'],\n});\n\nconst pool = new ActionsIdentityPoolV2(stack, 'Pool', {\n  openIdConnectProvider,\n  // ...\n});\n```\n\n### Audience\nIf the provider already exist, go ahead and add the clientId (audience) `cognito-identity.amazonaws.com` to the existing provider. This is the audience used as default value for actions `catnekaise/cognito-idpool-auth` and `catnekaise/cognito-idpool-auth-basic`.\n\n## Mapped Claims\nBecause there's a limit for how much data can be put into a session in AWS, not all existing GitHub Actions claims can be mapped by Cognito Identity. Attempting to map all (or too many) claims will result in failure when assuming a role.\n\n`ActionsIdentityMappedClaims` is used for selecting which claims should be mapped in the identity pool. It can also be used to map claims using different tag names than the claim name. Using shorter tag names gives room for mapping additional claims and staying under the limit. Further details for how to manage the session limit can be found [here](https://catnekaise.github.io/github-actions-abac-aws/detailed-explanation/#session-limit).\n\nA good starting point of which claims to map that should not affect these limits:\n\n```typescript\nconst mappedClaims = ActionsIdentityMappedClaims.create(\n        GhaClaim.REPOSITORY,\n        GhaClaim.ACTOR,\n        GhaClaim.RUN_ID,\n        GhaClaim.SHA,\n        GhaClaim.REF,\n        GhaClaim.JOB_WORKFLOW_REF,\n);\n```\n\n### Custom Tag Names\nClaims can be mapped to a tag with a different name than the original claim. The key is the name of the claim and value will become name of the tag.\n\n```typescript\nconst mappedClaims = ActionsIdentityMappedClaims.createCustom({\n  repository: 'repo',\n  job_workflow_ref: 'jWorkRef'\n})\n```\n\n### Library Tag Abbreviations\nThis library has a set of abbreviations that can be used to shorten the tag names. Value of the abbreviation tag names can be found in [claims.ts](./src/identity-pool/claims.ts).\n\n```typescript\nconst mappedClaims = ActionsIdentityMappedClaims.createWithAbbreviations(\n        // becomes `repo` instead of `repository`\n        GhaClaim.REPOSITORY,\n        // becomes `jWorkRef` instead of `job_workflow_ref`\n        GhaClaim.JOB_WORKFLOW_REF,\n);\n```\n\n## Enhanced AuthFlow\n\u003e [!NOTE]\n\u003e Using the `basic` auth-flow will feel the most similar to how you have been using GitHub Actions OIDC with AWS STS today, so it may be a better starting point than the `enhanced` auth-flow.\n\nBy default the `ActionsIdentityPoolV2` uses the `Basic (Classic) AuthFlow` but it can also be configured to use the `Enhanced (Simplified) AuthFlow`.\n\nUsing the enhanced auth-flow requires an extra step where a role-assignment is made. More details about the differences of these auth-flows in the context of GitHub Actions can be found [here](https://catnekaise.github.io/github-actions-abac-aws/detailed-explanation/#authentication-flows).\n\n```typescript\nconst pool = new ActionsIdentityPoolV2(stack, 'Pool', {\n  useEnhancedAuthFlow: true,\n  // other props\n});\n\nconst role: iam.Role = pool.defaultAuthenticatedRole;\n\n// The claims used in role assignment does not have to be claims that are mapped in the identity pool.\npool.enhancedFlowAssignRole(role, GhaClaim.REPOSITORY_OWNER, EnhancedFlowMatchType.EQUALS, 'catnekaise');\n```\n\n# ActionsIdentityPolicyUtility\nUse the policy utility to:\n\n- Create principals (trust policy) for additional IAM Roles assumed via Cognito Identity\n- Create principals (trust policy) for IAM Roles assumed via the roles above (role-chaining)\n- Build resource paths containing a mix of text and GitHub Actions claims\n- Append grants with conditions based on claims from GitHub Actions\n- Append policy statements with conditions based on claims from GitHub Actions\n\n```typescript\nimport { ActionsIdentityPoolV2, ActionsIdentityPolicyUtility } from '@catnekaise/actions-constructs';\n\ndeclare const pool: ActionsIdentityPoolV2;\n\n// Available via ActionsIdentityPoolV2\nlet policyUtility = pool.policyUtility;\n\n// Configure separately\npolicyUtility = ActionsIdentityPolicyUtility.create(scope, {\n  mappedClaims: ActionsIdentityMappedClaims.create(GhaClaim.REPOSITORY /** additional claims **/),\n  // Additional utility configuration\n});\n```\n\n## Philosophy\nThe `ActionsIdentityPolicyUtility` aims to make it as easy as possible to work with the GitHub Actions claims while minimizing interference with how you work with AWS CDK today.\n\n- The utility prevents you from using claims you have not mapped in your identity pool.\n- The utility will select correct tag names if you have mapped a claim to a tag with a different name.\n- Any changes to mapped claims configuration is reflected in any conditions or resource paths created via the policy utility\n\n\n## Constraints\nIn the context of this library (and [catnekaise/cdk-iam-utilities](https://github.com/catnekaise/cdk-iam-utilities)), constraining is the act of appending existing `iam.PolicyStatement(s)` with conditions. One applied constraint may conditionally add `none`, `one` or `many` conditions to the provided policy statement(s).\n\nThe goal of constraints is to simplify working with the condition block of a policy statement when there are many conditions being used, and to help make working with the conditions \"contextual\" to the task at hand, such as creating policies for GitHub Actions attribute based access controls.\n\n### Example\nUsing the existing functionality in CDK of granting a role the permissions to read and write in a S3 bucket, there are these example requirements:\n\n- Can only read/write to an object prefix that matches `artifacts/workflow_run/${{ github.repository }}/${{ github.run_id }}/*`\n- Can only read/write when workflow is running on a `self-hosted` runner\n- Can only read/write when workflows running is a re-usable workflow on the `main` branch in repository `catnekaise/shared-workflows`\n\n#### Regular Usage\n\n```typescript\ndeclare const bucket: s3.Bucket;\n\nconst grant = bucket.grantReadWrite(role,\n        'artifacts/workflow_run/${aws:principalTag/repository}/${aws:principalTag/run_id}/*',\n);\n\ngrant.principalStatements[0].addCondition('StringEquals', {\n  'aws:principalTag/runner_environment': 'self-hosted',\n});\n\ngrant.principalStatements[0].addCondition('StringLike', {\n  'aws:principalTag/job_workflow_ref': 'catnekaise/shared-workflows/.github/workflows/*@refs/heads/main',\n});\n```\n\n#### With Policy Utility\n\n```typescript\ndeclare const pool: ActionsIdentityPoolV2;\ndeclare const bucket: s3.Bucket;\n\nconst grant = bucket.grantReadWrite(role,\n        pool.policyUtility.util.resourcePath('artifacts/workflow_run', GhaClaim.REPOSITORY, GhaCLaim.RUN_ID, '*'),\n);\n\npool.policyUtility.constrainGrant(grant)\n        .whenSelfHosted()\n        .jobWorkflowLike('catnekaise', 'shared-workflows', '*', 'refs/heads/main');\n```\n\n## Policy Constraining\nA single policy statement can be constrained in the same way as a grant.\n\n```typescript\ndeclare const role: iam.Role;\ndeclare const pool: ActionsIdentityPoolV2;\n\nconst policy = new iam.PolicyStatement({\n  effect: iam.Effect.ALLOW,\n  actions: ['ssm:GetParameter'],\n  resources: ['*'],\n});\n\npool.policyUtility.constrain(policy)\n        .hasResourceTagEqualToClaim('repository', GhaClaim.REPOSITORY);\n\nrole.addToPolicy(policy);\n```\n\n## Policy Variable and PrincipalTagConditionKey\nIt's also possible to create individual policy variables and/or principal tags via the policy utility and use them in condition statements. Any changes made to the mapped claims configurations will be reflected in variables/tags created via the policy utility.\n\n```typescript\nimport { ActionsIdentityPolicyUtility, GhaClaim } from '@catnekaise/actions-constructs';\n\nconst policyUtility = ActionsIdentityPolicyUtility.create(scope, {\n  mappedClaims: ActionsIdentityMappedClaims.create(GhaClaim.REPOSITORY, GhaClaim.ACTOR),\n});\n\nconst policyVariable = policyUtility.policyVar(GhaClaim.REPOSITORY);\nconst principalTag = policyUtility.principalTagConditionKey(GhaClaim.ACTOR);\n\nconst policy = new iam.PolicyStatement({\n  conditions: {\n    StringEquals: {\n      [principalTag.toString()]: 'catnekaise/example-repo',\n      'aws:ResourceTag/owner': policyVariable.toString(),\n    },\n  },\n});\n```\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Condition\": {\n        \"StringEquals\": {\n          \"aws:PrincipalTag/repository\": \"catnekaise/example-repo\",\n          \"aws:ResourceTag/owner\": \"${aws:PrincipalTag/actor}\"\n        }\n      }\n    }\n  ]\n}\n```\n\n## ActionsIdentityConstraints\nWhen working with constraints via the policy utility you will be using `ActionsIdentityConstraints` where convenience methods has been created for some common scenarios such as `whenSelfHosted()` and `repoOwners('catnekaise', 'additional-org')`.\n\nThese convenience methods do cover all possible scenarios. The following methods can be used for those scenarios.\n\n```typescript\nimport { ConditionOperator } from '@catnekaise/cdk-iam-utilities';\n\npool.policyUtility.newPrincipalBuilder()\n        .claimEquals(GhaClaim.REPOSITORY_VISIBILITY, 'public') // StringEquals\n        .claimLike(GhaClaim.ENVIRONMENT, 'dev-*') // StringLike\n        .claimCondition(ConditionOperator.STRING_NOT_EQUALS, GhaClaim.ENVIRONMENT, 'dev-custom');\n```\n\n\n### Constraints and CDK\nWhether on a single policy statement or on several policy statements included in a grant, each condition is added to each policy statement via `policyStatement.addCondition()`. If the combination of `Operator` and `ConditionKey` already exists, existing value(s) gets replaced.\n\n```typescript\ndeclare const role: iam.Role;\ndeclare const pool: ActionsIdentityPoolV2;\n\nconst policy = new iam.PolicyStatement({\n  effect: iam.Effect.ALLOW,\n  actions: [],\n  resources: [],\n  conditions: {\n    StringEquals: {\n      'aws:PrincipalTag/runner_environment': 'github-hosted',\n    },\n  },\n});\n\n// Condition value runner_environment is changed from github-hosted to self-hosted\npool.policyUtility.constrain(policy).whenSelfHosted();\n\n// Condition value is changed from self-hosted to github-hosted\npolicy.addCondition('StringEquals', { 'aws:PrincipalTag/runner_environment': 'self-hosted' });\n\nrole.addToPolicy(policy);\n```\n\n## Resource Paths\nConcatenate strings and claims to a resource path. All examples below results in the resource path: `items/${aws:principalTag/repository}/${aws:principalTag/environment}/*`.\n\n```typescript\ndeclare const pool: ActionsIdentityPoolV2;\ndeclare const bucket: s3.Bucket;\n\nlet resourcePath: string;\n\nresourcePath = policyUtility.resourcePath('items', GhaClaim.REPOSITORY, GhaCLaim.ENVIRONMENT, '*').toString();\n\nresourcePath = policyUtility.resourcePath()\n        .text('items')\n        .claim(GhaClaim.REPOSITORY, GhaClaim.ENVIRONMENT)\n        .text('*').toString();\n\nresourcePath = policyUtility.resourcePath('items')\n        .value(GhaClaim.REPOSITORY, GhaClaim.ENVIRONMENT, '*').toString();\n\nconst grant = bucket.grantReadWrite(role, resourcePath);\n```\n\n### ToString\nWhile some CDK constructs allow passing in resource paths of `any` type and will implicitly invoke the toString() method, it's best to invoke toString() before passing the resource path to where it will be used.\n\n### More Examples\n\n```typescript\nimport { ActionsIdentityPoolV2 } from '@catnekaise/actions-constructs';\n\ndeclare const pool: ActionsIdentityPoolV2;\ndeclare const bucket: s3.Bucket;\ndeclare const terraformStateTable: dynamodb.TableV2;\n\n// Allow Github Actions to store cache to an S3 bucket in context of repository and the ref that is running\nbucket.grantReadWrite(pool.defaultAuthenticatedRole,\n        pool.policyUtility.resourcePath('cache', GhaClaim.REPOSITORY, GhaCLaim.REF, '*'));\n\n// Allow Github Actions to upload/download artifacts in context of repository and the specific workflow run\nbucket.grantReadWrite(pool.defaultAuthenticatedRole,\n        pool.policyUtility.resourcePath('artifacts/workflow_run', GhaClaim.REPOSITORY, GhaCLaim.RUN_ID, '*'));\n\n\n// Allow terraform running in GitHub Actions to to acquire state lock, but only on the combination of repository and environment running\nconst tableGrant = terraformStateTable.grant(pool.defaultAuthenticatedRole, 'dynamodb:DescribeTable', 'dynamodb:GetItem', 'dynamodb:PutItem', 'dynamodb:DeleteItem');\ntableGrant.principalStatements.addCondition('ForAllValues:StringEquals', {\n  'dynamodb:LeadingKeys': [\n    pool.policyUtility.resourcePath(GhaClaim.REPOSITORY, GhaClaim.ENVIRONMENT, 'terraform.tfstate').toString(),\n  ],\n});\n```\n\n## Role-Chaining (Cross Account)\nThe example below can be split into separate CDK Apps.\n\n```typescript\nconst poolStack = new cdk.Stack(app, 'PoolStack', {\n  env: {\n    region: 'eu-west-1',\n    account: '111111111111',\n  },\n});\n\nconst workloadStack = new cdk.Stack(app, 'WorkloadStack', {\n  env: {\n    region: 'eu-west-1',\n    account: '222222222222',\n  },\n});\n\nworkloadStack.addDependency(poolStack);\n\nconst mappedClaims = ActionsIdentityMappedClaims.create(GhaClaim.REPOSITORY, GhaClaim.ENVIRONMENT);\n\nconst pool = new ActionsIdentityPoolV2(poolStack, 'Pool', {\n  authenticatedRoleConstraints: [\n    // change value to the name(s) of your GitHub organization(s) or username(s) that shall be allowed to authenticate\n    GitHubActionsClaimConstraint.repoOwners('catnekaise'),\n  ],\n  mappedClaims,\n  authenticatedRoleName: 'GhaCognito',\n});\n\npool.defaultAuthenticatedRole.grantAssumeRole(new iam.SessionTagsPrincipal(new iam.ArnPrincipal('arn:aws:iam::222222222222:role/github-actions/WorkloadDeploymentDev')));\n\n\nnew iam.Role(workloadStack, 'Role', {\n  roleName: 'WorkloadDeploymentDev',\n  path: '/github-actions/',\n  assumedBy: util.newChainedPrincipalBuilder()\n          .repositoryEquals('catnekaise/workload-repo')\n          .environmentEquals('dev')\n          .createPrincipalAssumedBy(workloadStack, new iam.AccountPrincipal('111111111111'), {\n            // Read more about passing claims further below\n            passClaims: mappedClaims.toPassClaims(),\n          }),\n});\n```\n\n## Role Chaining in Workflows\nFor more information, read documentation at [catnekaise/cognito-idpool-auth](https://github.com/catnekaise/cognito-idpool-auth)\n\n```yaml\non:\n  workflow_dispatch:\n    inputs:\n      environment:\n        required: true\n        description: Environment\n\njobs:\n  job1:\n    runs-on: ubuntu-latest\n    environment: ${{ inputs.environment }}\n    permissions:\n      id-token: write\n      contents: read\n    steps:\n      - name: \"Get Credentials from Amazon Cognito Identity\"\n        uses: catnekaise/cognito-idpool-auth@v1\n        with:\n          # Change values below to match your environment\n          cognito-identity-pool-id: \"eu-west-1:11111111-example\"\n          aws-role-arn: \"arn:aws:iam::111111111111:role/GhaCognito\"\n          aws-region: \"eu-west-1\"\n          audience: \"cognito-identity.amazonaws.com\"\n          aws-account-id: \"111111111111\"\n          set-in-environment: true\n          role-chain-pass-claims: \"repository,environment\"\n          role-chain-arn: \"arn:aws:iam::222222222222:role/WorkloadDevDeployment\"\n```\n\n## Passing Claims\nIn the context of this library, passing claims means tagging the next session with one or more of the GitHub Actions claims present in the current role session. Doing this is optional, but it makes it possible to use the GitHub Actions claims with policies inside the workload account.\n\nIn order to not allow the workflow to alter the value of claims during role chaining or setting any additional session tags, the trust policy in the workload account needs to be configured accordingly.\n\n```typescript\nimport * as iam from 'aws-cdk-lib/aws-iam';\nimport * as cdk from 'aws-cdk-lib';\nimport { ActionsIdentityPolicyUtility, ActionsIdentityMappedClaims, GhaClaim } from '@catnekaise/actions-constructs';\n\nconst stack = new cdk.Stack(new cdk.App(), 'WorkloadStack');\n\nconst mappedClaims = ActionsIdentityMappedClaims.create(GhaClaim.ACTOR, GhaClaim.REPOSITORY, GhaClaim.SHA, GhaClaim.REF, GhaClaim.ENVIRONMENT);\n\n// All claims will have to be defined as session tags when using AssumeRole\nconst passAllClaims = mappedClaims.toPassClaims();\n\n// Only the Repository and Environment claim has to be set when using AssumeRole\nconst passSomeClaims = mappedClaims.toPassClaims(GhaClaim.ENVIRONMENT, GhaClaim.REPOSITORY);\n\n// Only Repository and Environment has to be set, but has to be set with customized tag names\nconst passCustomClaims = mappedClaims.toPassClaimsCustom({\n  // claim: tagName\n  repository: 'repo',\n  environment: 'github_environment',\n});\n\n// Allows directly using the configure-aws-credentials action: https://github.com/aws-actions/configure-aws-credentials#session-tagging-and-name\n// Note: The only real claims passable using this is: Repository, Actor, Sha and Ref\nconst passConfigureAwsCredentialsClaims = mappedClaims.toConfigureAwsCredentialsPassClaims();\n\nconst util = ActionsIdentityPolicyUtility.create(stack, {\n  mappedClaims,\n});\n\nnew iam.Role(workloadStack, 'Role', {\n  roleName: 'WorkloadRole',\n  assumedBy: util.newChainedPrincipalBuilder()\n          .repositoryEquals('catnekaise/workload-repo')\n          .environmentEquals('dev')\n          .createPrincipalAssumedBy(workloadStack, new iam.AccountPrincipal('111111111111'), {\n            // Set one of the examples above\n            passClaims: passSomeClaims,\n          }),\n});\n```\n\n### Trust Policy\nThe example above would produce this trust policy:\n\n```json5\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"AWS\": \"arn:aws:iam::111111111111:root\"\n      },\n      \"Action\": [\n        \"sts:AssumeRole\",\n        \"sts:TagSession\"\n      ],\n      \"Condition\": {\n        \"StringEquals\": {\n          // Requires role was assumed via Cognito Identity\n          \"aws:FederatedProvider\": \"cognito-identity.amazonaws.com\",\n          // Requires that value of current session tags equals the following environment and repository\n          \"aws:PrincipalTag/environment\": \"dev\",\n          \"aws:PrincipalTag/repository\": \"catnekaise/workload-repo\",\n          // Requires setting session tags with same value as current session tags\n          \"aws:RequestTag/environment\": \"${aws:principalTag/environment}\",\n          \"aws:RequestTag/repository\": \"${aws:principalTag/repository}\"\n        },\n        \"ForAllValues:StringEquals\": {\n          // Requires that only the two following session tags are set\n          \"aws:TagKeys\": [\n            \"repository\",\n            \"environment\"\n          ]\n        }\n      }\n    }\n  ]\n}\n```\n\n# Entrypoint Account\nIn the context of this library (and its associated documentation), an `entrypoint` account is a production AWS account in an organization that is trusted to have correctly configured Cognito Identity, associated AWS IAM Roles and their permissions. This account is where GitHub Actions authentication starts.\n\nThe AWS credentials received from the Cognito Identity Pool in the entrypoint account is primarily used to perform role chaining into many workload accounts.\n\n## Grant Organization Role Chain\nWhen assuming a role cross-account, the role that performs `AssumeRole` needs permissions to do this (in addition to the cross-account role trust policy allowing the assumption to begin with). In order to not have to add permission for each individual workload role that an identity pool role shall be allowed to assume, and in order to not grant the same role the ability to assume any roles that have a weak trust policy, `grantOrganizationRoleChain` can be used.\n\n`grantOrganizationRoleChain` provides an opinionated interface of granting the type of permissions required to meet the criteria above.\n\n```typescript\nimport { ActionsIdentityPoolV2 } from '@catnekaise/actions-constructs';\n\ndeclare const pool: ActionsIdentityPoolV2;\n\npool.policyUtility.grantOrganizationRoleChain(pool.defaultAuthenticatedRole, {\n  // One of rolePath or roleHasResourceTags is required, or both.\n  rolePath: '/github-actions/',\n  roleHasResourceTags: {\n    usedViaGitHubActions: '1',\n  },\n  // Optional\n  resourceOrgPaths: ['o-example/r-ab12/ou-ab12-11111111/*'],\n  // Optional\n  // Identity Pool AWS Account ID is automatically excluded when using policyUtility via ActionsIdentityPoolV2\n  excludeAccountIds: ['333333333333', '444444444444'],\n});\n```\n\n### Policy Statement\nThe following permissions are granted to the role in the example above.\n\n```json5\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sts:TagSession\",\n      \"Resource\": \"*\"\n    },\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sts:AssumeRole\",\n      // Limited to assuming workload roles under path /github-actions/\n      \"Resource\": \"arn:aws:iam::*:role/github-actions/*\",\n      \"Condition\": {\n        \"StringEquals\": {\n          // Limited to assuming workload roles that have the following tag\n          \"aws:ResourceTag/usedViaGitHubActions\": \"1\",\n          // Limit to assuming roles within the same organization as the current role\n          \"aws:ResourceOrgID\": \"${aws:PrincipalOrgID}\"\n        },\n        \"StringNotEquals\": {\n          // Shall explicitly not be allowed to assume roles in identity pool account (111111111111)\n          // Shall explicitly not be allowed to assume roles in accounts 333333333333, 444444444444\n          \"aws:ResourceAccount\": [\"111111111111\", \"333333333333\", \"444444444444\"]\n        },\n        \"ForAnyValue:StringLike\": {\n          // Shall only be allowed to assume roles under the following organization path\n          \"aws:ResourceOrgPaths\": [\n            \"o-example/r-ab12/ou-ab12-11111111/*\"\n          ]\n        }\n      }\n    }\n  ]\n}\n```\n\n# Additional Roles\nMore roles can be created and assumed via a Cognito Identity Pool. If using the `enhanced` auth-flow, these roles must exist in the same AWS account as the identity pool. If using the `basic` auth-flow, these roles can be created in any AWS Account and reference the ID of the identity pool.\n\nUsing the [ActionsIdentityPolicyUtility](#actionsidentitypolicyutility), the trust policies for these roles can be created that align with the configuration of an identity pool in any stack.\n\n## Same Stack\n\n```typescript\ndeclare const pool: ActionsIdentityPoolV2;\n\nconst principal = pool.policyUtility.newPrincipalBuilder()\n        .repositoryEquals('catnekaise/workload-repo')\n        .createPrincipal(pool);\n\nconst role = new iam.Role(scope, 'AdditionalRole', {\n  assumedBy: principal,\n});\n\n// If using the enhanced auth-flow, assignment must be configured\npool.enhancedFlowAssignRole(role, GhaClaim.REPOSITORY, EnhancedFlowMatchType.EQUALS, 'catnekaise/workload-repo');\n```\n\n## Separate Stack\n\n```typescript\nimport { ActionsIdentityPolicyUtility } from '@catnekaise/actions-constructs';\n\nconst mappedClaims = ActionsIdentityMappedClaims.create(\n        GhaClaim.REPOSITORY,\n        // additional claims\n);\n\nconst utility = ActionsIdentityPolicyUtility.create(stack, {\n  mappedClaims,\n  // identityPoolId is required when using the principalBuilder.\n  identityPoolId: 'eu-west-1:11111111-example',\n});\n\nconst principal = utility.newPrincipalBuilder()\n        .repositoryEquals('catnekaise/workload-repo')\n        .createPrincipal(pool);\n\nnew iam.Role(scope, 'AdditionalRole', {\n  assumedBy: principal,\n});\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatnekaise%2Factions-constructs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcatnekaise%2Factions-constructs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatnekaise%2Factions-constructs/lists"}