├── .eslintrc.json ├── .gitattributes ├── .github ├── pull_request_template.md └── workflows │ ├── build.yml │ ├── pull-request-lint.yml │ ├── release.yml │ └── upgrade-main.yml ├── .gitignore ├── .mergify.yml ├── .npmignore ├── .nvmrc ├── .projen ├── deps.json ├── files.json └── tasks.json ├── .projenrc.js ├── API.md ├── LICENSE ├── README.md ├── assets ├── github-aws-oidc.drawio └── github-aws-oidc.svg ├── package-lock.json ├── package.json ├── src ├── iam-role-props.ts ├── index.ts ├── owner-regexp.ts ├── provider.ts └── role.ts ├── test ├── provider.test.ts └── role.test.ts └── tsconfig.dev.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | // ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | { 3 | "env": { 4 | "jest": true, 5 | "node": true 6 | }, 7 | "root": true, 8 | "plugins": [ 9 | "@typescript-eslint", 10 | "import" 11 | ], 12 | "parser": "@typescript-eslint/parser", 13 | "parserOptions": { 14 | "ecmaVersion": 2018, 15 | "sourceType": "module", 16 | "project": "./tsconfig.dev.json" 17 | }, 18 | "extends": [ 19 | "plugin:import/typescript" 20 | ], 21 | "settings": { 22 | "import/parsers": { 23 | "@typescript-eslint/parser": [ 24 | ".ts", 25 | ".tsx" 26 | ] 27 | }, 28 | "import/resolver": { 29 | "node": {}, 30 | "typescript": { 31 | "project": "./tsconfig.dev.json", 32 | "alwaysTryTypes": true 33 | } 34 | } 35 | }, 36 | "ignorePatterns": [ 37 | "*.js", 38 | "*.d.ts", 39 | "node_modules/", 40 | "*.generated.ts", 41 | "coverage", 42 | "!.projenrc.js" 43 | ], 44 | "rules": { 45 | "indent": [ 46 | "off" 47 | ], 48 | "@typescript-eslint/indent": [ 49 | "error", 50 | 2 51 | ], 52 | "quotes": [ 53 | "error", 54 | "single", 55 | { 56 | "avoidEscape": true 57 | } 58 | ], 59 | "comma-dangle": [ 60 | "error", 61 | "always-multiline" 62 | ], 63 | "comma-spacing": [ 64 | "error", 65 | { 66 | "before": false, 67 | "after": true 68 | } 69 | ], 70 | "no-multi-spaces": [ 71 | "error", 72 | { 73 | "ignoreEOLComments": false 74 | } 75 | ], 76 | "array-bracket-spacing": [ 77 | "error", 78 | "never" 79 | ], 80 | "array-bracket-newline": [ 81 | "error", 82 | "consistent" 83 | ], 84 | "object-curly-spacing": [ 85 | "error", 86 | "always" 87 | ], 88 | "object-curly-newline": [ 89 | "error", 90 | { 91 | "multiline": true, 92 | "consistent": true 93 | } 94 | ], 95 | "object-property-newline": [ 96 | "error", 97 | { 98 | "allowAllPropertiesOnSameLine": true 99 | } 100 | ], 101 | "keyword-spacing": [ 102 | "error" 103 | ], 104 | "brace-style": [ 105 | "error", 106 | "1tbs", 107 | { 108 | "allowSingleLine": true 109 | } 110 | ], 111 | "space-before-blocks": [ 112 | "error" 113 | ], 114 | "curly": [ 115 | "error", 116 | "multi-line", 117 | "consistent" 118 | ], 119 | "@typescript-eslint/member-delimiter-style": [ 120 | "error" 121 | ], 122 | "semi": [ 123 | "error", 124 | "always" 125 | ], 126 | "max-len": [ 127 | "error", 128 | { 129 | "code": 150, 130 | "ignoreUrls": true, 131 | "ignoreStrings": true, 132 | "ignoreTemplateLiterals": true, 133 | "ignoreComments": true, 134 | "ignoreRegExpLiterals": true 135 | } 136 | ], 137 | "quote-props": [ 138 | "error", 139 | "consistent-as-needed" 140 | ], 141 | "@typescript-eslint/no-require-imports": [ 142 | "error" 143 | ], 144 | "import/no-extraneous-dependencies": [ 145 | "error", 146 | { 147 | "devDependencies": [ 148 | "**/test/**", 149 | "**/build-tools/**" 150 | ], 151 | "optionalDependencies": false, 152 | "peerDependencies": true 153 | } 154 | ], 155 | "import/no-unresolved": [ 156 | "error" 157 | ], 158 | "import/order": [ 159 | "warn", 160 | { 161 | "groups": [ 162 | "builtin", 163 | "external" 164 | ], 165 | "alphabetize": { 166 | "order": "asc", 167 | "caseInsensitive": true 168 | } 169 | } 170 | ], 171 | "no-duplicate-imports": [ 172 | "error" 173 | ], 174 | "no-shadow": [ 175 | "off" 176 | ], 177 | "@typescript-eslint/no-shadow": [ 178 | "error" 179 | ], 180 | "key-spacing": [ 181 | "error" 182 | ], 183 | "no-multiple-empty-lines": [ 184 | "error" 185 | ], 186 | "@typescript-eslint/no-floating-promises": [ 187 | "error" 188 | ], 189 | "no-return-await": [ 190 | "off" 191 | ], 192 | "@typescript-eslint/return-await": [ 193 | "error" 194 | ], 195 | "no-trailing-spaces": [ 196 | "error" 197 | ], 198 | "dot-notation": [ 199 | "error" 200 | ], 201 | "no-bitwise": [ 202 | "error" 203 | ], 204 | "@typescript-eslint/member-ordering": [ 205 | "error", 206 | { 207 | "default": [ 208 | "public-static-field", 209 | "public-static-method", 210 | "protected-static-field", 211 | "protected-static-method", 212 | "private-static-field", 213 | "private-static-method", 214 | "field", 215 | "constructor", 216 | "method" 217 | ] 218 | } 219 | ] 220 | }, 221 | "overrides": [ 222 | { 223 | "files": [ 224 | ".projenrc.js" 225 | ], 226 | "rules": { 227 | "@typescript-eslint/no-require-imports": "off", 228 | "import/no-extraneous-dependencies": "off" 229 | } 230 | } 231 | ] 232 | } 233 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | *.snap linguist-generated 4 | /.eslintrc.json linguist-generated 5 | /.gitattributes linguist-generated 6 | /.github/pull_request_template.md linguist-generated 7 | /.github/workflows/build.yml linguist-generated 8 | /.github/workflows/pull-request-lint.yml linguist-generated 9 | /.github/workflows/release.yml linguist-generated 10 | /.github/workflows/upgrade-main.yml linguist-generated 11 | /.gitignore linguist-generated 12 | /.mergify.yml linguist-generated 13 | /.npmignore linguist-generated 14 | /.npmrc linguist-generated 15 | /.nvmrc linguist-generated 16 | /.projen/** linguist-generated 17 | /.projen/deps.json linguist-generated 18 | /.projen/files.json linguist-generated 19 | /.projen/tasks.json linguist-generated 20 | /API.md linguist-generated 21 | /LICENSE linguist-generated 22 | /package-lock.json linguist-generated 23 | /package.json linguist-generated 24 | /tsconfig.dev.json linguist-generated -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Fixes # -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: build 4 | on: 5 | pull_request: {} 6 | workflow_dispatch: {} 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: write 12 | outputs: 13 | self_mutation_happened: ${{ steps.self_mutation.outputs.self_mutation_happened }} 14 | env: 15 | CI: "true" 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | with: 20 | ref: ${{ github.event.pull_request.head.ref }} 21 | repository: ${{ github.event.pull_request.head.repo.full_name }} 22 | - name: Setup Node.js 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version: 16.20.0 26 | - name: Install dependencies 27 | run: npm install 28 | - name: build 29 | run: npx projen build 30 | - name: Upload coverage to Codecov 31 | uses: codecov/codecov-action@v3 32 | with: 33 | directory: coverage 34 | - name: Find mutations 35 | id: self_mutation 36 | run: |- 37 | git add . 38 | git diff --staged --patch --exit-code > .repo.patch || echo "self_mutation_happened=true" >> $GITHUB_OUTPUT 39 | - name: Upload patch 40 | if: steps.self_mutation.outputs.self_mutation_happened 41 | uses: actions/upload-artifact@v3 42 | with: 43 | name: .repo.patch 44 | path: .repo.patch 45 | - name: Fail build on mutation 46 | if: steps.self_mutation.outputs.self_mutation_happened 47 | run: |- 48 | echo "::error::Files were changed during build (see build log). If this was triggered from a fork, you will need to update your branch." 49 | cat .repo.patch 50 | exit 1 51 | - name: Backup artifact permissions 52 | run: cd dist && getfacl -R . > permissions-backup.acl 53 | continue-on-error: true 54 | - name: Upload artifact 55 | uses: actions/upload-artifact@v3 56 | with: 57 | name: build-artifact 58 | path: dist 59 | self-mutation: 60 | needs: build 61 | runs-on: ubuntu-latest 62 | permissions: 63 | contents: write 64 | if: always() && needs.build.outputs.self_mutation_happened && !(github.event.pull_request.head.repo.full_name != github.repository) 65 | steps: 66 | - name: Checkout 67 | uses: actions/checkout@v3 68 | with: 69 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 70 | ref: ${{ github.event.pull_request.head.ref }} 71 | repository: ${{ github.event.pull_request.head.repo.full_name }} 72 | - name: Download patch 73 | uses: actions/download-artifact@v3 74 | with: 75 | name: .repo.patch 76 | path: ${{ runner.temp }} 77 | - name: Apply patch 78 | run: '[ -s ${{ runner.temp }}/.repo.patch ] && git apply ${{ runner.temp }}/.repo.patch || echo "Empty patch. Skipping."' 79 | - name: Set git identity 80 | run: |- 81 | git config user.name "github-actions" 82 | git config user.email "github-actions@github.com" 83 | - name: Push changes 84 | env: 85 | PULL_REQUEST_REF: ${{ github.event.pull_request.head.ref }} 86 | run: |- 87 | git add . 88 | git commit -s -m "chore: self mutation" 89 | git push origin HEAD:$PULL_REQUEST_REF 90 | package-js: 91 | needs: build 92 | runs-on: ubuntu-latest 93 | permissions: {} 94 | if: "! needs.build.outputs.self_mutation_happened" 95 | steps: 96 | - uses: actions/setup-node@v3 97 | with: 98 | node-version: 16.20.0 99 | - name: Download build artifacts 100 | uses: actions/download-artifact@v3 101 | with: 102 | name: build-artifact 103 | path: dist 104 | - name: Restore build artifact permissions 105 | run: cd dist && setfacl --restore=permissions-backup.acl 106 | continue-on-error: true 107 | - name: Prepare Repository 108 | run: mv dist .repo 109 | - name: Install Dependencies 110 | run: cd .repo && npm ci 111 | - name: Create js artifact 112 | run: cd .repo && npx projen package:js 113 | - name: Collect js Artifact 114 | run: mv .repo/dist dist 115 | package-python: 116 | needs: build 117 | runs-on: ubuntu-latest 118 | permissions: {} 119 | if: "! needs.build.outputs.self_mutation_happened" 120 | steps: 121 | - uses: actions/setup-node@v3 122 | with: 123 | node-version: 16.20.0 124 | - uses: actions/setup-python@v4 125 | with: 126 | python-version: 3.x 127 | - name: Download build artifacts 128 | uses: actions/download-artifact@v3 129 | with: 130 | name: build-artifact 131 | path: dist 132 | - name: Restore build artifact permissions 133 | run: cd dist && setfacl --restore=permissions-backup.acl 134 | continue-on-error: true 135 | - name: Prepare Repository 136 | run: mv dist .repo 137 | - name: Install Dependencies 138 | run: cd .repo && npm ci 139 | - name: Create python artifact 140 | run: cd .repo && npx projen package:python 141 | - name: Collect python Artifact 142 | run: mv .repo/dist dist 143 | package-go: 144 | needs: build 145 | runs-on: ubuntu-latest 146 | permissions: {} 147 | if: "! needs.build.outputs.self_mutation_happened" 148 | steps: 149 | - uses: actions/setup-node@v3 150 | with: 151 | node-version: 16.20.0 152 | - uses: actions/setup-go@v3 153 | with: 154 | go-version: ^1.16.0 155 | - name: Download build artifacts 156 | uses: actions/download-artifact@v3 157 | with: 158 | name: build-artifact 159 | path: dist 160 | - name: Restore build artifact permissions 161 | run: cd dist && setfacl --restore=permissions-backup.acl 162 | continue-on-error: true 163 | - name: Prepare Repository 164 | run: mv dist .repo 165 | - name: Install Dependencies 166 | run: cd .repo && npm ci 167 | - name: Create go artifact 168 | run: cd .repo && npx projen package:go 169 | - name: Collect go Artifact 170 | run: mv .repo/dist dist 171 | -------------------------------------------------------------------------------- /.github/workflows/pull-request-lint.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: pull-request-lint 4 | on: 5 | pull_request_target: 6 | types: 7 | - labeled 8 | - opened 9 | - synchronize 10 | - reopened 11 | - ready_for_review 12 | - edited 13 | jobs: 14 | validate: 15 | name: Validate PR title 16 | runs-on: ubuntu-latest 17 | permissions: 18 | pull-requests: write 19 | steps: 20 | - uses: amannn/action-semantic-pull-request@v5.0.2 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | with: 24 | types: |- 25 | feat 26 | fix 27 | chore 28 | requireScope: false 29 | githubBaseUrl: ${{ github.api_url }} 30 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: release 4 | on: 5 | push: 6 | branches: 7 | - main 8 | workflow_dispatch: {} 9 | jobs: 10 | release: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: write 14 | outputs: 15 | latest_commit: ${{ steps.git_remote.outputs.latest_commit }} 16 | env: 17 | CI: "true" 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v3 21 | with: 22 | fetch-depth: 0 23 | - name: Set git identity 24 | run: |- 25 | git config user.name "github-actions" 26 | git config user.email "github-actions@github.com" 27 | - name: Setup Node.js 28 | uses: actions/setup-node@v3 29 | with: 30 | node-version: 16.20.0 31 | - name: Install dependencies 32 | run: npm ci 33 | - name: release 34 | run: npx projen release 35 | - name: Upload coverage to Codecov 36 | uses: codecov/codecov-action@v3 37 | with: 38 | directory: coverage 39 | - name: Check for new commits 40 | id: git_remote 41 | run: echo "latest_commit=$(git ls-remote origin -h ${{ github.ref }} | cut -f1)" >> $GITHUB_OUTPUT 42 | - name: Backup artifact permissions 43 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 44 | run: cd dist && getfacl -R . > permissions-backup.acl 45 | continue-on-error: true 46 | - name: Upload artifact 47 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 48 | uses: actions/upload-artifact@v3 49 | with: 50 | name: build-artifact 51 | path: dist 52 | release_github: 53 | name: Publish to GitHub Releases 54 | needs: release 55 | runs-on: ubuntu-latest 56 | permissions: 57 | contents: write 58 | if: needs.release.outputs.latest_commit == github.sha 59 | steps: 60 | - uses: actions/setup-node@v3 61 | with: 62 | node-version: 16.20.0 63 | - name: Download build artifacts 64 | uses: actions/download-artifact@v3 65 | with: 66 | name: build-artifact 67 | path: dist 68 | - name: Restore build artifact permissions 69 | run: cd dist && setfacl --restore=permissions-backup.acl 70 | continue-on-error: true 71 | - name: Prepare Repository 72 | run: mv dist .repo 73 | - name: Collect GitHub Metadata 74 | run: mv .repo/dist dist 75 | - name: Release 76 | env: 77 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 78 | GITHUB_REPOSITORY: ${{ github.repository }} 79 | GITHUB_REF: ${{ github.ref }} 80 | run: errout=$(mktemp); gh release create $(cat dist/releasetag.txt) -R $GITHUB_REPOSITORY -F dist/changelog.md -t $(cat dist/releasetag.txt) --target $GITHUB_REF 2> $errout && true; exitcode=$?; if [ $exitcode -ne 0 ] && ! grep -q "Release.tag_name already exists" $errout; then cat $errout; exit $exitcode; fi 81 | release_npm: 82 | name: Publish to npm 83 | needs: release 84 | runs-on: ubuntu-latest 85 | permissions: 86 | contents: read 87 | if: needs.release.outputs.latest_commit == github.sha 88 | steps: 89 | - uses: actions/setup-node@v3 90 | with: 91 | node-version: 16.20.0 92 | - name: Download build artifacts 93 | uses: actions/download-artifact@v3 94 | with: 95 | name: build-artifact 96 | path: dist 97 | - name: Restore build artifact permissions 98 | run: cd dist && setfacl --restore=permissions-backup.acl 99 | continue-on-error: true 100 | - name: Prepare Repository 101 | run: mv dist .repo 102 | - name: Install Dependencies 103 | run: cd .repo && npm ci 104 | - name: Create js artifact 105 | run: cd .repo && npx projen package:js 106 | - name: Collect js Artifact 107 | run: mv .repo/dist dist 108 | - name: Release 109 | env: 110 | NPM_DIST_TAG: latest 111 | NPM_REGISTRY: registry.npmjs.org 112 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 113 | run: npx -p publib@latest publib-npm 114 | release_pypi: 115 | name: Publish to PyPI 116 | needs: release 117 | runs-on: ubuntu-latest 118 | permissions: 119 | contents: read 120 | if: needs.release.outputs.latest_commit == github.sha 121 | steps: 122 | - uses: actions/setup-node@v3 123 | with: 124 | node-version: 16.20.0 125 | - uses: actions/setup-python@v4 126 | with: 127 | python-version: 3.x 128 | - name: Download build artifacts 129 | uses: actions/download-artifact@v3 130 | with: 131 | name: build-artifact 132 | path: dist 133 | - name: Restore build artifact permissions 134 | run: cd dist && setfacl --restore=permissions-backup.acl 135 | continue-on-error: true 136 | - name: Prepare Repository 137 | run: mv dist .repo 138 | - name: Install Dependencies 139 | run: cd .repo && npm ci 140 | - name: Create python artifact 141 | run: cd .repo && npx projen package:python 142 | - name: Collect python Artifact 143 | run: mv .repo/dist dist 144 | - name: Release 145 | env: 146 | TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} 147 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} 148 | run: npx -p publib@latest publib-pypi 149 | release_golang: 150 | name: Publish to GitHub Go Module Repository 151 | needs: release 152 | runs-on: ubuntu-latest 153 | permissions: 154 | contents: read 155 | if: needs.release.outputs.latest_commit == github.sha 156 | steps: 157 | - uses: actions/setup-node@v3 158 | with: 159 | node-version: 16.20.0 160 | - uses: actions/setup-go@v3 161 | with: 162 | go-version: ^1.16.0 163 | - name: Download build artifacts 164 | uses: actions/download-artifact@v3 165 | with: 166 | name: build-artifact 167 | path: dist 168 | - name: Restore build artifact permissions 169 | run: cd dist && setfacl --restore=permissions-backup.acl 170 | continue-on-error: true 171 | - name: Prepare Repository 172 | run: mv dist .repo 173 | - name: Install Dependencies 174 | run: cd .repo && npm ci 175 | - name: Create go artifact 176 | run: cd .repo && npx projen package:go 177 | - name: Collect go Artifact 178 | run: mv .repo/dist dist 179 | - name: Release 180 | env: 181 | GIT_USER_NAME: github-actions 182 | GIT_USER_EMAIL: github-actions@github.com 183 | GITHUB_TOKEN: ${{ secrets.GO_GITHUB_TOKEN }} 184 | run: npx -p publib@latest publib-golang 185 | -------------------------------------------------------------------------------- /.github/workflows/upgrade-main.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: upgrade-main 4 | on: 5 | workflow_dispatch: {} 6 | schedule: 7 | - cron: 0 0 * * * 8 | jobs: 9 | upgrade: 10 | name: Upgrade 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: read 14 | outputs: 15 | patch_created: ${{ steps.create_patch.outputs.patch_created }} 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | with: 20 | ref: main 21 | - name: Setup Node.js 22 | uses: actions/setup-node@v3 23 | with: 24 | node-version: 16.20.0 25 | - name: Install dependencies 26 | run: npm ci 27 | - name: Upgrade dependencies 28 | run: npx projen upgrade 29 | - name: Find mutations 30 | id: create_patch 31 | run: |- 32 | git add . 33 | git diff --staged --patch --exit-code > .repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT 34 | - name: Upload patch 35 | if: steps.create_patch.outputs.patch_created 36 | uses: actions/upload-artifact@v3 37 | with: 38 | name: .repo.patch 39 | path: .repo.patch 40 | pr: 41 | name: Create Pull Request 42 | needs: upgrade 43 | runs-on: ubuntu-latest 44 | permissions: 45 | contents: read 46 | if: ${{ needs.upgrade.outputs.patch_created }} 47 | steps: 48 | - name: Checkout 49 | uses: actions/checkout@v3 50 | with: 51 | ref: main 52 | - name: Download patch 53 | uses: actions/download-artifact@v3 54 | with: 55 | name: .repo.patch 56 | path: ${{ runner.temp }} 57 | - name: Apply patch 58 | run: '[ -s ${{ runner.temp }}/.repo.patch ] && git apply ${{ runner.temp }}/.repo.patch || echo "Empty patch. Skipping."' 59 | - name: Set git identity 60 | run: |- 61 | git config user.name "github-actions" 62 | git config user.email "github-actions@github.com" 63 | - name: Create Pull Request 64 | id: create-pr 65 | uses: peter-evans/create-pull-request@v4 66 | with: 67 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 68 | commit-message: |- 69 | chore(deps): upgrade dependencies 70 | 71 | Upgrades project dependencies. See details in [workflow run]. 72 | 73 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 74 | 75 | ------ 76 | 77 | *Automatically created by projen via the "upgrade-main" workflow* 78 | branch: github-actions/upgrade-main 79 | title: "chore(deps): upgrade dependencies" 80 | body: |- 81 | Upgrades project dependencies. See details in [workflow run]. 82 | 83 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 84 | 85 | ------ 86 | 87 | *Automatically created by projen via the "upgrade-main" workflow* 88 | author: github-actions 89 | committer: github-actions 90 | signoff: true 91 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | !/.gitattributes 3 | !/.projen/tasks.json 4 | !/.projen/deps.json 5 | !/.projen/files.json 6 | !/.github/workflows/pull-request-lint.yml 7 | !/package.json 8 | !/LICENSE 9 | !/.npmignore 10 | logs 11 | *.log 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | lerna-debug.log* 16 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | lib-cov 22 | coverage 23 | *.lcov 24 | .nyc_output 25 | build/Release 26 | node_modules/ 27 | jspm_packages/ 28 | *.tsbuildinfo 29 | .eslintcache 30 | *.tgz 31 | .yarn-integrity 32 | .cache 33 | .DS_Store 34 | /examples/**/cdk.context.json 35 | /examples/**/node_modules 36 | /examples/**/cdk.out 37 | /examples/**/.git 38 | **/*.drawio.bkp 39 | !/.projenrc.js 40 | /test-reports/ 41 | junit.xml 42 | /coverage/ 43 | !/.github/workflows/build.yml 44 | /dist/changelog.md 45 | /dist/version.txt 46 | !/.github/workflows/release.yml 47 | !/.mergify.yml 48 | !/.github/workflows/upgrade-main.yml 49 | !/.github/pull_request_template.md 50 | !/.npmrc 51 | !/test/ 52 | !/tsconfig.dev.json 53 | !/src/ 54 | /lib 55 | /dist/ 56 | !/.eslintrc.json 57 | .jsii 58 | tsconfig.json 59 | !/API.md 60 | !/.nvmrc 61 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | queue_rules: 4 | - name: default 5 | update_method: merge 6 | conditions: 7 | - "#approved-reviews-by>=1" 8 | - -label~=(do-not-merge) 9 | - status-success=build 10 | - status-success=package-js 11 | - status-success=package-python 12 | - status-success=package-go 13 | pull_request_rules: 14 | - name: Automatic merge on approval and successful build 15 | actions: 16 | delete_head_branch: {} 17 | queue: 18 | method: squash 19 | name: default 20 | commit_message_template: |- 21 | {{ title }} (#{{ number }}) 22 | 23 | {{ body }} 24 | conditions: 25 | - "#approved-reviews-by>=1" 26 | - -label~=(do-not-merge) 27 | - status-success=build 28 | - status-success=package-js 29 | - status-success=package-python 30 | - status-success=package-go 31 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | /.projen/ 3 | /test-reports/ 4 | junit.xml 5 | /coverage/ 6 | permissions-backup.acl 7 | /dist/changelog.md 8 | /dist/version.txt 9 | /.mergify.yml 10 | /test/ 11 | /tsconfig.dev.json 12 | /src/ 13 | !/lib/ 14 | !/lib/**/*.js 15 | !/lib/**/*.d.ts 16 | dist 17 | /tsconfig.json 18 | /.github/ 19 | /.vscode/ 20 | /.idea/ 21 | /.projenrc.js 22 | tsconfig.tsbuildinfo 23 | /.eslintrc.json 24 | !.jsii 25 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 16.20.0 -------------------------------------------------------------------------------- /.projen/deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | { 4 | "name": "@types/github-username-regex", 5 | "type": "build" 6 | }, 7 | { 8 | "name": "@types/jest", 9 | "version": "^27", 10 | "type": "build" 11 | }, 12 | { 13 | "name": "@types/node", 14 | "version": "^16", 15 | "type": "build" 16 | }, 17 | { 18 | "name": "@typescript-eslint/eslint-plugin", 19 | "version": "^5", 20 | "type": "build" 21 | }, 22 | { 23 | "name": "@typescript-eslint/parser", 24 | "version": "^5", 25 | "type": "build" 26 | }, 27 | { 28 | "name": "aws-cdk-lib", 29 | "version": "2.89.0", 30 | "type": "build" 31 | }, 32 | { 33 | "name": "constructs", 34 | "version": "10.0.0", 35 | "type": "build" 36 | }, 37 | { 38 | "name": "eslint-import-resolver-node", 39 | "type": "build" 40 | }, 41 | { 42 | "name": "eslint-import-resolver-typescript", 43 | "type": "build" 44 | }, 45 | { 46 | "name": "eslint-plugin-import", 47 | "type": "build" 48 | }, 49 | { 50 | "name": "eslint", 51 | "version": "^8", 52 | "type": "build" 53 | }, 54 | { 55 | "name": "jest-junit", 56 | "version": "^15", 57 | "type": "build" 58 | }, 59 | { 60 | "name": "jest", 61 | "version": "^27", 62 | "type": "build" 63 | }, 64 | { 65 | "name": "jsii-diff", 66 | "type": "build" 67 | }, 68 | { 69 | "name": "jsii-docgen", 70 | "type": "build" 71 | }, 72 | { 73 | "name": "jsii-pacmak", 74 | "type": "build" 75 | }, 76 | { 77 | "name": "jsii-rosetta", 78 | "version": "1.x", 79 | "type": "build" 80 | }, 81 | { 82 | "name": "jsii", 83 | "version": "1.x", 84 | "type": "build" 85 | }, 86 | { 87 | "name": "npm-check-updates", 88 | "version": "^16", 89 | "type": "build" 90 | }, 91 | { 92 | "name": "projen", 93 | "type": "build" 94 | }, 95 | { 96 | "name": "standard-version", 97 | "version": "^9", 98 | "type": "build" 99 | }, 100 | { 101 | "name": "ts-jest", 102 | "version": "^27", 103 | "type": "build" 104 | }, 105 | { 106 | "name": "typescript", 107 | "type": "build" 108 | }, 109 | { 110 | "name": "@types/babel__traverse", 111 | "version": "7.18.2", 112 | "type": "override" 113 | }, 114 | { 115 | "name": "@types/prettier", 116 | "version": "2.6.0", 117 | "type": "override" 118 | }, 119 | { 120 | "name": "aws-cdk-lib", 121 | "version": "^2.89.0", 122 | "type": "peer" 123 | }, 124 | { 125 | "name": "constructs", 126 | "version": "^10.0.0", 127 | "type": "peer" 128 | } 129 | ], 130 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 131 | } 132 | -------------------------------------------------------------------------------- /.projen/files.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | ".eslintrc.json", 4 | ".gitattributes", 5 | ".github/pull_request_template.md", 6 | ".github/workflows/build.yml", 7 | ".github/workflows/pull-request-lint.yml", 8 | ".github/workflows/release.yml", 9 | ".github/workflows/upgrade-main.yml", 10 | ".gitignore", 11 | ".mergify.yml", 12 | ".npmrc", 13 | ".nvmrc", 14 | ".projen/deps.json", 15 | ".projen/files.json", 16 | ".projen/tasks.json", 17 | "LICENSE", 18 | "tsconfig.dev.json" 19 | ], 20 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 21 | } 22 | -------------------------------------------------------------------------------- /.projen/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "build": { 4 | "name": "build", 5 | "description": "Full release build", 6 | "steps": [ 7 | { 8 | "spawn": "default" 9 | }, 10 | { 11 | "spawn": "pre-compile" 12 | }, 13 | { 14 | "spawn": "compile" 15 | }, 16 | { 17 | "spawn": "post-compile" 18 | }, 19 | { 20 | "spawn": "test" 21 | }, 22 | { 23 | "spawn": "package" 24 | } 25 | ] 26 | }, 27 | "bump": { 28 | "name": "bump", 29 | "description": "Bumps version based on latest git tag and generates a changelog entry", 30 | "env": { 31 | "OUTFILE": "package.json", 32 | "CHANGELOG": "dist/changelog.md", 33 | "BUMPFILE": "dist/version.txt", 34 | "RELEASETAG": "dist/releasetag.txt", 35 | "RELEASE_TAG_PREFIX": "" 36 | }, 37 | "steps": [ 38 | { 39 | "builtin": "release/bump-version" 40 | } 41 | ], 42 | "condition": "! git log --oneline -1 | grep -q \"chore(release):\"" 43 | }, 44 | "clobber": { 45 | "name": "clobber", 46 | "description": "hard resets to HEAD of origin and cleans the local repo", 47 | "env": { 48 | "BRANCH": "$(git branch --show-current)" 49 | }, 50 | "steps": [ 51 | { 52 | "exec": "git checkout -b scratch", 53 | "name": "save current HEAD in \"scratch\" branch" 54 | }, 55 | { 56 | "exec": "git checkout $BRANCH" 57 | }, 58 | { 59 | "exec": "git fetch origin", 60 | "name": "fetch latest changes from origin" 61 | }, 62 | { 63 | "exec": "git reset --hard origin/$BRANCH", 64 | "name": "hard reset to origin commit" 65 | }, 66 | { 67 | "exec": "git clean -fdx", 68 | "name": "clean all untracked files" 69 | }, 70 | { 71 | "say": "ready to rock! (unpushed commits are under the \"scratch\" branch)" 72 | } 73 | ], 74 | "condition": "git diff --exit-code > /dev/null" 75 | }, 76 | "compat": { 77 | "name": "compat", 78 | "description": "Perform API compatibility check against latest version", 79 | "steps": [ 80 | { 81 | "exec": "jsii-diff npm:$(node -p \"require('./package.json').name\") -k --ignore-file .compatignore || (echo \"\nUNEXPECTED BREAKING CHANGES: add keys such as 'removed:constructs.Node.of' to .compatignore to skip.\n\" && exit 1)" 82 | } 83 | ] 84 | }, 85 | "compile": { 86 | "name": "compile", 87 | "description": "Only compile", 88 | "steps": [ 89 | { 90 | "exec": "jsii --silence-warnings=reserved-word" 91 | } 92 | ] 93 | }, 94 | "default": { 95 | "name": "default", 96 | "description": "Synthesize project files", 97 | "steps": [ 98 | { 99 | "exec": "node .projenrc.js" 100 | } 101 | ] 102 | }, 103 | "docgen": { 104 | "name": "docgen", 105 | "description": "Generate API.md from .jsii manifest", 106 | "steps": [ 107 | { 108 | "exec": "jsii-docgen -o API.md" 109 | } 110 | ] 111 | }, 112 | "eject": { 113 | "name": "eject", 114 | "description": "Remove projen from the project", 115 | "env": { 116 | "PROJEN_EJECTING": "true" 117 | }, 118 | "steps": [ 119 | { 120 | "spawn": "default" 121 | } 122 | ] 123 | }, 124 | "eslint": { 125 | "name": "eslint", 126 | "description": "Runs eslint against the codebase", 127 | "steps": [ 128 | { 129 | "exec": "eslint --ext .ts,.tsx --fix --no-error-on-unmatched-pattern src test build-tools .projenrc.js" 130 | } 131 | ] 132 | }, 133 | "install": { 134 | "name": "install", 135 | "description": "Install project dependencies and update lockfile (non-frozen)", 136 | "steps": [ 137 | { 138 | "exec": "npm install" 139 | } 140 | ] 141 | }, 142 | "install:ci": { 143 | "name": "install:ci", 144 | "description": "Install project dependencies using frozen lockfile", 145 | "steps": [ 146 | { 147 | "exec": "npm ci" 148 | } 149 | ] 150 | }, 151 | "package": { 152 | "name": "package", 153 | "description": "Creates the distribution package", 154 | "steps": [ 155 | { 156 | "exec": "if [ ! -z ${CI} ]; then rsync -a . .repo --exclude .git --exclude node_modules && rm -rf dist && mv .repo dist; else npx projen package-all; fi" 157 | } 158 | ] 159 | }, 160 | "package-all": { 161 | "name": "package-all", 162 | "description": "Packages artifacts for all target languages", 163 | "steps": [ 164 | { 165 | "spawn": "package:js" 166 | }, 167 | { 168 | "spawn": "package:python" 169 | }, 170 | { 171 | "spawn": "package:go" 172 | } 173 | ] 174 | }, 175 | "package:go": { 176 | "name": "package:go", 177 | "description": "Create go language bindings", 178 | "steps": [ 179 | { 180 | "exec": "jsii-pacmak -v --target go" 181 | } 182 | ] 183 | }, 184 | "package:js": { 185 | "name": "package:js", 186 | "description": "Create js language bindings", 187 | "steps": [ 188 | { 189 | "exec": "jsii-pacmak -v --target js" 190 | } 191 | ] 192 | }, 193 | "package:python": { 194 | "name": "package:python", 195 | "description": "Create python language bindings", 196 | "steps": [ 197 | { 198 | "exec": "jsii-pacmak -v --target python" 199 | } 200 | ] 201 | }, 202 | "post-compile": { 203 | "name": "post-compile", 204 | "description": "Runs after successful compilation", 205 | "steps": [ 206 | { 207 | "spawn": "docgen" 208 | } 209 | ] 210 | }, 211 | "post-upgrade": { 212 | "name": "post-upgrade", 213 | "description": "Runs after upgrading dependencies" 214 | }, 215 | "pre-compile": { 216 | "name": "pre-compile", 217 | "description": "Prepare the project for compilation" 218 | }, 219 | "release": { 220 | "name": "release", 221 | "description": "Prepare a release from \"main\" branch", 222 | "env": { 223 | "RELEASE": "true", 224 | "MAJOR": "2" 225 | }, 226 | "steps": [ 227 | { 228 | "exec": "rm -fr dist" 229 | }, 230 | { 231 | "spawn": "bump" 232 | }, 233 | { 234 | "spawn": "build" 235 | }, 236 | { 237 | "spawn": "unbump" 238 | }, 239 | { 240 | "exec": "git diff --ignore-space-at-eol --exit-code" 241 | } 242 | ] 243 | }, 244 | "test": { 245 | "name": "test", 246 | "description": "Run tests", 247 | "steps": [ 248 | { 249 | "exec": "jest --passWithNoTests --coverageProvider=v8 --updateSnapshot", 250 | "receiveArgs": true 251 | }, 252 | { 253 | "spawn": "eslint" 254 | } 255 | ] 256 | }, 257 | "test:watch": { 258 | "name": "test:watch", 259 | "description": "Run jest in watch mode", 260 | "steps": [ 261 | { 262 | "exec": "jest --watch" 263 | } 264 | ] 265 | }, 266 | "unbump": { 267 | "name": "unbump", 268 | "description": "Restores version to 0.0.0", 269 | "env": { 270 | "OUTFILE": "package.json", 271 | "CHANGELOG": "dist/changelog.md", 272 | "BUMPFILE": "dist/version.txt", 273 | "RELEASETAG": "dist/releasetag.txt", 274 | "RELEASE_TAG_PREFIX": "" 275 | }, 276 | "steps": [ 277 | { 278 | "builtin": "release/reset-version" 279 | } 280 | ] 281 | }, 282 | "upgrade": { 283 | "name": "upgrade", 284 | "description": "upgrade dependencies", 285 | "env": { 286 | "CI": "0" 287 | }, 288 | "steps": [ 289 | { 290 | "exec": "npm update npm-check-updates" 291 | }, 292 | { 293 | "exec": "npm-check-updates --dep dev --upgrade --target=minor --reject='aws-cdk-lib,constructs,jsii-rosetta,jsii'" 294 | }, 295 | { 296 | "exec": "npm-check-updates --dep bundle --upgrade --target=minor --reject='aws-cdk-lib,constructs,jsii-rosetta,jsii'" 297 | }, 298 | { 299 | "exec": "npm-check-updates --dep peer --upgrade --target=minor --reject='aws-cdk-lib,constructs,jsii-rosetta,jsii'" 300 | }, 301 | { 302 | "exec": "npm-check-updates --dep prod --upgrade --target=minor --reject='aws-cdk-lib,constructs,jsii-rosetta,jsii'" 303 | }, 304 | { 305 | "exec": "npm-check-updates --dep optional --upgrade --target=minor --reject='aws-cdk-lib,constructs,jsii-rosetta,jsii'" 306 | }, 307 | { 308 | "exec": "npm install" 309 | }, 310 | { 311 | "exec": "npm update @types/github-username-regex @types/jest @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser aws-cdk-lib constructs eslint-import-resolver-node eslint-import-resolver-typescript eslint-plugin-import eslint jest-junit jest jsii-diff jsii-docgen jsii-pacmak jsii-rosetta jsii npm-check-updates projen standard-version ts-jest typescript aws-cdk-lib constructs" 312 | }, 313 | { 314 | "exec": "npx projen" 315 | }, 316 | { 317 | "spawn": "post-upgrade" 318 | } 319 | ] 320 | }, 321 | "watch": { 322 | "name": "watch", 323 | "description": "Watch & compile in the background", 324 | "steps": [ 325 | { 326 | "exec": "jsii -w --silence-warnings=reserved-word" 327 | } 328 | ] 329 | } 330 | }, 331 | "env": { 332 | "PATH": "$(npx -c \"node --print process.env.PATH\")" 333 | }, 334 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 335 | } 336 | -------------------------------------------------------------------------------- /.projenrc.js: -------------------------------------------------------------------------------- 1 | const { awscdk, github, TextFile, javascript } = require('projen'); 2 | 3 | const nodejsVersion = '16.20.0'; 4 | 5 | const project = new awscdk.AwsCdkConstructLibrary({ 6 | 7 | // Metadata 8 | stability: 'experimental', 9 | authorName: 'Ari Palo', 10 | authorOrganization: false, 11 | authorAddress: 'opensource@aripalo.com', 12 | name: 'aws-cdk-github-oidc', 13 | description: 'CDK constructs to use OpenID Connect for authenticating your Github Action workflow with AWS IAM', 14 | repositoryUrl: 'https://github.com/aripalo/aws-cdk-github-oidc.git', 15 | keywords: ['cdk', 'aws-cdk', 'awscdk', 'aws', 'iam', 'github', 'github-actions', 'oidc', 'openid-connect'], 16 | 17 | // Publish configuration 18 | defaultReleaseBranch: 'main', 19 | majorVersion: 2, // we default release the main branch(cdkv2) with major version 2. 20 | packageManager: javascript.NodePackageManager.NPM, 21 | npmAccess: javascript.NpmAccess.PUBLIC, 22 | publishToPypi: { 23 | distName: 'aws-cdk-github-oidc', 24 | module: 'aws_cdk_github_oidc', 25 | }, 26 | publishToGo: { 27 | moduleName: 'github.com/aripalo/aws-cdk-github-oidc-go', 28 | }, 29 | 30 | // Dependencies 31 | minNodeVersion: nodejsVersion, 32 | cdkVersion: '2.89.0', 33 | constructsVersion: '10.0.0', 34 | peerDeps: ['constructs', 'aws-cdk-lib'], 35 | devDeps: ['@types/github-username-regex', 'constructs'], 36 | bundledDeps: [], 37 | 38 | // Gitignore 39 | gitignore: [ 40 | '.DS_Store', 41 | '/examples/**/cdk.context.json', 42 | '/examples/**/node_modules', 43 | '/examples/**/cdk.out', 44 | '/examples/**/.git', 45 | '**/*.drawio.bkp', 46 | ], 47 | 48 | tsconfig: { 49 | compilerOptions: { 50 | esModuleInterop: true, // required for github-username-regex 51 | }, 52 | }, 53 | 54 | 55 | codeCov: true, 56 | }); 57 | 58 | new TextFile(project, '.nvmrc', { 59 | lines: [nodejsVersion], 60 | }); 61 | 62 | project.synth(); 63 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | # API Reference 2 | 3 | ## Constructs 4 | 5 | ### GithubActionsIdentityProvider 6 | 7 | - *Implements:* [`aws-cdk-github-oidc.IGithubActionsIdentityProvider`](#aws-cdk-github-oidc.IGithubActionsIdentityProvider) 8 | 9 | Github Actions as OpenID Connect Identity Provider for AWS IAM. There can be only one (per AWS Account). 10 | 11 | Use `fromAccount` to retrieve a reference to existing Github OIDC provider. 12 | 13 | > https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services 14 | 15 | #### Initializers 16 | 17 | ```typescript 18 | import { GithubActionsIdentityProvider } from 'aws-cdk-github-oidc' 19 | 20 | new GithubActionsIdentityProvider(scope: Construct, id: string) 21 | ``` 22 | 23 | ##### `scope`Required 24 | 25 | - *Type:* [`constructs.Construct`](#constructs.Construct) 26 | 27 | CDK Stack or Construct to which the provider is assigned to. 28 | 29 | --- 30 | 31 | ##### `id`Required 32 | 33 | - *Type:* `string` 34 | 35 | CDK Construct ID given to the construct. 36 | 37 | --- 38 | 39 | 40 | #### Static Functions 41 | 42 | ##### `fromAccount` 43 | 44 | ```typescript 45 | import { GithubActionsIdentityProvider } from 'aws-cdk-github-oidc' 46 | 47 | GithubActionsIdentityProvider.fromAccount(scope: Construct, id: string) 48 | ``` 49 | 50 | ###### `scope`Required 51 | 52 | - *Type:* [`constructs.Construct`](#constructs.Construct) 53 | 54 | CDK Stack or Construct to which the provider is assigned to. 55 | 56 | --- 57 | 58 | ###### `id`Required 59 | 60 | - *Type:* `string` 61 | 62 | CDK Construct ID given to the construct. 63 | 64 | --- 65 | 66 | 67 | #### Constants 68 | 69 | ##### `issuer` 70 | 71 | - *Type:* `string` 72 | 73 | --- 74 | 75 | ### GithubActionsRole 76 | 77 | Define an IAM Role that can be assumed by Github Actions workflow via Github OpenID Connect Identity Provider. 78 | 79 | Besides `GithubConfiguration`, you may pass in any `iam.RoleProps` except `assumedBy` 80 | which will be defined by this construct (CDK will fail if you do). 81 | 82 | #### Initializers 83 | 84 | ```typescript 85 | import { GithubActionsRole } from 'aws-cdk-github-oidc' 86 | 87 | new GithubActionsRole(scope: Construct, id: string, props: GithubActionsRoleProps) 88 | ``` 89 | 90 | ##### `scope`Required 91 | 92 | - *Type:* [`constructs.Construct`](#constructs.Construct) 93 | 94 | --- 95 | 96 | ##### `id`Required 97 | 98 | - *Type:* `string` 99 | 100 | --- 101 | 102 | ##### `props`Required 103 | 104 | - *Type:* [`aws-cdk-github-oidc.GithubActionsRoleProps`](#aws-cdk-github-oidc.GithubActionsRoleProps) 105 | 106 | --- 107 | 108 | 109 | 110 | 111 | 112 | ## Structs 113 | 114 | ### GithubActionsRoleProps 115 | 116 | Props that define the IAM Role that can be assumed by Github Actions workflow via Github OpenID Connect Identity Provider. 117 | 118 | Besides `GithubConfiguration`, you may pass in any `iam.RoleProps` except `assumedBy` 119 | which will be defined by this construct (CDK will fail if you do). 120 | 121 | #### Initializer 122 | 123 | ```typescript 124 | import { GithubActionsRoleProps } from 'aws-cdk-github-oidc' 125 | 126 | const githubActionsRoleProps: GithubActionsRoleProps = { ... } 127 | ``` 128 | 129 | ##### `owner`Required 130 | 131 | ```typescript 132 | public readonly owner: string; 133 | ``` 134 | 135 | - *Type:* `string` 136 | 137 | Repository owner (organization or username). 138 | 139 | --- 140 | 141 | ##### `provider`Required 142 | 143 | ```typescript 144 | public readonly provider: IGithubActionsIdentityProvider; 145 | ``` 146 | 147 | - *Type:* [`aws-cdk-github-oidc.IGithubActionsIdentityProvider`](#aws-cdk-github-oidc.IGithubActionsIdentityProvider) 148 | 149 | Reference to Github OpenID Connect Provider configured in AWS IAM. 150 | 151 | Either pass an construct defined by `new GithubActionsIdentityProvider` 152 | or a retrieved reference from `GithubActionsIdentityProvider.fromAccount`. 153 | There can be only one (per AWS Account). 154 | 155 | --- 156 | 157 | ##### `repo`Required 158 | 159 | ```typescript 160 | public readonly repo: string; 161 | ``` 162 | 163 | - *Type:* `string` 164 | 165 | Repository name (slug) without the owner. 166 | 167 | --- 168 | 169 | ##### `filter`Optional 170 | 171 | ```typescript 172 | public readonly filter: string; 173 | ``` 174 | 175 | - *Type:* `string` 176 | - *Default:* '*' 177 | 178 | You may use this value to only allow Github to assume the role on specific branches, tags, environments, pull requests etc. 179 | 180 | Subject condition filter, appended after `repo:${owner}/${repo}:` string in IAM Role trust relationship. 181 | 182 | > https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#examples 183 | 184 | --- 185 | 186 | ##### `description`Optional 187 | 188 | ```typescript 189 | public readonly description: string; 190 | ``` 191 | 192 | - *Type:* `string` 193 | - *Default:* No description. 194 | 195 | A description of the role. 196 | 197 | It can be up to 1000 characters long. 198 | 199 | --- 200 | 201 | ##### `externalIds`Optional 202 | 203 | ```typescript 204 | public readonly externalIds: string[]; 205 | ``` 206 | 207 | - *Type:* `string`[] 208 | - *Default:* No external ID required 209 | 210 | List of IDs that the role assumer needs to provide one of when assuming this role. 211 | 212 | If the configured and provided external IDs do not match, the 213 | AssumeRole operation will fail. 214 | 215 | --- 216 | 217 | ##### `inlinePolicies`Optional 218 | 219 | ```typescript 220 | public readonly inlinePolicies: {[ key: string ]: PolicyDocument}; 221 | ``` 222 | 223 | - *Type:* {[ key: string ]: [`aws-cdk-lib.aws_iam.PolicyDocument`](#aws-cdk-lib.aws_iam.PolicyDocument)} 224 | - *Default:* No policy is inlined in the Role resource. 225 | 226 | A list of named policies to inline into this role. 227 | 228 | These policies will be 229 | created with the role, whereas those added by ``addToPolicy`` are added 230 | using a separate CloudFormation resource (allowing a way around circular 231 | dependencies that could otherwise be introduced). 232 | 233 | --- 234 | 235 | ##### `managedPolicies`Optional 236 | 237 | ```typescript 238 | public readonly managedPolicies: IManagedPolicy[]; 239 | ``` 240 | 241 | - *Type:* [`aws-cdk-lib.aws_iam.IManagedPolicy`](#aws-cdk-lib.aws_iam.IManagedPolicy)[] 242 | - *Default:* No managed policies. 243 | 244 | A list of managed policies associated with this role. 245 | 246 | You can add managed policies later using 247 | `addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName(policyName))`. 248 | 249 | --- 250 | 251 | ##### `maxSessionDuration`Optional 252 | 253 | ```typescript 254 | public readonly maxSessionDuration: Duration; 255 | ``` 256 | 257 | - *Type:* [`aws-cdk-lib.Duration`](#aws-cdk-lib.Duration) 258 | - *Default:* Duration.hours(1) 259 | 260 | The maximum session duration that you want to set for the specified role. 261 | 262 | This setting can have a value from 1 hour (3600sec) to 12 (43200sec) hours. 263 | 264 | Anyone who assumes the role from the AWS CLI or API can use the 265 | DurationSeconds API parameter or the duration-seconds CLI parameter to 266 | request a longer session. The MaxSessionDuration setting determines the 267 | maximum duration that can be requested using the DurationSeconds 268 | parameter. 269 | 270 | If users don't specify a value for the DurationSeconds parameter, their 271 | security credentials are valid for one hour by default. This applies when 272 | you use the AssumeRole* API operations or the assume-role* CLI operations 273 | but does not apply when you use those operations to create a console URL. 274 | 275 | > [https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html) 276 | 277 | --- 278 | 279 | ##### `path`Optional 280 | 281 | ```typescript 282 | public readonly path: string; 283 | ``` 284 | 285 | - *Type:* `string` 286 | - *Default:* / 287 | 288 | The path associated with this role. 289 | 290 | For information about IAM paths, see 291 | Friendly Names and Paths in IAM User Guide. 292 | 293 | --- 294 | 295 | ##### `permissionsBoundary`Optional 296 | 297 | ```typescript 298 | public readonly permissionsBoundary: IManagedPolicy; 299 | ``` 300 | 301 | - *Type:* [`aws-cdk-lib.aws_iam.IManagedPolicy`](#aws-cdk-lib.aws_iam.IManagedPolicy) 302 | - *Default:* No permissions boundary. 303 | 304 | AWS supports permissions boundaries for IAM entities (users or roles). 305 | 306 | A permissions boundary is an advanced feature for using a managed policy 307 | to set the maximum permissions that an identity-based policy can grant to 308 | an IAM entity. An entity's permissions boundary allows it to perform only 309 | the actions that are allowed by both its identity-based policies and its 310 | permissions boundaries. 311 | 312 | > [https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html) 313 | 314 | --- 315 | 316 | ##### `roleName`Optional 317 | 318 | ```typescript 319 | public readonly roleName: string; 320 | ``` 321 | 322 | - *Type:* `string` 323 | - *Default:* AWS CloudFormation generates a unique physical ID and uses that ID 324 | for the role name. 325 | 326 | A name for the IAM role. 327 | 328 | For valid values, see the RoleName parameter for 329 | the CreateRole action in the IAM API Reference. 330 | 331 | IMPORTANT: If you specify a name, you cannot perform updates that require 332 | replacement of this resource. You can perform updates that require no or 333 | some interruption. If you must replace the resource, specify a new name. 334 | 335 | If you specify a name, you must specify the CAPABILITY_NAMED_IAM value to 336 | acknowledge your template's capabilities. For more information, see 337 | Acknowledging IAM Resources in AWS CloudFormation Templates. 338 | 339 | --- 340 | 341 | ### GithubConfiguration 342 | 343 | Github related configuration that forms the trust policy for this IAM Role. 344 | 345 | #### Initializer 346 | 347 | ```typescript 348 | import { GithubConfiguration } from 'aws-cdk-github-oidc' 349 | 350 | const githubConfiguration: GithubConfiguration = { ... } 351 | ``` 352 | 353 | ##### `owner`Required 354 | 355 | ```typescript 356 | public readonly owner: string; 357 | ``` 358 | 359 | - *Type:* `string` 360 | 361 | Repository owner (organization or username). 362 | 363 | --- 364 | 365 | ##### `provider`Required 366 | 367 | ```typescript 368 | public readonly provider: IGithubActionsIdentityProvider; 369 | ``` 370 | 371 | - *Type:* [`aws-cdk-github-oidc.IGithubActionsIdentityProvider`](#aws-cdk-github-oidc.IGithubActionsIdentityProvider) 372 | 373 | Reference to Github OpenID Connect Provider configured in AWS IAM. 374 | 375 | Either pass an construct defined by `new GithubActionsIdentityProvider` 376 | or a retrieved reference from `GithubActionsIdentityProvider.fromAccount`. 377 | There can be only one (per AWS Account). 378 | 379 | --- 380 | 381 | ##### `repo`Required 382 | 383 | ```typescript 384 | public readonly repo: string; 385 | ``` 386 | 387 | - *Type:* `string` 388 | 389 | Repository name (slug) without the owner. 390 | 391 | --- 392 | 393 | ##### `filter`Optional 394 | 395 | ```typescript 396 | public readonly filter: string; 397 | ``` 398 | 399 | - *Type:* `string` 400 | - *Default:* '*' 401 | 402 | You may use this value to only allow Github to assume the role on specific branches, tags, environments, pull requests etc. 403 | 404 | Subject condition filter, appended after `repo:${owner}/${repo}:` string in IAM Role trust relationship. 405 | 406 | > https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#examples 407 | 408 | --- 409 | 410 | ### RoleProps 411 | 412 | Properties for defining an IAM Role. 413 | 414 | These are copied fron @aws-cdk/aws-iam, but since JSII does not support 415 | TypeScript > (or Omit), we have to do this stupid thing. 416 | 417 | Basically exactly the same as source, but with assumedBy removed. 418 | 419 | #### Initializer 420 | 421 | ```typescript 422 | import { RoleProps } from 'aws-cdk-github-oidc' 423 | 424 | const roleProps: RoleProps = { ... } 425 | ``` 426 | 427 | ##### `description`Optional 428 | 429 | ```typescript 430 | public readonly description: string; 431 | ``` 432 | 433 | - *Type:* `string` 434 | - *Default:* No description. 435 | 436 | A description of the role. 437 | 438 | It can be up to 1000 characters long. 439 | 440 | --- 441 | 442 | ##### `externalIds`Optional 443 | 444 | ```typescript 445 | public readonly externalIds: string[]; 446 | ``` 447 | 448 | - *Type:* `string`[] 449 | - *Default:* No external ID required 450 | 451 | List of IDs that the role assumer needs to provide one of when assuming this role. 452 | 453 | If the configured and provided external IDs do not match, the 454 | AssumeRole operation will fail. 455 | 456 | --- 457 | 458 | ##### `inlinePolicies`Optional 459 | 460 | ```typescript 461 | public readonly inlinePolicies: {[ key: string ]: PolicyDocument}; 462 | ``` 463 | 464 | - *Type:* {[ key: string ]: [`aws-cdk-lib.aws_iam.PolicyDocument`](#aws-cdk-lib.aws_iam.PolicyDocument)} 465 | - *Default:* No policy is inlined in the Role resource. 466 | 467 | A list of named policies to inline into this role. 468 | 469 | These policies will be 470 | created with the role, whereas those added by ``addToPolicy`` are added 471 | using a separate CloudFormation resource (allowing a way around circular 472 | dependencies that could otherwise be introduced). 473 | 474 | --- 475 | 476 | ##### `managedPolicies`Optional 477 | 478 | ```typescript 479 | public readonly managedPolicies: IManagedPolicy[]; 480 | ``` 481 | 482 | - *Type:* [`aws-cdk-lib.aws_iam.IManagedPolicy`](#aws-cdk-lib.aws_iam.IManagedPolicy)[] 483 | - *Default:* No managed policies. 484 | 485 | A list of managed policies associated with this role. 486 | 487 | You can add managed policies later using 488 | `addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName(policyName))`. 489 | 490 | --- 491 | 492 | ##### `maxSessionDuration`Optional 493 | 494 | ```typescript 495 | public readonly maxSessionDuration: Duration; 496 | ``` 497 | 498 | - *Type:* [`aws-cdk-lib.Duration`](#aws-cdk-lib.Duration) 499 | - *Default:* Duration.hours(1) 500 | 501 | The maximum session duration that you want to set for the specified role. 502 | 503 | This setting can have a value from 1 hour (3600sec) to 12 (43200sec) hours. 504 | 505 | Anyone who assumes the role from the AWS CLI or API can use the 506 | DurationSeconds API parameter or the duration-seconds CLI parameter to 507 | request a longer session. The MaxSessionDuration setting determines the 508 | maximum duration that can be requested using the DurationSeconds 509 | parameter. 510 | 511 | If users don't specify a value for the DurationSeconds parameter, their 512 | security credentials are valid for one hour by default. This applies when 513 | you use the AssumeRole* API operations or the assume-role* CLI operations 514 | but does not apply when you use those operations to create a console URL. 515 | 516 | > [https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html) 517 | 518 | --- 519 | 520 | ##### `path`Optional 521 | 522 | ```typescript 523 | public readonly path: string; 524 | ``` 525 | 526 | - *Type:* `string` 527 | - *Default:* / 528 | 529 | The path associated with this role. 530 | 531 | For information about IAM paths, see 532 | Friendly Names and Paths in IAM User Guide. 533 | 534 | --- 535 | 536 | ##### `permissionsBoundary`Optional 537 | 538 | ```typescript 539 | public readonly permissionsBoundary: IManagedPolicy; 540 | ``` 541 | 542 | - *Type:* [`aws-cdk-lib.aws_iam.IManagedPolicy`](#aws-cdk-lib.aws_iam.IManagedPolicy) 543 | - *Default:* No permissions boundary. 544 | 545 | AWS supports permissions boundaries for IAM entities (users or roles). 546 | 547 | A permissions boundary is an advanced feature for using a managed policy 548 | to set the maximum permissions that an identity-based policy can grant to 549 | an IAM entity. An entity's permissions boundary allows it to perform only 550 | the actions that are allowed by both its identity-based policies and its 551 | permissions boundaries. 552 | 553 | > [https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html) 554 | 555 | --- 556 | 557 | ##### `roleName`Optional 558 | 559 | ```typescript 560 | public readonly roleName: string; 561 | ``` 562 | 563 | - *Type:* `string` 564 | - *Default:* AWS CloudFormation generates a unique physical ID and uses that ID 565 | for the role name. 566 | 567 | A name for the IAM role. 568 | 569 | For valid values, see the RoleName parameter for 570 | the CreateRole action in the IAM API Reference. 571 | 572 | IMPORTANT: If you specify a name, you cannot perform updates that require 573 | replacement of this resource. You can perform updates that require no or 574 | some interruption. If you must replace the resource, specify a new name. 575 | 576 | If you specify a name, you must specify the CAPABILITY_NAMED_IAM value to 577 | acknowledge your template's capabilities. For more information, see 578 | Acknowledging IAM Resources in AWS CloudFormation Templates. 579 | 580 | --- 581 | 582 | 583 | ## Protocols 584 | 585 | ### IGithubActionsIdentityProvider 586 | 587 | - *Extends:* [`aws-cdk-lib.aws_iam.IOpenIdConnectProvider`](#aws-cdk-lib.aws_iam.IOpenIdConnectProvider) 588 | 589 | - *Implemented By:* [`aws-cdk-github-oidc.GithubActionsIdentityProvider`](#aws-cdk-github-oidc.GithubActionsIdentityProvider), [`aws-cdk-github-oidc.IGithubActionsIdentityProvider`](#aws-cdk-github-oidc.IGithubActionsIdentityProvider) 590 | 591 | Describes a Github OpenID Connect Identity Provider for AWS IAM. 592 | 593 | 594 | #### Properties 595 | 596 | ##### `node`Required 597 | 598 | ```typescript 599 | public readonly node: Node; 600 | ``` 601 | 602 | - *Type:* [`constructs.Node`](#constructs.Node) 603 | 604 | The tree node. 605 | 606 | --- 607 | 608 | ##### `env`Required 609 | 610 | ```typescript 611 | public readonly env: ResourceEnvironment; 612 | ``` 613 | 614 | - *Type:* [`aws-cdk-lib.ResourceEnvironment`](#aws-cdk-lib.ResourceEnvironment) 615 | 616 | The environment this resource belongs to. 617 | 618 | For resources that are created and managed by the CDK 619 | (generally, those created by creating new class instances like Role, Bucket, etc.), 620 | this is always the same as the environment of the stack they belong to; 621 | however, for imported resources 622 | (those obtained from static methods like fromRoleArn, fromBucketName, etc.), 623 | that might be different than the stack they were imported into. 624 | 625 | --- 626 | 627 | ##### `stack`Required 628 | 629 | ```typescript 630 | public readonly stack: Stack; 631 | ``` 632 | 633 | - *Type:* [`aws-cdk-lib.Stack`](#aws-cdk-lib.Stack) 634 | 635 | The stack in which this resource is defined. 636 | 637 | --- 638 | 639 | ##### `openIdConnectProviderArn`Required 640 | 641 | ```typescript 642 | public readonly openIdConnectProviderArn: string; 643 | ``` 644 | 645 | - *Type:* `string` 646 | 647 | The Amazon Resource Name (ARN) of the IAM OpenID Connect provider. 648 | 649 | --- 650 | 651 | ##### `openIdConnectProviderIssuer`Required 652 | 653 | ```typescript 654 | public readonly openIdConnectProviderIssuer: string; 655 | ``` 656 | 657 | - *Type:* `string` 658 | 659 | The issuer for OIDC Provider. 660 | 661 | --- 662 | 663 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS CDK Github OpenID Connect 2 | 3 | ![cdk-support](https://img.shields.io/badge/cdk-%20typescript%20|%20python%20-informational "TypeScript | Python") 4 | [![release](https://github.com/aripalo/aws-cdk-github-oidc/actions/workflows/release.yml/badge.svg)](https://github.com/aripalo/aws-cdk-github-oidc/actions/workflows/release.yml) 5 | [![codecov](https://codecov.io/gh/aripalo/aws-cdk-github-oidc/branch/main/graph/badge.svg?token=5X44RM6J17)](https://codecov.io/gh/aripalo/aws-cdk-github-oidc) 6 | 7 | --- 8 | 9 | AWS [CDK](https://aws.amazon.com/cdk/) constructs that define: 10 | 11 | - Github Actions as OpenID Connect Identity Provider into AWS IAM 12 | - IAM Roles that can be assumed by Github Actions workflows 13 | 14 | These constructs allows you to harden your AWS deployment security by removing the need to create long-term access keys for Github Actions and instead use OpenID Connect to Authenticate your Github Action workflow with AWS IAM. 15 | 16 | ## Background information 17 | 18 | ![github-aws-oidc](/assets/github-aws-oidc.svg "Github OIDC with AWS") 19 | 20 | - [GitHub Actions: Secure cloud deployments with OpenID Connect](https://github.blog/changelog/2021-10-27-github-actions-secure-cloud-deployments-with-openid-connect/) on Github Changelog Blog. 21 | - [Security hardening your deployments](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments) on Github Docs. 22 | - [Assuming a role with `aws-actions/configure-aws-credentials`](https://github.com/aws-actions/configure-aws-credentials#assuming-a-role). 23 | - Shout-out to [Richard H. Boyd](https://twitter.com/rchrdbyd) for helping me to debug Github OIDC setup with AWS IAM and his [Deploying to AWS with Github Actions](https://www.githubuniverse.com/2021/session/692586/deploying-to-aws-with-github-actions)-talk. 24 | - Shout-out to [Aidan W Steele](https://twitter.com/__steele) and his blog post [AWS federation comes to GitHub Actions](https://awsteele.com/blog/2021/09/15/aws-federation-comes-to-github-actions.html) for being the original inspiration for this. 25 | 26 |
27 | 28 | ## Getting started 29 | 30 | ```shell 31 | npm i -D aws-cdk-github-oidc 32 | ``` 33 | 34 |
35 | 36 | ### OpenID Connect Identity Provider trust for AWS IAM 37 | 38 | To create a new Github OIDC provider configuration into AWS IAM: 39 | 40 | ```ts 41 | import { GithubActionsIdentityProvider } from "aws-cdk-github-oidc"; 42 | 43 | const provider = new GithubActionsIdentityProvider(scope, "GithubProvider"); 44 | ``` 45 | 46 | In the background this creates an OIDC provider trust configuration into AWS IAM with an [issuer URL of `https://token.actions.githubusercontent.com`](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services#adding-the-identity-provider-to-aws) and audiences (client IDs) configured as `['sts.amazonaws.com']` (which matches the [`aws-actions/configure-aws-credentials`](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services#adding-the-identity-provider-to-aws) implementation). 47 | 48 |
49 | 50 | ### Retrieving a reference to an existing Github OIDC provider configuration 51 | 52 | Remember, **there can be only one (Github OIDC provider per AWS Account)**, so to retrieve a reference to existing Github OIDC provider use `fromAccount` static method: 53 | 54 | ```ts 55 | import { GithubActionsIdentityProvider } from "aws-cdk-github-oidc"; 56 | 57 | const provider = GithubActionsIdentityProvider.fromAccount( 58 | scope, 59 | "GithubProvider" 60 | ); 61 | ``` 62 | 63 |
64 | 65 | ### Defining a role for Github Actions workflow to assume 66 | 67 | ```ts 68 | import { GithubActionsRole } from "aws-cdk-github-oidc"; 69 | 70 | const uploadRole = new GithubActionsRole(scope, "UploadRole", { 71 | provider: provider, // reference into the OIDC provider 72 | owner: "octo-org", // your repository owner (organization or user) name 73 | repo: "octo-repo", // your repository name (without the owner name) 74 | filter: "ref:refs/tags/v*", // JWT sub suffix filter, defaults to '*' 75 | }); 76 | 77 | // use it like any other role, for example grant S3 bucket write access: 78 | myBucket.grantWrite(uploadRole); 79 | ``` 80 | 81 | You may pass in any `iam.RoleProps` into the construct's props, except `assumedBy` which will be defined by this construct (CDK will fail if you do): 82 | 83 | ```ts 84 | const deployRole = new GithubActionsRole(scope, "DeployRole", { 85 | provider: provider, 86 | owner: "octo-org", 87 | repo: "octo-repo", 88 | roleName: "MyDeployRole", 89 | description: "This role deploys stuff to AWS", 90 | maxSessionDuration: cdk.Duration.hours(2), 91 | }); 92 | 93 | // You may also use various "add*" policy methods! 94 | // "AdministratorAccess" not really a good idea, just for an example here: 95 | deployRole.addManagedPolicy( 96 | iam.ManagedPolicy.fromAwsManagedPolicyName("AdministratorAccess") 97 | ); 98 | ``` 99 | 100 |
101 | 102 | #### Subject Filter 103 | 104 | By default the value of `filter` property will be `'*'` which means any workflow (from given repository) from any branch, tag, environment or pull request can assume this role. To further stricten the OIDC trust policy on the role, you may adjust the subject filter as seen on the [examples in Github Docs](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#configuring-the-oidc-trust-with-the-cloud); For example: 105 | 106 | | `filter` value | Descrition | 107 | | :----------------------------- | :--------------------------------------- | 108 | | `'ref:refs/tags/v*'` | Allow only tags with prefix of `v` | 109 | | `'ref:refs/heads/demo-branch'` | Allow only from branch `demo-branch` | 110 | | `'pull_request'` | Allow only from pull request | 111 | | `'environment:Production'` | Allow only from `Production` environment | 112 | 113 |
114 | 115 | ### Github Actions Workflow 116 | 117 | To actually utilize this in your Github Actions workflow, use [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) to [assume a role](https://github.com/aws-actions/configure-aws-credentials#assuming-a-role). 118 | 119 | At the moment you must use the `master` version (until AWS releases a new tag): 120 | 121 | ```yaml 122 | jobs: 123 | deploy: 124 | name: Upload to Amazon S3 125 | runs-on: ubuntu-latest 126 | permissions: 127 | id-token: write # needed to interact with GitHub's OIDC Token endpoint. 128 | contents: read 129 | steps: 130 | - name: Checkout 131 | uses: actions/checkout@v2 132 | 133 | - name: Configure AWS credentials 134 | uses: aws-actions/configure-aws-credentials@master 135 | with: 136 | role-to-assume: arn:aws:iam::123456789012:role/MyUploadRole 137 | #role-session-name: MySessionName # Optional 138 | aws-region: us-east-1 139 | 140 | - name: Sync files to S3 141 | run: | 142 | aws s3 sync . s3://my-example-bucket 143 | ``` 144 | 145 |
146 | -------------------------------------------------------------------------------- /assets/github-aws-oidc.drawio: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /assets/github-aws-oidc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Github ActionsWorkflow
AWS Account
AWS Account
Target Role
with Trust Relationship
Target Rol...
AWS resources
AWS resour...
JWT TokenOIDC TrustConfiguration
Github Actions
Github Actions
Github ActionsOIDC Provider
STS returns
Session Credentials
STS r...
Operate with
Session Credentials
for Target Role
Opera...
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-cdk-github-oidc", 3 | "description": "CDK constructs to use OpenID Connect for authenticating your Github Action workflow with AWS IAM", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/aripalo/aws-cdk-github-oidc.git" 7 | }, 8 | "scripts": { 9 | "build": "npx projen build", 10 | "bump": "npx projen bump", 11 | "clobber": "npx projen clobber", 12 | "compat": "npx projen compat", 13 | "compile": "npx projen compile", 14 | "default": "npx projen default", 15 | "docgen": "npx projen docgen", 16 | "eject": "npx projen eject", 17 | "eslint": "npx projen eslint", 18 | "package": "npx projen package", 19 | "package-all": "npx projen package-all", 20 | "package:go": "npx projen package:go", 21 | "package:js": "npx projen package:js", 22 | "package:python": "npx projen package:python", 23 | "post-compile": "npx projen post-compile", 24 | "post-upgrade": "npx projen post-upgrade", 25 | "pre-compile": "npx projen pre-compile", 26 | "release": "npx projen release", 27 | "test": "npx projen test", 28 | "test:watch": "npx projen test:watch", 29 | "unbump": "npx projen unbump", 30 | "upgrade": "npx projen upgrade", 31 | "watch": "npx projen watch", 32 | "projen": "npx projen" 33 | }, 34 | "author": { 35 | "name": "Ari Palo", 36 | "email": "opensource@aripalo.com", 37 | "organization": false 38 | }, 39 | "devDependencies": { 40 | "@types/github-username-regex": "^1.0.0", 41 | "@types/jest": "^27", 42 | "@types/node": "^16", 43 | "@typescript-eslint/eslint-plugin": "^5", 44 | "@typescript-eslint/parser": "^5", 45 | "aws-cdk-lib": "2.89.0", 46 | "constructs": "10.0.0", 47 | "eslint": "^8", 48 | "eslint-import-resolver-node": "^0.3.7", 49 | "eslint-import-resolver-typescript": "^2.7.1", 50 | "eslint-plugin-import": "^2.28.0", 51 | "jest": "^27", 52 | "jest-junit": "^15", 53 | "jsii": "1.x", 54 | "jsii-diff": "^1.86.1", 55 | "jsii-docgen": "^3.8.31", 56 | "jsii-pacmak": "^1.86.1", 57 | "jsii-rosetta": "1.x", 58 | "npm-check-updates": "^16", 59 | "projen": "^0.71.161", 60 | "standard-version": "^9", 61 | "ts-jest": "^27", 62 | "typescript": "^4.9.5" 63 | }, 64 | "peerDependencies": { 65 | "aws-cdk-lib": "^2.89.0", 66 | "constructs": "^10.0.0" 67 | }, 68 | "overrides": { 69 | "@types/babel__traverse": "7.18.2", 70 | "@types/prettier": "2.6.0" 71 | }, 72 | "keywords": [ 73 | "aws", 74 | "aws-cdk", 75 | "awscdk", 76 | "cdk", 77 | "github", 78 | "github-actions", 79 | "iam", 80 | "oidc", 81 | "openid-connect" 82 | ], 83 | "engines": { 84 | "node": ">= 16.20.0" 85 | }, 86 | "main": "lib/index.js", 87 | "license": "Apache-2.0", 88 | "version": "0.0.0", 89 | "jest": { 90 | "testMatch": [ 91 | "/src/**/__tests__/**/*.ts?(x)", 92 | "/(test|src)/**/*(*.)@(spec|test).ts?(x)" 93 | ], 94 | "clearMocks": true, 95 | "collectCoverage": true, 96 | "coverageReporters": [ 97 | "json", 98 | "lcov", 99 | "clover", 100 | "cobertura", 101 | "text" 102 | ], 103 | "coverageDirectory": "coverage", 104 | "coveragePathIgnorePatterns": [ 105 | "/node_modules/" 106 | ], 107 | "testPathIgnorePatterns": [ 108 | "/node_modules/" 109 | ], 110 | "watchPathIgnorePatterns": [ 111 | "/node_modules/" 112 | ], 113 | "reporters": [ 114 | "default", 115 | [ 116 | "jest-junit", 117 | { 118 | "outputDirectory": "test-reports" 119 | } 120 | ] 121 | ], 122 | "preset": "ts-jest", 123 | "globals": { 124 | "ts-jest": { 125 | "tsconfig": "tsconfig.dev.json" 126 | } 127 | } 128 | }, 129 | "types": "lib/index.d.ts", 130 | "stability": "experimental", 131 | "jsii": { 132 | "outdir": "dist", 133 | "targets": { 134 | "python": { 135 | "distName": "aws-cdk-github-oidc", 136 | "module": "aws_cdk_github_oidc" 137 | }, 138 | "go": { 139 | "moduleName": "github.com/aripalo/aws-cdk-github-oidc-go" 140 | } 141 | }, 142 | "tsc": { 143 | "outDir": "lib", 144 | "rootDir": "src" 145 | } 146 | }, 147 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 148 | } 149 | -------------------------------------------------------------------------------- /src/iam-role-props.ts: -------------------------------------------------------------------------------- 1 | import { Duration } from 'aws-cdk-lib'; 2 | import { 3 | IManagedPolicy, 4 | PolicyDocument, 5 | } from 'aws-cdk-lib/aws-iam'; 6 | 7 | 8 | /** 9 | * Properties for defining an IAM Role. 10 | * These are copied fron @aws-cdk/aws-iam, but since JSII does not support 11 | * TypeScript > (or Omit), we have to do this stupid thing. 12 | * 13 | * Basically exactly the same as source, but with assumedBy removed. 14 | * 15 | * @stability stable 16 | */ 17 | export interface RoleProps { 18 | /** 19 | * List of IDs that the role assumer needs to provide one of when assuming this role. 20 | * 21 | * If the configured and provided external IDs do not match, the 22 | * AssumeRole operation will fail. 23 | * 24 | * @default No external ID required 25 | * @stability stable 26 | */ 27 | readonly externalIds?: string[]; 28 | /** 29 | * A list of managed policies associated with this role. 30 | * 31 | * You can add managed policies later using 32 | * `addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName(policyName))`. 33 | * 34 | * @default - No managed policies. 35 | * @stability stable 36 | */ 37 | readonly managedPolicies?: IManagedPolicy[]; 38 | /** 39 | * A list of named policies to inline into this role. 40 | * 41 | * These policies will be 42 | * created with the role, whereas those added by ``addToPolicy`` are added 43 | * using a separate CloudFormation resource (allowing a way around circular 44 | * dependencies that could otherwise be introduced). 45 | * 46 | * @default - No policy is inlined in the Role resource. 47 | * @stability stable 48 | */ 49 | readonly inlinePolicies?: { 50 | [name: string]: PolicyDocument; 51 | }; 52 | /** 53 | * The path associated with this role. 54 | * 55 | * For information about IAM paths, see 56 | * Friendly Names and Paths in IAM User Guide. 57 | * 58 | * @default / 59 | * @stability stable 60 | */ 61 | readonly path?: string; 62 | /** 63 | * AWS supports permissions boundaries for IAM entities (users or roles). 64 | * 65 | * A permissions boundary is an advanced feature for using a managed policy 66 | * to set the maximum permissions that an identity-based policy can grant to 67 | * an IAM entity. An entity's permissions boundary allows it to perform only 68 | * the actions that are allowed by both its identity-based policies and its 69 | * permissions boundaries. 70 | * 71 | * @default - No permissions boundary. 72 | * @stability stable 73 | * @link https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html 74 | */ 75 | readonly permissionsBoundary?: IManagedPolicy; 76 | /** 77 | * A name for the IAM role. 78 | * 79 | * For valid values, see the RoleName parameter for 80 | * the CreateRole action in the IAM API Reference. 81 | * 82 | * IMPORTANT: If you specify a name, you cannot perform updates that require 83 | * replacement of this resource. You can perform updates that require no or 84 | * some interruption. If you must replace the resource, specify a new name. 85 | * 86 | * If you specify a name, you must specify the CAPABILITY_NAMED_IAM value to 87 | * acknowledge your template's capabilities. For more information, see 88 | * Acknowledging IAM Resources in AWS CloudFormation Templates. 89 | * 90 | * @default - AWS CloudFormation generates a unique physical ID and uses that ID 91 | * for the role name. 92 | * @stability stable 93 | */ 94 | readonly roleName?: string; 95 | /** 96 | * The maximum session duration that you want to set for the specified role. 97 | * 98 | * This setting can have a value from 1 hour (3600sec) to 12 (43200sec) hours. 99 | * 100 | * Anyone who assumes the role from the AWS CLI or API can use the 101 | * DurationSeconds API parameter or the duration-seconds CLI parameter to 102 | * request a longer session. The MaxSessionDuration setting determines the 103 | * maximum duration that can be requested using the DurationSeconds 104 | * parameter. 105 | * 106 | * If users don't specify a value for the DurationSeconds parameter, their 107 | * security credentials are valid for one hour by default. This applies when 108 | * you use the AssumeRole* API operations or the assume-role* CLI operations 109 | * but does not apply when you use those operations to create a console URL. 110 | * 111 | * @default Duration.hours(1) 112 | * @stability stable 113 | * @link https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html 114 | */ 115 | readonly maxSessionDuration?: Duration; 116 | /** 117 | * A description of the role. 118 | * 119 | * It can be up to 1000 characters long. 120 | * 121 | * @default - No description. 122 | * @stability stable 123 | */ 124 | readonly description?: string; 125 | } 126 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { GithubActionsIdentityProvider, IGithubActionsIdentityProvider } from './provider'; 2 | export { GithubActionsRole, GithubActionsRoleProps, GithubConfiguration } from './role'; 3 | export { RoleProps } from './iam-role-props'; 4 | -------------------------------------------------------------------------------- /src/owner-regexp.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/shinnn/github-username-regex/blob/master/module.js 2 | export default /^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}$/i; 3 | -------------------------------------------------------------------------------- /src/provider.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import * as iam from 'aws-cdk-lib/aws-iam'; 3 | import { Construct } from 'constructs'; 4 | 5 | /** 6 | * Describes a Github OpenID Connect Identity Provider for AWS IAM. 7 | */ 8 | export interface IGithubActionsIdentityProvider extends iam.IOpenIdConnectProvider { } 9 | 10 | /** 11 | * Github Actions as OpenID Connect Identity Provider for AWS IAM. 12 | * There can be only one (per AWS Account). 13 | * 14 | * Use `fromAccount` to retrieve a reference to existing Github OIDC provider. 15 | * 16 | * @see https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services 17 | */ 18 | export class GithubActionsIdentityProvider extends iam.OpenIdConnectProvider implements IGithubActionsIdentityProvider { 19 | 20 | public static readonly issuer: string = 'token.actions.githubusercontent.com'; 21 | 22 | /** 23 | * Retrieve a reference to existing Github OIDC provider in your AWS account. 24 | * An AWS account can only have single Github OIDC provider configured into it, 25 | * so internally the reference is made by constructing the ARN from AWS 26 | * Account ID & Github issuer URL. 27 | * 28 | * @param scope CDK Stack or Construct to which the provider is assigned to 29 | * @param id CDK Construct ID given to the construct 30 | * @returns a CDK Construct representing the Github OIDC provider 31 | * 32 | * @example 33 | * GithubActionsIdentityProvider.fromAccount(scope, "GithubProvider"); 34 | */ 35 | public static fromAccount(scope: Construct, id: string): IGithubActionsIdentityProvider { 36 | const accountId = cdk.Stack.of(scope).account; 37 | const providerArn = `arn:aws:iam::${accountId}:oidc-provider/${GithubActionsIdentityProvider.issuer}`; 38 | return iam.OpenIdConnectProvider.fromOpenIdConnectProviderArn(scope, id, providerArn); 39 | } 40 | 41 | /** 42 | * Define a new Github OpenID Connect Identity PRovider for AWS IAM. 43 | * There can be only one (per AWS Account). 44 | * 45 | * @param scope CDK Stack or Construct to which the provider is assigned to 46 | * @param id CDK Construct ID given to the construct 47 | * 48 | * @example 49 | * new GithubActionsIdentityProvider(scope, "GithubProvider"); 50 | */ 51 | constructor(scope: Construct, id: string) { 52 | super(scope, id, { 53 | url: `https://${GithubActionsIdentityProvider.issuer}`, 54 | clientIds: ['sts.amazonaws.com'], 55 | }); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/role.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import * as iam from 'aws-cdk-lib/aws-iam'; 3 | import { Construct } from 'constructs'; 4 | import { RoleProps } from './iam-role-props'; 5 | import githubUsernameRegex from './owner-regexp'; 6 | import { GithubActionsIdentityProvider, IGithubActionsIdentityProvider } from './provider'; 7 | 8 | /** 9 | * Github related configuration that forms the trust policy for this IAM Role. 10 | */ 11 | export interface GithubConfiguration { 12 | 13 | /** 14 | * Reference to Github OpenID Connect Provider configured in AWS IAM. 15 | * 16 | * Either pass an construct defined by `new GithubActionsIdentityProvider` 17 | * or a retrieved reference from `GithubActionsIdentityProvider.fromAccount`. 18 | * There can be only one (per AWS Account). 19 | */ 20 | readonly provider: IGithubActionsIdentityProvider; 21 | 22 | /** 23 | * Repository owner (organization or username). 24 | * 25 | * @example 26 | * 'octo-org' 27 | */ 28 | readonly owner: string; 29 | 30 | /** 31 | * Repository name (slug) without the owner. 32 | * 33 | * @example 34 | * 'octo-repo' 35 | */ 36 | readonly repo: string; 37 | 38 | /** 39 | * Subject condition filter, appended after `repo:${owner}/${repo}:` string in IAM Role trust relationship. 40 | * 41 | * @default 42 | * '*' 43 | * 44 | * You may use this value to only allow Github to assume the role on specific branches, tags, environments, pull requests etc. 45 | * @example 46 | * 'ref:refs/tags/v*' 47 | * 'ref:refs/heads/demo-branch' 48 | * 'pull_request' 49 | * 'environment:Production' 50 | * 51 | * @see https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#examples 52 | */ 53 | readonly filter?: string; 54 | } 55 | 56 | /** 57 | * Props that define the IAM Role that can be assumed by Github Actions workflow 58 | * via Github OpenID Connect Identity Provider. 59 | * 60 | * Besides `GithubConfiguration`, you may pass in any `iam.RoleProps` except `assumedBy` 61 | * which will be defined by this construct (CDK will fail if you do). 62 | * 63 | * @example 64 | * { 65 | * provider: GithubActionsIdentityProvider.fromAccount(scope, "GithubProvider"), 66 | * owner: 'octo-org', 67 | * repo: 'octo-repo', 68 | * filter: 'ref:refs/tags/v*', 69 | * roleName: 'MyDeployRole', 70 | * } 71 | */ 72 | export interface GithubActionsRoleProps extends GithubConfiguration, RoleProps {} 73 | 74 | /** 75 | * Define an IAM Role that can be assumed by Github Actions workflow 76 | * via Github OpenID Connect Identity Provider. 77 | * 78 | * Besides `GithubConfiguration`, you may pass in any `iam.RoleProps` except `assumedBy` 79 | * which will be defined by this construct (CDK will fail if you do). 80 | * 81 | * @example 82 | * const uploadRole = new GithubActionsRole(scope, "UploadRole", { 83 | * provider: GithubActionsIdentityProvider.fromAccount(scope, "GithubProvider"), 84 | * owner: 'octo-org', 85 | * repo: 'octo-repo', 86 | * filter: 'ref:refs/tags/v*', 87 | * roleName: 'MyUploadRole', 88 | * }); 89 | * 90 | * myBucket.grantWrite(uploadRole); 91 | */ 92 | export class GithubActionsRole extends iam.Role { 93 | 94 | /** 95 | * Extracts props given for the created IAM Role Construct. 96 | * @param props for the GithubActionsRole 97 | * @returns for the IAM Role 98 | */ 99 | private static extractRoleProps(props: GithubActionsRoleProps): iam.RoleProps { 100 | const extractProps = props; 101 | delete extractProps.provider; 102 | delete extractProps.owner; 103 | delete extractProps.repo; 104 | delete extractProps.filter; 105 | return extractProps; 106 | } 107 | 108 | /** Validates the Github owner (organization or user) name. */ 109 | private static validateOwner(scope: Construct, owner: string): void { 110 | if (githubUsernameRegex.test(owner) !== true) { 111 | cdk.Annotations.of(scope).addError(`Invalid Github Repository Owner "${owner}". Must only contain alphanumeric characters or hyphens, cannot have multiple consecutive hyphens, cannot begin or end with a hypen and maximum lenght is 39 characters.`); 112 | } 113 | } 114 | 115 | /** Validates the Github repository name (without owner). */ 116 | private static validateRepo(scope: Construct, repo: string): void { 117 | if (repo === '') { 118 | cdk.Annotations.of(scope).addError(`Invalid Github Repository Name "${repo}". May not be empty string.`); 119 | } 120 | } 121 | 122 | /** Formats the `sub` value used in trust policy. */ 123 | private static formatSubject(props: GithubConfiguration): string { 124 | const { owner, repo, filter = '*' } = props; 125 | return `repo:${owner}/${repo}:${filter}`; 126 | } 127 | 128 | 129 | /** 130 | * Define an IAM Role that can be assumed by Github Actions workflow 131 | * via Github OpenID Connect Identity Provider. 132 | * 133 | * Besides `GithubConfiguration`, you may pass in any `iam.RoleProps` except `assumedBy` 134 | * which will be defined by this construct (CDK will fail if you do). 135 | * 136 | * @example 137 | * const uploadRole = new GithubActionsRole(scope, "UploadRole", { 138 | * provider: GithubActionsIdentityProvider.fromAccount(scope, "GithubProvider"), 139 | * owner: 'octo-org', 140 | * repo: 'octo-repo', 141 | * filter: 'ref:refs/tags/v*', 142 | * roleName: 'MyUploadRole', 143 | * }); 144 | * 145 | * myBucket.grantWrite(uploadRole); 146 | */ 147 | constructor(scope: Construct, id: string, props: GithubActionsRoleProps) { 148 | 149 | const { provider, owner, repo } = props; 150 | 151 | // Perform validations 152 | GithubActionsRole.validateOwner(scope, owner); 153 | GithubActionsRole.validateRepo(scope, repo); 154 | 155 | // Prepare values 156 | const subject = GithubActionsRole.formatSubject(props); 157 | const roleProps = GithubActionsRole.extractRoleProps(props); 158 | 159 | // The actual IAM Role creation 160 | super(scope, id, { 161 | ...roleProps, 162 | assumedBy: new iam.WebIdentityPrincipal(provider.openIdConnectProviderArn, { 163 | StringLike: { 164 | // Only allow specified subjects to assume this role 165 | [`${GithubActionsIdentityProvider.issuer}:sub`]: subject, 166 | }, 167 | StringEquals: { 168 | // Audience is always sts.amazonaws.com with AWS official Github Action 169 | // https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services#adding-the-identity-provider-to-aws 170 | [`${GithubActionsIdentityProvider.issuer}:aud`]: 'sts.amazonaws.com', 171 | }, 172 | }), 173 | }); 174 | 175 | } 176 | } 177 | 178 | -------------------------------------------------------------------------------- /test/provider.test.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import { Template } from 'aws-cdk-lib/assertions'; 3 | import { GithubActionsIdentityProvider } from '../src/provider'; 4 | 5 | const providerArnRegexp = /^arn:aws:iam::\$\{Token\[.+\]\}:oidc-provider\/token\.actions\.githubusercontent\.com$/i; 6 | 7 | test('New Provider', () => { 8 | 9 | const app = new cdk.App(); 10 | const stack = new cdk.Stack(app); 11 | new GithubActionsIdentityProvider(stack, 'GithubProvider'); 12 | const template = Template.fromStack(stack); 13 | 14 | template.hasResourceProperties('Custom::AWSCDKOpenIdConnectProvider', { 15 | ClientIDList: [ 16 | 'sts.amazonaws.com', 17 | ], 18 | Url: 'https://token.actions.githubusercontent.com', 19 | }); 20 | }); 21 | 22 | test('Existing Provider', () => { 23 | 24 | const app = new cdk.App(); 25 | const stack = new cdk.Stack(app); 26 | const provider = GithubActionsIdentityProvider.fromAccount(stack, 'GithubProvider'); 27 | 28 | expect(provider.openIdConnectProviderIssuer).toBe('token.actions.githubusercontent.com'); 29 | expect(provider.openIdConnectProviderArn).toMatch(providerArnRegexp); 30 | }); 31 | -------------------------------------------------------------------------------- /test/role.test.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import { Template, Match } from 'aws-cdk-lib/assertions'; 3 | import * as iam from 'aws-cdk-lib/aws-iam'; 4 | import { GithubActionsIdentityProvider } from '../src/provider'; 5 | import { GithubActionsRole } from '../src/role'; 6 | 7 | 8 | test('Role with defaults', () => { 9 | 10 | const app = new cdk.App(); 11 | const stack = new cdk.Stack(app); 12 | const provider = GithubActionsIdentityProvider.fromAccount(stack, 'GithubProvider'); 13 | 14 | new GithubActionsRole(stack, 'TestRole', { 15 | provider, 16 | owner: 'octo-org', 17 | repo: 'octo-repo', 18 | }); 19 | 20 | const template = Template.fromStack(stack); 21 | 22 | template.hasResourceProperties('AWS::IAM::Role', { 23 | AssumeRolePolicyDocument: Match.objectLike({ 24 | Statement: Match.arrayWith([ 25 | Match.objectLike({ 26 | Action: 'sts:AssumeRoleWithWebIdentity', 27 | Effect: 'Allow', 28 | Condition: { 29 | StringLike: { 30 | 'token.actions.githubusercontent.com:sub': 'repo:octo-org/octo-repo:*', 31 | }, 32 | StringEquals: { 33 | 'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com', 34 | }, 35 | }, 36 | Principal: { 37 | Federated: { 38 | 'Fn::Join': [ 39 | '', 40 | [ 41 | 'arn:aws:iam::', 42 | { 43 | Ref: 'AWS::AccountId', 44 | }, 45 | ':oidc-provider/token.actions.githubusercontent.com', 46 | ], 47 | ], 48 | }, 49 | }, 50 | }), 51 | ]), 52 | }), 53 | }); 54 | }); 55 | 56 | test('Role with custom props', () => { 57 | 58 | const app = new cdk.App(); 59 | const stack = new cdk.Stack(app); 60 | const provider = GithubActionsIdentityProvider.fromAccount(stack, 'GithubProvider'); 61 | 62 | const role = new GithubActionsRole(stack, 'TestRole', { 63 | provider, 64 | owner: 'octo-org', 65 | repo: 'octo-repo', 66 | filter: 'ref:refs/tags/v*', 67 | roleName: 'MyTestRole', 68 | description: 'This role deploys stuff to AWS', 69 | maxSessionDuration: cdk.Duration.hours(2), 70 | managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName('AdministratorAccess')], 71 | }); 72 | 73 | const stmt = new iam.PolicyStatement(); 74 | stmt.addActions('s3:PutObject'); 75 | stmt.addResources('arn:aws:s3:::mybucket/*'); 76 | stmt.effect = iam.Effect.DENY; 77 | role.addToPolicy(stmt); 78 | 79 | const template = Template.fromStack(stack); 80 | 81 | template.hasResourceProperties('AWS::IAM::Role', { 82 | RoleName: 'MyTestRole', 83 | Description: 'This role deploys stuff to AWS', 84 | MaxSessionDuration: 7200, 85 | AssumeRolePolicyDocument: Match.objectLike({ 86 | Statement: Match.arrayWith([ 87 | Match.objectLike({ 88 | Action: 'sts:AssumeRoleWithWebIdentity', 89 | Effect: 'Allow', 90 | Condition: { 91 | StringLike: { 92 | 'token.actions.githubusercontent.com:sub': 'repo:octo-org/octo-repo:ref:refs/tags/v*', 93 | }, 94 | StringEquals: { 95 | 'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com', 96 | }, 97 | }, 98 | Principal: { 99 | Federated: { 100 | 'Fn::Join': [ 101 | '', 102 | [ 103 | 'arn:aws:iam::', 104 | { 105 | Ref: 'AWS::AccountId', 106 | }, 107 | ':oidc-provider/token.actions.githubusercontent.com', 108 | ], 109 | ], 110 | }, 111 | }, 112 | }), 113 | ]), 114 | }), 115 | ManagedPolicyArns: [ 116 | { 117 | 'Fn::Join': [ 118 | '', 119 | [ 120 | 'arn:', 121 | { 122 | Ref: 'AWS::Partition', 123 | }, 124 | ':iam::aws:policy/AdministratorAccess', 125 | ], 126 | ], 127 | }, 128 | ], 129 | }); 130 | 131 | 132 | template.hasResourceProperties('AWS::IAM::Policy', { 133 | PolicyDocument: { 134 | Statement: [ 135 | { 136 | Action: 's3:PutObject', 137 | Effect: 'Deny', 138 | Resource: 'arn:aws:s3:::mybucket/*', 139 | }, 140 | ], 141 | Version: '2012-10-17', 142 | }, 143 | PolicyName: Match.stringLikeRegexp('TestRoleDefaultPolicy*'), 144 | Roles: [ 145 | { 146 | Ref: Match.stringLikeRegexp('TestRole*'), 147 | }, 148 | ], 149 | 150 | }); 151 | 152 | }); 153 | 154 | 155 | test('Role with invalid owner', () => { 156 | 157 | const app = new cdk.App(); 158 | const stack = new cdk.Stack(app); 159 | const provider = GithubActionsIdentityProvider.fromAccount(stack, 'GithubProvider'); 160 | 161 | new GithubActionsRole(stack, 'TestRole', { 162 | provider, 163 | owner: 'invalid/@owner--', 164 | repo: 'octo-repo', 165 | }); 166 | 167 | expect(stack.node.metadata).toHaveLength(1); 168 | expect(stack.node.metadata[0].data).toBe( 169 | 'Invalid Github Repository Owner "invalid/@owner--". Must only contain alphanumeric characters or hyphens, cannot have multiple consecutive hyphens, cannot begin or end with a hypen and maximum lenght is 39 characters.', 170 | ); 171 | }); 172 | 173 | test('Role with invalid repo', () => { 174 | 175 | const app = new cdk.App(); 176 | const stack = new cdk.Stack(app); 177 | const provider = GithubActionsIdentityProvider.fromAccount(stack, 'GithubProvider'); 178 | 179 | new GithubActionsRole(stack, 'TestRole', { 180 | provider, 181 | owner: 'octo-org', 182 | repo: '', 183 | }); 184 | 185 | expect(stack.node.metadata).toHaveLength(1); 186 | expect(stack.node.metadata[0].data).toBe( 187 | 'Invalid Github Repository Name "". May not be empty string.', 188 | ); 189 | }); 190 | -------------------------------------------------------------------------------- /tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | // ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | { 3 | "compilerOptions": { 4 | "alwaysStrict": true, 5 | "declaration": true, 6 | "esModuleInterop": true, 7 | "experimentalDecorators": true, 8 | "inlineSourceMap": true, 9 | "inlineSources": true, 10 | "lib": [ 11 | "es2019" 12 | ], 13 | "module": "CommonJS", 14 | "noEmitOnError": false, 15 | "noFallthroughCasesInSwitch": true, 16 | "noImplicitAny": true, 17 | "noImplicitReturns": true, 18 | "noImplicitThis": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "resolveJsonModule": true, 22 | "strict": true, 23 | "strictNullChecks": true, 24 | "strictPropertyInitialization": true, 25 | "stripInternal": true, 26 | "target": "ES2019" 27 | }, 28 | "include": [ 29 | ".projenrc.js", 30 | "src/**/*.ts", 31 | "test/**/*.ts" 32 | ], 33 | "exclude": [ 34 | "node_modules" 35 | ] 36 | } 37 | --------------------------------------------------------------------------------