├── .eslintrc.json ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ ├── build.yml │ ├── pull-request-lint.yml │ ├── release.yml │ └── upgrade-main.yml ├── .gitignore ├── .mergify.yml ├── .npmignore ├── .prettierignore ├── .prettierrc.json ├── .projen ├── deps.json ├── files.json └── tasks.json ├── .projenrc.ts ├── API.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── cdk.context.json ├── decisions ├── ADR-01-Repo-Structure.md ├── ADR-02-using-projen.md ├── ADR-03-merge-tooling.md └── ADR-04-conflicting-contributions.md ├── package-lock.json ├── package.json ├── src ├── aws-codeartifact │ ├── README.md │ ├── domain.ts │ ├── index.ts │ └── repository.ts ├── aws-cur │ ├── README.md │ ├── cost-report.ts │ └── index.ts ├── aws-ec2 │ ├── README.md │ ├── index.ts │ └── instance-connect-endpoint.ts ├── aws-elasticache │ ├── README.md │ ├── daily-snapshot-time.ts │ ├── index.ts │ ├── serverless-cache.ts │ ├── user-group.ts │ ├── user.ts │ └── util.ts ├── aws-fsx │ ├── README.md │ ├── daily-automatic-backup-start-time.ts │ ├── index.ts │ ├── maintenance-time.ts │ └── ontap-file-system.ts ├── aws-redshiftserverless │ ├── README.md │ ├── index.ts │ ├── namespace.ts │ └── workgroup.ts └── index.ts ├── test ├── .gitkeep ├── aws-codeartifact │ ├── domain.test.ts │ ├── integ.codeartifact.ts │ ├── integ.codeartifact.ts.snapshot │ │ ├── CodeArtifactDomainAndRepository.assets.json │ │ ├── CodeArtifactDomainAndRepository.template.json │ │ ├── CodeArtifactDomainAndRepositoryIntegDefaultTestDeployAssert135F1932.assets.json │ │ ├── CodeArtifactDomainAndRepositoryIntegDefaultTestDeployAssert135F1932.template.json │ │ ├── cdk.out │ │ ├── integ.json │ │ ├── manifest.json │ │ └── tree.json │ └── repository.test.ts ├── aws-cur │ ├── cost-report.test.ts │ ├── integ.cost-report.ts │ └── integ.cost-report.ts.snapshot │ │ ├── CurReportDefaultTestDeployAssert2956AD14.assets.json │ │ ├── CurReportDefaultTestDeployAssert2956AD14.template.json │ │ ├── asset.b7f33614a69548d6bafe224d751a7ef238cde19097415e553fe8b63a4c8fd8a6 │ │ └── index.js │ │ ├── cdk.out │ │ ├── cur-report.assets.json │ │ ├── cur-report.template.json │ │ ├── integ.json │ │ ├── manifest.json │ │ └── tree.json ├── aws-ec2 │ ├── instance-connect-endpoint.test.ts │ ├── integ.instance-connect-endpoint.ts │ └── integ.instance-connect-endpoint.ts.snapshot │ │ ├── InstanceConnectEndpointDefaultTestDeployAssert284B1FD7.assets.json │ │ ├── InstanceConnectEndpointDefaultTestDeployAssert284B1FD7.template.json │ │ ├── InstanceConnectEndpointStack.assets.json │ │ ├── InstanceConnectEndpointStack.template.json │ │ ├── asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e │ │ ├── __entrypoint__.js │ │ └── index.js │ │ ├── cdk.out │ │ ├── integ.json │ │ ├── manifest.json │ │ └── tree.json ├── aws-elasticache │ ├── daily-snapshot-time.test.ts │ ├── integ.elasticache-serverless-cache.ts │ ├── integ.elasticache-serverless-cache.ts.snapshot │ │ ├── ElastiCacheServerlessCacheStack.assets.json │ │ ├── ElastiCacheServerlessCacheStack.template.json │ │ ├── ElastiCacheServerlessCacheTestDefaultTestDeployAssertB226C8E2.assets.json │ │ ├── ElastiCacheServerlessCacheTestDefaultTestDeployAssertB226C8E2.template.json │ │ ├── asset.4984c845346313a408899c8ff361d3b7b97953a9d4202e47694ef2a101f4b5c3.bundle │ │ │ └── index.js │ │ ├── asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e │ │ │ ├── __entrypoint__.js │ │ │ └── index.js │ │ ├── cdk.out │ │ ├── integ.json │ │ ├── manifest.json │ │ └── tree.json │ ├── integ.import-aws-default-user.ts │ ├── integ.import-aws-default-user.ts.snapshot │ │ ├── ImportDefaultUserStack.assets.json │ │ ├── ImportDefaultUserStack.template.json │ │ ├── ImportDefaultUserTestDefaultTestDeployAssert51B7C19D.assets.json │ │ ├── ImportDefaultUserTestDefaultTestDeployAssert51B7C19D.template.json │ │ ├── cdk.out │ │ ├── integ.json │ │ ├── manifest.json │ │ └── tree.json │ ├── serverless-cache.test.ts │ ├── user-group.test.ts │ └── user.test.ts ├── aws-examplemodule │ ├── example.test.ts │ ├── integ.example.ts │ └── integ.example.ts.snapshot │ │ ├── IntegTestExampleDefaultTestDeployAssertB2D38A87.assets.json │ │ ├── IntegTestExampleDefaultTestDeployAssertB2D38A87.template.json │ │ ├── IntegrationTestExampleStack.assets.json │ │ ├── IntegrationTestExampleStack.template.json │ │ ├── cdk.out │ │ ├── integ.json │ │ ├── manifest.json │ │ └── tree.json ├── aws-fsx │ ├── integ.ontap-file-system.ts │ ├── integ.ontap-file-system.ts.snapshot │ │ ├── FsxForOntapTestDefaultTestDeployAssert20A933DE.assets.json │ │ ├── FsxForOntapTestDefaultTestDeployAssert20A933DE.template.json │ │ ├── FsxForOntapTestStack.assets.json │ │ ├── FsxForOntapTestStack.template.json │ │ ├── asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e │ │ │ ├── __entrypoint__.js │ │ │ └── index.js │ │ ├── cdk.out │ │ ├── integ.json │ │ ├── manifest.json │ │ └── tree.json │ └── ontap-file-system.test.ts └── aws-redshiftserverless │ ├── integ.redshift-serverless-namespace-workgroup.ts │ ├── integ.redshift-serverless-namespace-workgroup.ts.snapshot │ ├── RedshiftServerlessNamespaceWorkgroupStack.assets.json │ ├── RedshiftServerlessNamespaceWorkgroupStack.template.json │ ├── RedshiftServerlessNamespaceWorkgroupTestDefaultTestDeployAssertE2D901CB.assets.json │ ├── RedshiftServerlessNamespaceWorkgroupTestDefaultTestDeployAssertE2D901CB.template.json │ ├── cdk.out │ ├── integ.json │ ├── manifest.json │ └── tree.json │ ├── namespace.test.ts │ └── workgroup.test.ts └── tsconfig.dev.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | // ~~ Generated by projen. To modify, edit .projenrc.ts 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 | "plugin:prettier/recommended" 21 | ], 22 | "settings": { 23 | "import/parsers": { 24 | "@typescript-eslint/parser": [ 25 | ".ts", 26 | ".tsx" 27 | ] 28 | }, 29 | "import/resolver": { 30 | "node": {}, 31 | "typescript": { 32 | "project": "./tsconfig.dev.json", 33 | "alwaysTryTypes": true 34 | } 35 | } 36 | }, 37 | "ignorePatterns": [ 38 | "*.js", 39 | "*.d.ts", 40 | "node_modules/", 41 | "*.generated.ts", 42 | "coverage", 43 | "!.projenrc.ts", 44 | "!projenrc/**/*.ts" 45 | ], 46 | "rules": { 47 | "@typescript-eslint/no-require-imports": [ 48 | "error" 49 | ], 50 | "import/no-extraneous-dependencies": [ 51 | "error", 52 | { 53 | "devDependencies": [ 54 | "**/test/**", 55 | "**/build-tools/**", 56 | ".projenrc.ts", 57 | "projenrc/**/*.ts" 58 | ], 59 | "optionalDependencies": false, 60 | "peerDependencies": true 61 | } 62 | ], 63 | "import/no-unresolved": [ 64 | "error" 65 | ], 66 | "import/order": [ 67 | "warn", 68 | { 69 | "groups": [ 70 | "builtin", 71 | "external" 72 | ], 73 | "alphabetize": { 74 | "order": "asc", 75 | "caseInsensitive": true 76 | } 77 | } 78 | ], 79 | "import/no-duplicates": [ 80 | "error" 81 | ], 82 | "no-shadow": [ 83 | "off" 84 | ], 85 | "@typescript-eslint/no-shadow": [ 86 | "error" 87 | ], 88 | "key-spacing": [ 89 | "error" 90 | ], 91 | "no-multiple-empty-lines": [ 92 | "error" 93 | ], 94 | "@typescript-eslint/no-floating-promises": [ 95 | "error" 96 | ], 97 | "no-return-await": [ 98 | "off" 99 | ], 100 | "@typescript-eslint/return-await": [ 101 | "error" 102 | ], 103 | "no-trailing-spaces": [ 104 | "error" 105 | ], 106 | "dot-notation": [ 107 | "error" 108 | ], 109 | "no-bitwise": [ 110 | "error" 111 | ], 112 | "@typescript-eslint/member-ordering": [ 113 | "error", 114 | { 115 | "default": [ 116 | "public-static-field", 117 | "public-static-method", 118 | "protected-static-field", 119 | "protected-static-method", 120 | "private-static-field", 121 | "private-static-method", 122 | "field", 123 | "constructor", 124 | "method" 125 | ] 126 | } 127 | ] 128 | }, 129 | "overrides": [ 130 | { 131 | "files": [ 132 | ".projenrc.ts" 133 | ], 134 | "rules": { 135 | "@typescript-eslint/no-require-imports": "off", 136 | "import/no-extraneous-dependencies": "off" 137 | } 138 | } 139 | ] 140 | } 141 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | * text=auto eol=lf 4 | *.snap linguist-generated 5 | /.eslintrc.json linguist-generated 6 | /.gitattributes linguist-generated 7 | /.github/pull_request_template.md linguist-generated 8 | /.github/workflows/build.yml linguist-generated 9 | /.github/workflows/pull-request-lint.yml linguist-generated 10 | /.github/workflows/release.yml linguist-generated 11 | /.github/workflows/upgrade-main.yml linguist-generated 12 | /.gitignore linguist-generated 13 | /.gitpod.yml linguist-generated 14 | /.mergify.yml linguist-generated 15 | /.npmignore linguist-generated 16 | /.prettierignore linguist-generated 17 | /.prettierrc.json linguist-generated 18 | /.projen/** linguist-generated 19 | /.projen/deps.json linguist-generated 20 | /.projen/files.json linguist-generated 21 | /.projen/tasks.json linguist-generated 22 | /API.md linguist-generated 23 | /LICENSE linguist-generated 24 | /package-lock.json linguist-generated 25 | /package.json linguist-generated 26 | /tsconfig.dev.json linguist-generated -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Describe the bug 11 | A clear and concise description of what the bug is. 12 | 13 | ## To Reproduce 14 | The following minimal code reproduces the error: 15 | 16 | ## Expected behavior 17 | A clear and concise description of what you expected to happen. 18 | 19 | ## Additional context 20 | Add any other context about the problem here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Would you be willing to help with a PR?** 14 | - [ ] Yes, absolutely 15 | - [ ] Yes, with some guidance 16 | - [ ] Unfortunately no time :'-( 17 | 18 | ## Describe the solution you'd like 19 | A clear and concise description of what you want to happen. 20 | 21 | ## Describe alternatives you've considered 22 | A clear and concise description of any alternative solutions or features you've considered. 23 | 24 | ## Additional context 25 | Add any other context or screenshots about the feature request here. 26 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Issue # (if applicable) 2 | 3 | Closes #. 4 | 5 | ### Reason for this change 6 | 7 | 8 | 9 | ### Description of changes 10 | 11 | 12 | 13 | ### Description of how you validated changes 14 | 15 | 16 | 17 | ### Checklist 18 | 19 | - [ ] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/open-constructs/aws-cdk-library/blob/main/CONTRIBUTING.md) 20 | - [ ] My pull request adheres to the [Pull Request Rule](https://github.com/open-constructs/aws-cdk-library/blob/main/CONTRIBUTING.md#pull-request) 21 | - **Do not omit the `aws-` part in the scope of the PR title if the PR relates to a specific AWS service module.** 22 | - e.g.) feat(**aws-s3**): description of the change 23 | 24 | --- 25 | _By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license_ 26 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts 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@v4 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@v4 24 | with: 25 | node-version: 18.x 26 | - name: Install dependencies 27 | run: npm install 28 | - name: build 29 | run: npx projen build 30 | - name: Find mutations 31 | id: self_mutation 32 | run: |- 33 | git add . 34 | git diff --staged --patch --exit-code > repo.patch || echo "self_mutation_happened=true" >> $GITHUB_OUTPUT 35 | working-directory: ./ 36 | - name: Upload patch 37 | if: steps.self_mutation.outputs.self_mutation_happened 38 | uses: actions/upload-artifact@v4.4.0 39 | with: 40 | name: repo.patch 41 | path: repo.patch 42 | overwrite: true 43 | - name: Fail build on mutation 44 | if: steps.self_mutation.outputs.self_mutation_happened 45 | run: |- 46 | echo "::error::Files were changed during build (see build log). If this was triggered from a fork, you will need to update your branch." 47 | cat repo.patch 48 | exit 1 49 | - name: Backup artifact permissions 50 | run: cd dist && getfacl -R . > permissions-backup.acl 51 | continue-on-error: true 52 | - name: Upload artifact 53 | uses: actions/upload-artifact@v4.4.0 54 | with: 55 | name: build-artifact 56 | path: dist 57 | overwrite: true 58 | self-mutation: 59 | needs: build 60 | runs-on: ubuntu-latest 61 | permissions: 62 | contents: write 63 | if: always() && needs.build.outputs.self_mutation_happened && !(github.event.pull_request.head.repo.full_name != github.repository) 64 | steps: 65 | - name: Generate token 66 | id: generate_token 67 | uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a 68 | with: 69 | app_id: ${{ secrets.PROJEN_APP_ID }} 70 | private_key: ${{ secrets.PROJEN_APP_PRIVATE_KEY }} 71 | - name: Checkout 72 | uses: actions/checkout@v4 73 | with: 74 | token: ${{ steps.generate_token.outputs.token }} 75 | ref: ${{ github.event.pull_request.head.ref }} 76 | repository: ${{ github.event.pull_request.head.repo.full_name }} 77 | - name: Download patch 78 | uses: actions/download-artifact@v4 79 | with: 80 | name: repo.patch 81 | path: ${{ runner.temp }} 82 | - name: Apply patch 83 | run: '[ -s ${{ runner.temp }}/repo.patch ] && git apply ${{ runner.temp }}/repo.patch || echo "Empty patch. Skipping."' 84 | - name: Set git identity 85 | run: |- 86 | git config user.name "github-actions" 87 | git config user.email "github-actions@github.com" 88 | - name: Push changes 89 | env: 90 | PULL_REQUEST_REF: ${{ github.event.pull_request.head.ref }} 91 | run: |- 92 | git add . 93 | git commit -s -m "chore: self mutation" 94 | git push origin HEAD:$PULL_REQUEST_REF 95 | package-js: 96 | needs: build 97 | runs-on: ubuntu-latest 98 | permissions: 99 | contents: read 100 | if: ${{ !needs.build.outputs.self_mutation_happened }} 101 | steps: 102 | - uses: actions/setup-node@v4 103 | with: 104 | node-version: 18.x 105 | - name: Download build artifacts 106 | uses: actions/download-artifact@v4 107 | with: 108 | name: build-artifact 109 | path: dist 110 | - name: Restore build artifact permissions 111 | run: cd dist && setfacl --restore=permissions-backup.acl 112 | continue-on-error: true 113 | - name: Checkout 114 | uses: actions/checkout@v4 115 | with: 116 | ref: ${{ github.event.pull_request.head.ref }} 117 | repository: ${{ github.event.pull_request.head.repo.full_name }} 118 | path: .repo 119 | - name: Install Dependencies 120 | run: cd .repo && npm ci 121 | - name: Extract build artifact 122 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 123 | - name: Move build artifact out of the way 124 | run: mv dist dist.old 125 | - name: Create js artifact 126 | run: cd .repo && npx projen package:js 127 | - name: Collect js artifact 128 | run: mv .repo/dist dist 129 | package-python: 130 | needs: build 131 | runs-on: ubuntu-latest 132 | permissions: 133 | contents: read 134 | if: ${{ !needs.build.outputs.self_mutation_happened }} 135 | steps: 136 | - uses: actions/setup-node@v4 137 | with: 138 | node-version: 18.x 139 | - uses: actions/setup-python@v5 140 | with: 141 | python-version: 3.x 142 | - name: Download build artifacts 143 | uses: actions/download-artifact@v4 144 | with: 145 | name: build-artifact 146 | path: dist 147 | - name: Restore build artifact permissions 148 | run: cd dist && setfacl --restore=permissions-backup.acl 149 | continue-on-error: true 150 | - name: Checkout 151 | uses: actions/checkout@v4 152 | with: 153 | ref: ${{ github.event.pull_request.head.ref }} 154 | repository: ${{ github.event.pull_request.head.repo.full_name }} 155 | path: .repo 156 | - name: Install Dependencies 157 | run: cd .repo && npm ci 158 | - name: Extract build artifact 159 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 160 | - name: Move build artifact out of the way 161 | run: mv dist dist.old 162 | - name: Create python artifact 163 | run: cd .repo && npx projen package:python 164 | - name: Collect python artifact 165 | run: mv .repo/dist dist 166 | -------------------------------------------------------------------------------- /.github/workflows/pull-request-lint.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts 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 | merge_group: {} 14 | jobs: 15 | validate: 16 | name: Validate PR title 17 | runs-on: ubuntu-latest 18 | permissions: 19 | pull-requests: write 20 | if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') 21 | steps: 22 | - uses: amannn/action-semantic-pull-request@v5.4.0 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | with: 26 | types: |- 27 | feat 28 | fix 29 | chore 30 | ci 31 | docs 32 | style 33 | refactor 34 | test 35 | revert 36 | Revert 37 | requireScope: false 38 | contributorStatement: 39 | name: Require Contributor Statement 40 | runs-on: ubuntu-latest 41 | permissions: 42 | pull-requests: read 43 | env: 44 | PR_BODY: ${{ github.event.pull_request.body }} 45 | EXPECTED: _By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license_ 46 | HELP: Contributor statement missing from PR description. Please include the following text in the PR description 47 | if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') 48 | steps: 49 | - uses: actions/github-script@v6 50 | with: 51 | script: |- 52 | const actual = process.env.PR_BODY.replace(/\r?\n/g, "\n"); 53 | const expected = process.env.EXPECTED.replace(/\r?\n/g, "\n"); 54 | if (!actual.includes(expected)) { 55 | console.log("%j", actual); 56 | console.log("%j", expected); 57 | core.setFailed(`${process.env.HELP}: ${expected}`); 58 | } 59 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: release 4 | on: 5 | push: 6 | branches: 7 | - main 8 | workflow_dispatch: {} 9 | concurrency: 10 | group: ${{ github.workflow }} 11 | cancel-in-progress: false 12 | jobs: 13 | release: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: write 17 | outputs: 18 | latest_commit: ${{ steps.git_remote.outputs.latest_commit }} 19 | tag_exists: ${{ steps.check_tag_exists.outputs.exists }} 20 | env: 21 | CI: "true" 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v4 25 | with: 26 | fetch-depth: 0 27 | - name: Set git identity 28 | run: |- 29 | git config user.name "github-actions" 30 | git config user.email "github-actions@github.com" 31 | - name: Setup Node.js 32 | uses: actions/setup-node@v4 33 | with: 34 | node-version: 18.x 35 | - name: Install dependencies 36 | run: npm ci 37 | - name: release 38 | run: npx projen release 39 | - name: Check if version has already been tagged 40 | id: check_tag_exists 41 | run: |- 42 | TAG=$(cat dist/releasetag.txt) 43 | ([ ! -z "$TAG" ] && git ls-remote -q --exit-code --tags origin $TAG && (echo "exists=true" >> $GITHUB_OUTPUT)) || (echo "exists=false" >> $GITHUB_OUTPUT) 44 | cat $GITHUB_OUTPUT 45 | - name: Check for new commits 46 | id: git_remote 47 | run: |- 48 | echo "latest_commit=$(git ls-remote origin -h ${{ github.ref }} | cut -f1)" >> $GITHUB_OUTPUT 49 | cat $GITHUB_OUTPUT 50 | - name: Backup artifact permissions 51 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 52 | run: cd dist && getfacl -R . > permissions-backup.acl 53 | continue-on-error: true 54 | - name: Upload artifact 55 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 56 | uses: actions/upload-artifact@v4.4.0 57 | with: 58 | name: build-artifact 59 | path: dist 60 | overwrite: true 61 | release_github: 62 | name: Publish to GitHub Releases 63 | needs: 64 | - release 65 | - release_npm 66 | - release_pypi 67 | runs-on: ubuntu-latest 68 | permissions: 69 | contents: write 70 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 71 | steps: 72 | - uses: actions/setup-node@v4 73 | with: 74 | node-version: 18.x 75 | - name: Download build artifacts 76 | uses: actions/download-artifact@v4 77 | with: 78 | name: build-artifact 79 | path: dist 80 | - name: Restore build artifact permissions 81 | run: cd dist && setfacl --restore=permissions-backup.acl 82 | continue-on-error: true 83 | - name: Release 84 | env: 85 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 86 | GITHUB_REPOSITORY: ${{ github.repository }} 87 | GITHUB_REF: ${{ github.sha }} 88 | 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 89 | release_npm: 90 | name: Publish to npm 91 | needs: release 92 | runs-on: ubuntu-latest 93 | permissions: 94 | id-token: write 95 | contents: read 96 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 97 | steps: 98 | - uses: actions/setup-node@v4 99 | with: 100 | node-version: 18.x 101 | - name: Download build artifacts 102 | uses: actions/download-artifact@v4 103 | with: 104 | name: build-artifact 105 | path: dist 106 | - name: Restore build artifact permissions 107 | run: cd dist && setfacl --restore=permissions-backup.acl 108 | continue-on-error: true 109 | - name: Checkout 110 | uses: actions/checkout@v4 111 | with: 112 | path: .repo 113 | - name: Install Dependencies 114 | run: cd .repo && npm ci 115 | - name: Extract build artifact 116 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 117 | - name: Move build artifact out of the way 118 | run: mv dist dist.old 119 | - name: Create js artifact 120 | run: cd .repo && npx projen package:js 121 | - name: Collect js artifact 122 | run: mv .repo/dist dist 123 | - name: Release 124 | env: 125 | NPM_DIST_TAG: latest 126 | NPM_REGISTRY: registry.npmjs.org 127 | NPM_CONFIG_PROVENANCE: "true" 128 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 129 | run: npx -p publib@latest publib-npm 130 | release_pypi: 131 | name: Publish to PyPI 132 | needs: release 133 | runs-on: ubuntu-latest 134 | permissions: 135 | contents: read 136 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 137 | steps: 138 | - uses: actions/setup-node@v4 139 | with: 140 | node-version: 18.x 141 | - uses: actions/setup-python@v5 142 | with: 143 | python-version: 3.x 144 | - name: Download build artifacts 145 | uses: actions/download-artifact@v4 146 | with: 147 | name: build-artifact 148 | path: dist 149 | - name: Restore build artifact permissions 150 | run: cd dist && setfacl --restore=permissions-backup.acl 151 | continue-on-error: true 152 | - name: Checkout 153 | uses: actions/checkout@v4 154 | with: 155 | path: .repo 156 | - name: Install Dependencies 157 | run: cd .repo && npm ci 158 | - name: Extract build artifact 159 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 160 | - name: Move build artifact out of the way 161 | run: mv dist dist.old 162 | - name: Create python artifact 163 | run: cd .repo && npx projen package:python 164 | - name: Collect python artifact 165 | run: mv .repo/dist dist 166 | - name: Release 167 | env: 168 | TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} 169 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} 170 | run: npx -p publib@latest publib-pypi 171 | -------------------------------------------------------------------------------- /.github/workflows/upgrade-main.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: upgrade-main 4 | on: 5 | workflow_dispatch: {} 6 | schedule: 7 | - cron: 0 0 * * 1 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@v4 19 | with: 20 | ref: main 21 | - name: Setup Node.js 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: 18.x 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 | working-directory: ./ 35 | - name: Upload patch 36 | if: steps.create_patch.outputs.patch_created 37 | uses: actions/upload-artifact@v4.4.0 38 | with: 39 | name: repo.patch 40 | path: repo.patch 41 | overwrite: true 42 | pr: 43 | name: Create Pull Request 44 | needs: upgrade 45 | runs-on: ubuntu-latest 46 | permissions: 47 | contents: read 48 | if: ${{ needs.upgrade.outputs.patch_created }} 49 | steps: 50 | - name: Generate token 51 | id: generate_token 52 | uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a 53 | with: 54 | app_id: ${{ secrets.PROJEN_APP_ID }} 55 | private_key: ${{ secrets.PROJEN_APP_PRIVATE_KEY }} 56 | - name: Checkout 57 | uses: actions/checkout@v4 58 | with: 59 | ref: main 60 | - name: Download patch 61 | uses: actions/download-artifact@v4 62 | with: 63 | name: repo.patch 64 | path: ${{ runner.temp }} 65 | - name: Apply patch 66 | run: '[ -s ${{ runner.temp }}/repo.patch ] && git apply ${{ runner.temp }}/repo.patch || echo "Empty patch. Skipping."' 67 | - name: Set git identity 68 | run: |- 69 | git config user.name "github-actions" 70 | git config user.email "github-actions@github.com" 71 | - name: Create Pull Request 72 | id: create-pr 73 | uses: peter-evans/create-pull-request@v6 74 | with: 75 | token: ${{ steps.generate_token.outputs.token }} 76 | commit-message: |- 77 | chore(deps): upgrade dependencies 78 | 79 | Upgrades project dependencies. See details in [workflow run]. 80 | 81 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 82 | 83 | ------ 84 | 85 | *Automatically created by projen via the "upgrade-main" workflow* 86 | branch: github-actions/upgrade-main 87 | title: "chore(deps): upgrade dependencies" 88 | body: |- 89 | Upgrades project dependencies. See details in [workflow run]. 90 | 91 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 92 | 93 | ------ 94 | 95 | *Automatically created by projen via the "upgrade-main" workflow* 96 | author: github-actions 97 | committer: github-actions 98 | signoff: true 99 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts 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 | !/.gitpod.yml 8 | !/package.json 9 | !/LICENSE 10 | !/.npmignore 11 | logs 12 | *.log 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | lerna-debug.log* 17 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | lib-cov 23 | coverage 24 | *.lcov 25 | .nyc_output 26 | build/Release 27 | node_modules/ 28 | jspm_packages/ 29 | *.tsbuildinfo 30 | .eslintcache 31 | *.tgz 32 | .yarn-integrity 33 | .cache 34 | /test-reports/ 35 | junit.xml 36 | /coverage/ 37 | !/.github/workflows/build.yml 38 | /dist/changelog.md 39 | /dist/version.txt 40 | !/.github/workflows/release.yml 41 | !/.mergify.yml 42 | !/.github/workflows/upgrade-main.yml 43 | !/.github/pull_request_template.md 44 | !/.prettierignore 45 | !/.prettierrc.json 46 | !/test/ 47 | !/tsconfig.dev.json 48 | !/src/ 49 | /lib 50 | /dist/ 51 | !/.eslintrc.json 52 | .jsii 53 | tsconfig.json 54 | !/API.md 55 | !/.projenrc.ts 56 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts 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 | merge_method: squash 13 | commit_message_template: |- 14 | {{ title }} (#{{ number }}) 15 | 16 | {{ body }} 17 | pull_request_rules: 18 | - name: Automatic merge on approval and successful build 19 | actions: 20 | delete_head_branch: {} 21 | queue: 22 | name: default 23 | conditions: 24 | - "#approved-reviews-by>=1" 25 | - -label~=(do-not-merge) 26 | - status-success=build 27 | - status-success=package-js 28 | - status-success=package-python 29 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts 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 | /.prettierignore 11 | /.prettierrc.json 12 | /test/ 13 | /tsconfig.dev.json 14 | /src/ 15 | !/lib/ 16 | !/lib/**/*.js 17 | !/lib/**/*.d.ts 18 | dist 19 | /tsconfig.json 20 | /.github/ 21 | /.vscode/ 22 | /.idea/ 23 | /.projenrc.js 24 | tsconfig.tsbuildinfo 25 | /.eslintrc.json 26 | !.jsii 27 | /.gitattributes 28 | /.projenrc.ts 29 | /projenrc 30 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | *.md 3 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 120, 4 | "arrowParens": "avoid", 5 | "overrides": [] 6 | } 7 | -------------------------------------------------------------------------------- /.projen/deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | { 4 | "name": "@aws-cdk/integ-runner", 5 | "version": "2.168.0-alpha.0", 6 | "type": "build" 7 | }, 8 | { 9 | "name": "@aws-cdk/integ-tests-alpha", 10 | "version": "2.168.0-alpha.0", 11 | "type": "build" 12 | }, 13 | { 14 | "name": "@types/jest", 15 | "type": "build" 16 | }, 17 | { 18 | "name": "@types/node", 19 | "version": "^18", 20 | "type": "build" 21 | }, 22 | { 23 | "name": "@typescript-eslint/eslint-plugin", 24 | "version": "^8", 25 | "type": "build" 26 | }, 27 | { 28 | "name": "@typescript-eslint/parser", 29 | "version": "^8", 30 | "type": "build" 31 | }, 32 | { 33 | "name": "commit-and-tag-version", 34 | "version": "^12", 35 | "type": "build" 36 | }, 37 | { 38 | "name": "eslint-config-prettier", 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-plugin-prettier", 51 | "type": "build" 52 | }, 53 | { 54 | "name": "eslint", 55 | "version": "^9", 56 | "type": "build" 57 | }, 58 | { 59 | "name": "jest", 60 | "type": "build" 61 | }, 62 | { 63 | "name": "jest-junit", 64 | "version": "^16", 65 | "type": "build" 66 | }, 67 | { 68 | "name": "jsii-diff", 69 | "type": "build" 70 | }, 71 | { 72 | "name": "jsii-docgen", 73 | "version": "^10.5.0", 74 | "type": "build" 75 | }, 76 | { 77 | "name": "jsii-pacmak", 78 | "type": "build" 79 | }, 80 | { 81 | "name": "jsii-rosetta", 82 | "version": "~5.7.0", 83 | "type": "build" 84 | }, 85 | { 86 | "name": "jsii", 87 | "version": "~5.7.0", 88 | "type": "build" 89 | }, 90 | { 91 | "name": "prettier", 92 | "type": "build" 93 | }, 94 | { 95 | "name": "projen", 96 | "type": "build" 97 | }, 98 | { 99 | "name": "ts-jest", 100 | "type": "build" 101 | }, 102 | { 103 | "name": "ts-node", 104 | "type": "build" 105 | }, 106 | { 107 | "name": "typescript", 108 | "type": "build" 109 | }, 110 | { 111 | "name": "aws-cdk-lib", 112 | "version": "^2.168.0", 113 | "type": "peer" 114 | }, 115 | { 116 | "name": "constructs", 117 | "version": "^10.3.0", 118 | "type": "peer" 119 | } 120 | ], 121 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 122 | } 123 | -------------------------------------------------------------------------------- /.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 | ".gitpod.yml", 12 | ".mergify.yml", 13 | ".prettierignore", 14 | ".prettierrc.json", 15 | ".projen/deps.json", 16 | ".projen/files.json", 17 | ".projen/tasks.json", 18 | "LICENSE", 19 | "tsconfig.dev.json" 20 | ], 21 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 22 | } 23 | -------------------------------------------------------------------------------- /.projenrc.ts: -------------------------------------------------------------------------------- 1 | import { ReleasableCommits, awscdk, github, javascript, release } from 'projen'; 2 | import { ArrowParens, NodePackageManager } from 'projen/lib/javascript'; 3 | 4 | let cdkVersion = '2.168.0'; 5 | const project = new awscdk.AwsCdkConstructLibrary({ 6 | author: 'Open Construct Foundation', 7 | authorAddress: 'thorsten.hoeger@taimos.de', 8 | cdkVersion: cdkVersion, 9 | defaultReleaseBranch: 'main', 10 | jsiiVersion: '~5.7.0', 11 | constructsVersion: '10.3.0', 12 | name: '@open-constructs/aws-cdk', 13 | projenrcTs: true, 14 | repositoryUrl: 'https://github.com/open-constructs/aws-cdk-library.git', 15 | licensed: true, 16 | license: 'Apache-2.0', 17 | packageManager: NodePackageManager.NPM, 18 | experimentalIntegRunner: false, // we're using the AWS CDK-provided runner 19 | // autoApproveUpgrades: true, 20 | // autoApproveOptions: { allowedUsernames: ['hoegertn'] }, 21 | depsUpgradeOptions: { 22 | workflowOptions: { 23 | schedule: javascript.UpgradeDependenciesSchedule.WEEKLY, 24 | }, 25 | }, 26 | githubOptions: { 27 | projenCredentials: github.GithubCredentials.fromApp(), 28 | pullRequestLintOptions: { 29 | semanticTitleOptions: { 30 | types: ['feat', 'fix', 'chore', 'ci', 'docs', 'style', 'refactor', 'test', 'revert', 'Revert'], 31 | }, 32 | contributorStatement: 33 | '_By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license_', 34 | }, 35 | }, 36 | pullRequestTemplateContents: [ 37 | `### Issue # (if applicable) 38 | 39 | Closes #. 40 | 41 | ### Reason for this change 42 | 43 | 44 | 45 | ### Description of changes 46 | 47 | 48 | 49 | ### Description of how you validated changes 50 | 51 | 52 | 53 | ### Checklist 54 | 55 | - [ ] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/open-constructs/aws-cdk-library/blob/main/CONTRIBUTING.md) 56 | - [ ] My pull request adheres to the [Pull Request Rule](https://github.com/open-constructs/aws-cdk-library/blob/main/CONTRIBUTING.md#pull-request) 57 | - **Do not omit the \`aws-\` part in the scope of the PR title if the PR relates to a specific AWS service module.** 58 | - e.g.) feat(**aws-s3**): description of the change`, 59 | ], 60 | releaseTrigger: release.ReleaseTrigger.continuous(), 61 | releasableCommits: ReleasableCommits.ofType(['feat', 'fix', 'revert', 'Revert']), 62 | gitpod: true, 63 | npmAccess: javascript.NpmAccess.PUBLIC, 64 | publishToPypi: { 65 | distName: 'open-constructs-aws-cdk', 66 | module: 'open_constructs_aws_cdk', 67 | }, 68 | workflowNodeVersion: '18.x', 69 | minNodeVersion: '18.0.0', 70 | // publishToMaven: { 71 | // mavenGroupId: 'org.open-constructs', 72 | // mavenArtifactId: 'aws-cdk', 73 | // javaPackage: 'org.open_constructs.aws_cdk', 74 | // }, 75 | // publishToNuget: { 76 | // packageId: 'OpenConstructs.AwsCdk', 77 | // dotNetNamespace: 'OpenConstructs.AwsCdk', 78 | // }, 79 | devDeps: [`@aws-cdk/integ-runner@${cdkVersion}-alpha.0`, `@aws-cdk/integ-tests-alpha@${cdkVersion}-alpha.0`], 80 | eslintOptions: { 81 | dirs: ['src', 'test'], 82 | prettier: true, 83 | }, 84 | prettier: true, 85 | prettierOptions: { 86 | settings: { 87 | singleQuote: true, 88 | printWidth: 120, 89 | arrowParens: ArrowParens.AVOID, 90 | }, 91 | ignoreFileOptions: { 92 | ignorePatterns: ['*.md'], 93 | }, 94 | }, 95 | }); 96 | 97 | project.addTask('integ', { 98 | exec: 'integ-runner', 99 | description: 'Run integration tests', 100 | receiveArgs: true, 101 | }); 102 | 103 | project.addTask('integ:update', { 104 | exec: 'integ-runner --update-on-failed', 105 | description: 'Run integration tests and update on any failed tests', 106 | receiveArgs: true, 107 | }); 108 | 109 | project.synth(); 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open Constructs Library 2 | 3 | Welcome to the Open Constructs Library, an innovative solution designed to enhance cloud infrastructure deployment and management. 4 | Our library aims to provide a comprehensive set of tools and constructs that streamline the process of using AWS Cloud Development Kit (AWS CDK) for automation and deployment tasks. 5 | 6 | ## About The Project 7 | 8 | The Open Constructs Library is an open-source initiative aimed at supporting and simplifying the development and management of cloud-native applications. 9 | By leveraging the power of AWS CDK, our library offers developers a wide range of pre-built constructs that encapsulate best practices, reduce boilerplate code, and accelerate the development process. 10 | 11 | ### Features 12 | 13 | - **Pre-built Constructs**: A wide variety of constructs for common cloud infrastructure patterns. 14 | - **Best Practices Encapsulation**: Implements AWS best practices out of the box. 15 | - **Customizability**: Easily extend and customize constructs to fit your specific requirements. 16 | - **Community-driven**: Contributed to and used by a growing community of cloud professionals. 17 | 18 | ## Getting Started 19 | 20 | To get started with the Open Constructs Library, you'll need to have Node.js and the AWS CDK installed on your machine (Other languages will follow very soon). Follow these steps to set up your project. 21 | 22 | ### Prerequisites 23 | 24 | - Node.js (version 18.x or later) 25 | - AWS CDK (version 2.120.0 or later) 26 | 27 | ### Installation 28 | 29 | 1. Install the library via npm: 30 | 31 | ```bash 32 | npm install @open-constructs/aws-cdk 33 | ``` 34 | 35 | 2. Import the constructs you need in your CDK stack: 36 | 37 | ```typescript 38 | import { SomeConstruct } from '@open-constructs/aws-cdk'; 39 | ``` 40 | 41 | 3. Follow the library documentation to see how to use the constructs in your application. 42 | 43 | ## Documentation 44 | 45 | For more detailed documentation, including API references and examples, please visit our [documentation site](./API.md). 46 | 47 | ## Contributing 48 | 49 | We welcome contributions from the community! If you're interested in contributing to the Open Constructs Library, please read our [Contributing Guide](./CONTRIBUTING.md) for more information on how to get started. 50 | 51 | ## Support and Community 52 | 53 | If you need help or want to discuss the Open Constructs Library, join our community on cdk.dev Slack. 54 | 55 | ## License 56 | 57 | The Open Constructs Library is open-source software licensed under the [Apache License](./LICENSE). 58 | 59 | ## Acknowledgements 60 | 61 | - AWS CDK 62 | - [Contributors](https://github.com/open-constructs/aws-cdk-library/graphs/contributors) 63 | 64 | We are grateful to all the contributors who have helped to build and maintain this library. 65 | 66 | --- 67 | 68 | For more information, please visit our [official website](https://www.open-constructs.org). 69 | -------------------------------------------------------------------------------- /cdk.context.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": {} 3 | } -------------------------------------------------------------------------------- /decisions/ADR-01-Repo-Structure.md: -------------------------------------------------------------------------------- 1 | # ADR-01-Repo-Structure 2 | 3 | | Status | | community-review | 4 | | --- | --- | --- | 5 | | Date | | 2024-03-14 | 6 | | Decider | | @maintainers | 7 | 8 | ## Context 9 | 10 | In order to get started with accepting code contributions the basic repository must be defined. 11 | This structure should enable contributors to find things quickly and makes sure it feels natural to use. 12 | 13 | ## Project Structure 14 | 15 | ``` 16 | . 17 | ├── src 18 | │ ├── aws-elasticache 19 | │ │ ├── redis-replication-group.ts 20 | │ ├── ... 21 | │ ├── static-website 22 | │ │ ├── static-website.ts 23 | │ ├── monitoring 24 | │ │ ├── lambda-monitoring 25 | │ │ │ ├── lambda-monitoring.ts 26 | │ │ ├── monitoring-aspect.ts 27 | │ ├── ... 28 | │ ├── core 29 | │ │ ├── typescript-asset 30 | │ │ │ ├── typescript-asset.ts 31 | │ │ └── ... 32 | ├── test 33 | │ ├── aws-elasticache 34 | │ │ ├── integ.redis-replication-group.ts 35 | │ │ ├── redis-replication-group.test.ts 36 | │ ├── ... 37 | │ ├── static-website 38 | │ │ ├── static-website.test.ts 39 | │ ├── monitoring 40 | │ │ ├── lambda-monitoring 41 | │ │ │ ├── lambda-monitoring.test.ts 42 | │ │ ├── integ.monitoring.ts 43 | │ │ ├── monitoring-aspect.test.ts 44 | │ ├── ... 45 | │ ├── core 46 | │ │ ├── typescript-asset 47 | │ │ │ ├── integ.typescript-asset.ts 48 | │ │ │ ├── typescript-asset.test.ts 49 | │ │ └── ... 50 | 51 | ``` 52 | 53 | The CDK construct library is managed as a mono-package with folders for each group of constructs. 54 | 55 | The project structure for the TypeScript project hosting the CDK construct library should have the following folders: 56 | 57 | The root for all code that is published as part of this library is `src`. There can be other folders on the same level as `src` to manage the development and deployment processes for this library. 58 | 59 | Inside `src`, there are three types of folders: 60 | - `core` contains the core features of this library that don't belong to any specific service or use-case, as well as code that is shared between multiple services or use-cases. 61 | - Each service has its own folder, named after the service, e.g. `aws-elasticache`. The folder name starts with the name of the provider of that service. E.g. if it's an AWS service, the folder name starts with `aws-`. The folder contains all constructs, aspects, and other code specific to that service, typically mostly L2-constructs. 62 | - Each use-case has its own folder, named after the use-case, e.g. `monitoring`. The folder name does not include the provider name, as use cases can potentially span multiple providers. The folder contains all constructs, aspects, and other code specific to that use-case, typically mostly L3-constructs. 63 | 64 | Next to `src`, there is a folder called `test` which mirrors the folder structure from `src` and contains the related unit and integration tests. 65 | 66 | -------------------------------------------------------------------------------- /decisions/ADR-02-using-projen.md: -------------------------------------------------------------------------------- 1 | # ADR-02-Using-Projen 2 | 3 | | Status | | decided | 4 | | --- | --- | --- | 5 | | Date | | 2024-03-20 | 6 | | Decider | | @maintainers | 7 | 8 | 9 | ## projen 10 | 11 | The project will be set up using the `projen` tool. `projen` is a development tool that helps create and maintain modern project configurations. 12 | It automates the setup of common project files, dependencies, and build configurations. For this project, the project type will be `jsiiProject`. 13 | `jsii` stands for "JavaScript Interoperability Interface" and is a framework for building and publishing JavaScript libraries in multiple languages. 14 | 15 | `projen` will generate the initial project structure and configuration files based on the `jsiiProject` type, including the necessary build scripts, package configuration, and CI/CD setup. 16 | 17 | ### GitHub Actions 18 | 19 | The project will be hosted on GitHub, and CI/CD will be implemented using GitHub Actions. 20 | GitHub Actions is a powerful workflow automation tool that allows you to build, test, and deploy your code directly from your GitHub repository. 21 | It provides pre-built actions and allows for custom actions, making it a convenient choice for CI/CD pipelines. 22 | With GitHub Actions, you can have confidence in the reliability and efficiency of your project's CI/CD process. 23 | 24 | The following workflows will be implemented using GitHub Actions for the development of a library: 25 | 26 | - Build and Test Workflow: This workflow will build the library and run tests to ensure code quality and functionality. 27 | - Code Quality Workflow: This workflow will run code quality checks, such as linting and static analysis, to maintain code standards. 28 | - Documentation Workflow: This workflow will generate documentation for the library, ensuring up-to-date and comprehensive documentation. 29 | - Release Workflow: This workflow will automate the release process, including versioning, generating release notes, and publishing artifacts. 30 | - Pull Request Workflow: This workflow will automate the process of reviewing and merging pull requests. These workflows need to be green to be able to merge 31 | 32 | This workflow will help ensure code quality and streamline the pull request process for efficient collaboration and integration of new features or bug fixes. 33 | 34 | These workflows will help automate and streamline the development, testing, and documentation processes, ensuring a smooth and efficient library development workflow. 35 | -------------------------------------------------------------------------------- /decisions/ADR-03-merge-tooling.md: -------------------------------------------------------------------------------- 1 | # ADR-03-Merge-Tooling 2 | 3 | | Status | | decided | 4 | | --- | --- | --- | 5 | | Date | | 2024-04-14 | 6 | | Decider | | @maintainers | 7 | 8 | ## Context 9 | 10 | To accept contributions from other people, these PRs need to be merged. 11 | 12 | There are different ways to handle automatic merging. 13 | 14 | ## Options 15 | 16 | ### GH Merge Trains 17 | 18 | The merging process for this project will be done using Github merge trains. Github merge trains are a feature of Github's pull request workflow that helps manage and organize the merging of pull requests. Instead of merging pull requests individually, merge trains allow multiple pull requests to be merged sequentially. 19 | 20 | When a pull request is ready to be merged, it joins the merge train and waits for its turn. The pull requests are merged one after another in the order they join the merge train. This ensures that the changes are integrated in a controlled and orderly manner, reducing conflicts and maintaining a stable codebase. 21 | 22 | GitHub merge trains also provide additional features like auto-merging, where pull requests that pass all checks and do not have conflicts can be automatically merged without manual intervention. This helps streamline the merging process and reduce manual effort. 23 | 24 | Using Github merge trains for merging pull requests in this project will help ensure a smooth and efficient merging process, minimize conflicts, and maintain the stability of the codebase. 25 | 26 | ### Mergify 27 | 28 | Mergify is an alternative solution for managing the merging of pull requests in a project. It is a GitHub app that automates the merging process based on a set of predefined rules and conditions. Mergify allows you to define custom workflows and actions to handle the merging of pull requests. 29 | 30 | With Mergify, you can set up rules that specify when and how pull requests should be merged. For example, you can define rules to automatically merge pull requests that pass all checks and have no conflicts or rules to require a certain number of approved reviews before merging. 31 | 32 | One of Mergify's key features is its ability to handle complex merging scenarios. It supports various merging strategies, such as rebase, squash, and merge commits, allowing you to choose the best strategy for your project. Mergify also provides advanced conflict resolution options, making it easier to handle conflicts when merging pull requests. 33 | 34 | Mergify provides a web-based interface where you can configure and manage the merging rules for your project. It offers various customization options, allowing you to tailor the merging process to fit your project's specific needs. 35 | 36 | ## Decision 37 | 38 | We decided to use GitHub MergeTrains/MergeQueue. 39 | -------------------------------------------------------------------------------- /decisions/ADR-04-conflicting-contributions.md: -------------------------------------------------------------------------------- 1 | # ADR-04-Conflicting-contributions 2 | 3 | | Status | | decided | 4 | | --- | --- | --- | 5 | | Date | | 2024-05-02 | 6 | | Decider | | @maintainers | 7 | 8 | ## Context 9 | 10 | Sometimes, new functionality for this library is suggested that is already covered by other open source projects. Multiple of those projects might try to get incorporated into this one. 11 | 12 | ## Options 13 | 14 | ### Forgo adding existing solutions 15 | 16 | We could forgo adding existing solutions to our codebase. This would get around any kind of related conflict and the solutions would still be available. 17 | 18 | On the other hand, one of the goals of this library is to provide thoroughly tested solutions to make it easier for enterprise users to consume just one approved library. 19 | 20 | ### Combine learnings from multiple solutions 21 | 22 | We could research alternatives and try to combine what we learn from those that are willing to contribute to create an improved solution. 23 | 24 | ### Decide for a competing solution 25 | 26 | We could also decide on one of the competing solutions where the maintainer is interested in contributing it to this library. This would be less work than combining multiple solutions, but it could lead to sub-par results. 27 | 28 | ## Decision 29 | 30 | We research alternative solutions before accepting a contribution and reach out to their maintainers, so that we can discuss together the advantages and disadvantages of different solutions. We aim not to pit maintainers against each other, but instead to push for collaboration to combine the learnings from all solutions. 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@open-constructs/aws-cdk", 3 | "repository": { 4 | "type": "git", 5 | "url": "https://github.com/open-constructs/aws-cdk-library.git" 6 | }, 7 | "scripts": { 8 | "build": "npx projen build", 9 | "bump": "npx projen bump", 10 | "clobber": "npx projen clobber", 11 | "compat": "npx projen compat", 12 | "compile": "npx projen compile", 13 | "default": "npx projen default", 14 | "docgen": "npx projen docgen", 15 | "eject": "npx projen eject", 16 | "eslint": "npx projen eslint", 17 | "integ": "npx projen integ", 18 | "integ:update": "npx projen integ:update", 19 | "package": "npx projen package", 20 | "package-all": "npx projen package-all", 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": "Open Construct Foundation", 36 | "email": "thorsten.hoeger@taimos.de", 37 | "organization": false 38 | }, 39 | "devDependencies": { 40 | "@aws-cdk/integ-runner": "2.168.0-alpha.0", 41 | "@aws-cdk/integ-tests-alpha": "2.168.0-alpha.0", 42 | "@types/jest": "^29.5.13", 43 | "@types/node": "^18", 44 | "@typescript-eslint/eslint-plugin": "^8", 45 | "@typescript-eslint/parser": "^8", 46 | "aws-cdk-lib": "2.168.0", 47 | "commit-and-tag-version": "^12", 48 | "constructs": "10.3.0", 49 | "eslint": "^9", 50 | "eslint-config-prettier": "^10.0.1", 51 | "eslint-import-resolver-typescript": "^3.7.0", 52 | "eslint-plugin-import": "^2.31.0", 53 | "eslint-plugin-prettier": "^5.2.3", 54 | "jest": "^29.7.0", 55 | "jest-junit": "^16", 56 | "jsii": "~5.7.0", 57 | "jsii-diff": "^1.106.0", 58 | "jsii-docgen": "^10.5.0", 59 | "jsii-pacmak": "^1.106.0", 60 | "jsii-rosetta": "~5.7.0", 61 | "prettier": "^3.3.3", 62 | "projen": "^0.91.8", 63 | "ts-jest": "^29.2.5", 64 | "ts-node": "^10.9.2", 65 | "typescript": "^5.6.2" 66 | }, 67 | "peerDependencies": { 68 | "aws-cdk-lib": "^2.168.0", 69 | "constructs": "^10.3.0" 70 | }, 71 | "keywords": [ 72 | "cdk" 73 | ], 74 | "engines": { 75 | "node": ">= 18.0.0" 76 | }, 77 | "main": "lib/index.js", 78 | "license": "Apache-2.0", 79 | "publishConfig": { 80 | "access": "public" 81 | }, 82 | "version": "0.0.0", 83 | "jest": { 84 | "coverageProvider": "v8", 85 | "testMatch": [ 86 | "/@(src|test)/**/*(*.)@(spec|test).ts?(x)", 87 | "/@(src|test)/**/__tests__/**/*.ts?(x)", 88 | "/@(projenrc)/**/*(*.)@(spec|test).ts?(x)", 89 | "/@(projenrc)/**/__tests__/**/*.ts?(x)" 90 | ], 91 | "clearMocks": true, 92 | "collectCoverage": true, 93 | "coverageReporters": [ 94 | "json", 95 | "lcov", 96 | "clover", 97 | "cobertura", 98 | "text" 99 | ], 100 | "coverageDirectory": "coverage", 101 | "coveragePathIgnorePatterns": [ 102 | "/node_modules/" 103 | ], 104 | "testPathIgnorePatterns": [ 105 | "/node_modules/" 106 | ], 107 | "watchPathIgnorePatterns": [ 108 | "/node_modules/" 109 | ], 110 | "reporters": [ 111 | "default", 112 | [ 113 | "jest-junit", 114 | { 115 | "outputDirectory": "test-reports" 116 | } 117 | ] 118 | ], 119 | "transform": { 120 | "^.+\\.[t]sx?$": [ 121 | "ts-jest", 122 | { 123 | "tsconfig": "tsconfig.dev.json" 124 | } 125 | ] 126 | } 127 | }, 128 | "types": "lib/index.d.ts", 129 | "stability": "stable", 130 | "jsii": { 131 | "outdir": "dist", 132 | "targets": { 133 | "python": { 134 | "distName": "open-constructs-aws-cdk", 135 | "module": "open_constructs_aws_cdk" 136 | } 137 | }, 138 | "tsc": { 139 | "outDir": "lib", 140 | "rootDir": "src" 141 | } 142 | }, 143 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 144 | } 145 | -------------------------------------------------------------------------------- /src/aws-codeartifact/README.md: -------------------------------------------------------------------------------- 1 | Constructs for the AWS CodeArtifact service 2 | 3 | # CDK Constructs for CodeArtifact Service 4 | 5 | ## Overview 6 | 7 | The `Domain` and `Repository` constructs simplify the creation and management of AWS CodeArtifact domains and repositories within AWS CDK 8 | applications. These constructs allow users to manage private repositories for software packages and define domains to group repositories, 9 | facilitating secure sharing and version control across teams. 10 | 11 | ## Usage 12 | 13 | Import the `Domain` and `Repository` constructs and create a new CodeArtifact domain & repository within your AWS CDK stack. 14 | 15 | ```ts 16 | import { App, Stack } from 'aws-cdk-lib'; 17 | import { Domain, Repository } from '@open-constructs/aws-cdk/aws-codeartifact'; 18 | 19 | const app = new App(); 20 | const stack = new Stack(app, 'CodeArtifactDomainStack'); 21 | 22 | const domain = new Domain(stack, 'MyDomain', { 23 | domainName: 'my-domain', 24 | }); 25 | 26 | const repository = new Repository(this, 'MyRepo', { 27 | domain: domain, 28 | repositoryName: 'my-repo', 29 | }); 30 | ``` 31 | 32 | ### Importing existing resources 33 | 34 | If you need to manage an existing CodeArtifact repository, you can import it into your CDK stack. Since the domain is implicit in the ARN of the repository it will be automatically imported as well. 35 | 36 | ```ts 37 | import { Repository } from '@open-constructs/aws-cdk/aws-codeartifact'; 38 | 39 | const existingRepo = Repository.fromRepositoryArn(stack, 'ImportedRepo', 'arn:aws:codeartifact:us-east-1:123456789012:repository/my-domain/my-repo'); 40 | ``` -------------------------------------------------------------------------------- /src/aws-codeartifact/index.ts: -------------------------------------------------------------------------------- 1 | export * from './domain'; 2 | export * from './repository'; 3 | -------------------------------------------------------------------------------- /src/aws-cur/README.md: -------------------------------------------------------------------------------- 1 | Constructs for the AWS Cost and Usage reports 2 | 3 | # CostReport CDK Construct 4 | 5 | ## Overview 6 | 7 | The `CostReport` construct facilitates the creation and management of AWS Cost and Usage Reports (CUR) 8 | within AWS CDK applications. This construct automates the setup of the necessary S3 bucket for storing 9 | the reports and configures permissions for AWS billing to write to this bucket. You can specify 10 | various properties of the report like name, format, and granularity. 11 | 12 | ## Usage 13 | 14 | Import the necessary classes and enums from AWS CDK and this construct: 15 | 16 | ```typescript 17 | import { App, Stack } from 'aws-cdk-lib'; 18 | import { CostReport, ReportGranularity, CurFormat } from '@open-constructs/aws-cdk/aws-cur'; 19 | ``` 20 | 21 | ### Basic Example 22 | 23 | Here's how you can create a monthly cost and usage report in Parquet format: 24 | 25 | ```typescript 26 | const app = new App(); 27 | // Cannot specify regions other than us-east-1 28 | const stack = new Stack(app, 'CostReportStack', { env: { region: 'us-east-1' } }); 29 | 30 | new CostReport(stack, 'MyCostReport', { 31 | costReportName: 'monthly-business-report', 32 | reportGranularity: ReportGranularity.MONTHLY, 33 | format: CurFormat.PARQUET, 34 | }); 35 | ``` 36 | 37 | ### Advanced Example 38 | 39 | Creating a report with a custom S3 bucket and hourly granularity: 40 | 41 | ```typescript 42 | import { Bucket } from 'aws-cdk-lib/aws-s3'; 43 | 44 | const customBucket = new Bucket(stack, 'MyCustomBucket'); 45 | 46 | new CostReport(stack, 'MyDetailedCostReport', { 47 | costReportName: 'detailed-hourly-report', 48 | bucket: customBucket, 49 | reportGranularity: ReportGranularity.HOURLY, 50 | format: CurFormat.TEXT_OR_CSV, 51 | }); 52 | ``` 53 | 54 | ### Generating Unique Report Name By Default 55 | 56 | If you set the `enableDefaultUniqueReportName` property to `true`, the construct will automatically 57 | generate a unique report name by default. 58 | 59 | The cost report name must be unique within your AWS account. So this property is useful when you want 60 | to create multiple reports without specifying a report name for each one. 61 | 62 | If you specify a report name directly via the `costReportName`, the construct will use that name instead 63 | of generating a unique one. 64 | 65 | ```typescript 66 | new CostReport(stack, 'MyCostReport', { 67 | enableDefaultUniqueReportName: true, 68 | }); 69 | ``` 70 | 71 | ### Additional Notes 72 | 73 | The construct automatically handles the permissions required for AWS billing services to access the S3 bucket. 74 | -------------------------------------------------------------------------------- /src/aws-cur/cost-report.ts: -------------------------------------------------------------------------------- 1 | import { Names, Resource, Stack, Token, aws_cur, aws_iam, aws_s3 } from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | 4 | /** 5 | * Enum for the possible granularities of a cost report 6 | */ 7 | export class ReportGranularity { 8 | /** Hourly granularity */ 9 | public static readonly HOURLY: ReportGranularity = new ReportGranularity('HOURLY'); 10 | /** Daily granularity */ 11 | public static readonly DAILY: ReportGranularity = new ReportGranularity('DAILY'); 12 | /** Weekly granularity */ 13 | public static readonly MONTHLY: ReportGranularity = new ReportGranularity('MONTHLY'); 14 | 15 | /** 16 | * Returns a ReportGranularity instance for the given granularity string value. 17 | * 18 | * @param granularity - The granularity string value to create an instance for. 19 | * @returns A ReportGranularity instance. 20 | */ 21 | public static for(granularity: string): ReportGranularity { 22 | return new ReportGranularity(granularity); 23 | } 24 | 25 | protected constructor(public readonly value: string) {} 26 | } 27 | 28 | /** 29 | * Enum for the possible formats of a cost report 30 | */ 31 | export class CurFormat { 32 | /** GZIP compressed text or CSV format */ 33 | public static readonly TEXT_OR_CSV: CurFormat = new CurFormat('GZIP', 'textORcsv'); 34 | /** Parquet format */ 35 | public static readonly PARQUET: CurFormat = new CurFormat('Parquet', 'Parquet'); 36 | 37 | /** 38 | * Returns a CurFormat instance for the given compression and format string values. 39 | * 40 | * @param compression - The compression string value 41 | * @param format - The format string value 42 | * @returns A CurFormat instance. 43 | */ 44 | public static for(compression: string, format: string): CurFormat { 45 | return new CurFormat(compression, format); 46 | } 47 | 48 | protected constructor( 49 | public readonly compression: string, 50 | public readonly format: string, 51 | ) {} 52 | } 53 | 54 | /** 55 | * Properties for defining a Cost and Usage Report. 56 | */ 57 | export interface CostReportProps { 58 | /** 59 | * Whether to generate a unique report name automatically if the `costReportName` property 60 | * is not specified. 61 | * 62 | * The default value of the `costReportName` is normally ‘default-cur’, but setting this flag 63 | * to true will generate a unique default value. 64 | * 65 | * This flag is ignored if the `costReportName` property is specified. 66 | * 67 | * @default false 68 | */ 69 | readonly enableDefaultUniqueReportName?: boolean; 70 | 71 | /** 72 | * The name of the cost report. 73 | * 74 | * The name must be unique, is case sensitive, and can't include spaces. 75 | * 76 | * The length of this name must be between 1 and 256. 77 | * 78 | * @default - a unique name automatically generated if `enableDefaultUniqueReportName` is 79 | * true, otherwise 'default-cur'. 80 | */ 81 | readonly costReportName?: string; 82 | 83 | /** 84 | * The bucket to place the cost report into. 85 | * If non is provided, a new bucket will be created. 86 | * 87 | * @default - a new bucket will be created. 88 | */ 89 | readonly bucket?: aws_s3.IBucket; 90 | 91 | /** 92 | * The format to use for the cost and usage report. 93 | * 94 | * @default - TEXT_OR_CSV 95 | */ 96 | readonly format?: CurFormat; 97 | 98 | /** 99 | * The granularity of the line items in the report 100 | * 101 | * @default - HOURLY 102 | */ 103 | readonly reportGranularity?: ReportGranularity; 104 | } 105 | 106 | /** 107 | * Represents a Cost Report construct in AWS CDK. 108 | * This class creates an AWS Cost and Usage Report, stored in an S3 bucket, and configures the necessary permissions. 109 | * 110 | * @example 111 | * const report = new CostReport(stack, 'MyReport', { 112 | * costReportName: 'business-report', 113 | * reportGranularity: ReportGranularity.MONTHLY, 114 | * format: CurFormat.PARQUET 115 | * }); 116 | */ 117 | export class CostReport extends Resource { 118 | /** The S3 bucket that stores the cost report */ 119 | public readonly reportBucket: aws_s3.IBucket; 120 | /** The name of the cost report */ 121 | public readonly costReportName: string; 122 | 123 | constructor(scope: Construct, id: string, props: CostReportProps) { 124 | super(scope, id); 125 | 126 | const currentStack = Stack.of(this); 127 | 128 | if (!Token.isUnresolved(currentStack.region) && currentStack.region !== 'us-east-1') { 129 | throw new Error( 130 | `The \`CostReport\` construct is only available in the us-east-1 region, got: ${currentStack.region} region`, 131 | ); 132 | } 133 | 134 | this.reportBucket = 135 | props.bucket ?? 136 | this.createReportBucket(this, 'Bucket', { 137 | blockPublicAccess: aws_s3.BlockPublicAccess.BLOCK_ALL, 138 | enforceSSL: true, 139 | }); 140 | 141 | const billingPrincipal = new aws_iam.ServicePrincipal('billingreports.amazonaws.com').withConditions({ 142 | StringEquals: { 143 | 'aws:SourceArn': `arn:aws:cur:${currentStack.region}:${currentStack.account}:definition/*`, 144 | 'aws:SourceAccount': currentStack.account, 145 | }, 146 | }); 147 | // Grant the global CUR Account write access to the bucket 148 | this.reportBucket.grantPut(billingPrincipal); 149 | this.reportBucket.addToResourcePolicy( 150 | new aws_iam.PolicyStatement({ 151 | effect: aws_iam.Effect.ALLOW, 152 | principals: [billingPrincipal], 153 | actions: ['s3:GetBucketAcl', 's3:GetBucketPolicy'], 154 | resources: [this.reportBucket.bucketArn], 155 | }), 156 | ); 157 | 158 | const format = props.format ?? CurFormat.TEXT_OR_CSV; 159 | 160 | if (props.costReportName !== undefined && !Token.isUnresolved(props.costReportName)) { 161 | if (props.costReportName.length < 1 || props.costReportName.length > 256) { 162 | throw new Error( 163 | `'costReportName' must be between 1 and 256 characters long, got: ${props.costReportName.length}`, 164 | ); 165 | } 166 | if (!/^[0-9A-Za-z!\-_.*'()]+$/.test(props.costReportName)) { 167 | throw new Error( 168 | `'costReportName' must only contain alphanumeric characters and the following special characters: !-_.*'(), got: '${props.costReportName}'`, 169 | ); 170 | } 171 | } 172 | 173 | const reportName = 174 | props.costReportName ?? 175 | (props.enableDefaultUniqueReportName 176 | ? Names.uniqueResourceName(this, { 177 | maxLength: 256, 178 | allowedSpecialCharacters: "!-_.*'()", 179 | }) 180 | : 'default-cur'); 181 | 182 | const reportDefinition = this.createReportDefinition(this, 'Resource', { 183 | compression: format.compression, 184 | format: format.format, 185 | refreshClosedReports: false, 186 | reportName, 187 | reportVersioning: 'CREATE_NEW_REPORT', 188 | s3Bucket: this.reportBucket.bucketName, 189 | s3Prefix: 'reports', 190 | s3Region: currentStack.region, 191 | timeUnit: props.reportGranularity?.value ?? 'HOURLY', 192 | additionalSchemaElements: ['RESOURCES'], 193 | }); 194 | reportDefinition.node.addDependency(this.reportBucket.policy!); 195 | 196 | this.costReportName = reportDefinition.ref; 197 | } 198 | 199 | protected createReportBucket(scope: Construct, id: string, props: aws_s3.BucketProps): aws_s3.IBucket { 200 | return new aws_s3.Bucket(scope, id, props); 201 | } 202 | 203 | protected createReportDefinition( 204 | scope: Construct, 205 | id: string, 206 | props: aws_cur.CfnReportDefinitionProps, 207 | ): aws_cur.CfnReportDefinition { 208 | return new aws_cur.CfnReportDefinition(scope, id, props); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/aws-cur/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cost-report'; 2 | -------------------------------------------------------------------------------- /src/aws-ec2/README.md: -------------------------------------------------------------------------------- 1 | Constructs for the AWS EC2 service 2 | 3 | # EC2 Instance Connect Endpoint CDK Construct 4 | 5 | ## Overview 6 | 7 | The `InstanceConnectEndpoint` construct facilitates the creation and management of [EC2 Instance Connect endpoints](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-with-ec2-instance-connect-endpoint.html) 8 | within AWS CDK applications. 9 | 10 | ## Usage 11 | 12 | Import the necessary classes from AWS CDK and this construct and create a VPC for the endpoint: 13 | 14 | ```ts 15 | import { App, Stack } from 'aws-cdk-lib'; 16 | import * as ec2 from 'aws-cdk-lib/aws-ec2'; 17 | import { InstanceConnectEndpoint } from '@open-constructs/aws-cdk/aws-ec2'; 18 | 19 | const app = new App(); 20 | const stack = new Stack(app, 'InstanceConnectEndpointStack'); 21 | const vpc = new ec2.Vpc(stack, 'MyVpc'); 22 | ``` 23 | 24 | ### Basic Example 25 | 26 | Here's how you can create an EC2 Instance Connect endpoint and allow connections to an EC2 instance: 27 | 28 | ```ts 29 | const instance = new ec2.Instance(this, 'Instance', { 30 | vpc, 31 | instanceType: ec2.InstanceType.of( 32 | ec2.InstanceClass.C5, 33 | ec2.InstanceSize.LARGE, 34 | ), 35 | machineImage: new ec2.AmazonLinuxImage({ 36 | generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2023, 37 | }), 38 | }); 39 | 40 | const endpoint = new InstanceConnectEndpoint(stack, 'MyEndpoint', { 41 | vpc, 42 | }); 43 | 44 | // Allow SSH connections to the instance 45 | // You can also use the port 3389 for RDP connections 46 | endpoint.connections.allowTo(instance, ec2.Port.tcp(22)); 47 | ``` 48 | 49 | ### Advanced Example 50 | 51 | Creating an endpoint with a custom settings: 52 | 53 | ```ts 54 | declare const endpointSecurityGroup: ec2.ISecurityGroup; 55 | 56 | const endpoint = new InstanceConnectEndpoint(stack, 'MyCustomEndpoint', { 57 | vpc, 58 | securityGroups: [endpointSecurityGroup], // Specify user-defined security groups 59 | preserveClientIp: true, // Whether your client's IP address is preserved as the source 60 | clientToken: 'my-client-token', // Specify client token to ensure the idempotency of the request. 61 | }); 62 | ``` 63 | 64 | Import an existing endpoint: 65 | 66 | ```ts 67 | declare const existingEndpoint: ec2.IInstanceConnectEndpoint; 68 | declare const securityGroups: ec2.ISecurityGroup[]; 69 | 70 | const existingEndpoint = InstanceConnectEndpoint.fromInstanceConnectEndpointAttributes( 71 | stack, 72 | 'MyExistingEndpoint', 73 | { 74 | instanceConnectEndpointId: existingEndpoint.instanceConnectEndpointId, 75 | securityGroups, 76 | }, 77 | ); 78 | ``` 79 | -------------------------------------------------------------------------------- /src/aws-ec2/index.ts: -------------------------------------------------------------------------------- 1 | export * from './instance-connect-endpoint'; 2 | -------------------------------------------------------------------------------- /src/aws-ec2/instance-connect-endpoint.ts: -------------------------------------------------------------------------------- 1 | import { IResource, Resource, aws_ec2 } from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | 4 | /** 5 | * An EC2 Instance Connect Endpoint. 6 | */ 7 | export interface IInstanceConnectEndpoint extends aws_ec2.IConnectable, IResource { 8 | /** 9 | * The ID of the EC2 Instance Connect Endpoint. 10 | * 11 | * @attribute 12 | */ 13 | readonly instanceConnectEndpointId: string; 14 | } 15 | 16 | /** 17 | * Properties for defining an EC2 Instance Connect Endpoint. 18 | */ 19 | export interface InstanceConnectEndpointProps { 20 | /** 21 | * Unique, case-sensitive identifier that you provide to ensure the idempotency of the request. 22 | * 23 | * @see https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-instanceconnectendpoint.html#cfn-ec2-instanceconnectendpoint-clienttoken 24 | */ 25 | readonly clientToken?: string; 26 | 27 | /** 28 | * Indicates whether your client's IP address is preserved as the source. 29 | * 30 | * @see https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-instanceconnectendpoint.html#cfn-ec2-instanceconnectendpoint-preserveclientip 31 | * @default true 32 | */ 33 | readonly preserveClientIp?: boolean; 34 | 35 | /** 36 | * The security groups to associate with the EC2 Instance Connect Endpoint. 37 | * 38 | * @default - a new security group is created 39 | */ 40 | readonly securityGroups?: aws_ec2.ISecurityGroup[]; 41 | 42 | /** 43 | * The VPC in which the EC2 Instance Connect Endpoint is created. 44 | */ 45 | readonly vpc: aws_ec2.IVpc; 46 | } 47 | 48 | /** 49 | * Attributes for importing an EC2 Instance Connect Endpoint. 50 | */ 51 | export interface InstanceConnectEndpointAttributes { 52 | /** 53 | * The ID of the EC2 Instance Connect Endpoint. 54 | */ 55 | readonly instanceConnectEndpointId: string; 56 | 57 | /** 58 | * The security groups associated with the EC2 Instance Connect Endpoint. 59 | */ 60 | readonly securityGroups: aws_ec2.ISecurityGroup[]; 61 | } 62 | 63 | /** 64 | * Represents an EC2 Instance Connect Endpoint construct in AWS CDK. 65 | * 66 | * @example 67 | * declare const securityGroups: aws_ec2.ISecurityGroup[]; 68 | * declare const vpc: aws_ec2.IVpc; 69 | * 70 | * const instanceConnectEndpoint = new InstanceConnectEndpoint( 71 | * stack, 72 | * 'InstanceConnectEndpoint', 73 | * { 74 | * clientToken: 'my-client-token', 75 | * preserveClientIp: true, 76 | * securityGroups, 77 | * vpc, 78 | * }, 79 | * ); 80 | */ 81 | export class InstanceConnectEndpoint extends Resource implements IInstanceConnectEndpoint { 82 | /** 83 | * Import an existing endpoint to the stack from its attributes. 84 | */ 85 | public static fromInstanceConnectEndpointAttributes( 86 | scope: Construct, 87 | id: string, 88 | attrs: InstanceConnectEndpointAttributes, 89 | ): IInstanceConnectEndpoint { 90 | class Import extends Resource implements IInstanceConnectEndpoint { 91 | public readonly instanceConnectEndpointId = attrs.instanceConnectEndpointId; 92 | public readonly connections = new aws_ec2.Connections({ 93 | securityGroups: attrs.securityGroups, 94 | }); 95 | } 96 | 97 | return new Import(scope, id); 98 | } 99 | 100 | /** 101 | * The ID of the EC2 Instance Connect Endpoint. 102 | */ 103 | public readonly instanceConnectEndpointId: string; 104 | 105 | /** 106 | * The connection object associated with the EC2 Instance Connect Endpoint. 107 | */ 108 | public readonly connections: aws_ec2.Connections; 109 | 110 | private readonly props: InstanceConnectEndpointProps; 111 | private readonly securityGroups: aws_ec2.ISecurityGroup[]; 112 | 113 | constructor(scope: Construct, id: string, props: InstanceConnectEndpointProps) { 114 | super(scope, id); 115 | this.props = props; 116 | 117 | this.securityGroups = props.securityGroups ?? [this.createSecurityGroup()]; 118 | 119 | this.connections = new aws_ec2.Connections({ 120 | securityGroups: this.securityGroups, 121 | }); 122 | 123 | const instanceConnectEndpoint = this.createInstanceConnectEndpoint(); 124 | 125 | this.instanceConnectEndpointId = instanceConnectEndpoint.attrId; 126 | } 127 | 128 | protected createInstanceConnectEndpoint(): aws_ec2.CfnInstanceConnectEndpoint { 129 | return new aws_ec2.CfnInstanceConnectEndpoint(this, 'Resource', { 130 | clientToken: this.props.clientToken, 131 | preserveClientIp: this.props.preserveClientIp, 132 | securityGroupIds: this.securityGroups.map(sg => sg.securityGroupId), 133 | subnetId: this.props.vpc.selectSubnets().subnetIds[0], 134 | }); 135 | } 136 | 137 | protected createSecurityGroup(): aws_ec2.SecurityGroup { 138 | return new aws_ec2.SecurityGroup(this, 'SecurityGroup', { 139 | vpc: this.props.vpc, 140 | }); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/aws-elasticache/daily-snapshot-time.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Properties required for setting up a daily snapshot time. 3 | */ 4 | export interface DailySnapshotTimeProps { 5 | /** 6 | * The hour of the day (from 0-23) for snapshot starts. 7 | */ 8 | readonly hour: number; 9 | /** 10 | * The minute of the hour (from 0-59) for snapshot starts. 11 | */ 12 | readonly minute: number; 13 | } 14 | 15 | /** 16 | * Class for scheduling a daily snapshot time. 17 | */ 18 | export class DailySnapshotTime { 19 | /** 20 | * The start hour of the snapshot time in Coordinated Universal Time (UTC), using 24-hour time. 21 | * For example, 17 refers to 5:00 P.M. UTC. 22 | * 23 | * @default - 22 24 | */ 25 | private readonly hour: string; 26 | /** 27 | * The start minute of the snapshot time, in UTC. 28 | * 29 | * @default - 0 30 | */ 31 | private readonly minute: string; 32 | 33 | constructor(props: DailySnapshotTimeProps) { 34 | this.validate(props.hour, props.minute); 35 | 36 | this.hour = this.getTwoDigitString(props.hour); 37 | this.minute = this.getTwoDigitString(props.minute); 38 | } 39 | /** 40 | * Converts an hour, and minute into HH:MM string. 41 | */ 42 | public toTimestamp(): string { 43 | return `${this.hour}:${this.minute}`; 44 | } 45 | 46 | /** 47 | * Pad an integer so that it always contains at least 2 digits. 48 | * Assumes the number is a positive integer. 49 | */ 50 | private getTwoDigitString(n: number): string { 51 | const numberString = n.toString(); 52 | if (numberString.length === 1) { 53 | return `0${n}`; 54 | } 55 | return numberString; 56 | } 57 | 58 | /** 59 | * Validation needed for the values of the daily snapshot time. 60 | */ 61 | private validate(hour: number, minute: number) { 62 | if (!Number.isInteger(hour) || hour < 0 || hour > 23) { 63 | throw new Error(`dailySnapshotTime hour must be an integer between 0 and 23, got: ${hour}`); 64 | } 65 | if (!Number.isInteger(minute) || minute < 0 || minute > 59) { 66 | throw new Error(`dailySnapshotTime minute must be an integer between 0 and 59, got: ${minute}`); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/aws-elasticache/index.ts: -------------------------------------------------------------------------------- 1 | export * from './daily-snapshot-time'; 2 | export * from './serverless-cache'; 3 | export * from './util'; 4 | export * from './user'; 5 | export * from './user-group'; 6 | -------------------------------------------------------------------------------- /src/aws-elasticache/user-group.ts: -------------------------------------------------------------------------------- 1 | import { ArnFormat, IResource, Lazy, Names, Resource, Stack, Token, aws_elasticache } from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | import { IUser } from './user'; 4 | import { Engine } from './util'; 5 | 6 | /** 7 | * Interface for a User Group 8 | */ 9 | export interface IUserGroup extends IResource { 10 | /** 11 | * The ARN of the user group. 12 | * 13 | * @attribute 14 | */ 15 | readonly userGroupArn: string; 16 | /** 17 | * The ID of the user group. 18 | */ 19 | readonly userGroupId: string; 20 | } 21 | 22 | /** 23 | * Properties for defining a User Group. 24 | */ 25 | export interface UserGroupProps { 26 | /** 27 | * The ID of the user group. 28 | * 29 | * \`userGroupId\` can have up to 40 characters. 30 | * 31 | * \`userGroupId\` must consist only of alphanumeric characters or hyphens, 32 | * with the first character as a letter, and it can't end with a hyphen or contain two consecutive hyphens. 33 | * 34 | * @default - auto generate 35 | */ 36 | readonly userGroupId?: string; 37 | 38 | /** 39 | * The list of User that belong to the user group. 40 | * 41 | * A user with the username `default` must be included in `users`. 42 | */ 43 | readonly users: IUser[]; 44 | } 45 | 46 | /** 47 | * Attributes for importing a User Group. 48 | */ 49 | export interface UserGroupAttributes { 50 | /** 51 | * The ID of the user group. 52 | */ 53 | readonly userGroupId: string; 54 | } 55 | 56 | /** 57 | * Represents a user group construct in AWS CDK. 58 | * 59 | * @example 60 | * declare const user: User; 61 | * 62 | * const userGroup = new UserGroup( 63 | * stack, 64 | * 'UserGroup', 65 | * { 66 | * users: [user], 67 | * }, 68 | * ); 69 | */ 70 | export class UserGroup extends Resource implements IUserGroup { 71 | /** 72 | * Imports an existing user group from attributes 73 | */ 74 | public static fromUserGroupId(scope: Construct, id: string, userGroupId: string): IUserGroup { 75 | class Import extends Resource implements IUserGroup { 76 | public readonly userGroupId = userGroupId; 77 | public readonly userGroupArn = Stack.of(this).formatArn({ 78 | service: 'elasticache', 79 | resource: 'usergroup', 80 | resourceName: userGroupId, 81 | arnFormat: ArnFormat.COLON_RESOURCE_NAME, 82 | }); 83 | } 84 | return new Import(scope, id); 85 | } 86 | 87 | /** 88 | * The ARN of the user group. 89 | */ 90 | readonly userGroupArn: string; 91 | 92 | /** 93 | * The ID of the user group. 94 | */ 95 | readonly userGroupId: string; 96 | 97 | private readonly props: UserGroupProps; 98 | 99 | private readonly users: IUser[]; 100 | 101 | constructor(scope: Construct, id: string, props: UserGroupProps) { 102 | super(scope, id, { 103 | physicalName: 104 | props.userGroupId ?? 105 | Lazy.string({ 106 | produce: () => 107 | Names.uniqueResourceName(this, { 108 | maxLength: 40, 109 | separator: '-', 110 | }).toLowerCase(), 111 | }), 112 | }); 113 | this.props = props; 114 | this.users = this.props.users; 115 | 116 | this.validateUserGroupId(); 117 | this.node.addValidation({ validate: () => this.validateDefaultUser() }); 118 | this.node.addValidation({ validate: () => this.validateDuplicateUsernames() }); 119 | 120 | const userGroup = this.createResource(this, 'Resource', { 121 | engine: Engine.REDIS, 122 | userGroupId: this.physicalName, 123 | userIds: Lazy.list({ produce: () => this.users.map(user => user.userId) }), 124 | }); 125 | 126 | this.userGroupArn = userGroup.attrArn; 127 | this.userGroupId = userGroup.ref; 128 | } 129 | 130 | protected createResource( 131 | scope: Construct, 132 | id: string, 133 | props: aws_elasticache.CfnUserGroupProps, 134 | ): aws_elasticache.CfnUserGroup { 135 | return new aws_elasticache.CfnUserGroup(scope, id, props); 136 | } 137 | 138 | /** 139 | * Validates user group id. 140 | */ 141 | private validateUserGroupId(): void { 142 | const userGroupId = this.props.userGroupId; 143 | if (Token.isUnresolved(userGroupId) || userGroupId === undefined) { 144 | return; 145 | } 146 | 147 | if (userGroupId.length < 1 || userGroupId.length > 40) { 148 | throw new Error(`\`userGroupId\` must be between 1 and 40 characters, got ${userGroupId.length} characters.`); 149 | } 150 | 151 | if (!/^[A-Za-z][A-Za-z0-9]*(-[A-Za-z0-9]+)*$/.test(userGroupId)) { 152 | throw new Error( 153 | `\`userGroupId\` must consist only of alphanumeric characters or hyphens, with the first character as a letter, and it can't end with a hyphen or contain two consecutive hyphens, got: ${userGroupId}.`, 154 | ); 155 | } 156 | } 157 | 158 | /** 159 | * Validates default user. 160 | */ 161 | private validateDefaultUser(): string[] { 162 | const userNameList = this.users.map(user => user.userName); 163 | 164 | if (userNameList.some(userName => Token.isUnresolved(userName))) { 165 | return []; 166 | } 167 | const errors: string[] = []; 168 | 169 | if (!userNameList.includes('default')) { 170 | errors.push('A user with the username `default` must be included in `users`.'); 171 | } 172 | 173 | return errors; 174 | } 175 | 176 | /** 177 | * Validates that there are no duplicate usernames in the user group 178 | */ 179 | private validateDuplicateUsernames(): string[] { 180 | const userNameList = this.users.map(user => user.userName); 181 | 182 | if (userNameList.some(username => Token.isUnresolved(username))) { 183 | return []; 184 | } 185 | 186 | const seenUsernames = new Set(); 187 | const errors: string[] = []; 188 | 189 | for (const username of userNameList) { 190 | if (seenUsernames.has(username)) { 191 | errors.push( 192 | `Duplicate username found in user group: \`${username}\` is duplicated. Each username must be unique within a user group.`, 193 | ); 194 | } 195 | seenUsernames.add(username); 196 | } 197 | 198 | return errors; 199 | } 200 | 201 | /** 202 | * Adds a user to the user group 203 | * 204 | * @param user the user to add 205 | */ 206 | public addUser(user: IUser): void { 207 | this.users.push(user); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/aws-elasticache/util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The engine the cache uses. 3 | */ 4 | export enum Engine { 5 | /** 6 | * Redis 7 | */ 8 | REDIS = 'redis', 9 | /** 10 | * Valkey 11 | */ 12 | VALKEY = 'valkey', 13 | /** 14 | * Memcached 15 | */ 16 | MEMCACHED = 'memcached', 17 | } 18 | -------------------------------------------------------------------------------- /src/aws-fsx/daily-automatic-backup-start-time.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Properties required for setting up a daily automatic backup time. 3 | */ 4 | export interface DailyAutomaticBackupStartTimeProps { 5 | /** 6 | * The hour of the day (from 0-23) for automatic backup starts. 7 | */ 8 | readonly hour: number; 9 | /** 10 | * The minute of the hour (from 0-59) for automatic backup starts. 11 | */ 12 | readonly minute: number; 13 | } 14 | 15 | /** 16 | * Class for scheduling a daily automatic backup time. 17 | */ 18 | export class DailyAutomaticBackupStartTime { 19 | /** 20 | * The start hour of the automatic backup time in Coordinated Universal Time (UTC), using 24-hour time. 21 | * For example, 17 refers to 5:00 P.M. UTC. 22 | * 23 | * @default - 22 24 | */ 25 | private readonly hour: string; 26 | /** 27 | * The start minute of the automatic backup time, in UTC. 28 | * 29 | * @default - 0 30 | */ 31 | private readonly minute: string; 32 | 33 | constructor(props: DailyAutomaticBackupStartTimeProps) { 34 | this.validate(props.hour, props.minute); 35 | 36 | this.hour = this.getTwoDigitString(props.hour); 37 | this.minute = this.getTwoDigitString(props.minute); 38 | } 39 | /** 40 | * Converts an hour, and minute into HH:MM string. 41 | */ 42 | public toTimestamp(): string { 43 | return `${this.hour}:${this.minute}`; 44 | } 45 | 46 | /** 47 | * Pad an integer so that it always contains at least 2 digits. Assumes the number is a positive integer. 48 | */ 49 | private getTwoDigitString(n: number): string { 50 | const numberString = n.toString(); 51 | if (numberString.length === 1) { 52 | return `0${n}`; 53 | } 54 | return numberString; 55 | } 56 | 57 | /** 58 | * Validation needed for the values of the daily automatic backup time. 59 | */ 60 | private validate(hour: number, minute: number) { 61 | if (!Number.isInteger(hour) || hour < 0 || hour > 23) { 62 | throw new Error(`dailyAutomaticBackupStartTime hour must be an integer between 0 and 24. received: ${hour}`); 63 | } 64 | if (!Number.isInteger(minute) || minute < 0 || minute > 59) { 65 | throw new Error(`dailyAutomaticBackupStartTime minute must be an integer between 0 and 59. received: ${minute}`); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/aws-fsx/index.ts: -------------------------------------------------------------------------------- 1 | export * from './daily-automatic-backup-start-time'; 2 | export * from './maintenance-time'; 3 | export * from './ontap-file-system'; 4 | -------------------------------------------------------------------------------- /src/aws-fsx/maintenance-time.ts: -------------------------------------------------------------------------------- 1 | import { aws_fsx } from 'aws-cdk-lib'; 2 | 3 | /** 4 | * Properties required for setting up a weekly maintenance time 5 | */ 6 | export interface MaintenanceTimeProps { 7 | /** 8 | * The day of the week for maintenance to be performed. 9 | */ 10 | readonly day: aws_fsx.Weekday; 11 | /** 12 | * The hour of the day (from 0-23) for maintenance to be performed. 13 | */ 14 | readonly hour: number; 15 | /** 16 | * The minute of the hour (from 0-59) for maintenance to be performed. 17 | */ 18 | readonly minute: number; 19 | } 20 | 21 | /** 22 | * Class for scheduling a weekly maintenance time. 23 | */ 24 | export class MaintenanceTime { 25 | /** 26 | * The day of the week for maintenance to be performed. 27 | */ 28 | private readonly day: aws_fsx.Weekday; 29 | /** 30 | * The hour of the day (from 00-23) for maintenance to be performed. 31 | */ 32 | private readonly hour: string; 33 | /** 34 | * The minute of the hour (from 00-59) for maintenance to be performed. 35 | */ 36 | private readonly minute: string; 37 | 38 | constructor(props: MaintenanceTimeProps) { 39 | this.validate(props.hour, props.minute); 40 | 41 | this.day = props.day; 42 | this.hour = this.getTwoDigitString(props.hour); 43 | this.minute = this.getTwoDigitString(props.minute); 44 | } 45 | /** 46 | * Converts a day, hour, and minute into a timestamp as used by FSx for Lustre's weeklyMaintenanceStartTime field. 47 | */ 48 | public toTimestamp(): string { 49 | return `${this.day.valueOf()}:${this.hour}:${this.minute}`; 50 | } 51 | 52 | /** 53 | * Pad an integer so that it always contains at least 2 digits. Assumes the number is a positive integer. 54 | */ 55 | private getTwoDigitString(n: number): string { 56 | const numberString = n.toString(); 57 | if (numberString.length === 1) { 58 | return `0${n}`; 59 | } 60 | return numberString; 61 | } 62 | 63 | /** 64 | * Validation needed for the values of the maintenance time. 65 | */ 66 | private validate(hour: number, minute: number) { 67 | if (!Number.isInteger(hour) || hour < 0 || hour > 23) { 68 | throw new Error('Maintenance time hour must be an integer between 0 and 23'); 69 | } 70 | if (!Number.isInteger(minute) || minute < 0 || minute > 59) { 71 | throw new Error('Maintenance time minute must be an integer between 0 and 59'); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/aws-redshiftserverless/README.md: -------------------------------------------------------------------------------- 1 | Constructs for the Amazon Redshift Serverless 2 | 3 | # Redshift Serverless CDK Construct 4 | 5 | ## Overview 6 | 7 | The `Namespace` construct and the `Workgroup` construct facilitate the creation and management of [Redshift Serverless Workgroups and namespaces](https://docs.aws.amazon.com/redshift/latest/mgmt/serverless-workgroup-namespace.html) within AWS CDK applications. 8 | 9 | ## Usage 10 | 11 | Import the necessary classes from AWS CDK and this construct and create a VPC for the workgroup: 12 | 13 | ```ts 14 | import { App, Stack } from 'aws-cdk-lib'; 15 | import * as ec2 from 'aws-cdk-lib/aws-ec2'; 16 | import { Namespace, Workgroup } from '@open-constructs/aws-cdk/aws-redshiftserverless'; 17 | 18 | const app = new App(); 19 | const stack = new Stack(app, 'RedshiftServerlessStack',{ 20 | account: '012345678901' 21 | region: 'us-east-1', 22 | }); 23 | const vpc = new ec2.Vpc(stack, 'MyVpc'); 24 | ``` 25 | 26 | **Note** If you want to use `Vpc` Construct to create a VPC for `Workgroup`, you must specify `account` and `region` in `Stack`. 27 | `Workgroup` needs at least three subnets, and they must span across three Availability Zones. 28 | 29 | The environment-agnostic stacks will be created with access to only 2 AZs (Ref: [`maxAzs` property docs](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.Vpc.html#maxazs)) 30 | 31 | For more information about Redshift Serverless's limitations, see [Considerations when using Amazon Redshift Serverless](https://docs.aws.amazon.com/redshift/latest/mgmt/serverless-usage-considerations.html). 32 | 33 | ### Basic Example 34 | 35 | Here's how you can create a namespace and a workgroup: 36 | 37 | ```ts 38 | import { SecretValue } from 'aws-cdk-lib'; 39 | import * as iam from 'aws-cdk-lib/aws-iam'; 40 | declare const defaultIamRole: iam.IRole; 41 | declare const anotherIamRole: iam.IRole; 42 | 43 | const namespace = new redshiftserverless.Namespace(stack, 'Namespace', { 44 | namespaceName: 'my-namespace', 45 | defaultIamRole: myIamRole, // Specify a default IAM role 46 | iamRoles: [defaultIamRole, anotherIamRole], // Assign IAM roles list which must include default IAM Role 47 | }); 48 | 49 | const workgroup = new redshiftserverless.Workgroup(stack, 'MyWorkgroup', { 50 | workgroupName: 'my-workgroup', 51 | namespace, 52 | vpc, 53 | }); 54 | ``` 55 | 56 | ### Advanced Example 57 | 58 | Creating a namespace and a workgroup with custom settings: 59 | 60 | ```ts 61 | declare const workgroupSecurityGroup: ec2.ISecurityGroup; 62 | 63 | const namespace = new redshiftserverless.Namespace(stack, 'MyCustomNamespace', { 64 | namespaceName: 'my-custom-namespace', 65 | dbName: 'mydb', // Specify user-defined database name 66 | adminUsername: 'admin', // Specify user-defined admin username 67 | adminUserPassword: SecretValue.unsafePlainText('My-password-123!'), // Specify user-defined admin password 68 | logExports: [redshiftserverless.LogExport.USER_LOG], // Log export settings 69 | }); 70 | 71 | const workgroup = new redshiftserverless.Workgroup(stack, 'MyCustomWorkgroup', { 72 | workgroupName: 'my-custom-workgroup', 73 | namespace, 74 | vpc, 75 | baseCapacity: 32, // Specify Base Capacity uses to serve queries 76 | securityGroups: [workgroupSecurityGroup], // Specify user-defined security groups 77 | }); 78 | ``` 79 | 80 | ### Import an existing endpoint: 81 | You can import existing namespaces and workgroups: 82 | 83 | ```ts 84 | declare const securityGroup: ec2.ISecurityGroup; 85 | 86 | const importedNamespace = redshiftserverless.Namespace.fromNamespaceAttributes(stack, 'ImportedNamespace', { 87 | namespaceId: 'my-namespace-id', 88 | namespaceName: 'my-namespace-name', 89 | }); 90 | 91 | const importedWorkgroup = redshiftserverless.Workgroup.fromWorkgroupAttributes(stack, 'ImportedWorkgroup', { 92 | workgroupName: 'my-workgroup', 93 | workgroupId: 'my-workgroup-id', 94 | endpointAddress: 'my-workgroup.endpoint.com', 95 | port: 5439, 96 | securityGroups: [securityGroup], 97 | }); 98 | ``` 99 | -------------------------------------------------------------------------------- /src/aws-redshiftserverless/index.ts: -------------------------------------------------------------------------------- 1 | export * from './namespace'; 2 | export * from './workgroup'; 3 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Export constructs here 2 | export * as aws_cur from './aws-cur'; 3 | export * as aws_codeartifact from './aws-codeartifact'; 4 | export * as aws_ec2 from './aws-ec2'; 5 | export * as aws_elasticache from './aws-elasticache'; 6 | export * as aws_fsx from './aws-fsx'; 7 | export * as aws_redshiftserverless from './aws-redshiftserverless'; 8 | -------------------------------------------------------------------------------- /test/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-constructs/aws-cdk-library/d2160f3874f1235db77d82f6bea90ac16a7ef206/test/.gitkeep -------------------------------------------------------------------------------- /test/aws-codeartifact/domain.test.ts: -------------------------------------------------------------------------------- 1 | import { Stack, Tags } from 'aws-cdk-lib'; 2 | import { Match, Template } from 'aws-cdk-lib/assertions'; 3 | import { AccountPrincipal } from 'aws-cdk-lib/aws-iam'; 4 | import { Key } from 'aws-cdk-lib/aws-kms'; 5 | import { Domain } from '../../src/aws-codeartifact'; 6 | 7 | let stack: Stack; 8 | 9 | beforeEach(() => { 10 | stack = testStack(); 11 | }); 12 | 13 | test('Domain: Instantiation works and defaults are adhered to', () => { 14 | // WHEN 15 | const domain = new Domain(stack, 'domain', { 16 | domainName: 'dummy', 17 | }); 18 | Tags.of(domain).add('tag1', 'value1'); 19 | 20 | Template.fromStack(stack).hasResource('AWS::CodeArtifact::Domain', { 21 | Properties: { 22 | DomainName: 'dummy', 23 | EncryptionKey: Match.absent(), 24 | PermissionsPolicyDocument: Match.absent(), 25 | Tags: [{ Key: 'tag1', Value: 'value1' }], 26 | }, 27 | }); 28 | }); 29 | 30 | test('Domain: Can use provided key', () => { 31 | // WHEN 32 | const key = new Key(stack, 'key'); 33 | const domain = new Domain(stack, 'domain', { 34 | domainName: 'dummy', 35 | encryptionKey: key, 36 | }); 37 | Tags.of(domain).add('tag1', 'value1'); 38 | 39 | Template.fromStack(stack).hasResource('AWS::CodeArtifact::Domain', { 40 | Properties: { 41 | DomainName: 'dummy', 42 | EncryptionKey: { 43 | 'Fn::GetAtt': ['keyFEDD6EC0', 'Arn'], 44 | }, 45 | PermissionsPolicyDocument: Match.absent(), 46 | Tags: [{ Key: 'tag1', Value: 'value1' }], 47 | }, 48 | }); 49 | }); 50 | 51 | test('Domain: Can import Domain', () => { 52 | // WHEN 53 | const domain = Domain.fromDomainArn(stack, 'domain', 'arn:aws:codeartifact:us-east-1:12345:domain/dummy'); 54 | 55 | expect(domain.domainArn).toEqual('arn:aws:codeartifact:us-east-1:12345:domain/dummy'); 56 | expect(domain.domainName).toEqual('dummy'); 57 | expect(domain.encryptionKey).toBeUndefined(); 58 | expect(domain.domainOwner).toEqual('12345'); 59 | }); 60 | 61 | test('Domain: can grant access', () => { 62 | // GIVEN 63 | const domain = new Domain(stack, 'domain', { 64 | domainName: 'dummy', 65 | }); 66 | const principal = new AccountPrincipal('123456789012'); 67 | // WHEN 68 | domain.grantContribute(principal); 69 | 70 | Template.fromStack(stack).hasResource('AWS::CodeArtifact::Domain', { 71 | Properties: { 72 | DomainName: 'dummy', 73 | EncryptionKey: Match.absent(), 74 | PermissionsPolicyDocument: { 75 | Statement: [ 76 | { 77 | Action: [ 78 | 'codeartifact:CreateRepository', 79 | 'codeartifact:DescribeDomain', 80 | 'codeartifact:GetAuthorizationToken', 81 | 'codeartifact:GetDomainPermissionsPolicy', 82 | 'codeartifact:ListRepositoriesInDomain', 83 | 'sts:GetServiceBearerToken', 84 | ], 85 | Effect: 'Allow', 86 | Principal: { 87 | AWS: { 88 | 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::123456789012:root']], 89 | }, 90 | }, 91 | Resource: '*', 92 | }, 93 | ], 94 | Version: '2012-10-17', 95 | }, 96 | Tags: Match.absent(), 97 | }, 98 | }); 99 | }); 100 | 101 | function testStack() { 102 | const newTestStack = new Stack(undefined, undefined, { env: { account: '12345', region: 'us-test-1' } }); 103 | newTestStack.node.setContext('availability-zones:12345:us-test-1', ['us-test-1a', 'us-test-1b']); 104 | return newTestStack; 105 | } 106 | -------------------------------------------------------------------------------- /test/aws-codeartifact/integ.codeartifact.ts: -------------------------------------------------------------------------------- 1 | import { IntegTest } from '@aws-cdk/integ-tests-alpha'; 2 | import * as cdk from 'aws-cdk-lib'; 3 | import { Construct } from 'constructs'; 4 | import * as ocf from '../../src'; 5 | 6 | class CodeArtifactStack extends cdk.Stack { 7 | constructor(scope: Construct) { 8 | super(scope, 'CodeArtifactDomainAndRepository'); 9 | 10 | const domain = new ocf.aws_codeartifact.Domain(this, 'domain', { 11 | domainName: 'test-domain', 12 | }); 13 | 14 | new ocf.aws_codeartifact.Repository(this, 'repository', { 15 | domain, 16 | repositoryName: 'repo1', 17 | }); 18 | } 19 | } 20 | 21 | const app = new cdk.App(); 22 | const testCase = new CodeArtifactStack(app); 23 | new IntegTest(app, 'CodeArtifactDomainAndRepositoryInteg', { 24 | testCases: [testCase], 25 | }); 26 | 27 | app.synth(); 28 | -------------------------------------------------------------------------------- /test/aws-codeartifact/integ.codeartifact.ts.snapshot/CodeArtifactDomainAndRepository.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "d7f06cae3ee8a4bbe39e606a1874c7a4241b88c1eef5bef6aea676c794e33b65": { 5 | "source": { 6 | "path": "CodeArtifactDomainAndRepository.template.json", 7 | "packaging": "file" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "d7f06cae3ee8a4bbe39e606a1874c7a4241b88c1eef5bef6aea676c794e33b65.json", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | } 17 | }, 18 | "dockerImages": {} 19 | } -------------------------------------------------------------------------------- /test/aws-codeartifact/integ.codeartifact.ts.snapshot/CodeArtifactDomainAndRepository.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Resources": { 3 | "domainFBFFA2F6": { 4 | "Type": "AWS::CodeArtifact::Domain", 5 | "Properties": { 6 | "DomainName": "test-domain" 7 | } 8 | }, 9 | "repository9F1A3F0B": { 10 | "Type": "AWS::CodeArtifact::Repository", 11 | "Properties": { 12 | "DomainName": { 13 | "Fn::GetAtt": [ 14 | "domainFBFFA2F6", 15 | "Name" 16 | ] 17 | }, 18 | "DomainOwner": { 19 | "Fn::GetAtt": [ 20 | "domainFBFFA2F6", 21 | "Owner" 22 | ] 23 | }, 24 | "RepositoryName": "repo1" 25 | } 26 | } 27 | }, 28 | "Parameters": { 29 | "BootstrapVersion": { 30 | "Type": "AWS::SSM::Parameter::Value", 31 | "Default": "/cdk-bootstrap/hnb659fds/version", 32 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 33 | } 34 | }, 35 | "Rules": { 36 | "CheckBootstrapVersion": { 37 | "Assertions": [ 38 | { 39 | "Assert": { 40 | "Fn::Not": [ 41 | { 42 | "Fn::Contains": [ 43 | [ 44 | "1", 45 | "2", 46 | "3", 47 | "4", 48 | "5" 49 | ], 50 | { 51 | "Ref": "BootstrapVersion" 52 | } 53 | ] 54 | } 55 | ] 56 | }, 57 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 58 | } 59 | ] 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /test/aws-codeartifact/integ.codeartifact.ts.snapshot/CodeArtifactDomainAndRepositoryIntegDefaultTestDeployAssert135F1932.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { 5 | "source": { 6 | "path": "CodeArtifactDomainAndRepositoryIntegDefaultTestDeployAssert135F1932.template.json", 7 | "packaging": "file" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | } 17 | }, 18 | "dockerImages": {} 19 | } -------------------------------------------------------------------------------- /test/aws-codeartifact/integ.codeartifact.ts.snapshot/CodeArtifactDomainAndRepositoryIntegDefaultTestDeployAssert135F1932.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Parameters": { 3 | "BootstrapVersion": { 4 | "Type": "AWS::SSM::Parameter::Value", 5 | "Default": "/cdk-bootstrap/hnb659fds/version", 6 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 7 | } 8 | }, 9 | "Rules": { 10 | "CheckBootstrapVersion": { 11 | "Assertions": [ 12 | { 13 | "Assert": { 14 | "Fn::Not": [ 15 | { 16 | "Fn::Contains": [ 17 | [ 18 | "1", 19 | "2", 20 | "3", 21 | "4", 22 | "5" 23 | ], 24 | { 25 | "Ref": "BootstrapVersion" 26 | } 27 | ] 28 | } 29 | ] 30 | }, 31 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 32 | } 33 | ] 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /test/aws-codeartifact/integ.codeartifact.ts.snapshot/cdk.out: -------------------------------------------------------------------------------- 1 | {"version":"36.0.0"} -------------------------------------------------------------------------------- /test/aws-codeartifact/integ.codeartifact.ts.snapshot/integ.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "testCases": { 4 | "CodeArtifactDomainAndRepositoryInteg/DefaultTest": { 5 | "stacks": [ 6 | "CodeArtifactDomainAndRepository" 7 | ], 8 | "assertionStack": "CodeArtifactDomainAndRepositoryInteg/DefaultTest/DeployAssert", 9 | "assertionStackName": "CodeArtifactDomainAndRepositoryIntegDefaultTestDeployAssert135F1932" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /test/aws-codeartifact/integ.codeartifact.ts.snapshot/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "artifacts": { 4 | "CodeArtifactDomainAndRepository.assets": { 5 | "type": "cdk:asset-manifest", 6 | "properties": { 7 | "file": "CodeArtifactDomainAndRepository.assets.json", 8 | "requiresBootstrapStackVersion": 6, 9 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 10 | } 11 | }, 12 | "CodeArtifactDomainAndRepository": { 13 | "type": "aws:cloudformation:stack", 14 | "environment": "aws://unknown-account/unknown-region", 15 | "properties": { 16 | "templateFile": "CodeArtifactDomainAndRepository.template.json", 17 | "terminationProtection": false, 18 | "validateOnSynth": false, 19 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", 20 | "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", 21 | "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/d7f06cae3ee8a4bbe39e606a1874c7a4241b88c1eef5bef6aea676c794e33b65.json", 22 | "requiresBootstrapStackVersion": 6, 23 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", 24 | "additionalDependencies": [ 25 | "CodeArtifactDomainAndRepository.assets" 26 | ], 27 | "lookupRole": { 28 | "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", 29 | "requiresBootstrapStackVersion": 8, 30 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 31 | } 32 | }, 33 | "dependencies": [ 34 | "CodeArtifactDomainAndRepository.assets" 35 | ], 36 | "metadata": { 37 | "/CodeArtifactDomainAndRepository/domain/Resource": [ 38 | { 39 | "type": "aws:cdk:logicalId", 40 | "data": "domainFBFFA2F6" 41 | } 42 | ], 43 | "/CodeArtifactDomainAndRepository/repository/Resource": [ 44 | { 45 | "type": "aws:cdk:logicalId", 46 | "data": "repository9F1A3F0B" 47 | } 48 | ], 49 | "/CodeArtifactDomainAndRepository/BootstrapVersion": [ 50 | { 51 | "type": "aws:cdk:logicalId", 52 | "data": "BootstrapVersion" 53 | } 54 | ], 55 | "/CodeArtifactDomainAndRepository/CheckBootstrapVersion": [ 56 | { 57 | "type": "aws:cdk:logicalId", 58 | "data": "CheckBootstrapVersion" 59 | } 60 | ] 61 | }, 62 | "displayName": "CodeArtifactDomainAndRepository" 63 | }, 64 | "CodeArtifactDomainAndRepositoryIntegDefaultTestDeployAssert135F1932.assets": { 65 | "type": "cdk:asset-manifest", 66 | "properties": { 67 | "file": "CodeArtifactDomainAndRepositoryIntegDefaultTestDeployAssert135F1932.assets.json", 68 | "requiresBootstrapStackVersion": 6, 69 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 70 | } 71 | }, 72 | "CodeArtifactDomainAndRepositoryIntegDefaultTestDeployAssert135F1932": { 73 | "type": "aws:cloudformation:stack", 74 | "environment": "aws://unknown-account/unknown-region", 75 | "properties": { 76 | "templateFile": "CodeArtifactDomainAndRepositoryIntegDefaultTestDeployAssert135F1932.template.json", 77 | "terminationProtection": false, 78 | "validateOnSynth": false, 79 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", 80 | "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", 81 | "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", 82 | "requiresBootstrapStackVersion": 6, 83 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", 84 | "additionalDependencies": [ 85 | "CodeArtifactDomainAndRepositoryIntegDefaultTestDeployAssert135F1932.assets" 86 | ], 87 | "lookupRole": { 88 | "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", 89 | "requiresBootstrapStackVersion": 8, 90 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 91 | } 92 | }, 93 | "dependencies": [ 94 | "CodeArtifactDomainAndRepositoryIntegDefaultTestDeployAssert135F1932.assets" 95 | ], 96 | "metadata": { 97 | "/CodeArtifactDomainAndRepositoryInteg/DefaultTest/DeployAssert/BootstrapVersion": [ 98 | { 99 | "type": "aws:cdk:logicalId", 100 | "data": "BootstrapVersion" 101 | } 102 | ], 103 | "/CodeArtifactDomainAndRepositoryInteg/DefaultTest/DeployAssert/CheckBootstrapVersion": [ 104 | { 105 | "type": "aws:cdk:logicalId", 106 | "data": "CheckBootstrapVersion" 107 | } 108 | ] 109 | }, 110 | "displayName": "CodeArtifactDomainAndRepositoryInteg/DefaultTest/DeployAssert" 111 | }, 112 | "Tree": { 113 | "type": "cdk:tree", 114 | "properties": { 115 | "file": "tree.json" 116 | } 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /test/aws-codeartifact/integ.codeartifact.ts.snapshot/tree.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "tree-0.1", 3 | "tree": { 4 | "id": "App", 5 | "path": "", 6 | "children": { 7 | "CodeArtifactDomainAndRepository": { 8 | "id": "CodeArtifactDomainAndRepository", 9 | "path": "CodeArtifactDomainAndRepository", 10 | "children": { 11 | "domain": { 12 | "id": "domain", 13 | "path": "CodeArtifactDomainAndRepository/domain", 14 | "children": { 15 | "Resource": { 16 | "id": "Resource", 17 | "path": "CodeArtifactDomainAndRepository/domain/Resource", 18 | "attributes": { 19 | "aws:cdk:cloudformation:type": "AWS::CodeArtifact::Domain", 20 | "aws:cdk:cloudformation:props": { 21 | "domainName": "test-domain" 22 | } 23 | }, 24 | "constructInfo": { 25 | "fqn": "aws-cdk-lib.aws_codeartifact.CfnDomain", 26 | "version": "2.120.0" 27 | } 28 | } 29 | }, 30 | "constructInfo": { 31 | "fqn": "aws-cdk-lib.Resource", 32 | "version": "2.120.0" 33 | } 34 | }, 35 | "repository": { 36 | "id": "repository", 37 | "path": "CodeArtifactDomainAndRepository/repository", 38 | "children": { 39 | "Resource": { 40 | "id": "Resource", 41 | "path": "CodeArtifactDomainAndRepository/repository/Resource", 42 | "attributes": { 43 | "aws:cdk:cloudformation:type": "AWS::CodeArtifact::Repository", 44 | "aws:cdk:cloudformation:props": { 45 | "domainName": { 46 | "Fn::GetAtt": [ 47 | "domainFBFFA2F6", 48 | "Name" 49 | ] 50 | }, 51 | "domainOwner": { 52 | "Fn::GetAtt": [ 53 | "domainFBFFA2F6", 54 | "Owner" 55 | ] 56 | }, 57 | "repositoryName": "repo1" 58 | } 59 | }, 60 | "constructInfo": { 61 | "fqn": "aws-cdk-lib.aws_codeartifact.CfnRepository", 62 | "version": "2.120.0" 63 | } 64 | }, 65 | "Domain": { 66 | "id": "Domain", 67 | "path": "CodeArtifactDomainAndRepository/repository/Domain", 68 | "constructInfo": { 69 | "fqn": "aws-cdk-lib.Resource", 70 | "version": "2.120.0" 71 | } 72 | } 73 | }, 74 | "constructInfo": { 75 | "fqn": "aws-cdk-lib.Resource", 76 | "version": "2.120.0" 77 | } 78 | }, 79 | "BootstrapVersion": { 80 | "id": "BootstrapVersion", 81 | "path": "CodeArtifactDomainAndRepository/BootstrapVersion", 82 | "constructInfo": { 83 | "fqn": "aws-cdk-lib.CfnParameter", 84 | "version": "2.120.0" 85 | } 86 | }, 87 | "CheckBootstrapVersion": { 88 | "id": "CheckBootstrapVersion", 89 | "path": "CodeArtifactDomainAndRepository/CheckBootstrapVersion", 90 | "constructInfo": { 91 | "fqn": "aws-cdk-lib.CfnRule", 92 | "version": "2.120.0" 93 | } 94 | } 95 | }, 96 | "constructInfo": { 97 | "fqn": "aws-cdk-lib.Stack", 98 | "version": "2.120.0" 99 | } 100 | }, 101 | "CodeArtifactDomainAndRepositoryInteg": { 102 | "id": "CodeArtifactDomainAndRepositoryInteg", 103 | "path": "CodeArtifactDomainAndRepositoryInteg", 104 | "children": { 105 | "DefaultTest": { 106 | "id": "DefaultTest", 107 | "path": "CodeArtifactDomainAndRepositoryInteg/DefaultTest", 108 | "children": { 109 | "Default": { 110 | "id": "Default", 111 | "path": "CodeArtifactDomainAndRepositoryInteg/DefaultTest/Default", 112 | "constructInfo": { 113 | "fqn": "constructs.Construct", 114 | "version": "10.3.0" 115 | } 116 | }, 117 | "DeployAssert": { 118 | "id": "DeployAssert", 119 | "path": "CodeArtifactDomainAndRepositoryInteg/DefaultTest/DeployAssert", 120 | "children": { 121 | "BootstrapVersion": { 122 | "id": "BootstrapVersion", 123 | "path": "CodeArtifactDomainAndRepositoryInteg/DefaultTest/DeployAssert/BootstrapVersion", 124 | "constructInfo": { 125 | "fqn": "aws-cdk-lib.CfnParameter", 126 | "version": "2.120.0" 127 | } 128 | }, 129 | "CheckBootstrapVersion": { 130 | "id": "CheckBootstrapVersion", 131 | "path": "CodeArtifactDomainAndRepositoryInteg/DefaultTest/DeployAssert/CheckBootstrapVersion", 132 | "constructInfo": { 133 | "fqn": "aws-cdk-lib.CfnRule", 134 | "version": "2.120.0" 135 | } 136 | } 137 | }, 138 | "constructInfo": { 139 | "fqn": "aws-cdk-lib.Stack", 140 | "version": "2.120.0" 141 | } 142 | } 143 | }, 144 | "constructInfo": { 145 | "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", 146 | "version": "2.120.0-alpha.0" 147 | } 148 | } 149 | }, 150 | "constructInfo": { 151 | "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", 152 | "version": "2.120.0-alpha.0" 153 | } 154 | }, 155 | "Tree": { 156 | "id": "Tree", 157 | "path": "Tree", 158 | "constructInfo": { 159 | "fqn": "constructs.Construct", 160 | "version": "10.3.0" 161 | } 162 | } 163 | }, 164 | "constructInfo": { 165 | "fqn": "aws-cdk-lib.App", 166 | "version": "2.120.0" 167 | } 168 | } 169 | } -------------------------------------------------------------------------------- /test/aws-cur/cost-report.test.ts: -------------------------------------------------------------------------------- 1 | import { App, CfnElement, Stack, aws_s3 } from 'aws-cdk-lib'; 2 | import { Match, Template } from 'aws-cdk-lib/assertions'; 3 | import { CostReport, ReportGranularity, CurFormat } from '../../src/aws-cur'; 4 | 5 | describe('CostReport', () => { 6 | let app: App; 7 | let stack: Stack; 8 | 9 | beforeEach(() => { 10 | app = new App(); 11 | stack = new Stack(app, 'TestStack', { env: { region: 'us-east-1' } }); 12 | }); 13 | 14 | test('default configuration', () => { 15 | new CostReport(stack, 'MyCostReport', {}); 16 | 17 | const template = Template.fromStack(stack); 18 | 19 | // Check if S3 bucket is created 20 | template.resourceCountIs('AWS::S3::Bucket', 1); 21 | 22 | // Check if the CUR report definition is created 23 | template.resourceCountIs('AWS::CUR::ReportDefinition', 1); 24 | 25 | // Validate properties of the CUR report 26 | template.hasResourceProperties('AWS::CUR::ReportDefinition', { 27 | ReportName: 'default-cur', 28 | TimeUnit: 'HOURLY', 29 | Format: 'textORcsv', 30 | Compression: 'GZIP', 31 | }); 32 | }); 33 | 34 | test('custom configuration', () => { 35 | new CostReport(stack, 'MyCustomCostReport', { 36 | costReportName: 'custom-cur', 37 | reportGranularity: ReportGranularity.DAILY, 38 | format: CurFormat.PARQUET, 39 | }); 40 | 41 | const template = Template.fromStack(stack); 42 | 43 | // Check the CUR report properties for custom settings 44 | template.hasResourceProperties('AWS::CUR::ReportDefinition', { 45 | ReportName: 'custom-cur', 46 | TimeUnit: 'DAILY', 47 | Format: 'Parquet', 48 | Compression: 'Parquet', 49 | }); 50 | }); 51 | 52 | test('bucket provided', () => { 53 | const bucket = new aws_s3.Bucket(stack, 'ProvidedBucket'); 54 | 55 | new CostReport(stack, 'ReportWithExistingBucket', { 56 | costReportName: 'with-existing-bucket', 57 | bucket: bucket, 58 | reportGranularity: ReportGranularity.MONTHLY, 59 | }); 60 | 61 | const template = Template.fromStack(stack); 62 | 63 | // Validate that no new bucket is created 64 | template.resourceCountIs('AWS::S3::Bucket', 1); // Only the provided one 65 | 66 | // Ensure the bucket policy allows access to the CUR account 67 | template.hasResourceProperties('AWS::S3::BucketPolicy', { 68 | Bucket: { 69 | Ref: stack.resolve((bucket.node.defaultChild as CfnElement).logicalId), 70 | }, 71 | PolicyDocument: { 72 | Statement: Match.arrayWith([ 73 | Match.objectLike({ 74 | Principal: { 75 | Service: 'billingreports.amazonaws.com', 76 | }, 77 | Action: ['s3:GetBucketAcl', 's3:GetBucketPolicy'], 78 | }), 79 | ]), 80 | }, 81 | }); 82 | }); 83 | 84 | test('multiple reports', () => { 85 | new CostReport(stack, 'Report1', { 86 | costReportName: 'report1', 87 | }); 88 | new CostReport(stack, 'Report2', { 89 | costReportName: 'report2', 90 | }); 91 | 92 | const template = Template.fromStack(stack); 93 | 94 | template.resourceCountIs('AWS::CUR::ReportDefinition', 2); 95 | }); 96 | 97 | test('multiple reports with bucket', () => { 98 | const bucket = new aws_s3.Bucket(stack, 'MyBucket'); 99 | 100 | new CostReport(stack, 'Report1', { 101 | costReportName: 'report1', 102 | bucket: bucket, 103 | }); 104 | new CostReport(stack, 'Report2', { 105 | costReportName: 'report2', 106 | bucket: bucket, 107 | }); 108 | 109 | const template = Template.fromStack(stack); 110 | 111 | template.resourceCountIs('AWS::CUR::ReportDefinition', 2); 112 | }); 113 | 114 | test('unique report name is generated if costReportName is not specified and enableDefaultUniqueReportName is true', () => { 115 | new CostReport(stack, 'MyCostReport', { 116 | enableDefaultUniqueReportName: true, 117 | }); 118 | 119 | const template = Template.fromStack(stack); 120 | 121 | template.hasResourceProperties('AWS::CUR::ReportDefinition', { 122 | ReportName: 'TestStackMyCostReportF831D765', 123 | }); 124 | }); 125 | 126 | test('fixed report name is generated if costReportName is not specified and enableDefaultUniqueReportName is false', () => { 127 | new CostReport(stack, 'MyCostReport', { 128 | enableDefaultUniqueReportName: false, 129 | }); 130 | 131 | const template = Template.fromStack(stack); 132 | 133 | template.hasResourceProperties('AWS::CUR::ReportDefinition', { 134 | ReportName: 'default-cur', 135 | }); 136 | }); 137 | 138 | test('report name can be specified even if enableDefaultUniqueReportName is true', () => { 139 | new CostReport(stack, 'MyCostReport', { 140 | enableDefaultUniqueReportName: true, 141 | costReportName: 'custom-cur', 142 | }); 143 | 144 | const template = Template.fromStack(stack); 145 | 146 | template.hasResourceProperties('AWS::CUR::ReportDefinition', { 147 | ReportName: 'custom-cur', 148 | }); 149 | }); 150 | 151 | test('report name can have special characters', () => { 152 | new CostReport(stack, 'Report', { 153 | costReportName: "report1!-_.*'()", 154 | }); 155 | 156 | const template = Template.fromStack(stack); 157 | 158 | template.hasResourceProperties('AWS::CUR::ReportDefinition', { 159 | ReportName: "report1!-_.*'()", 160 | }); 161 | }); 162 | 163 | test('throws if the report name has spaces', () => { 164 | expect( 165 | () => 166 | new CostReport(stack, 'MyCostReport', { 167 | costReportName: 'report with spaces', 168 | }), 169 | ).toThrow( 170 | "'costReportName' must only contain alphanumeric characters and the following special characters: !-_.*'(), got: 'report with spaces'", 171 | ); 172 | }); 173 | 174 | test('throws if the length of the report name is greater than 256 characters', () => { 175 | expect( 176 | () => 177 | new CostReport(stack, 'MyCostReport', { 178 | costReportName: 'a'.repeat(257), 179 | }), 180 | ).toThrow("'costReportName' must be between 1 and 256 characters long, got: 257"); 181 | }); 182 | 183 | test('throws if the length of the report name is less than 1 character', () => { 184 | expect( 185 | () => 186 | new CostReport(stack, 'MyCostReport', { 187 | costReportName: '', 188 | }), 189 | ).toThrow("'costReportName' must be between 1 and 256 characters long, got: 0"); 190 | }); 191 | 192 | test('regions other than us-east-1', () => { 193 | const regionOtherStack = new Stack(app, 'OtherRegionStack', { 194 | env: { region: 'ap-northeast-1' }, 195 | }); 196 | 197 | expect( 198 | () => 199 | new CostReport(regionOtherStack, 'MyCustomCostReport', { 200 | costReportName: 'custom-cur', 201 | reportGranularity: ReportGranularity.DAILY, 202 | format: CurFormat.PARQUET, 203 | }), 204 | ).toThrow( 205 | `The \`CostReport\` construct is only available in the us-east-1 region, got: ${regionOtherStack.region} region`, 206 | ); 207 | }); 208 | 209 | test('skip validation if no region is specified (i.e. it is a token)', () => { 210 | // Default region is used if region is not specified. 211 | const stackWithRegionToken = new Stack(app, 'OtherRegionStack'); 212 | 213 | expect( 214 | () => 215 | new CostReport(stackWithRegionToken, 'MyCustomCostReport', { 216 | costReportName: 'custom-cur', 217 | reportGranularity: ReportGranularity.DAILY, 218 | format: CurFormat.PARQUET, 219 | }), 220 | ).not.toThrow(); 221 | }); 222 | }); 223 | -------------------------------------------------------------------------------- /test/aws-cur/integ.cost-report.ts: -------------------------------------------------------------------------------- 1 | import { IntegTest } from '@aws-cdk/integ-tests-alpha'; 2 | import * as cdk from 'aws-cdk-lib'; 3 | import { Construct } from 'constructs'; 4 | import * as ocf from '../../src'; 5 | 6 | class CostReportStack extends cdk.Stack { 7 | constructor(scope: Construct) { 8 | super(scope, 'cur-report', { env: { region: 'us-east-1' } }); 9 | 10 | const bucket = new cdk.aws_s3.Bucket(this, 'ReportBucket', { 11 | removalPolicy: cdk.RemovalPolicy.DESTROY, 12 | autoDeleteObjects: true, 13 | blockPublicAccess: cdk.aws_s3.BlockPublicAccess.BLOCK_ALL, 14 | }); 15 | 16 | const report = new ocf.aws_cur.CostReport(this, 'CostReport', { 17 | bucket, 18 | }); 19 | const reportWithUniqueReportName = new ocf.aws_cur.CostReport(this, 'CostReportWithUniqueReportName', { 20 | bucket, 21 | enableDefaultUniqueReportName: true, 22 | }); 23 | 24 | new cdk.CfnOutput(this, 'CostReportName', { 25 | value: report.costReportName, 26 | }); 27 | new cdk.CfnOutput(this, 'UniqueCostReportName', { 28 | value: reportWithUniqueReportName.costReportName, 29 | }); 30 | } 31 | } 32 | 33 | const app = new cdk.App(); 34 | const testCase = new CostReportStack(app); 35 | new IntegTest(app, 'CurReport', { 36 | testCases: [testCase], 37 | }); 38 | 39 | app.synth(); 40 | -------------------------------------------------------------------------------- /test/aws-cur/integ.cost-report.ts.snapshot/CurReportDefaultTestDeployAssert2956AD14.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { 5 | "source": { 6 | "path": "CurReportDefaultTestDeployAssert2956AD14.template.json", 7 | "packaging": "file" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | } 17 | }, 18 | "dockerImages": {} 19 | } -------------------------------------------------------------------------------- /test/aws-cur/integ.cost-report.ts.snapshot/CurReportDefaultTestDeployAssert2956AD14.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Parameters": { 3 | "BootstrapVersion": { 4 | "Type": "AWS::SSM::Parameter::Value", 5 | "Default": "/cdk-bootstrap/hnb659fds/version", 6 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 7 | } 8 | }, 9 | "Rules": { 10 | "CheckBootstrapVersion": { 11 | "Assertions": [ 12 | { 13 | "Assert": { 14 | "Fn::Not": [ 15 | { 16 | "Fn::Contains": [ 17 | [ 18 | "1", 19 | "2", 20 | "3", 21 | "4", 22 | "5" 23 | ], 24 | { 25 | "Ref": "BootstrapVersion" 26 | } 27 | ] 28 | } 29 | ] 30 | }, 31 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 32 | } 33 | ] 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /test/aws-cur/integ.cost-report.ts.snapshot/asset.b7f33614a69548d6bafe224d751a7ef238cde19097415e553fe8b63a4c8fd8a6/index.js: -------------------------------------------------------------------------------- 1 | "use strict";var C=Object.create,i=Object.defineProperty,I=Object.getOwnPropertyDescriptor,w=Object.getOwnPropertyNames,P=Object.getPrototypeOf,A=Object.prototype.hasOwnProperty,L=(e,t)=>{for(var o in t)i(e,o,{get:t[o],enumerable:!0})},d=(e,t,o,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of w(t))!A.call(e,s)&&s!==o&&i(e,s,{get:()=>t[s],enumerable:!(r=I(t,s))||r.enumerable});return e},l=(e,t,o)=>(o=e!=null?C(P(e)):{},d(t||!e||!e.__esModule?i(o,"default",{value:e,enumerable:!0}):o,e)),k=e=>d(i({},"__esModule",{value:!0}),e),U={};L(U,{autoDeleteHandler:()=>S,handler:()=>_}),module.exports=k(U);var h=require("@aws-sdk/client-s3"),y=l(require("https")),m=l(require("url")),a={sendHttpRequest:T,log:b,includeStackTraces:!0,userHandlerIndex:"./index"},p="AWSCDK::CustomResourceProviderFramework::CREATE_FAILED",B="AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID";function R(e){return async(t,o)=>{let r={...t,ResponseURL:"..."};if(a.log(JSON.stringify(r,void 0,2)),t.RequestType==="Delete"&&t.PhysicalResourceId===p){a.log("ignoring DELETE event caused by a failed CREATE event"),await u("SUCCESS",t);return}try{let s=await e(r,o),n=D(t,s);await u("SUCCESS",n)}catch(s){let n={...t,Reason:a.includeStackTraces?s.stack:s.message};n.PhysicalResourceId||(t.RequestType==="Create"?(a.log("CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored"),n.PhysicalResourceId=p):a.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(t)}`)),await u("FAILED",n)}}}function D(e,t={}){let o=t.PhysicalResourceId??e.PhysicalResourceId??e.RequestId;if(e.RequestType==="Delete"&&o!==e.PhysicalResourceId)throw new Error(`DELETE: cannot change the physical resource ID from "${e.PhysicalResourceId}" to "${t.PhysicalResourceId}" during deletion`);return{...e,...t,PhysicalResourceId:o}}async function u(e,t){let o={Status:e,Reason:t.Reason??e,StackId:t.StackId,RequestId:t.RequestId,PhysicalResourceId:t.PhysicalResourceId||B,LogicalResourceId:t.LogicalResourceId,NoEcho:t.NoEcho,Data:t.Data};a.log("submit response to cloudformation",o);let r=JSON.stringify(o),s=m.parse(t.ResponseURL),n={hostname:s.hostname,path:s.path,method:"PUT",headers:{"content-type":"","content-length":Buffer.byteLength(r,"utf8")}};await O({attempts:5,sleep:1e3},a.sendHttpRequest)(n,r)}async function T(e,t){return new Promise((o,r)=>{try{let s=y.request(e,n=>o());s.on("error",r),s.write(t),s.end()}catch(s){r(s)}})}function b(e,...t){console.log(e,...t)}function O(e,t){return async(...o)=>{let r=e.attempts,s=e.sleep;for(;;)try{return await t(...o)}catch(n){if(r--<=0)throw n;await x(Math.floor(Math.random()*s)),s*=2}}}async function x(e){return new Promise(t=>setTimeout(t,e))}var g="aws-cdk:auto-delete-objects",H=JSON.stringify({Version:"2012-10-17",Statement:[]}),c=new h.S3({}),_=R(S);async function S(e){switch(e.RequestType){case"Create":return;case"Update":return F(e);case"Delete":return f(e.ResourceProperties?.BucketName)}}async function F(e){let t=e,o=t.OldResourceProperties?.BucketName,r=t.ResourceProperties?.BucketName;if(r!=null&&o!=null&&r!==o)return f(o)}async function N(e){try{let t=(await c.getBucketPolicy({Bucket:e}))?.Policy??H,o=JSON.parse(t);o.Statement.push({Principal:"*",Effect:"Deny",Action:["s3:PutObject"],Resource:[`arn:aws:s3:::${e}/*`]}),await c.putBucketPolicy({Bucket:e,Policy:JSON.stringify(o)})}catch(t){if(t.name==="NoSuchBucket")throw t;console.log(`Could not set new object deny policy on bucket '${e}' prior to deletion.`)}}async function E(e){let t=await c.listObjectVersions({Bucket:e}),o=[...t.Versions??[],...t.DeleteMarkers??[]];if(o.length===0)return;let r=o.map(s=>({Key:s.Key,VersionId:s.VersionId}));await c.deleteObjects({Bucket:e,Delete:{Objects:r}}),t?.IsTruncated&&await E(e)}async function f(e){if(!e)throw new Error("No BucketName was provided.");try{if(!await W(e)){console.log(`Bucket does not have '${g}' tag, skipping cleaning.`);return}await N(e),await E(e)}catch(t){if(t.name==="NoSuchBucket"){console.log(`Bucket '${e}' does not exist.`);return}throw t}}async function W(e){return(await c.getBucketTagging({Bucket:e})).TagSet?.some(o=>o.Key===g&&o.Value==="true")} 2 | -------------------------------------------------------------------------------- /test/aws-cur/integ.cost-report.ts.snapshot/cdk.out: -------------------------------------------------------------------------------- 1 | {"version":"36.0.0"} -------------------------------------------------------------------------------- /test/aws-cur/integ.cost-report.ts.snapshot/cur-report.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "b7f33614a69548d6bafe224d751a7ef238cde19097415e553fe8b63a4c8fd8a6": { 5 | "source": { 6 | "path": "asset.b7f33614a69548d6bafe224d751a7ef238cde19097415e553fe8b63a4c8fd8a6", 7 | "packaging": "zip" 8 | }, 9 | "destinations": { 10 | "current_account-us-east-1": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", 12 | "objectKey": "b7f33614a69548d6bafe224d751a7ef238cde19097415e553fe8b63a4c8fd8a6.zip", 13 | "region": "us-east-1", 14 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" 15 | } 16 | } 17 | }, 18 | "a019b27bb46cfc5076913cf39dac56a1e51b11dcdc8521cf57b88a9c8d2b0f32": { 19 | "source": { 20 | "path": "cur-report.template.json", 21 | "packaging": "file" 22 | }, 23 | "destinations": { 24 | "current_account-us-east-1": { 25 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", 26 | "objectKey": "a019b27bb46cfc5076913cf39dac56a1e51b11dcdc8521cf57b88a9c8d2b0f32.json", 27 | "region": "us-east-1", 28 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" 29 | } 30 | } 31 | } 32 | }, 33 | "dockerImages": {} 34 | } -------------------------------------------------------------------------------- /test/aws-cur/integ.cost-report.ts.snapshot/integ.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "testCases": { 4 | "CurReport/DefaultTest": { 5 | "stacks": [ 6 | "cur-report" 7 | ], 8 | "assertionStack": "CurReport/DefaultTest/DeployAssert", 9 | "assertionStackName": "CurReportDefaultTestDeployAssert2956AD14" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /test/aws-cur/integ.cost-report.ts.snapshot/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "artifacts": { 4 | "cur-report.assets": { 5 | "type": "cdk:asset-manifest", 6 | "properties": { 7 | "file": "cur-report.assets.json", 8 | "requiresBootstrapStackVersion": 6, 9 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 10 | } 11 | }, 12 | "cur-report": { 13 | "type": "aws:cloudformation:stack", 14 | "environment": "aws://unknown-account/us-east-1", 15 | "properties": { 16 | "templateFile": "cur-report.template.json", 17 | "terminationProtection": false, 18 | "validateOnSynth": false, 19 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-1", 20 | "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-1", 21 | "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/a019b27bb46cfc5076913cf39dac56a1e51b11dcdc8521cf57b88a9c8d2b0f32.json", 22 | "requiresBootstrapStackVersion": 6, 23 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", 24 | "additionalDependencies": [ 25 | "cur-report.assets" 26 | ], 27 | "lookupRole": { 28 | "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-us-east-1", 29 | "requiresBootstrapStackVersion": 8, 30 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 31 | } 32 | }, 33 | "dependencies": [ 34 | "cur-report.assets" 35 | ], 36 | "metadata": { 37 | "/cur-report/ReportBucket/Resource": [ 38 | { 39 | "type": "aws:cdk:logicalId", 40 | "data": "ReportBucket577F0FCD" 41 | } 42 | ], 43 | "/cur-report/ReportBucket/Policy/Resource": [ 44 | { 45 | "type": "aws:cdk:logicalId", 46 | "data": "ReportBucketPolicyA15D85AD" 47 | } 48 | ], 49 | "/cur-report/ReportBucket/AutoDeleteObjectsCustomResource/Default": [ 50 | { 51 | "type": "aws:cdk:logicalId", 52 | "data": "ReportBucketAutoDeleteObjectsCustomResource7FAF98BE" 53 | } 54 | ], 55 | "/cur-report/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role": [ 56 | { 57 | "type": "aws:cdk:logicalId", 58 | "data": "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" 59 | } 60 | ], 61 | "/cur-report/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler": [ 62 | { 63 | "type": "aws:cdk:logicalId", 64 | "data": "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F" 65 | } 66 | ], 67 | "/cur-report/CostReport/Resource": [ 68 | { 69 | "type": "aws:cdk:logicalId", 70 | "data": "CostReport0C3566A0" 71 | } 72 | ], 73 | "/cur-report/CostReportWithUniqueReportName/Resource": [ 74 | { 75 | "type": "aws:cdk:logicalId", 76 | "data": "CostReportWithUniqueReportName449C5FC3" 77 | } 78 | ], 79 | "/cur-report/CostReportName": [ 80 | { 81 | "type": "aws:cdk:logicalId", 82 | "data": "CostReportName" 83 | } 84 | ], 85 | "/cur-report/UniqueCostReportName": [ 86 | { 87 | "type": "aws:cdk:logicalId", 88 | "data": "UniqueCostReportName" 89 | } 90 | ], 91 | "/cur-report/BootstrapVersion": [ 92 | { 93 | "type": "aws:cdk:logicalId", 94 | "data": "BootstrapVersion" 95 | } 96 | ], 97 | "/cur-report/CheckBootstrapVersion": [ 98 | { 99 | "type": "aws:cdk:logicalId", 100 | "data": "CheckBootstrapVersion" 101 | } 102 | ] 103 | }, 104 | "displayName": "cur-report" 105 | }, 106 | "CurReportDefaultTestDeployAssert2956AD14.assets": { 107 | "type": "cdk:asset-manifest", 108 | "properties": { 109 | "file": "CurReportDefaultTestDeployAssert2956AD14.assets.json", 110 | "requiresBootstrapStackVersion": 6, 111 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 112 | } 113 | }, 114 | "CurReportDefaultTestDeployAssert2956AD14": { 115 | "type": "aws:cloudformation:stack", 116 | "environment": "aws://unknown-account/unknown-region", 117 | "properties": { 118 | "templateFile": "CurReportDefaultTestDeployAssert2956AD14.template.json", 119 | "terminationProtection": false, 120 | "validateOnSynth": false, 121 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", 122 | "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", 123 | "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", 124 | "requiresBootstrapStackVersion": 6, 125 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", 126 | "additionalDependencies": [ 127 | "CurReportDefaultTestDeployAssert2956AD14.assets" 128 | ], 129 | "lookupRole": { 130 | "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", 131 | "requiresBootstrapStackVersion": 8, 132 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 133 | } 134 | }, 135 | "dependencies": [ 136 | "CurReportDefaultTestDeployAssert2956AD14.assets" 137 | ], 138 | "metadata": { 139 | "/CurReport/DefaultTest/DeployAssert/BootstrapVersion": [ 140 | { 141 | "type": "aws:cdk:logicalId", 142 | "data": "BootstrapVersion" 143 | } 144 | ], 145 | "/CurReport/DefaultTest/DeployAssert/CheckBootstrapVersion": [ 146 | { 147 | "type": "aws:cdk:logicalId", 148 | "data": "CheckBootstrapVersion" 149 | } 150 | ] 151 | }, 152 | "displayName": "CurReport/DefaultTest/DeployAssert" 153 | }, 154 | "Tree": { 155 | "type": "cdk:tree", 156 | "properties": { 157 | "file": "tree.json" 158 | } 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /test/aws-ec2/instance-connect-endpoint.test.ts: -------------------------------------------------------------------------------- 1 | import { App, Stack, aws_ec2 } from 'aws-cdk-lib'; 2 | import { Template } from 'aws-cdk-lib/assertions'; 3 | import { InstanceConnectEndpoint } from '../../src/aws-ec2'; 4 | 5 | describe('InstanceConnectEndpoint', () => { 6 | let app: App; 7 | let stack: Stack; 8 | 9 | beforeEach(() => { 10 | app = new App(); 11 | stack = new Stack(app, 'TestStack'); 12 | }); 13 | 14 | test('default configuration', () => { 15 | new InstanceConnectEndpoint(stack, 'MyInstanceConnectEndpoint', { 16 | vpc: new aws_ec2.Vpc(stack, 'VPC', { 17 | maxAzs: 2, 18 | }), 19 | }); 20 | 21 | Template.fromStack(stack).hasResourceProperties('AWS::EC2::InstanceConnectEndpoint', { 22 | SecurityGroupIds: [ 23 | { 24 | 'Fn::GetAtt': ['MyInstanceConnectEndpointSecurityGroup99B9E814', 'GroupId'], 25 | }, 26 | ], 27 | SubnetId: { Ref: 'VPCPrivateSubnet1Subnet8BCA10E0' }, 28 | }); 29 | }); 30 | 31 | test('custom configuration', () => { 32 | const vpc = new aws_ec2.Vpc(stack, 'VPC', { 33 | maxAzs: 2, 34 | }); 35 | new InstanceConnectEndpoint(stack, 'MyCustomInstanceConnectEndpoint', { 36 | vpc, 37 | clientToken: 'my-client-token', 38 | preserveClientIp: false, 39 | securityGroups: [ 40 | new aws_ec2.SecurityGroup(stack, 'SecurityGroup', { 41 | vpc, 42 | allowAllOutbound: false, 43 | }), 44 | ], 45 | }); 46 | 47 | Template.fromStack(stack).hasResourceProperties('AWS::EC2::InstanceConnectEndpoint', { 48 | ClientToken: 'my-client-token', 49 | PreserveClientIp: false, 50 | SecurityGroupIds: [{ 'Fn::GetAtt': ['SecurityGroupDD263621', 'GroupId'] }], 51 | SubnetId: { Ref: 'VPCPrivateSubnet1Subnet8BCA10E0' }, 52 | }); 53 | }); 54 | 55 | test('import from attributes', () => { 56 | const vpc = new aws_ec2.Vpc(stack, 'VPC'); 57 | const securityGroup = new aws_ec2.SecurityGroup(stack, 'SecurityGroup', { 58 | vpc, 59 | allowAllOutbound: false, 60 | }); 61 | 62 | const existingEndpoint = InstanceConnectEndpoint.fromInstanceConnectEndpointAttributes( 63 | stack, 64 | 'ImportedInstanceConnectEndpoint', 65 | { 66 | instanceConnectEndpointId: 'my-endpoint-id', 67 | securityGroups: [securityGroup], 68 | }, 69 | ); 70 | 71 | expect(existingEndpoint.instanceConnectEndpointId).toEqual('my-endpoint-id'); 72 | expect(existingEndpoint.connections.securityGroups).toEqual([securityGroup]); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/aws-ec2/integ.instance-connect-endpoint.ts: -------------------------------------------------------------------------------- 1 | import { IntegTest } from '@aws-cdk/integ-tests-alpha'; 2 | import * as cdk from 'aws-cdk-lib'; 3 | import { Construct } from 'constructs'; 4 | import * as ocf from '../../src'; 5 | 6 | class InstanceConnectEndpointStack extends cdk.Stack { 7 | constructor(scope: Construct) { 8 | super(scope, 'InstanceConnectEndpointStack'); 9 | 10 | const vpc = new cdk.aws_ec2.Vpc(this, 'VPC', { 11 | maxAzs: 2, 12 | }); 13 | 14 | const instance = new cdk.aws_ec2.Instance(this, 'Instance', { 15 | vpc, 16 | instanceType: cdk.aws_ec2.InstanceType.of(cdk.aws_ec2.InstanceClass.C5, cdk.aws_ec2.InstanceSize.LARGE), 17 | machineImage: new cdk.aws_ec2.AmazonLinuxImage({ 18 | generation: cdk.aws_ec2.AmazonLinuxGeneration.AMAZON_LINUX_2023, 19 | }), 20 | }); 21 | 22 | const securityGroup = new cdk.aws_ec2.SecurityGroup(this, 'SecurityGroup', { 23 | vpc, 24 | allowAllOutbound: false, 25 | }); 26 | 27 | const instanceConnectEndpoint = new ocf.aws_ec2.InstanceConnectEndpoint(this, 'InstanceConnectEndpoint', { 28 | clientToken: 'my-client-token', 29 | securityGroups: [securityGroup], 30 | preserveClientIp: true, 31 | vpc, 32 | }); 33 | 34 | instanceConnectEndpoint.connections.allowTo(instance, cdk.aws_ec2.Port.tcp(22)); 35 | } 36 | } 37 | 38 | const app = new cdk.App(); 39 | const testCase = new InstanceConnectEndpointStack(app); 40 | new IntegTest(app, 'InstanceConnectEndpoint', { 41 | testCases: [testCase], 42 | }); 43 | -------------------------------------------------------------------------------- /test/aws-ec2/integ.instance-connect-endpoint.ts.snapshot/InstanceConnectEndpointDefaultTestDeployAssert284B1FD7.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { 5 | "source": { 6 | "path": "InstanceConnectEndpointDefaultTestDeployAssert284B1FD7.template.json", 7 | "packaging": "file" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | } 17 | }, 18 | "dockerImages": {} 19 | } -------------------------------------------------------------------------------- /test/aws-ec2/integ.instance-connect-endpoint.ts.snapshot/InstanceConnectEndpointDefaultTestDeployAssert284B1FD7.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Parameters": { 3 | "BootstrapVersion": { 4 | "Type": "AWS::SSM::Parameter::Value", 5 | "Default": "/cdk-bootstrap/hnb659fds/version", 6 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 7 | } 8 | }, 9 | "Rules": { 10 | "CheckBootstrapVersion": { 11 | "Assertions": [ 12 | { 13 | "Assert": { 14 | "Fn::Not": [ 15 | { 16 | "Fn::Contains": [ 17 | [ 18 | "1", 19 | "2", 20 | "3", 21 | "4", 22 | "5" 23 | ], 24 | { 25 | "Ref": "BootstrapVersion" 26 | } 27 | ] 28 | } 29 | ] 30 | }, 31 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 32 | } 33 | ] 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /test/aws-ec2/integ.instance-connect-endpoint.ts.snapshot/InstanceConnectEndpointStack.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e": { 5 | "source": { 6 | "path": "asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e", 7 | "packaging": "zip" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e.zip", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | }, 17 | "0c30d0501af434d3551d71e0423d0b989083d4d2f748dfae2cd738cc08f4c904": { 18 | "source": { 19 | "path": "InstanceConnectEndpointStack.template.json", 20 | "packaging": "file" 21 | }, 22 | "destinations": { 23 | "current_account-current_region": { 24 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 25 | "objectKey": "0c30d0501af434d3551d71e0423d0b989083d4d2f748dfae2cd738cc08f4c904.json", 26 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 27 | } 28 | } 29 | } 30 | }, 31 | "dockerImages": {} 32 | } -------------------------------------------------------------------------------- /test/aws-ec2/integ.instance-connect-endpoint.ts.snapshot/asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e/__entrypoint__.js: -------------------------------------------------------------------------------- 1 | "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.withRetries=exports.handler=exports.external=void 0;const https=require("https"),url=require("url");exports.external={sendHttpRequest:defaultSendHttpRequest,log:defaultLog,includeStackTraces:!0,userHandlerIndex:"./index"};const CREATE_FAILED_PHYSICAL_ID_MARKER="AWSCDK::CustomResourceProviderFramework::CREATE_FAILED",MISSING_PHYSICAL_ID_MARKER="AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID";async function handler(event,context){const sanitizedEvent={...event,ResponseURL:"..."};if(exports.external.log(JSON.stringify(sanitizedEvent,void 0,2)),event.RequestType==="Delete"&&event.PhysicalResourceId===CREATE_FAILED_PHYSICAL_ID_MARKER){exports.external.log("ignoring DELETE event caused by a failed CREATE event"),await submitResponse("SUCCESS",event);return}try{const userHandler=require(exports.external.userHandlerIndex).handler,result=await userHandler(sanitizedEvent,context),responseEvent=renderResponse(event,result);await submitResponse("SUCCESS",responseEvent)}catch(e){const resp={...event,Reason:exports.external.includeStackTraces?e.stack:e.message};resp.PhysicalResourceId||(event.RequestType==="Create"?(exports.external.log("CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored"),resp.PhysicalResourceId=CREATE_FAILED_PHYSICAL_ID_MARKER):exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`)),await submitResponse("FAILED",resp)}}exports.handler=handler;function renderResponse(cfnRequest,handlerResponse={}){const physicalResourceId=handlerResponse.PhysicalResourceId??cfnRequest.PhysicalResourceId??cfnRequest.RequestId;if(cfnRequest.RequestType==="Delete"&&physicalResourceId!==cfnRequest.PhysicalResourceId)throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`);return{...cfnRequest,...handlerResponse,PhysicalResourceId:physicalResourceId}}async function submitResponse(status,event){const json={Status:status,Reason:event.Reason??status,StackId:event.StackId,RequestId:event.RequestId,PhysicalResourceId:event.PhysicalResourceId||MISSING_PHYSICAL_ID_MARKER,LogicalResourceId:event.LogicalResourceId,NoEcho:event.NoEcho,Data:event.Data};exports.external.log("submit response to cloudformation",json);const responseBody=JSON.stringify(json),parsedUrl=url.parse(event.ResponseURL),req={hostname:parsedUrl.hostname,path:parsedUrl.path,method:"PUT",headers:{"content-type":"","content-length":Buffer.byteLength(responseBody,"utf8")}};await withRetries({attempts:5,sleep:1e3},exports.external.sendHttpRequest)(req,responseBody)}async function defaultSendHttpRequest(options,responseBody){return new Promise((resolve,reject)=>{try{const request=https.request(options,_=>resolve());request.on("error",reject),request.write(responseBody),request.end()}catch(e){reject(e)}})}function defaultLog(fmt,...params){console.log(fmt,...params)}function withRetries(options,fn){return async(...xs)=>{let attempts=options.attempts,ms=options.sleep;for(;;)try{return await fn(...xs)}catch(e){if(attempts--<=0)throw e;await sleep(Math.floor(Math.random()*ms)),ms*=2}}}exports.withRetries=withRetries;async function sleep(ms){return new Promise(ok=>setTimeout(ok,ms))} 2 | -------------------------------------------------------------------------------- /test/aws-ec2/integ.instance-connect-endpoint.ts.snapshot/asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e/index.js: -------------------------------------------------------------------------------- 1 | "use strict";var I=Object.create,t=Object.defineProperty,y=Object.getOwnPropertyDescriptor,P=Object.getOwnPropertyNames,g=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty,G=(r,e)=>{for(var o in e)t(r,o,{get:e[o],enumerable:!0})},n=(r,e,o,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of P(e))!l.call(r,s)&&s!==o&&t(r,s,{get:()=>e[s],enumerable:!(i=y(e,s))||i.enumerable});return r},R=(r,e,o)=>(o=r!=null?I(g(r)):{},n(e||!r||!r.__esModule?t(o,"default",{value:r,enumerable:!0}):o,r)),S=r=>n(t({},"__esModule",{value:!0}),r),k={};G(k,{handler:()=>f}),module.exports=S(k);var a=R(require("@aws-sdk/client-ec2")),u=new a.EC2({});function c(r,e){return{GroupId:r,IpPermissions:[{UserIdGroupPairs:[{GroupId:r,UserId:e}],IpProtocol:"-1"}]}}function d(r){return{GroupId:r,IpPermissions:[{IpRanges:[{CidrIp:"0.0.0.0/0"}],IpProtocol:"-1"}]}}async function f(r){let e=r.ResourceProperties.DefaultSecurityGroupId,o=r.ResourceProperties.Account;switch(r.RequestType){case"Create":return p(e,o);case"Update":return h(r);case"Delete":return m(e,o)}}async function h(r){let e=r.OldResourceProperties.DefaultSecurityGroupId,o=r.ResourceProperties.DefaultSecurityGroupId;e!==o&&(await m(e,r.ResourceProperties.Account),await p(o,r.ResourceProperties.Account))}async function p(r,e){try{await u.revokeSecurityGroupEgress(d(r))}catch(o){if(o.name!=="InvalidPermission.NotFound")throw o}try{await u.revokeSecurityGroupIngress(c(r,e))}catch(o){if(o.name!=="InvalidPermission.NotFound")throw o}}async function m(r,e){await u.authorizeSecurityGroupIngress(c(r,e)),await u.authorizeSecurityGroupEgress(d(r))} 2 | -------------------------------------------------------------------------------- /test/aws-ec2/integ.instance-connect-endpoint.ts.snapshot/cdk.out: -------------------------------------------------------------------------------- 1 | {"version":"36.0.0"} -------------------------------------------------------------------------------- /test/aws-ec2/integ.instance-connect-endpoint.ts.snapshot/integ.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "testCases": { 4 | "InstanceConnectEndpoint/DefaultTest": { 5 | "stacks": [ 6 | "InstanceConnectEndpointStack" 7 | ], 8 | "assertionStack": "InstanceConnectEndpoint/DefaultTest/DeployAssert", 9 | "assertionStackName": "InstanceConnectEndpointDefaultTestDeployAssert284B1FD7" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /test/aws-elasticache/daily-snapshot-time.test.ts: -------------------------------------------------------------------------------- 1 | import { strictEqual } from 'assert'; 2 | import { DailySnapshotTime } from '../../src/aws-elasticache'; 3 | 4 | test.each([ 5 | [0, 0, '00:00'], 6 | [23, 0, '23:00'], 7 | [0, 59, '00:59'], 8 | ])('valid daily snapshot time %d:%d returns %s', (hour: number, minute: number, expected: string) => { 9 | strictEqual(new DailySnapshotTime({ hour, minute }).toTimestamp(), expected); 10 | }); 11 | 12 | test.each([ 13 | [-1, 0], 14 | [24, 0], 15 | [1.2, 0], 16 | ])('invalid daily snapshot time hour %s:%d:%d', (hour: number, minute: number) => { 17 | expect(() => { 18 | new DailySnapshotTime({ hour, minute }); 19 | }).toThrow(`dailySnapshotTime hour must be an integer between 0 and 23, got: ${hour}`); 20 | }); 21 | 22 | test.each([ 23 | [0, -1], 24 | [0, 60], 25 | [0, 1.2], 26 | ])('invalid daily snapshot time minute %s:%d:%d', (hour: number, minute: number) => { 27 | expect(() => { 28 | new DailySnapshotTime({ hour, minute }); 29 | }).toThrow(`dailySnapshotTime minute must be an integer between 0 and 59, got: ${minute}`); 30 | }); 31 | -------------------------------------------------------------------------------- /test/aws-elasticache/integ.elasticache-serverless-cache.ts: -------------------------------------------------------------------------------- 1 | import { IntegTest, ExpectedResult, AwsApiCall } from '@aws-cdk/integ-tests-alpha'; 2 | import * as cdk from 'aws-cdk-lib'; 3 | import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch'; 4 | import { Construct } from 'constructs'; 5 | import * as ocf from '../../src'; 6 | import { DailySnapshotTime, Engine, MajorVersion } from '../../src/aws-elasticache'; 7 | 8 | class ElastiCacheStack extends cdk.Stack { 9 | public readonly alarms: cloudwatch.IAlarm[] = []; 10 | 11 | constructor(scope: Construct, id: string, props?: cdk.StackProps) { 12 | super(scope, id, props); 13 | 14 | const defaultUser = new ocf.aws_elasticache.NoPasswordRequiredUser(this, 'DefaultUser', { 15 | userId: 'new-default', 16 | userName: 'default', 17 | }); 18 | 19 | const iamUser = new ocf.aws_elasticache.IamUser(this, 'IamUser', { 20 | accessString: 'on ~* +@all', 21 | }); 22 | 23 | const noPasswordRequiredUser = new ocf.aws_elasticache.NoPasswordRequiredUser(this, 'NoPasswordRequiredUser', { 24 | userId: 'no-password-required-user', 25 | accessString: 'on ~* +@all', 26 | }); 27 | 28 | const passwordUser = new ocf.aws_elasticache.PasswordUser(this, 'PasswordUser', { 29 | userId: 'password-user', 30 | userName: 'password-user-names', 31 | accessString: 'on ~* +@all', 32 | passwords: [ 33 | cdk.SecretValue.unsafePlainText('adminUserPassword123'), 34 | cdk.SecretValue.unsafePlainText('hogehogeadminUserPassword123'), 35 | ], 36 | }); 37 | 38 | const generatedPasswordUser = new ocf.aws_elasticache.PasswordUser(this, 'GeneratedPasswordUser', { 39 | userId: 'generated-password-user', 40 | userName: 'generated-password-user-names', 41 | accessString: 'on ~* +@all', 42 | }); 43 | 44 | const iamUserForImport = new ocf.aws_elasticache.IamUser(this, 'IamUserForImport', { 45 | userId: 'iam-user-for-import', 46 | accessString: 'on ~* +@all', 47 | }); 48 | 49 | const importedIamUser = ocf.aws_elasticache.IamUser.fromUserId(this, 'ImportedIamUser', iamUserForImport.userId); 50 | 51 | const userGroup = new ocf.aws_elasticache.UserGroup(this, 'UserGroup', { 52 | userGroupId: 'my-user-group', 53 | users: [defaultUser, iamUser, noPasswordRequiredUser, generatedPasswordUser], 54 | }); 55 | 56 | userGroup.addUser(passwordUser); 57 | userGroup.addUser(importedIamUser); 58 | 59 | const vpc = new cdk.aws_ec2.Vpc(this, 'VPC', {}); 60 | 61 | const key = new cdk.aws_kms.Key(this, 'Key', { 62 | removalPolicy: cdk.RemovalPolicy.DESTROY, 63 | }); 64 | 65 | const serverlessCache = new ocf.aws_elasticache.ServerlessCache(this, 'ElastiCacheServerlessCluster', { 66 | engine: Engine.VALKEY, 67 | serverlessCacheName: 'my-serverless-cache', 68 | dailySnapshotTime: new DailySnapshotTime({ hour: 12, minute: 0 }), 69 | description: 'my serverless cache', 70 | finalSnapshotName: 'my-finalsnapshot', 71 | kmsKey: key, 72 | majorEngineVersion: MajorVersion.VER_8, 73 | snapshotRetentionLimit: 6, 74 | securityGroups: [ 75 | new cdk.aws_ec2.SecurityGroup(this, 'SecurityGroup', { 76 | vpc, 77 | }), 78 | ], 79 | vpc, 80 | vpcSubnets: vpc.selectSubnets({ 81 | subnetType: cdk.aws_ec2.SubnetType.PRIVATE_WITH_EGRESS, 82 | }), 83 | userGroup, 84 | }); 85 | 86 | const cacheHitsAlarm = serverlessCache.metric('CacheHits', {}).createAlarm(this, 'CacheHitsAlarm', { 87 | threshold: 50, 88 | evaluationPeriods: 1, 89 | }); 90 | const bytesUsedAlarm = serverlessCache.metricBytesUsedForCache().createAlarm(this, 'BytesUsedForCacheAlarm', { 91 | threshold: 50, 92 | evaluationPeriods: 1, 93 | }); 94 | serverlessCache.metricElastiCacheProcessingUnits().createAlarm(this, 'ElastiCacheProcessingUnitsAlarm', { 95 | threshold: 50, 96 | evaluationPeriods: 1, 97 | }); 98 | 99 | // Add the alarms to the array to assert on them later 100 | // ElastiCacheProcessingUnitsAlarm is not added because ElastiCacheProcessingUnits metric data is not available without executing commands 101 | this.alarms.push(cacheHitsAlarm, bytesUsedAlarm); 102 | 103 | const role = new cdk.aws_iam.Role(this, 'TestRole', { assumedBy: new cdk.aws_iam.AccountRootPrincipal() }); 104 | iamUser.grantConnect(role); 105 | importedIamUser.grantConnect(role); 106 | serverlessCache.grantConnect(role); 107 | } 108 | } 109 | 110 | const app = new cdk.App(); 111 | 112 | const testCase = new ElastiCacheStack(app, 'ElastiCacheServerlessCacheStack'); 113 | 114 | const integ = new IntegTest(app, 'ElastiCacheServerlessCacheTest', { 115 | testCases: [testCase], 116 | }); 117 | 118 | const describeAlarmsCall = integ.assertions 119 | .awsApiCall( 120 | 'cloudwatch', 121 | 'DescribeAlarmsCommand', 122 | { 123 | AlarmNames: testCase.alarms.map(a => a.alarmName), 124 | }, 125 | testCase.alarms.map((_, i) => `MetricAlarms.${i}.StateValue`), 126 | ) 127 | .waitForAssertions({ 128 | totalTimeout: cdk.Duration.minutes(2), 129 | }) as AwsApiCall; 130 | 131 | // In the current version of aws-cdk-lib, awsApiCall cannot generate the correct policy for CloudWatch API calls 132 | // https://github.com/aws/aws-cdk/pull/33078 133 | describeAlarmsCall.waiterProvider?.addToRolePolicy({ 134 | Effect: 'Allow', 135 | Action: ['cloudwatch:DescribeAlarms'], 136 | Resource: ['*'], 137 | }); 138 | 139 | for (const [i, _] of testCase.alarms.entries()) { 140 | describeAlarmsCall.assertAtPath(`MetricAlarms.${i}.StateValue`, ExpectedResult.stringLikeRegexp('OK')); 141 | } 142 | -------------------------------------------------------------------------------- /test/aws-elasticache/integ.elasticache-serverless-cache.ts.snapshot/ElastiCacheServerlessCacheStack.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e": { 5 | "source": { 6 | "path": "asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e", 7 | "packaging": "zip" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e.zip", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | }, 17 | "d64b11b9e7a58a88f37eced4fb01e4e1930be8cfc214dd9339b0146a929ed9c3": { 18 | "source": { 19 | "path": "ElastiCacheServerlessCacheStack.template.json", 20 | "packaging": "file" 21 | }, 22 | "destinations": { 23 | "current_account-current_region": { 24 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 25 | "objectKey": "d64b11b9e7a58a88f37eced4fb01e4e1930be8cfc214dd9339b0146a929ed9c3.json", 26 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 27 | } 28 | } 29 | } 30 | }, 31 | "dockerImages": {} 32 | } -------------------------------------------------------------------------------- /test/aws-elasticache/integ.elasticache-serverless-cache.ts.snapshot/ElastiCacheServerlessCacheTestDefaultTestDeployAssertB226C8E2.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "4984c845346313a408899c8ff361d3b7b97953a9d4202e47694ef2a101f4b5c3": { 5 | "source": { 6 | "path": "asset.4984c845346313a408899c8ff361d3b7b97953a9d4202e47694ef2a101f4b5c3.bundle", 7 | "packaging": "zip" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "4984c845346313a408899c8ff361d3b7b97953a9d4202e47694ef2a101f4b5c3.zip", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | }, 17 | "dbfafc122f2e9b8a060842904d5df7178d0e17369c63a7e472a779e2fe222876": { 18 | "source": { 19 | "path": "ElastiCacheServerlessCacheTestDefaultTestDeployAssertB226C8E2.template.json", 20 | "packaging": "file" 21 | }, 22 | "destinations": { 23 | "current_account-current_region": { 24 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 25 | "objectKey": "dbfafc122f2e9b8a060842904d5df7178d0e17369c63a7e472a779e2fe222876.json", 26 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 27 | } 28 | } 29 | } 30 | }, 31 | "dockerImages": {} 32 | } -------------------------------------------------------------------------------- /test/aws-elasticache/integ.elasticache-serverless-cache.ts.snapshot/asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e/__entrypoint__.js: -------------------------------------------------------------------------------- 1 | "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.withRetries=exports.handler=exports.external=void 0;const https=require("https"),url=require("url");exports.external={sendHttpRequest:defaultSendHttpRequest,log:defaultLog,includeStackTraces:!0,userHandlerIndex:"./index"};const CREATE_FAILED_PHYSICAL_ID_MARKER="AWSCDK::CustomResourceProviderFramework::CREATE_FAILED",MISSING_PHYSICAL_ID_MARKER="AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID";async function handler(event,context){const sanitizedEvent={...event,ResponseURL:"..."};if(exports.external.log(JSON.stringify(sanitizedEvent,void 0,2)),event.RequestType==="Delete"&&event.PhysicalResourceId===CREATE_FAILED_PHYSICAL_ID_MARKER){exports.external.log("ignoring DELETE event caused by a failed CREATE event"),await submitResponse("SUCCESS",event);return}try{const userHandler=require(exports.external.userHandlerIndex).handler,result=await userHandler(sanitizedEvent,context),responseEvent=renderResponse(event,result);await submitResponse("SUCCESS",responseEvent)}catch(e){const resp={...event,Reason:exports.external.includeStackTraces?e.stack:e.message};resp.PhysicalResourceId||(event.RequestType==="Create"?(exports.external.log("CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored"),resp.PhysicalResourceId=CREATE_FAILED_PHYSICAL_ID_MARKER):exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`)),await submitResponse("FAILED",resp)}}exports.handler=handler;function renderResponse(cfnRequest,handlerResponse={}){const physicalResourceId=handlerResponse.PhysicalResourceId??cfnRequest.PhysicalResourceId??cfnRequest.RequestId;if(cfnRequest.RequestType==="Delete"&&physicalResourceId!==cfnRequest.PhysicalResourceId)throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`);return{...cfnRequest,...handlerResponse,PhysicalResourceId:physicalResourceId}}async function submitResponse(status,event){const json={Status:status,Reason:event.Reason??status,StackId:event.StackId,RequestId:event.RequestId,PhysicalResourceId:event.PhysicalResourceId||MISSING_PHYSICAL_ID_MARKER,LogicalResourceId:event.LogicalResourceId,NoEcho:event.NoEcho,Data:event.Data};exports.external.log("submit response to cloudformation",json);const responseBody=JSON.stringify(json),parsedUrl=url.parse(event.ResponseURL),req={hostname:parsedUrl.hostname,path:parsedUrl.path,method:"PUT",headers:{"content-type":"","content-length":Buffer.byteLength(responseBody,"utf8")}};await withRetries({attempts:5,sleep:1e3},exports.external.sendHttpRequest)(req,responseBody)}async function defaultSendHttpRequest(options,responseBody){return new Promise((resolve,reject)=>{try{const request=https.request(options,_=>resolve());request.on("error",reject),request.write(responseBody),request.end()}catch(e){reject(e)}})}function defaultLog(fmt,...params){console.log(fmt,...params)}function withRetries(options,fn){return async(...xs)=>{let attempts=options.attempts,ms=options.sleep;for(;;)try{return await fn(...xs)}catch(e){if(attempts--<=0)throw e;await sleep(Math.floor(Math.random()*ms)),ms*=2}}}exports.withRetries=withRetries;async function sleep(ms){return new Promise(ok=>setTimeout(ok,ms))} 2 | -------------------------------------------------------------------------------- /test/aws-elasticache/integ.elasticache-serverless-cache.ts.snapshot/asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e/index.js: -------------------------------------------------------------------------------- 1 | "use strict";var I=Object.create,t=Object.defineProperty,y=Object.getOwnPropertyDescriptor,P=Object.getOwnPropertyNames,g=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty,G=(r,e)=>{for(var o in e)t(r,o,{get:e[o],enumerable:!0})},n=(r,e,o,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of P(e))!l.call(r,s)&&s!==o&&t(r,s,{get:()=>e[s],enumerable:!(i=y(e,s))||i.enumerable});return r},R=(r,e,o)=>(o=r!=null?I(g(r)):{},n(e||!r||!r.__esModule?t(o,"default",{value:r,enumerable:!0}):o,r)),S=r=>n(t({},"__esModule",{value:!0}),r),k={};G(k,{handler:()=>f}),module.exports=S(k);var a=R(require("@aws-sdk/client-ec2")),u=new a.EC2({});function c(r,e){return{GroupId:r,IpPermissions:[{UserIdGroupPairs:[{GroupId:r,UserId:e}],IpProtocol:"-1"}]}}function d(r){return{GroupId:r,IpPermissions:[{IpRanges:[{CidrIp:"0.0.0.0/0"}],IpProtocol:"-1"}]}}async function f(r){let e=r.ResourceProperties.DefaultSecurityGroupId,o=r.ResourceProperties.Account;switch(r.RequestType){case"Create":return p(e,o);case"Update":return h(r);case"Delete":return m(e,o)}}async function h(r){let e=r.OldResourceProperties.DefaultSecurityGroupId,o=r.ResourceProperties.DefaultSecurityGroupId;e!==o&&(await m(e,r.ResourceProperties.Account),await p(o,r.ResourceProperties.Account))}async function p(r,e){try{await u.revokeSecurityGroupEgress(d(r))}catch(o){if(o.name!=="InvalidPermission.NotFound")throw o}try{await u.revokeSecurityGroupIngress(c(r,e))}catch(o){if(o.name!=="InvalidPermission.NotFound")throw o}}async function m(r,e){await u.authorizeSecurityGroupIngress(c(r,e)),await u.authorizeSecurityGroupEgress(d(r))} 2 | -------------------------------------------------------------------------------- /test/aws-elasticache/integ.elasticache-serverless-cache.ts.snapshot/cdk.out: -------------------------------------------------------------------------------- 1 | {"version":"36.0.0"} -------------------------------------------------------------------------------- /test/aws-elasticache/integ.elasticache-serverless-cache.ts.snapshot/integ.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "testCases": { 4 | "ElastiCacheServerlessCacheTest/DefaultTest": { 5 | "stacks": [ 6 | "ElastiCacheServerlessCacheStack" 7 | ], 8 | "assertionStack": "ElastiCacheServerlessCacheTest/DefaultTest/DeployAssert", 9 | "assertionStackName": "ElastiCacheServerlessCacheTestDefaultTestDeployAssertB226C8E2" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /test/aws-elasticache/integ.import-aws-default-user.ts: -------------------------------------------------------------------------------- 1 | import { IntegTest } from '@aws-cdk/integ-tests-alpha'; 2 | import * as cdk from 'aws-cdk-lib'; 3 | import { Construct } from 'constructs'; 4 | import * as ocf from '../../src'; 5 | 6 | class ImportDefaultUserStack extends cdk.Stack { 7 | constructor(scope: Construct, id: string, props?: cdk.StackProps) { 8 | super(scope, id, props); 9 | 10 | const importedDefaultUser = ocf.aws_elasticache.NoPasswordRequiredUser.fromUserAttributes( 11 | this, 12 | 'ImportedDefaultUser', 13 | { 14 | userId: 'default', 15 | userName: 'default', 16 | }, 17 | ); 18 | 19 | new ocf.aws_elasticache.UserGroup(this, 'UserGroupForImportDefaultUser', { 20 | userGroupId: 'user-group-for-default-user', 21 | users: [importedDefaultUser], 22 | }); 23 | } 24 | } 25 | 26 | const app = new cdk.App(); 27 | 28 | const testCase = new ImportDefaultUserStack(app, 'ImportDefaultUserStack'); 29 | 30 | new IntegTest(app, 'ImportDefaultUserTest', { 31 | testCases: [testCase], 32 | }); 33 | -------------------------------------------------------------------------------- /test/aws-elasticache/integ.import-aws-default-user.ts.snapshot/ImportDefaultUserStack.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "3febd699eb4adedd6f2faf9a60bf7da04435b8444843d9cfe228ecc4b926d620": { 5 | "source": { 6 | "path": "ImportDefaultUserStack.template.json", 7 | "packaging": "file" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "3febd699eb4adedd6f2faf9a60bf7da04435b8444843d9cfe228ecc4b926d620.json", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | } 17 | }, 18 | "dockerImages": {} 19 | } -------------------------------------------------------------------------------- /test/aws-elasticache/integ.import-aws-default-user.ts.snapshot/ImportDefaultUserStack.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Resources": { 3 | "UserGroupForImportDefaultUserB4F83C04": { 4 | "Type": "AWS::ElastiCache::UserGroup", 5 | "Properties": { 6 | "Engine": "redis", 7 | "UserGroupId": "user-group-for-default-user", 8 | "UserIds": [ 9 | "default" 10 | ] 11 | } 12 | } 13 | }, 14 | "Parameters": { 15 | "BootstrapVersion": { 16 | "Type": "AWS::SSM::Parameter::Value", 17 | "Default": "/cdk-bootstrap/hnb659fds/version", 18 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 19 | } 20 | }, 21 | "Rules": { 22 | "CheckBootstrapVersion": { 23 | "Assertions": [ 24 | { 25 | "Assert": { 26 | "Fn::Not": [ 27 | { 28 | "Fn::Contains": [ 29 | [ 30 | "1", 31 | "2", 32 | "3", 33 | "4", 34 | "5" 35 | ], 36 | { 37 | "Ref": "BootstrapVersion" 38 | } 39 | ] 40 | } 41 | ] 42 | }, 43 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 44 | } 45 | ] 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /test/aws-elasticache/integ.import-aws-default-user.ts.snapshot/ImportDefaultUserTestDefaultTestDeployAssert51B7C19D.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { 5 | "source": { 6 | "path": "ImportDefaultUserTestDefaultTestDeployAssert51B7C19D.template.json", 7 | "packaging": "file" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | } 17 | }, 18 | "dockerImages": {} 19 | } -------------------------------------------------------------------------------- /test/aws-elasticache/integ.import-aws-default-user.ts.snapshot/ImportDefaultUserTestDefaultTestDeployAssert51B7C19D.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Parameters": { 3 | "BootstrapVersion": { 4 | "Type": "AWS::SSM::Parameter::Value", 5 | "Default": "/cdk-bootstrap/hnb659fds/version", 6 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 7 | } 8 | }, 9 | "Rules": { 10 | "CheckBootstrapVersion": { 11 | "Assertions": [ 12 | { 13 | "Assert": { 14 | "Fn::Not": [ 15 | { 16 | "Fn::Contains": [ 17 | [ 18 | "1", 19 | "2", 20 | "3", 21 | "4", 22 | "5" 23 | ], 24 | { 25 | "Ref": "BootstrapVersion" 26 | } 27 | ] 28 | } 29 | ] 30 | }, 31 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 32 | } 33 | ] 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /test/aws-elasticache/integ.import-aws-default-user.ts.snapshot/cdk.out: -------------------------------------------------------------------------------- 1 | {"version":"36.0.0"} -------------------------------------------------------------------------------- /test/aws-elasticache/integ.import-aws-default-user.ts.snapshot/integ.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "testCases": { 4 | "ImportDefaultUserTest/DefaultTest": { 5 | "stacks": [ 6 | "ImportDefaultUserStack" 7 | ], 8 | "assertionStack": "ImportDefaultUserTest/DefaultTest/DeployAssert", 9 | "assertionStackName": "ImportDefaultUserTestDefaultTestDeployAssert51B7C19D" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /test/aws-elasticache/integ.import-aws-default-user.ts.snapshot/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "artifacts": { 4 | "ImportDefaultUserStack.assets": { 5 | "type": "cdk:asset-manifest", 6 | "properties": { 7 | "file": "ImportDefaultUserStack.assets.json", 8 | "requiresBootstrapStackVersion": 6, 9 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 10 | } 11 | }, 12 | "ImportDefaultUserStack": { 13 | "type": "aws:cloudformation:stack", 14 | "environment": "aws://unknown-account/unknown-region", 15 | "properties": { 16 | "templateFile": "ImportDefaultUserStack.template.json", 17 | "terminationProtection": false, 18 | "validateOnSynth": false, 19 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", 20 | "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", 21 | "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/3febd699eb4adedd6f2faf9a60bf7da04435b8444843d9cfe228ecc4b926d620.json", 22 | "requiresBootstrapStackVersion": 6, 23 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", 24 | "additionalDependencies": [ 25 | "ImportDefaultUserStack.assets" 26 | ], 27 | "lookupRole": { 28 | "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", 29 | "requiresBootstrapStackVersion": 8, 30 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 31 | } 32 | }, 33 | "dependencies": [ 34 | "ImportDefaultUserStack.assets" 35 | ], 36 | "metadata": { 37 | "/ImportDefaultUserStack/UserGroupForImportDefaultUser/Resource": [ 38 | { 39 | "type": "aws:cdk:logicalId", 40 | "data": "UserGroupForImportDefaultUserB4F83C04" 41 | } 42 | ], 43 | "/ImportDefaultUserStack/BootstrapVersion": [ 44 | { 45 | "type": "aws:cdk:logicalId", 46 | "data": "BootstrapVersion" 47 | } 48 | ], 49 | "/ImportDefaultUserStack/CheckBootstrapVersion": [ 50 | { 51 | "type": "aws:cdk:logicalId", 52 | "data": "CheckBootstrapVersion" 53 | } 54 | ] 55 | }, 56 | "displayName": "ImportDefaultUserStack" 57 | }, 58 | "ImportDefaultUserTestDefaultTestDeployAssert51B7C19D.assets": { 59 | "type": "cdk:asset-manifest", 60 | "properties": { 61 | "file": "ImportDefaultUserTestDefaultTestDeployAssert51B7C19D.assets.json", 62 | "requiresBootstrapStackVersion": 6, 63 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 64 | } 65 | }, 66 | "ImportDefaultUserTestDefaultTestDeployAssert51B7C19D": { 67 | "type": "aws:cloudformation:stack", 68 | "environment": "aws://unknown-account/unknown-region", 69 | "properties": { 70 | "templateFile": "ImportDefaultUserTestDefaultTestDeployAssert51B7C19D.template.json", 71 | "terminationProtection": false, 72 | "validateOnSynth": false, 73 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", 74 | "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", 75 | "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", 76 | "requiresBootstrapStackVersion": 6, 77 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", 78 | "additionalDependencies": [ 79 | "ImportDefaultUserTestDefaultTestDeployAssert51B7C19D.assets" 80 | ], 81 | "lookupRole": { 82 | "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", 83 | "requiresBootstrapStackVersion": 8, 84 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 85 | } 86 | }, 87 | "dependencies": [ 88 | "ImportDefaultUserTestDefaultTestDeployAssert51B7C19D.assets" 89 | ], 90 | "metadata": { 91 | "/ImportDefaultUserTest/DefaultTest/DeployAssert/BootstrapVersion": [ 92 | { 93 | "type": "aws:cdk:logicalId", 94 | "data": "BootstrapVersion" 95 | } 96 | ], 97 | "/ImportDefaultUserTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ 98 | { 99 | "type": "aws:cdk:logicalId", 100 | "data": "CheckBootstrapVersion" 101 | } 102 | ] 103 | }, 104 | "displayName": "ImportDefaultUserTest/DefaultTest/DeployAssert" 105 | }, 106 | "Tree": { 107 | "type": "cdk:tree", 108 | "properties": { 109 | "file": "tree.json" 110 | } 111 | } 112 | } 113 | } -------------------------------------------------------------------------------- /test/aws-elasticache/integ.import-aws-default-user.ts.snapshot/tree.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "tree-0.1", 3 | "tree": { 4 | "id": "App", 5 | "path": "", 6 | "children": { 7 | "ImportDefaultUserStack": { 8 | "id": "ImportDefaultUserStack", 9 | "path": "ImportDefaultUserStack", 10 | "children": { 11 | "ImportedDefaultUser": { 12 | "id": "ImportedDefaultUser", 13 | "path": "ImportDefaultUserStack/ImportedDefaultUser", 14 | "constructInfo": { 15 | "fqn": "aws-cdk-lib.Resource", 16 | "version": "2.120.0" 17 | } 18 | }, 19 | "UserGroupForImportDefaultUser": { 20 | "id": "UserGroupForImportDefaultUser", 21 | "path": "ImportDefaultUserStack/UserGroupForImportDefaultUser", 22 | "children": { 23 | "Resource": { 24 | "id": "Resource", 25 | "path": "ImportDefaultUserStack/UserGroupForImportDefaultUser/Resource", 26 | "attributes": { 27 | "aws:cdk:cloudformation:type": "AWS::ElastiCache::UserGroup", 28 | "aws:cdk:cloudformation:props": { 29 | "engine": "redis", 30 | "userGroupId": "user-group-for-default-user", 31 | "userIds": [ 32 | "default" 33 | ] 34 | } 35 | }, 36 | "constructInfo": { 37 | "fqn": "aws-cdk-lib.aws_elasticache.CfnUserGroup", 38 | "version": "2.120.0" 39 | } 40 | } 41 | }, 42 | "constructInfo": { 43 | "fqn": "aws-cdk-lib.Resource", 44 | "version": "2.120.0" 45 | } 46 | }, 47 | "BootstrapVersion": { 48 | "id": "BootstrapVersion", 49 | "path": "ImportDefaultUserStack/BootstrapVersion", 50 | "constructInfo": { 51 | "fqn": "aws-cdk-lib.CfnParameter", 52 | "version": "2.120.0" 53 | } 54 | }, 55 | "CheckBootstrapVersion": { 56 | "id": "CheckBootstrapVersion", 57 | "path": "ImportDefaultUserStack/CheckBootstrapVersion", 58 | "constructInfo": { 59 | "fqn": "aws-cdk-lib.CfnRule", 60 | "version": "2.120.0" 61 | } 62 | } 63 | }, 64 | "constructInfo": { 65 | "fqn": "aws-cdk-lib.Stack", 66 | "version": "2.120.0" 67 | } 68 | }, 69 | "ImportDefaultUserTest": { 70 | "id": "ImportDefaultUserTest", 71 | "path": "ImportDefaultUserTest", 72 | "children": { 73 | "DefaultTest": { 74 | "id": "DefaultTest", 75 | "path": "ImportDefaultUserTest/DefaultTest", 76 | "children": { 77 | "Default": { 78 | "id": "Default", 79 | "path": "ImportDefaultUserTest/DefaultTest/Default", 80 | "constructInfo": { 81 | "fqn": "constructs.Construct", 82 | "version": "10.3.0" 83 | } 84 | }, 85 | "DeployAssert": { 86 | "id": "DeployAssert", 87 | "path": "ImportDefaultUserTest/DefaultTest/DeployAssert", 88 | "children": { 89 | "BootstrapVersion": { 90 | "id": "BootstrapVersion", 91 | "path": "ImportDefaultUserTest/DefaultTest/DeployAssert/BootstrapVersion", 92 | "constructInfo": { 93 | "fqn": "aws-cdk-lib.CfnParameter", 94 | "version": "2.120.0" 95 | } 96 | }, 97 | "CheckBootstrapVersion": { 98 | "id": "CheckBootstrapVersion", 99 | "path": "ImportDefaultUserTest/DefaultTest/DeployAssert/CheckBootstrapVersion", 100 | "constructInfo": { 101 | "fqn": "aws-cdk-lib.CfnRule", 102 | "version": "2.120.0" 103 | } 104 | } 105 | }, 106 | "constructInfo": { 107 | "fqn": "aws-cdk-lib.Stack", 108 | "version": "2.120.0" 109 | } 110 | } 111 | }, 112 | "constructInfo": { 113 | "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", 114 | "version": "2.120.0-alpha.0" 115 | } 116 | } 117 | }, 118 | "constructInfo": { 119 | "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", 120 | "version": "2.120.0-alpha.0" 121 | } 122 | }, 123 | "Tree": { 124 | "id": "Tree", 125 | "path": "Tree", 126 | "constructInfo": { 127 | "fqn": "constructs.Construct", 128 | "version": "10.3.0" 129 | } 130 | } 131 | }, 132 | "constructInfo": { 133 | "fqn": "aws-cdk-lib.App", 134 | "version": "2.120.0" 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /test/aws-examplemodule/example.test.ts: -------------------------------------------------------------------------------- 1 | import { Stack } from 'aws-cdk-lib'; 2 | import { Template } from 'aws-cdk-lib/assertions'; 3 | import { Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; 4 | import { Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda'; 5 | import { Construct } from 'constructs'; 6 | 7 | class MyConstruct extends Construct { 8 | constructor(scope: Construct, id: string, props: { role?: Role }) { 9 | super(scope, id); 10 | const role = 11 | props.role ?? 12 | new Role(this, 'DefaultRole', { 13 | assumedBy: new ServicePrincipal('lambda.amazonaws.com'), 14 | }); 15 | 16 | new Function(this, 'Resource', { 17 | code: Code.fromInline('module.exports.handler = async () => "hello world";'), 18 | handler: 'main.handler', 19 | runtime: Runtime.NODEJS_20_X, 20 | role: role, 21 | }); 22 | } 23 | } 24 | 25 | describe('Lambda Function Role', () => { 26 | test('Uses provided Role for Lambda Function', () => { 27 | // GIVEN 28 | const stack = new Stack(); 29 | 30 | // WHEN 31 | const providedRole = new Role(stack, 'RoleToUse', { 32 | assumedBy: new ServicePrincipal('lambda.amazonaws.com'), 33 | }); 34 | const construct = new MyConstruct(stack, 'MyConstruct', { 35 | role: providedRole, 36 | }); 37 | 38 | // THEN 39 | const lambdaFunction = construct.node.defaultChild as Function; 40 | expect(lambdaFunction.role).toEqual(providedRole); 41 | }); 42 | 43 | test('Creates a new role when none provided', () => { 44 | // GIVEN 45 | const stack = new Stack(); 46 | 47 | // WHEN 48 | new MyConstruct(stack, 'MyConstruct', {}); 49 | 50 | // THEN 51 | const assert = Template.fromStack(stack); 52 | assert.hasResourceProperties('AWS::IAM::Role', { 53 | AssumeRolePolicyDocument: { 54 | Statement: [ 55 | { 56 | Action: 'sts:AssumeRole', 57 | Effect: 'Allow', 58 | Principal: { 59 | Service: 'lambda.amazonaws.com', 60 | }, 61 | }, 62 | ], 63 | Version: '2012-10-17', 64 | }, 65 | }); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /test/aws-examplemodule/integ.example.ts: -------------------------------------------------------------------------------- 1 | import { IntegTest } from '@aws-cdk/integ-tests-alpha'; 2 | import { App, RemovalPolicy, Stack } from 'aws-cdk-lib'; 3 | import { Topic } from 'aws-cdk-lib/aws-sns'; 4 | 5 | const app = new App(); 6 | const stack = new Stack(app, 'IntegrationTestExampleStack', {}); 7 | const topic = new Topic(stack, 'Output', {}); 8 | topic.applyRemovalPolicy(RemovalPolicy.DESTROY); 9 | 10 | new IntegTest(app, 'IntegTestExample', { 11 | testCases: [stack], 12 | }); 13 | -------------------------------------------------------------------------------- /test/aws-examplemodule/integ.example.ts.snapshot/IntegTestExampleDefaultTestDeployAssertB2D38A87.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { 5 | "source": { 6 | "path": "IntegTestExampleDefaultTestDeployAssertB2D38A87.template.json", 7 | "packaging": "file" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | } 17 | }, 18 | "dockerImages": {} 19 | } -------------------------------------------------------------------------------- /test/aws-examplemodule/integ.example.ts.snapshot/IntegTestExampleDefaultTestDeployAssertB2D38A87.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Parameters": { 3 | "BootstrapVersion": { 4 | "Type": "AWS::SSM::Parameter::Value", 5 | "Default": "/cdk-bootstrap/hnb659fds/version", 6 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 7 | } 8 | }, 9 | "Rules": { 10 | "CheckBootstrapVersion": { 11 | "Assertions": [ 12 | { 13 | "Assert": { 14 | "Fn::Not": [ 15 | { 16 | "Fn::Contains": [ 17 | [ 18 | "1", 19 | "2", 20 | "3", 21 | "4", 22 | "5" 23 | ], 24 | { 25 | "Ref": "BootstrapVersion" 26 | } 27 | ] 28 | } 29 | ] 30 | }, 31 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 32 | } 33 | ] 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /test/aws-examplemodule/integ.example.ts.snapshot/IntegrationTestExampleStack.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "17d641b519586790260c7340c331c02496f5c401e5d6cc3d6fa56afa99ef34a2": { 5 | "source": { 6 | "path": "IntegrationTestExampleStack.template.json", 7 | "packaging": "file" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "17d641b519586790260c7340c331c02496f5c401e5d6cc3d6fa56afa99ef34a2.json", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | } 17 | }, 18 | "dockerImages": {} 19 | } -------------------------------------------------------------------------------- /test/aws-examplemodule/integ.example.ts.snapshot/IntegrationTestExampleStack.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Resources": { 3 | "OutputAB65CDDE": { 4 | "Type": "AWS::SNS::Topic", 5 | "UpdateReplacePolicy": "Delete", 6 | "DeletionPolicy": "Delete" 7 | } 8 | }, 9 | "Parameters": { 10 | "BootstrapVersion": { 11 | "Type": "AWS::SSM::Parameter::Value", 12 | "Default": "/cdk-bootstrap/hnb659fds/version", 13 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 14 | } 15 | }, 16 | "Rules": { 17 | "CheckBootstrapVersion": { 18 | "Assertions": [ 19 | { 20 | "Assert": { 21 | "Fn::Not": [ 22 | { 23 | "Fn::Contains": [ 24 | [ 25 | "1", 26 | "2", 27 | "3", 28 | "4", 29 | "5" 30 | ], 31 | { 32 | "Ref": "BootstrapVersion" 33 | } 34 | ] 35 | } 36 | ] 37 | }, 38 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 39 | } 40 | ] 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /test/aws-examplemodule/integ.example.ts.snapshot/cdk.out: -------------------------------------------------------------------------------- 1 | {"version":"36.0.0"} -------------------------------------------------------------------------------- /test/aws-examplemodule/integ.example.ts.snapshot/integ.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "testCases": { 4 | "IntegTestExample/DefaultTest": { 5 | "stacks": [ 6 | "IntegrationTestExampleStack" 7 | ], 8 | "assertionStack": "IntegTestExample/DefaultTest/DeployAssert", 9 | "assertionStackName": "IntegTestExampleDefaultTestDeployAssertB2D38A87" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /test/aws-examplemodule/integ.example.ts.snapshot/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "artifacts": { 4 | "IntegrationTestExampleStack.assets": { 5 | "type": "cdk:asset-manifest", 6 | "properties": { 7 | "file": "IntegrationTestExampleStack.assets.json", 8 | "requiresBootstrapStackVersion": 6, 9 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 10 | } 11 | }, 12 | "IntegrationTestExampleStack": { 13 | "type": "aws:cloudformation:stack", 14 | "environment": "aws://unknown-account/unknown-region", 15 | "properties": { 16 | "templateFile": "IntegrationTestExampleStack.template.json", 17 | "terminationProtection": false, 18 | "validateOnSynth": false, 19 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", 20 | "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", 21 | "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/17d641b519586790260c7340c331c02496f5c401e5d6cc3d6fa56afa99ef34a2.json", 22 | "requiresBootstrapStackVersion": 6, 23 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", 24 | "additionalDependencies": [ 25 | "IntegrationTestExampleStack.assets" 26 | ], 27 | "lookupRole": { 28 | "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", 29 | "requiresBootstrapStackVersion": 8, 30 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 31 | } 32 | }, 33 | "dependencies": [ 34 | "IntegrationTestExampleStack.assets" 35 | ], 36 | "metadata": { 37 | "/IntegrationTestExampleStack/Output/Resource": [ 38 | { 39 | "type": "aws:cdk:logicalId", 40 | "data": "OutputAB65CDDE" 41 | } 42 | ], 43 | "/IntegrationTestExampleStack/BootstrapVersion": [ 44 | { 45 | "type": "aws:cdk:logicalId", 46 | "data": "BootstrapVersion" 47 | } 48 | ], 49 | "/IntegrationTestExampleStack/CheckBootstrapVersion": [ 50 | { 51 | "type": "aws:cdk:logicalId", 52 | "data": "CheckBootstrapVersion" 53 | } 54 | ] 55 | }, 56 | "displayName": "IntegrationTestExampleStack" 57 | }, 58 | "IntegTestExampleDefaultTestDeployAssertB2D38A87.assets": { 59 | "type": "cdk:asset-manifest", 60 | "properties": { 61 | "file": "IntegTestExampleDefaultTestDeployAssertB2D38A87.assets.json", 62 | "requiresBootstrapStackVersion": 6, 63 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 64 | } 65 | }, 66 | "IntegTestExampleDefaultTestDeployAssertB2D38A87": { 67 | "type": "aws:cloudformation:stack", 68 | "environment": "aws://unknown-account/unknown-region", 69 | "properties": { 70 | "templateFile": "IntegTestExampleDefaultTestDeployAssertB2D38A87.template.json", 71 | "terminationProtection": false, 72 | "validateOnSynth": false, 73 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", 74 | "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", 75 | "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", 76 | "requiresBootstrapStackVersion": 6, 77 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", 78 | "additionalDependencies": [ 79 | "IntegTestExampleDefaultTestDeployAssertB2D38A87.assets" 80 | ], 81 | "lookupRole": { 82 | "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", 83 | "requiresBootstrapStackVersion": 8, 84 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 85 | } 86 | }, 87 | "dependencies": [ 88 | "IntegTestExampleDefaultTestDeployAssertB2D38A87.assets" 89 | ], 90 | "metadata": { 91 | "/IntegTestExample/DefaultTest/DeployAssert/BootstrapVersion": [ 92 | { 93 | "type": "aws:cdk:logicalId", 94 | "data": "BootstrapVersion" 95 | } 96 | ], 97 | "/IntegTestExample/DefaultTest/DeployAssert/CheckBootstrapVersion": [ 98 | { 99 | "type": "aws:cdk:logicalId", 100 | "data": "CheckBootstrapVersion" 101 | } 102 | ] 103 | }, 104 | "displayName": "IntegTestExample/DefaultTest/DeployAssert" 105 | }, 106 | "Tree": { 107 | "type": "cdk:tree", 108 | "properties": { 109 | "file": "tree.json" 110 | } 111 | } 112 | } 113 | } -------------------------------------------------------------------------------- /test/aws-examplemodule/integ.example.ts.snapshot/tree.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "tree-0.1", 3 | "tree": { 4 | "id": "App", 5 | "path": "", 6 | "children": { 7 | "IntegrationTestExampleStack": { 8 | "id": "IntegrationTestExampleStack", 9 | "path": "IntegrationTestExampleStack", 10 | "children": { 11 | "Output": { 12 | "id": "Output", 13 | "path": "IntegrationTestExampleStack/Output", 14 | "children": { 15 | "Resource": { 16 | "id": "Resource", 17 | "path": "IntegrationTestExampleStack/Output/Resource", 18 | "attributes": { 19 | "aws:cdk:cloudformation:type": "AWS::SNS::Topic", 20 | "aws:cdk:cloudformation:props": {} 21 | }, 22 | "constructInfo": { 23 | "fqn": "aws-cdk-lib.aws_sns.CfnTopic", 24 | "version": "2.120.0" 25 | } 26 | } 27 | }, 28 | "constructInfo": { 29 | "fqn": "aws-cdk-lib.aws_sns.Topic", 30 | "version": "2.120.0" 31 | } 32 | }, 33 | "BootstrapVersion": { 34 | "id": "BootstrapVersion", 35 | "path": "IntegrationTestExampleStack/BootstrapVersion", 36 | "constructInfo": { 37 | "fqn": "aws-cdk-lib.CfnParameter", 38 | "version": "2.120.0" 39 | } 40 | }, 41 | "CheckBootstrapVersion": { 42 | "id": "CheckBootstrapVersion", 43 | "path": "IntegrationTestExampleStack/CheckBootstrapVersion", 44 | "constructInfo": { 45 | "fqn": "aws-cdk-lib.CfnRule", 46 | "version": "2.120.0" 47 | } 48 | } 49 | }, 50 | "constructInfo": { 51 | "fqn": "aws-cdk-lib.Stack", 52 | "version": "2.120.0" 53 | } 54 | }, 55 | "IntegTestExample": { 56 | "id": "IntegTestExample", 57 | "path": "IntegTestExample", 58 | "children": { 59 | "DefaultTest": { 60 | "id": "DefaultTest", 61 | "path": "IntegTestExample/DefaultTest", 62 | "children": { 63 | "Default": { 64 | "id": "Default", 65 | "path": "IntegTestExample/DefaultTest/Default", 66 | "constructInfo": { 67 | "fqn": "constructs.Construct", 68 | "version": "10.0.5" 69 | } 70 | }, 71 | "DeployAssert": { 72 | "id": "DeployAssert", 73 | "path": "IntegTestExample/DefaultTest/DeployAssert", 74 | "children": { 75 | "BootstrapVersion": { 76 | "id": "BootstrapVersion", 77 | "path": "IntegTestExample/DefaultTest/DeployAssert/BootstrapVersion", 78 | "constructInfo": { 79 | "fqn": "aws-cdk-lib.CfnParameter", 80 | "version": "2.120.0" 81 | } 82 | }, 83 | "CheckBootstrapVersion": { 84 | "id": "CheckBootstrapVersion", 85 | "path": "IntegTestExample/DefaultTest/DeployAssert/CheckBootstrapVersion", 86 | "constructInfo": { 87 | "fqn": "aws-cdk-lib.CfnRule", 88 | "version": "2.120.0" 89 | } 90 | } 91 | }, 92 | "constructInfo": { 93 | "fqn": "aws-cdk-lib.Stack", 94 | "version": "2.120.0" 95 | } 96 | } 97 | }, 98 | "constructInfo": { 99 | "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", 100 | "version": "2.120.0-alpha.0" 101 | } 102 | } 103 | }, 104 | "constructInfo": { 105 | "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", 106 | "version": "2.120.0-alpha.0" 107 | } 108 | }, 109 | "Tree": { 110 | "id": "Tree", 111 | "path": "Tree", 112 | "constructInfo": { 113 | "fqn": "constructs.Construct", 114 | "version": "10.0.5" 115 | } 116 | } 117 | }, 118 | "constructInfo": { 119 | "fqn": "aws-cdk-lib.App", 120 | "version": "2.120.0" 121 | } 122 | } 123 | } -------------------------------------------------------------------------------- /test/aws-fsx/integ.ontap-file-system.ts: -------------------------------------------------------------------------------- 1 | import * as integ from '@aws-cdk/integ-tests-alpha'; 2 | import { App, Duration, RemovalPolicy, SecretValue, Stack, aws_ec2, aws_fsx } from 'aws-cdk-lib'; 3 | import * as ocf from '../../src'; 4 | import { DailyAutomaticBackupStartTime } from '../../src/aws-fsx/daily-automatic-backup-start-time'; 5 | 6 | const app = new App(); 7 | 8 | const stack = new Stack(app, 'FsxForOntapTestStack'); 9 | 10 | const vpc = new aws_ec2.Vpc(stack, 'Vpc'); 11 | 12 | new ocf.aws_fsx.OntapFileSystem(stack, 'OntapMultiAzFileSystem', { 13 | vpc, 14 | vpcSubnets: vpc.privateSubnets, 15 | storageCapacityGiB: 5120, 16 | ontapConfiguration: { 17 | automaticBackupRetention: Duration.days(7), 18 | dailyAutomaticBackupStartTime: new DailyAutomaticBackupStartTime({ 19 | hour: 1, 20 | minute: 0, 21 | }), 22 | deploymentType: ocf.aws_fsx.OntapDeploymentType.MULTI_AZ_2, 23 | diskIops: 15360, 24 | endpointIpAddressRange: '192.168.39.0/24', 25 | fsxAdminPassword: SecretValue.unsafePlainText('fsxPassword1'), 26 | haPairs: 1, 27 | preferredSubnet: vpc.privateSubnets[0], 28 | routeTables: [vpc.privateSubnets[0].routeTable, vpc.privateSubnets[1].routeTable], 29 | throughputCapacityPerHaPair: ocf.aws_fsx.MultiAz2ThroughputCapacityPerHaPair.MB_PER_SEC_384, 30 | weeklyMaintenanceStartTime: new ocf.aws_fsx.MaintenanceTime({ 31 | day: aws_fsx.Weekday.SUNDAY, 32 | hour: 1, 33 | minute: 0, 34 | }), 35 | }, 36 | removalPolicy: RemovalPolicy.DESTROY, 37 | }); 38 | 39 | new ocf.aws_fsx.OntapFileSystem(stack, 'OntapSingleAzFileSystem', { 40 | vpc, 41 | vpcSubnets: [vpc.privateSubnets[0]], 42 | storageCapacityGiB: 5120, 43 | ontapConfiguration: { 44 | automaticBackupRetention: Duration.days(7), 45 | dailyAutomaticBackupStartTime: new DailyAutomaticBackupStartTime({ 46 | hour: 1, 47 | minute: 0, 48 | }), 49 | deploymentType: ocf.aws_fsx.OntapDeploymentType.SINGLE_AZ_2, 50 | diskIops: 76800, 51 | fsxAdminPassword: SecretValue.unsafePlainText('fsxPassword1'), 52 | haPairs: 5, 53 | throughputCapacityPerHaPair: ocf.aws_fsx.SingleAz2ThroughputCapacityPerHaPair.MB_PER_SEC_1536, 54 | weeklyMaintenanceStartTime: new ocf.aws_fsx.MaintenanceTime({ 55 | day: aws_fsx.Weekday.SUNDAY, 56 | hour: 1, 57 | minute: 0, 58 | }), 59 | }, 60 | removalPolicy: RemovalPolicy.DESTROY, 61 | }); 62 | 63 | new integ.IntegTest(app, 'FsxForOntapTest', { 64 | testCases: [stack], 65 | }); 66 | -------------------------------------------------------------------------------- /test/aws-fsx/integ.ontap-file-system.ts.snapshot/FsxForOntapTestDefaultTestDeployAssert20A933DE.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { 5 | "source": { 6 | "path": "FsxForOntapTestDefaultTestDeployAssert20A933DE.template.json", 7 | "packaging": "file" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | } 17 | }, 18 | "dockerImages": {} 19 | } -------------------------------------------------------------------------------- /test/aws-fsx/integ.ontap-file-system.ts.snapshot/FsxForOntapTestDefaultTestDeployAssert20A933DE.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Parameters": { 3 | "BootstrapVersion": { 4 | "Type": "AWS::SSM::Parameter::Value", 5 | "Default": "/cdk-bootstrap/hnb659fds/version", 6 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 7 | } 8 | }, 9 | "Rules": { 10 | "CheckBootstrapVersion": { 11 | "Assertions": [ 12 | { 13 | "Assert": { 14 | "Fn::Not": [ 15 | { 16 | "Fn::Contains": [ 17 | [ 18 | "1", 19 | "2", 20 | "3", 21 | "4", 22 | "5" 23 | ], 24 | { 25 | "Ref": "BootstrapVersion" 26 | } 27 | ] 28 | } 29 | ] 30 | }, 31 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 32 | } 33 | ] 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /test/aws-fsx/integ.ontap-file-system.ts.snapshot/FsxForOntapTestStack.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e": { 5 | "source": { 6 | "path": "asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e", 7 | "packaging": "zip" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e.zip", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | }, 17 | "0ade450d022495a5e64c0da21f36f7a666ae41cbaed960049a2def353fc588b8": { 18 | "source": { 19 | "path": "FsxForOntapTestStack.template.json", 20 | "packaging": "file" 21 | }, 22 | "destinations": { 23 | "current_account-current_region": { 24 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 25 | "objectKey": "0ade450d022495a5e64c0da21f36f7a666ae41cbaed960049a2def353fc588b8.json", 26 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 27 | } 28 | } 29 | } 30 | }, 31 | "dockerImages": {} 32 | } -------------------------------------------------------------------------------- /test/aws-fsx/integ.ontap-file-system.ts.snapshot/asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e/__entrypoint__.js: -------------------------------------------------------------------------------- 1 | "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.withRetries=exports.handler=exports.external=void 0;const https=require("https"),url=require("url");exports.external={sendHttpRequest:defaultSendHttpRequest,log:defaultLog,includeStackTraces:!0,userHandlerIndex:"./index"};const CREATE_FAILED_PHYSICAL_ID_MARKER="AWSCDK::CustomResourceProviderFramework::CREATE_FAILED",MISSING_PHYSICAL_ID_MARKER="AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID";async function handler(event,context){const sanitizedEvent={...event,ResponseURL:"..."};if(exports.external.log(JSON.stringify(sanitizedEvent,void 0,2)),event.RequestType==="Delete"&&event.PhysicalResourceId===CREATE_FAILED_PHYSICAL_ID_MARKER){exports.external.log("ignoring DELETE event caused by a failed CREATE event"),await submitResponse("SUCCESS",event);return}try{const userHandler=require(exports.external.userHandlerIndex).handler,result=await userHandler(sanitizedEvent,context),responseEvent=renderResponse(event,result);await submitResponse("SUCCESS",responseEvent)}catch(e){const resp={...event,Reason:exports.external.includeStackTraces?e.stack:e.message};resp.PhysicalResourceId||(event.RequestType==="Create"?(exports.external.log("CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored"),resp.PhysicalResourceId=CREATE_FAILED_PHYSICAL_ID_MARKER):exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`)),await submitResponse("FAILED",resp)}}exports.handler=handler;function renderResponse(cfnRequest,handlerResponse={}){const physicalResourceId=handlerResponse.PhysicalResourceId??cfnRequest.PhysicalResourceId??cfnRequest.RequestId;if(cfnRequest.RequestType==="Delete"&&physicalResourceId!==cfnRequest.PhysicalResourceId)throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`);return{...cfnRequest,...handlerResponse,PhysicalResourceId:physicalResourceId}}async function submitResponse(status,event){const json={Status:status,Reason:event.Reason??status,StackId:event.StackId,RequestId:event.RequestId,PhysicalResourceId:event.PhysicalResourceId||MISSING_PHYSICAL_ID_MARKER,LogicalResourceId:event.LogicalResourceId,NoEcho:event.NoEcho,Data:event.Data};exports.external.log("submit response to cloudformation",json);const responseBody=JSON.stringify(json),parsedUrl=url.parse(event.ResponseURL),req={hostname:parsedUrl.hostname,path:parsedUrl.path,method:"PUT",headers:{"content-type":"","content-length":Buffer.byteLength(responseBody,"utf8")}};await withRetries({attempts:5,sleep:1e3},exports.external.sendHttpRequest)(req,responseBody)}async function defaultSendHttpRequest(options,responseBody){return new Promise((resolve,reject)=>{try{const request=https.request(options,_=>resolve());request.on("error",reject),request.write(responseBody),request.end()}catch(e){reject(e)}})}function defaultLog(fmt,...params){console.log(fmt,...params)}function withRetries(options,fn){return async(...xs)=>{let attempts=options.attempts,ms=options.sleep;for(;;)try{return await fn(...xs)}catch(e){if(attempts--<=0)throw e;await sleep(Math.floor(Math.random()*ms)),ms*=2}}}exports.withRetries=withRetries;async function sleep(ms){return new Promise(ok=>setTimeout(ok,ms))} 2 | -------------------------------------------------------------------------------- /test/aws-fsx/integ.ontap-file-system.ts.snapshot/asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e/index.js: -------------------------------------------------------------------------------- 1 | "use strict";var I=Object.create,t=Object.defineProperty,y=Object.getOwnPropertyDescriptor,P=Object.getOwnPropertyNames,g=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty,G=(r,e)=>{for(var o in e)t(r,o,{get:e[o],enumerable:!0})},n=(r,e,o,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of P(e))!l.call(r,s)&&s!==o&&t(r,s,{get:()=>e[s],enumerable:!(i=y(e,s))||i.enumerable});return r},R=(r,e,o)=>(o=r!=null?I(g(r)):{},n(e||!r||!r.__esModule?t(o,"default",{value:r,enumerable:!0}):o,r)),S=r=>n(t({},"__esModule",{value:!0}),r),k={};G(k,{handler:()=>f}),module.exports=S(k);var a=R(require("@aws-sdk/client-ec2")),u=new a.EC2({});function c(r,e){return{GroupId:r,IpPermissions:[{UserIdGroupPairs:[{GroupId:r,UserId:e}],IpProtocol:"-1"}]}}function d(r){return{GroupId:r,IpPermissions:[{IpRanges:[{CidrIp:"0.0.0.0/0"}],IpProtocol:"-1"}]}}async function f(r){let e=r.ResourceProperties.DefaultSecurityGroupId,o=r.ResourceProperties.Account;switch(r.RequestType){case"Create":return p(e,o);case"Update":return h(r);case"Delete":return m(e,o)}}async function h(r){let e=r.OldResourceProperties.DefaultSecurityGroupId,o=r.ResourceProperties.DefaultSecurityGroupId;e!==o&&(await m(e,r.ResourceProperties.Account),await p(o,r.ResourceProperties.Account))}async function p(r,e){try{await u.revokeSecurityGroupEgress(d(r))}catch(o){if(o.name!=="InvalidPermission.NotFound")throw o}try{await u.revokeSecurityGroupIngress(c(r,e))}catch(o){if(o.name!=="InvalidPermission.NotFound")throw o}}async function m(r,e){await u.authorizeSecurityGroupIngress(c(r,e)),await u.authorizeSecurityGroupEgress(d(r))} 2 | -------------------------------------------------------------------------------- /test/aws-fsx/integ.ontap-file-system.ts.snapshot/cdk.out: -------------------------------------------------------------------------------- 1 | {"version":"36.0.0"} -------------------------------------------------------------------------------- /test/aws-fsx/integ.ontap-file-system.ts.snapshot/integ.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "testCases": { 4 | "FsxForOntapTest/DefaultTest": { 5 | "stacks": [ 6 | "FsxForOntapTestStack" 7 | ], 8 | "assertionStack": "FsxForOntapTest/DefaultTest/DeployAssert", 9 | "assertionStackName": "FsxForOntapTestDefaultTestDeployAssert20A933DE" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /test/aws-redshiftserverless/integ.redshift-serverless-namespace-workgroup.ts: -------------------------------------------------------------------------------- 1 | import { IntegTest } from '@aws-cdk/integ-tests-alpha'; 2 | import * as cdk from 'aws-cdk-lib'; 3 | import { Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; 4 | import { Construct } from 'constructs'; 5 | import * as ocf from '../../src'; 6 | 7 | class RedshiftServerlessStack extends cdk.Stack { 8 | public readonly workgroup: ocf.aws_redshiftserverless.Workgroup; 9 | constructor(scope: Construct, id: string, props?: cdk.StackProps) { 10 | super(scope, id, props); 11 | 12 | const vpc = new cdk.aws_ec2.Vpc(this, 'VPC', {}); 13 | 14 | const defaultRole = new Role(this, 'DefaultRole', { 15 | assumedBy: new ServicePrincipal('redshift.amazonaws.com'), 16 | }); 17 | 18 | const anotherRole = new Role(this, 'AnotherRole', { 19 | assumedBy: new ServicePrincipal('redshift.amazonaws.com'), 20 | }); 21 | 22 | const namespace = new ocf.aws_redshiftserverless.Namespace(this, 'Namespace', { 23 | adminUsername: 'adminuser', 24 | adminUserPassword: cdk.SecretValue.unsafePlainText('adminUserPassword123'), 25 | defaultIamRole: defaultRole, 26 | iamRoles: [defaultRole, anotherRole], 27 | dbName: 'mydatabase', 28 | logExports: [ 29 | ocf.aws_redshiftserverless.LogExport.USER_LOG, 30 | ocf.aws_redshiftserverless.LogExport.CONNECTION_LOG, 31 | ocf.aws_redshiftserverless.LogExport.USER_ACTIVITY_LOG, 32 | ], 33 | }); 34 | 35 | const addRole = new Role(this, 'AddRole', { 36 | assumedBy: new ServicePrincipal('redshift.amazonaws.com'), 37 | }); 38 | 39 | namespace.addIamRole(addRole); 40 | 41 | const workgroup = new ocf.aws_redshiftserverless.Workgroup(this, 'WorkGroup', { 42 | baseCapacity: 8, 43 | configParameters: { 44 | datestyle: 'ISO, MDY', 45 | enable_user_activity_logging: 'true', 46 | query_group: 'default', 47 | require_ssl: 'true', 48 | search_path: '$user, public', 49 | max_query_execution_time: '14440', 50 | }, 51 | enhancedVpcRouting: true, 52 | namespace, 53 | publiclyAccessible: true, 54 | vpc, 55 | port: 5432, 56 | }); 57 | this.workgroup = workgroup; 58 | } 59 | } 60 | 61 | const app = new cdk.App(); 62 | const env = { 63 | account: process.env.CDK_INTEG_ACCOUNT || process.env.CDK_DEFAULT_ACCOUNT, 64 | region: process.env.CDK_INTEG_REGION || process.env.CDK_DEFAULT_REGION, 65 | }; 66 | 67 | const testCase = new RedshiftServerlessStack(app, 'RedshiftServerlessNamespaceWorkgroupStack', { env }); 68 | 69 | new IntegTest(app, 'RedshiftServerlessNamespaceWorkgroupTest', { 70 | testCases: [testCase], 71 | enableLookups: true, 72 | stackUpdateWorkflow: false, 73 | }); 74 | 75 | new cdk.CfnOutput(testCase, 'endpointAddress', { value: testCase.workgroup.endpointAddress }); 76 | -------------------------------------------------------------------------------- /test/aws-redshiftserverless/integ.redshift-serverless-namespace-workgroup.ts.snapshot/RedshiftServerlessNamespaceWorkgroupStack.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e": { 5 | "source": { 6 | "path": "asset.dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e", 7 | "packaging": "zip" 8 | }, 9 | "destinations": { 10 | "12345678-test-region": { 11 | "bucketName": "cdk-hnb659fds-assets-12345678-test-region", 12 | "objectKey": "dd5711540f04e06aa955d7f4862fc04e8cdea464cb590dae91ed2976bb78098e.zip", 13 | "region": "test-region", 14 | "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-test-region" 15 | } 16 | } 17 | }, 18 | "4a8832c353aec7c387162f2dce3cb6290e83346da06b1ecbcc2f4b8a5794133d": { 19 | "source": { 20 | "path": "RedshiftServerlessNamespaceWorkgroupStack.template.json", 21 | "packaging": "file" 22 | }, 23 | "destinations": { 24 | "12345678-test-region": { 25 | "bucketName": "cdk-hnb659fds-assets-12345678-test-region", 26 | "objectKey": "4a8832c353aec7c387162f2dce3cb6290e83346da06b1ecbcc2f4b8a5794133d.json", 27 | "region": "test-region", 28 | "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-test-region" 29 | } 30 | } 31 | } 32 | }, 33 | "dockerImages": {} 34 | } -------------------------------------------------------------------------------- /test/aws-redshiftserverless/integ.redshift-serverless-namespace-workgroup.ts.snapshot/RedshiftServerlessNamespaceWorkgroupTestDefaultTestDeployAssertE2D901CB.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "36.0.0", 3 | "files": { 4 | "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { 5 | "source": { 6 | "path": "RedshiftServerlessNamespaceWorkgroupTestDefaultTestDeployAssertE2D901CB.template.json", 7 | "packaging": "file" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | } 17 | }, 18 | "dockerImages": {} 19 | } -------------------------------------------------------------------------------- /test/aws-redshiftserverless/integ.redshift-serverless-namespace-workgroup.ts.snapshot/RedshiftServerlessNamespaceWorkgroupTestDefaultTestDeployAssertE2D901CB.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Parameters": { 3 | "BootstrapVersion": { 4 | "Type": "AWS::SSM::Parameter::Value", 5 | "Default": "/cdk-bootstrap/hnb659fds/version", 6 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 7 | } 8 | }, 9 | "Rules": { 10 | "CheckBootstrapVersion": { 11 | "Assertions": [ 12 | { 13 | "Assert": { 14 | "Fn::Not": [ 15 | { 16 | "Fn::Contains": [ 17 | [ 18 | "1", 19 | "2", 20 | "3", 21 | "4", 22 | "5" 23 | ], 24 | { 25 | "Ref": "BootstrapVersion" 26 | } 27 | ] 28 | } 29 | ] 30 | }, 31 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 32 | } 33 | ] 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /test/aws-redshiftserverless/integ.redshift-serverless-namespace-workgroup.ts.snapshot/cdk.out: -------------------------------------------------------------------------------- 1 | {"version":"36.0.0"} -------------------------------------------------------------------------------- /test/aws-redshiftserverless/integ.redshift-serverless-namespace-workgroup.ts.snapshot/integ.json: -------------------------------------------------------------------------------- 1 | { 2 | "enableLookups": true, 3 | "version": "36.0.0", 4 | "testCases": { 5 | "RedshiftServerlessNamespaceWorkgroupTest/DefaultTest": { 6 | "stacks": [ 7 | "RedshiftServerlessNamespaceWorkgroupStack" 8 | ], 9 | "stackUpdateWorkflow": false, 10 | "assertionStack": "RedshiftServerlessNamespaceWorkgroupTest/DefaultTest/DeployAssert", 11 | "assertionStackName": "RedshiftServerlessNamespaceWorkgroupTestDefaultTestDeployAssertE2D901CB" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | // ~~ Generated by projen. To modify, edit .projenrc.ts 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 | "src/**/*.ts", 30 | "test/**/*.ts", 31 | ".projenrc.ts", 32 | "projenrc/**/*.ts" 33 | ], 34 | "exclude": [ 35 | "node_modules" 36 | ] 37 | } 38 | --------------------------------------------------------------------------------