├── .eslintrc.json ├── .gitattributes ├── .github ├── pull_request_template.md └── workflows │ ├── auto-approve.yml │ ├── build.yml │ ├── pull-request-lint.yml │ ├── release.yml │ └── upgrade-master.yml ├── .gitignore ├── .gitpod.yml ├── .mergify.yml ├── .npmignore ├── .projen ├── deps.json ├── files.json └── tasks.json ├── .projenrc.js ├── API.md ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src ├── index.ts └── integ.default.ts ├── test ├── __snapshots__ │ └── integ.snapshot.test.ts.snap └── integ.snapshot.test.ts ├── tsconfig.dev.json ├── version.json └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | // ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | { 3 | "env": { 4 | "jest": true, 5 | "node": true 6 | }, 7 | "root": true, 8 | "plugins": [ 9 | "@typescript-eslint", 10 | "import" 11 | ], 12 | "parser": "@typescript-eslint/parser", 13 | "parserOptions": { 14 | "ecmaVersion": 2018, 15 | "sourceType": "module", 16 | "project": "./tsconfig.dev.json" 17 | }, 18 | "extends": [ 19 | "plugin:import/typescript" 20 | ], 21 | "settings": { 22 | "import/parsers": { 23 | "@typescript-eslint/parser": [ 24 | ".ts", 25 | ".tsx" 26 | ] 27 | }, 28 | "import/resolver": { 29 | "node": {}, 30 | "typescript": { 31 | "project": "./tsconfig.dev.json", 32 | "alwaysTryTypes": true 33 | } 34 | } 35 | }, 36 | "ignorePatterns": [ 37 | "*.js", 38 | "*.d.ts", 39 | "node_modules/", 40 | "*.generated.ts", 41 | "coverage", 42 | "!.projenrc.js" 43 | ], 44 | "rules": { 45 | "indent": [ 46 | "off" 47 | ], 48 | "@typescript-eslint/indent": [ 49 | "error", 50 | 2 51 | ], 52 | "quotes": [ 53 | "error", 54 | "single", 55 | { 56 | "avoidEscape": true 57 | } 58 | ], 59 | "comma-dangle": [ 60 | "error", 61 | "always-multiline" 62 | ], 63 | "comma-spacing": [ 64 | "error", 65 | { 66 | "before": false, 67 | "after": true 68 | } 69 | ], 70 | "no-multi-spaces": [ 71 | "error", 72 | { 73 | "ignoreEOLComments": false 74 | } 75 | ], 76 | "array-bracket-spacing": [ 77 | "error", 78 | "never" 79 | ], 80 | "array-bracket-newline": [ 81 | "error", 82 | "consistent" 83 | ], 84 | "object-curly-spacing": [ 85 | "error", 86 | "always" 87 | ], 88 | "object-curly-newline": [ 89 | "error", 90 | { 91 | "multiline": true, 92 | "consistent": true 93 | } 94 | ], 95 | "object-property-newline": [ 96 | "error", 97 | { 98 | "allowAllPropertiesOnSameLine": true 99 | } 100 | ], 101 | "keyword-spacing": [ 102 | "error" 103 | ], 104 | "brace-style": [ 105 | "error", 106 | "1tbs", 107 | { 108 | "allowSingleLine": true 109 | } 110 | ], 111 | "space-before-blocks": [ 112 | "error" 113 | ], 114 | "curly": [ 115 | "error", 116 | "multi-line", 117 | "consistent" 118 | ], 119 | "@typescript-eslint/member-delimiter-style": [ 120 | "error" 121 | ], 122 | "semi": [ 123 | "error", 124 | "always" 125 | ], 126 | "max-len": [ 127 | "error", 128 | { 129 | "code": 150, 130 | "ignoreUrls": true, 131 | "ignoreStrings": true, 132 | "ignoreTemplateLiterals": true, 133 | "ignoreComments": true, 134 | "ignoreRegExpLiterals": true 135 | } 136 | ], 137 | "quote-props": [ 138 | "error", 139 | "consistent-as-needed" 140 | ], 141 | "@typescript-eslint/no-require-imports": [ 142 | "error" 143 | ], 144 | "import/no-extraneous-dependencies": [ 145 | "error", 146 | { 147 | "devDependencies": [ 148 | "**/test/**", 149 | "**/build-tools/**" 150 | ], 151 | "optionalDependencies": false, 152 | "peerDependencies": true 153 | } 154 | ], 155 | "import/no-unresolved": [ 156 | "error" 157 | ], 158 | "import/order": [ 159 | "warn", 160 | { 161 | "groups": [ 162 | "builtin", 163 | "external" 164 | ], 165 | "alphabetize": { 166 | "order": "asc", 167 | "caseInsensitive": true 168 | } 169 | } 170 | ], 171 | "import/no-duplicates": [ 172 | "error" 173 | ], 174 | "no-shadow": [ 175 | "off" 176 | ], 177 | "@typescript-eslint/no-shadow": [ 178 | "error" 179 | ], 180 | "key-spacing": [ 181 | "error" 182 | ], 183 | "no-multiple-empty-lines": [ 184 | "error" 185 | ], 186 | "@typescript-eslint/no-floating-promises": [ 187 | "error" 188 | ], 189 | "no-return-await": [ 190 | "off" 191 | ], 192 | "@typescript-eslint/return-await": [ 193 | "error" 194 | ], 195 | "no-trailing-spaces": [ 196 | "error" 197 | ], 198 | "dot-notation": [ 199 | "error" 200 | ], 201 | "no-bitwise": [ 202 | "error" 203 | ], 204 | "@typescript-eslint/member-ordering": [ 205 | "error", 206 | { 207 | "default": [ 208 | "public-static-field", 209 | "public-static-method", 210 | "protected-static-field", 211 | "protected-static-method", 212 | "private-static-field", 213 | "private-static-method", 214 | "field", 215 | "constructor", 216 | "method" 217 | ] 218 | } 219 | ] 220 | }, 221 | "overrides": [ 222 | { 223 | "files": [ 224 | ".projenrc.js" 225 | ], 226 | "rules": { 227 | "@typescript-eslint/no-require-imports": "off", 228 | "import/no-extraneous-dependencies": "off" 229 | } 230 | } 231 | ] 232 | } 233 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | * 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/auto-approve.yml linguist-generated 9 | /.github/workflows/build.yml linguist-generated 10 | /.github/workflows/pull-request-lint.yml linguist-generated 11 | /.github/workflows/release.yml linguist-generated 12 | /.github/workflows/upgrade-master.yml linguist-generated 13 | /.gitignore linguist-generated 14 | /.gitpod.yml linguist-generated 15 | /.mergify.yml linguist-generated 16 | /.npmignore linguist-generated 17 | /.projen/** linguist-generated 18 | /.projen/deps.json linguist-generated 19 | /.projen/files.json linguist-generated 20 | /.projen/tasks.json linguist-generated 21 | /API.md linguist-generated 22 | /LICENSE linguist-generated 23 | /package.json linguist-generated 24 | /tsconfig.dev.json linguist-generated 25 | /yarn.lock linguist-generated -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Fixes # -------------------------------------------------------------------------------- /.github/workflows/auto-approve.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: auto-approve 4 | on: 5 | pull_request_target: 6 | types: 7 | - labeled 8 | - opened 9 | - synchronize 10 | - reopened 11 | - ready_for_review 12 | jobs: 13 | approve: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | pull-requests: write 17 | if: contains(github.event.pull_request.labels.*.name, 'auto-approve') && (github.event.pull_request.user.login == 'clarencetw') 18 | steps: 19 | - uses: hmarr/auto-approve-action@v2.2.1 20 | with: 21 | github-token: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: build 4 | on: 5 | pull_request: {} 6 | workflow_dispatch: {} 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: write 12 | outputs: 13 | self_mutation_happened: ${{ steps.self_mutation.outputs.self_mutation_happened }} 14 | env: 15 | CI: "true" 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@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: yarn install --check-files 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 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 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: Checkout 66 | uses: actions/checkout@v4 67 | with: 68 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 69 | ref: ${{ github.event.pull_request.head.ref }} 70 | repository: ${{ github.event.pull_request.head.repo.full_name }} 71 | - name: Download patch 72 | uses: actions/download-artifact@v4 73 | with: 74 | name: .repo.patch 75 | path: ${{ runner.temp }} 76 | - name: Apply patch 77 | run: '[ -s ${{ runner.temp }}/.repo.patch ] && git apply ${{ runner.temp }}/.repo.patch || echo "Empty patch. Skipping."' 78 | - name: Set git identity 79 | run: |- 80 | git config user.name "github-actions" 81 | git config user.email "github-actions@github.com" 82 | - name: Push changes 83 | env: 84 | PULL_REQUEST_REF: ${{ github.event.pull_request.head.ref }} 85 | run: |- 86 | git add . 87 | git commit -s -m "chore: self mutation" 88 | git push origin HEAD:$PULL_REQUEST_REF 89 | package-js: 90 | needs: build 91 | runs-on: ubuntu-latest 92 | permissions: {} 93 | if: "! needs.build.outputs.self_mutation_happened" 94 | steps: 95 | - uses: actions/setup-node@v4 96 | with: 97 | node-version: 18.x 98 | - name: Download build artifacts 99 | uses: actions/download-artifact@v4 100 | with: 101 | name: build-artifact 102 | path: dist 103 | - name: Restore build artifact permissions 104 | run: cd dist && setfacl --restore=permissions-backup.acl 105 | continue-on-error: true 106 | - name: Prepare Repository 107 | run: mv dist .repo 108 | - name: Install Dependencies 109 | run: cd .repo && yarn install --check-files --frozen-lockfile 110 | - name: Create js artifact 111 | run: cd .repo && npx projen package:js 112 | - name: Collect js Artifact 113 | run: mv .repo/dist dist 114 | package-python: 115 | needs: build 116 | runs-on: ubuntu-latest 117 | permissions: {} 118 | if: "! needs.build.outputs.self_mutation_happened" 119 | steps: 120 | - uses: actions/setup-node@v4 121 | with: 122 | node-version: 18.x 123 | - uses: actions/setup-python@v5 124 | with: 125 | python-version: 3.x 126 | - name: Download build artifacts 127 | uses: actions/download-artifact@v4 128 | with: 129 | name: build-artifact 130 | path: dist 131 | - name: Restore build artifact permissions 132 | run: cd dist && setfacl --restore=permissions-backup.acl 133 | continue-on-error: true 134 | - name: Prepare Repository 135 | run: mv dist .repo 136 | - name: Install Dependencies 137 | run: cd .repo && yarn install --check-files --frozen-lockfile 138 | - name: Create python artifact 139 | run: cd .repo && npx projen package:python 140 | - name: Collect python Artifact 141 | run: mv .repo/dist dist 142 | -------------------------------------------------------------------------------- /.github/workflows/pull-request-lint.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: pull-request-lint 4 | on: 5 | pull_request_target: 6 | types: 7 | - labeled 8 | - opened 9 | - synchronize 10 | - reopened 11 | - ready_for_review 12 | - edited 13 | jobs: 14 | validate: 15 | name: Validate PR title 16 | runs-on: ubuntu-latest 17 | permissions: 18 | pull-requests: write 19 | steps: 20 | - uses: amannn/action-semantic-pull-request@v5.4.0 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | with: 24 | types: |- 25 | feat 26 | fix 27 | chore 28 | requireScope: false 29 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: release 4 | on: 5 | push: 6 | branches: 7 | - master 8 | workflow_dispatch: {} 9 | jobs: 10 | release: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: write 14 | outputs: 15 | latest_commit: ${{ steps.git_remote.outputs.latest_commit }} 16 | tag_exists: ${{ steps.check_tag_exists.outputs.exists }} 17 | env: 18 | CI: "true" 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | with: 23 | fetch-depth: 0 24 | - name: Set git identity 25 | run: |- 26 | git config user.name "github-actions" 27 | git config user.email "github-actions@github.com" 28 | - name: Setup Node.js 29 | uses: actions/setup-node@v4 30 | with: 31 | node-version: 18.x 32 | - name: Install dependencies 33 | run: yarn install --check-files --frozen-lockfile 34 | - name: release 35 | run: npx projen release 36 | - name: Check if version has already been tagged 37 | id: check_tag_exists 38 | run: |- 39 | TAG=$(cat dist/dist/releasetag.txt) 40 | ([ ! -z "$TAG" ] && git ls-remote -q --exit-code --tags origin $TAG && (echo "exists=true" >> $GITHUB_OUTPUT)) || (echo "exists=false" >> $GITHUB_OUTPUT) 41 | cat $GITHUB_OUTPUT 42 | - name: Check for new commits 43 | id: git_remote 44 | run: |- 45 | echo "latest_commit=$(git ls-remote origin -h ${{ github.ref }} | cut -f1)" >> $GITHUB_OUTPUT 46 | cat $GITHUB_OUTPUT 47 | - name: Backup artifact permissions 48 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 49 | run: cd dist && getfacl -R . > permissions-backup.acl 50 | continue-on-error: true 51 | - name: Upload artifact 52 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 53 | uses: actions/upload-artifact@v4 54 | with: 55 | name: build-artifact 56 | path: dist 57 | overwrite: true 58 | release_github: 59 | name: Publish to GitHub Releases 60 | needs: 61 | - release 62 | - release_npm 63 | - release_pypi 64 | runs-on: ubuntu-latest 65 | permissions: 66 | contents: write 67 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 68 | steps: 69 | - uses: actions/setup-node@v4 70 | with: 71 | node-version: 18.x 72 | - name: Download build artifacts 73 | uses: actions/download-artifact@v4 74 | with: 75 | name: build-artifact 76 | path: dist 77 | - name: Restore build artifact permissions 78 | run: cd dist && setfacl --restore=permissions-backup.acl 79 | continue-on-error: true 80 | - name: Prepare Repository 81 | run: mv dist .repo 82 | - name: Collect GitHub Metadata 83 | run: mv .repo/dist dist 84 | - name: Release 85 | env: 86 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 87 | GITHUB_REPOSITORY: ${{ github.repository }} 88 | GITHUB_REF: ${{ github.sha }} 89 | 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 90 | release_npm: 91 | name: Publish to npm 92 | needs: release 93 | runs-on: ubuntu-latest 94 | permissions: 95 | id-token: write 96 | contents: read 97 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 98 | steps: 99 | - uses: actions/setup-node@v4 100 | with: 101 | node-version: 18.x 102 | - name: Download build artifacts 103 | uses: actions/download-artifact@v4 104 | with: 105 | name: build-artifact 106 | path: dist 107 | - name: Restore build artifact permissions 108 | run: cd dist && setfacl --restore=permissions-backup.acl 109 | continue-on-error: true 110 | - name: Prepare Repository 111 | run: mv dist .repo 112 | - name: Install Dependencies 113 | run: cd .repo && yarn install --check-files --frozen-lockfile 114 | - name: Create js artifact 115 | run: cd .repo && npx projen package:js 116 | - name: Collect js Artifact 117 | run: mv .repo/dist dist 118 | - name: Release 119 | env: 120 | NPM_DIST_TAG: latest 121 | NPM_REGISTRY: registry.npmjs.org 122 | NPM_CONFIG_PROVENANCE: "true" 123 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 124 | run: npx -p publib@latest publib-npm 125 | release_pypi: 126 | name: Publish to PyPI 127 | needs: release 128 | runs-on: ubuntu-latest 129 | permissions: 130 | contents: read 131 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 132 | steps: 133 | - uses: actions/setup-node@v4 134 | with: 135 | node-version: 18.x 136 | - uses: actions/setup-python@v5 137 | with: 138 | python-version: 3.x 139 | - name: Download build artifacts 140 | uses: actions/download-artifact@v4 141 | with: 142 | name: build-artifact 143 | path: dist 144 | - name: Restore build artifact permissions 145 | run: cd dist && setfacl --restore=permissions-backup.acl 146 | continue-on-error: true 147 | - name: Prepare Repository 148 | run: mv dist .repo 149 | - name: Install Dependencies 150 | run: cd .repo && yarn install --check-files --frozen-lockfile 151 | - name: Create python artifact 152 | run: cd .repo && npx projen package:python 153 | - name: Collect python Artifact 154 | run: mv .repo/dist dist 155 | - name: Release 156 | env: 157 | TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} 158 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} 159 | run: npx -p publib@latest publib-pypi 160 | -------------------------------------------------------------------------------- /.github/workflows/upgrade-master.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: upgrade-master 4 | on: 5 | workflow_dispatch: {} 6 | schedule: 7 | - cron: 0 0 * * * 8 | jobs: 9 | upgrade: 10 | name: Upgrade 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: read 14 | outputs: 15 | patch_created: ${{ steps.create_patch.outputs.patch_created }} 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | with: 20 | ref: master 21 | - name: Setup Node.js 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: 18.x 25 | - name: Install dependencies 26 | run: yarn install --check-files --frozen-lockfile 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 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: Checkout 51 | uses: actions/checkout@v4 52 | with: 53 | ref: master 54 | - name: Download patch 55 | uses: actions/download-artifact@v4 56 | with: 57 | name: .repo.patch 58 | path: ${{ runner.temp }} 59 | - name: Apply patch 60 | run: '[ -s ${{ runner.temp }}/.repo.patch ] && git apply ${{ runner.temp }}/.repo.patch || echo "Empty patch. Skipping."' 61 | - name: Set git identity 62 | run: |- 63 | git config user.name "github-actions" 64 | git config user.email "github-actions@github.com" 65 | - name: Create Pull Request 66 | id: create-pr 67 | uses: peter-evans/create-pull-request@v6 68 | with: 69 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 70 | commit-message: |- 71 | chore(deps): upgrade dependencies 72 | 73 | Upgrades project dependencies. See details in [workflow run]. 74 | 75 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 76 | 77 | ------ 78 | 79 | *Automatically created by projen via the "upgrade-master" workflow* 80 | branch: github-actions/upgrade-master 81 | title: "chore(deps): upgrade dependencies" 82 | labels: auto-approve 83 | body: |- 84 | Upgrades project dependencies. See details in [workflow run]. 85 | 86 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 87 | 88 | ------ 89 | 90 | *Automatically created by projen via the "upgrade-master" workflow* 91 | author: github-actions 92 | committer: github-actions 93 | signoff: true 94 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | !/.gitattributes 3 | !/.projen/tasks.json 4 | !/.projen/deps.json 5 | !/.projen/files.json 6 | !/.github/workflows/pull-request-lint.yml 7 | !/.github/workflows/auto-approve.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-master.yml 43 | !/.github/pull_request_template.md 44 | !/test/ 45 | !/tsconfig.dev.json 46 | !/src/ 47 | /lib 48 | /dist/ 49 | !/.eslintrc.json 50 | .jsii 51 | tsconfig.json 52 | !/API.md 53 | !/.gitpod.yml 54 | cdk.out 55 | cdk.context.json 56 | yarn-error.log 57 | !/.projenrc.js 58 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | tasks: 4 | - command: npx projen upgrade 5 | init: yarn install && yarn run build 6 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | queue_rules: 4 | - name: default 5 | update_method: merge 6 | conditions: 7 | - "#approved-reviews-by>=1" 8 | - -label~=(do-not-merge) 9 | - status-success=build 10 | - status-success=package-js 11 | - status-success=package-python 12 | pull_request_rules: 13 | - name: Automatic merge on approval and successful build 14 | actions: 15 | delete_head_branch: {} 16 | queue: 17 | method: squash 18 | name: default 19 | commit_message_template: |- 20 | {{ title }} (#{{ number }}) 21 | 22 | {{ body }} 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.js and run "npx projen". 2 | /.projen/ 3 | /test-reports/ 4 | junit.xml 5 | /coverage/ 6 | permissions-backup.acl 7 | /dist/changelog.md 8 | /dist/version.txt 9 | /.mergify.yml 10 | /test/ 11 | /tsconfig.dev.json 12 | /src/ 13 | !/lib/ 14 | !/lib/**/*.js 15 | !/lib/**/*.d.ts 16 | dist 17 | /tsconfig.json 18 | /.github/ 19 | /.vscode/ 20 | /.idea/ 21 | /.projenrc.js 22 | tsconfig.tsbuildinfo 23 | /.eslintrc.json 24 | !.jsii 25 | cdk.out 26 | cdk.context.json 27 | yarn-error.log 28 | /.gitattributes 29 | -------------------------------------------------------------------------------- /.projen/deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | { 4 | "name": "@types/jest", 5 | "version": "^27", 6 | "type": "build" 7 | }, 8 | { 9 | "name": "@types/node", 10 | "version": "^16 <= 16.18.78", 11 | "type": "build" 12 | }, 13 | { 14 | "name": "@typescript-eslint/eslint-plugin", 15 | "version": "^7", 16 | "type": "build" 17 | }, 18 | { 19 | "name": "@typescript-eslint/parser", 20 | "version": "^7", 21 | "type": "build" 22 | }, 23 | { 24 | "name": "commit-and-tag-version", 25 | "version": "^12", 26 | "type": "build" 27 | }, 28 | { 29 | "name": "eslint-import-resolver-typescript", 30 | "type": "build" 31 | }, 32 | { 33 | "name": "eslint-plugin-import", 34 | "type": "build" 35 | }, 36 | { 37 | "name": "eslint", 38 | "version": "^8", 39 | "type": "build" 40 | }, 41 | { 42 | "name": "jest-junit", 43 | "version": "^15", 44 | "type": "build" 45 | }, 46 | { 47 | "name": "jest", 48 | "version": "^27", 49 | "type": "build" 50 | }, 51 | { 52 | "name": "jsii-diff", 53 | "type": "build" 54 | }, 55 | { 56 | "name": "jsii-docgen", 57 | "version": "^10.5.0", 58 | "type": "build" 59 | }, 60 | { 61 | "name": "jsii-pacmak", 62 | "type": "build" 63 | }, 64 | { 65 | "name": "jsii-rosetta", 66 | "version": "1.x", 67 | "type": "build" 68 | }, 69 | { 70 | "name": "jsii", 71 | "version": "1.x", 72 | "type": "build" 73 | }, 74 | { 75 | "name": "projen", 76 | "type": "build" 77 | }, 78 | { 79 | "name": "ts-jest", 80 | "version": "^27", 81 | "type": "build" 82 | }, 83 | { 84 | "name": "typescript", 85 | "type": "build" 86 | }, 87 | { 88 | "name": "@types/babel__traverse", 89 | "version": "7.18.2", 90 | "type": "override" 91 | }, 92 | { 93 | "name": "@types/prettier", 94 | "version": "2.6.0", 95 | "type": "override" 96 | }, 97 | { 98 | "name": "@aws-cdk/aws-ec2", 99 | "version": "^1.71.0", 100 | "type": "peer" 101 | }, 102 | { 103 | "name": "@aws-cdk/aws-ecs-patterns", 104 | "version": "^1.71.0", 105 | "type": "peer" 106 | }, 107 | { 108 | "name": "@aws-cdk/aws-ecs", 109 | "version": "^1.71.0", 110 | "type": "peer" 111 | }, 112 | { 113 | "name": "@aws-cdk/aws-efs", 114 | "version": "^1.71.0", 115 | "type": "peer" 116 | }, 117 | { 118 | "name": "@aws-cdk/aws-rds", 119 | "version": "^1.71.0", 120 | "type": "peer" 121 | }, 122 | { 123 | "name": "@aws-cdk/core", 124 | "version": "^1.71.0", 125 | "type": "peer" 126 | }, 127 | { 128 | "name": "constructs", 129 | "version": "^3.2.27", 130 | "type": "peer" 131 | }, 132 | { 133 | "name": "@aws-cdk/aws-ec2", 134 | "version": "^1.71.0", 135 | "type": "runtime" 136 | }, 137 | { 138 | "name": "@aws-cdk/aws-ecs-patterns", 139 | "version": "^1.71.0", 140 | "type": "runtime" 141 | }, 142 | { 143 | "name": "@aws-cdk/aws-ecs", 144 | "version": "^1.71.0", 145 | "type": "runtime" 146 | }, 147 | { 148 | "name": "@aws-cdk/aws-efs", 149 | "version": "^1.71.0", 150 | "type": "runtime" 151 | }, 152 | { 153 | "name": "@aws-cdk/aws-rds", 154 | "version": "^1.71.0", 155 | "type": "runtime" 156 | }, 157 | { 158 | "name": "@aws-cdk/core", 159 | "version": "^1.71.0", 160 | "type": "runtime" 161 | }, 162 | { 163 | "name": "@aws-cdk/assert", 164 | "version": "^1.71.0", 165 | "type": "test" 166 | } 167 | ], 168 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 169 | } 170 | -------------------------------------------------------------------------------- /.projen/files.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | ".eslintrc.json", 4 | ".gitattributes", 5 | ".github/pull_request_template.md", 6 | ".github/workflows/auto-approve.yml", 7 | ".github/workflows/build.yml", 8 | ".github/workflows/pull-request-lint.yml", 9 | ".github/workflows/release.yml", 10 | ".github/workflows/upgrade-master.yml", 11 | ".gitignore", 12 | ".gitpod.yml", 13 | ".mergify.yml", 14 | ".projen/deps.json", 15 | ".projen/files.json", 16 | ".projen/tasks.json", 17 | "LICENSE", 18 | "tsconfig.dev.json" 19 | ], 20 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 21 | } 22 | -------------------------------------------------------------------------------- /.projen/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "build": { 4 | "name": "build", 5 | "description": "Full release build", 6 | "steps": [ 7 | { 8 | "spawn": "default" 9 | }, 10 | { 11 | "spawn": "pre-compile" 12 | }, 13 | { 14 | "spawn": "compile" 15 | }, 16 | { 17 | "spawn": "post-compile" 18 | }, 19 | { 20 | "spawn": "test" 21 | }, 22 | { 23 | "spawn": "package" 24 | } 25 | ] 26 | }, 27 | "bump": { 28 | "name": "bump", 29 | "description": "Bumps version based on latest git tag and generates a changelog entry", 30 | "env": { 31 | "OUTFILE": "package.json", 32 | "CHANGELOG": "dist/changelog.md", 33 | "BUMPFILE": "dist/version.txt", 34 | "RELEASETAG": "dist/releasetag.txt", 35 | "RELEASE_TAG_PREFIX": "", 36 | "BUMP_PACKAGE": "commit-and-tag-version@^12" 37 | }, 38 | "steps": [ 39 | { 40 | "builtin": "release/bump-version" 41 | } 42 | ], 43 | "condition": "git log --oneline -1 | grep -qv \"chore(release):\"" 44 | }, 45 | "clobber": { 46 | "name": "clobber", 47 | "description": "hard resets to HEAD of origin and cleans the local repo", 48 | "env": { 49 | "BRANCH": "$(git branch --show-current)" 50 | }, 51 | "steps": [ 52 | { 53 | "exec": "git checkout -b scratch", 54 | "name": "save current HEAD in \"scratch\" branch" 55 | }, 56 | { 57 | "exec": "git checkout $BRANCH" 58 | }, 59 | { 60 | "exec": "git fetch origin", 61 | "name": "fetch latest changes from origin" 62 | }, 63 | { 64 | "exec": "git reset --hard origin/$BRANCH", 65 | "name": "hard reset to origin commit" 66 | }, 67 | { 68 | "exec": "git clean -fdx", 69 | "name": "clean all untracked files" 70 | }, 71 | { 72 | "say": "ready to rock! (unpushed commits are under the \"scratch\" branch)" 73 | } 74 | ], 75 | "condition": "git diff --exit-code > /dev/null" 76 | }, 77 | "compat": { 78 | "name": "compat", 79 | "description": "Perform API compatibility check against latest version", 80 | "steps": [ 81 | { 82 | "exec": "jsii-diff npm:$(node -p \"require('./package.json').name\") -k --ignore-file .compatignore || (echo \"\nUNEXPECTED BREAKING CHANGES: add keys such as 'removed:constructs.Node.of' to .compatignore to skip.\n\" && exit 1)" 83 | } 84 | ] 85 | }, 86 | "compile": { 87 | "name": "compile", 88 | "description": "Only compile", 89 | "steps": [ 90 | { 91 | "exec": "jsii --silence-warnings=reserved-word" 92 | } 93 | ] 94 | }, 95 | "default": { 96 | "name": "default", 97 | "description": "Synthesize project files", 98 | "steps": [ 99 | { 100 | "exec": "node .projenrc.js" 101 | } 102 | ] 103 | }, 104 | "docgen": { 105 | "name": "docgen", 106 | "description": "Generate API.md from .jsii manifest", 107 | "steps": [ 108 | { 109 | "exec": "jsii-docgen -o API.md" 110 | } 111 | ] 112 | }, 113 | "eject": { 114 | "name": "eject", 115 | "description": "Remove projen from the project", 116 | "env": { 117 | "PROJEN_EJECTING": "true" 118 | }, 119 | "steps": [ 120 | { 121 | "spawn": "default" 122 | } 123 | ] 124 | }, 125 | "eslint": { 126 | "name": "eslint", 127 | "description": "Runs eslint against the codebase", 128 | "steps": [ 129 | { 130 | "exec": "eslint --ext .ts,.tsx --fix --no-error-on-unmatched-pattern $@ src test build-tools .projenrc.js", 131 | "receiveArgs": true 132 | } 133 | ] 134 | }, 135 | "install": { 136 | "name": "install", 137 | "description": "Install project dependencies and update lockfile (non-frozen)", 138 | "steps": [ 139 | { 140 | "exec": "yarn install --check-files" 141 | } 142 | ] 143 | }, 144 | "install:ci": { 145 | "name": "install:ci", 146 | "description": "Install project dependencies using frozen lockfile", 147 | "steps": [ 148 | { 149 | "exec": "yarn install --check-files --frozen-lockfile" 150 | } 151 | ] 152 | }, 153 | "package": { 154 | "name": "package", 155 | "description": "Creates the distribution package", 156 | "steps": [ 157 | { 158 | "exec": "rsync -a . .repo --exclude .git --exclude node_modules && rm -rf dist && mv .repo dist", 159 | "condition": "node -e \"if (!process.env.CI) process.exit(1)\"" 160 | }, 161 | { 162 | "spawn": "package-all", 163 | "condition": "node -e \"if (process.env.CI) process.exit(1)\"" 164 | } 165 | ] 166 | }, 167 | "package-all": { 168 | "name": "package-all", 169 | "description": "Packages artifacts for all target languages", 170 | "steps": [ 171 | { 172 | "spawn": "package:js" 173 | }, 174 | { 175 | "spawn": "package:python" 176 | } 177 | ] 178 | }, 179 | "package:js": { 180 | "name": "package:js", 181 | "description": "Create js language bindings", 182 | "steps": [ 183 | { 184 | "exec": "jsii-pacmak -v --target js" 185 | } 186 | ] 187 | }, 188 | "package:python": { 189 | "name": "package:python", 190 | "description": "Create python language bindings", 191 | "steps": [ 192 | { 193 | "exec": "jsii-pacmak -v --target python" 194 | } 195 | ] 196 | }, 197 | "post-compile": { 198 | "name": "post-compile", 199 | "description": "Runs after successful compilation", 200 | "steps": [ 201 | { 202 | "spawn": "docgen" 203 | } 204 | ] 205 | }, 206 | "post-upgrade": { 207 | "name": "post-upgrade", 208 | "description": "Runs after upgrading dependencies" 209 | }, 210 | "pre-compile": { 211 | "name": "pre-compile", 212 | "description": "Prepare the project for compilation" 213 | }, 214 | "release": { 215 | "name": "release", 216 | "description": "Prepare a release from \"master\" branch", 217 | "env": { 218 | "RELEASE": "true" 219 | }, 220 | "steps": [ 221 | { 222 | "exec": "rm -fr dist" 223 | }, 224 | { 225 | "spawn": "bump" 226 | }, 227 | { 228 | "spawn": "build" 229 | }, 230 | { 231 | "spawn": "unbump" 232 | }, 233 | { 234 | "exec": "git diff --ignore-space-at-eol --exit-code" 235 | } 236 | ] 237 | }, 238 | "test": { 239 | "name": "test", 240 | "description": "Run tests", 241 | "steps": [ 242 | { 243 | "exec": "jest --passWithNoTests --updateSnapshot", 244 | "receiveArgs": true 245 | }, 246 | { 247 | "spawn": "eslint" 248 | } 249 | ] 250 | }, 251 | "test:watch": { 252 | "name": "test:watch", 253 | "description": "Run jest in watch mode", 254 | "steps": [ 255 | { 256 | "exec": "jest --watch" 257 | } 258 | ] 259 | }, 260 | "unbump": { 261 | "name": "unbump", 262 | "description": "Restores version to 0.0.0", 263 | "env": { 264 | "OUTFILE": "package.json", 265 | "CHANGELOG": "dist/changelog.md", 266 | "BUMPFILE": "dist/version.txt", 267 | "RELEASETAG": "dist/releasetag.txt", 268 | "RELEASE_TAG_PREFIX": "", 269 | "BUMP_PACKAGE": "commit-and-tag-version@^12" 270 | }, 271 | "steps": [ 272 | { 273 | "builtin": "release/reset-version" 274 | } 275 | ] 276 | }, 277 | "upgrade": { 278 | "name": "upgrade", 279 | "description": "upgrade dependencies", 280 | "env": { 281 | "CI": "0" 282 | }, 283 | "steps": [ 284 | { 285 | "exec": "npx npm-check-updates@16 --upgrade --target=minor --peer --dep=dev,peer,prod,optional --filter=eslint-import-resolver-typescript,eslint-plugin-import,jsii-diff,jsii-pacmak,projen,typescript" 286 | }, 287 | { 288 | "exec": "yarn install --check-files" 289 | }, 290 | { 291 | "exec": "yarn upgrade @types/jest @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser commit-and-tag-version eslint-import-resolver-typescript eslint-plugin-import eslint jest-junit jest jsii-diff jsii-docgen jsii-pacmak jsii-rosetta jsii projen ts-jest typescript @aws-cdk/aws-ec2 @aws-cdk/aws-ecs-patterns @aws-cdk/aws-ecs @aws-cdk/aws-efs @aws-cdk/aws-rds @aws-cdk/core constructs @aws-cdk/assert" 292 | }, 293 | { 294 | "exec": "npx projen" 295 | }, 296 | { 297 | "spawn": "post-upgrade" 298 | } 299 | ] 300 | }, 301 | "watch": { 302 | "name": "watch", 303 | "description": "Watch & compile in the background", 304 | "steps": [ 305 | { 306 | "exec": "jsii -w --silence-warnings=reserved-word" 307 | } 308 | ] 309 | } 310 | }, 311 | "env": { 312 | "PATH": "$(npx -c \"node --print process.env.PATH\")" 313 | }, 314 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 315 | } 316 | -------------------------------------------------------------------------------- /.projenrc.js: -------------------------------------------------------------------------------- 1 | const { awscdk, Gitpod } = require('projen'); 2 | 3 | const AUTOMATION_TOKEN = 'PROJEN_GITHUB_TOKEN'; 4 | 5 | const project = new awscdk.AwsCdkConstructLibrary({ 6 | authorAddress: 'mr.lin.clarence@gmail.com', 7 | authorName: 'Clarence Lin', 8 | cdkVersion: '1.71.0', 9 | name: 'cdk-wordpress', 10 | repository: 'https://github.com/clarencetw/cdk-wordpress.git', 11 | keywords: ['aws', 'cdk', 'wordpress'], 12 | defaultReleaseBranch: 'master', 13 | autoApproveOptions: { 14 | secret: 'GITHUB_TOKEN', 15 | allowedUsernames: ['clarencetw'], 16 | }, 17 | autoApproveUpgrades: true, 18 | catalog: { 19 | twitter: 'Clarence_Lin', 20 | announce: false, 21 | }, 22 | python: { 23 | distName: 'cdk-wordpress', 24 | module: 'cdk_wordpress', 25 | }, 26 | cdkDependencies: [ 27 | '@aws-cdk/core', 28 | '@aws-cdk/aws-ec2', 29 | '@aws-cdk/aws-rds', 30 | '@aws-cdk/aws-ecs', 31 | '@aws-cdk/aws-ecs-patterns', 32 | '@aws-cdk/aws-efs', 33 | ], 34 | }); 35 | 36 | const gitpod = new Gitpod(project); 37 | gitpod.addCustomTask({ 38 | init: 'yarn install && yarn run build', 39 | command: 'npx projen upgrade', 40 | }); 41 | 42 | const common_exclude = ['cdk.out', 'cdk.context.json', 'yarn-error.log']; 43 | project.npmignore.exclude(...common_exclude); 44 | project.gitignore.exclude(...common_exclude); 45 | 46 | project.synth(); 47 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | # API Reference 2 | 3 | ## Constructs 4 | 5 | ### WordPress 6 | 7 | #### Initializers 8 | 9 | ```typescript 10 | import { WordPress } from 'cdk-wordpress' 11 | 12 | new WordPress(scope: Construct, id: string, props?: WordPressProps) 13 | ``` 14 | 15 | | **Name** | **Type** | **Description** | 16 | | --- | --- | --- | 17 | | scope | @aws-cdk/core.Construct | *No description.* | 18 | | id | string | *No description.* | 19 | | props | WordPressProps | *No description.* | 20 | 21 | --- 22 | 23 | ##### `scope`Required 24 | 25 | - *Type:* @aws-cdk/core.Construct 26 | 27 | --- 28 | 29 | ##### `id`Required 30 | 31 | - *Type:* string 32 | 33 | --- 34 | 35 | ##### `props`Optional 36 | 37 | - *Type:* WordPressProps 38 | 39 | --- 40 | 41 | #### Methods 42 | 43 | | **Name** | **Description** | 44 | | --- | --- | 45 | | toString | Returns a string representation of this construct. | 46 | 47 | --- 48 | 49 | ##### `toString` 50 | 51 | ```typescript 52 | public toString(): string 53 | ``` 54 | 55 | Returns a string representation of this construct. 56 | 57 | #### Static Functions 58 | 59 | | **Name** | **Description** | 60 | | --- | --- | 61 | | isConstruct | Return whether the given object is a Construct. | 62 | 63 | --- 64 | 65 | ##### `isConstruct` 66 | 67 | ```typescript 68 | import { WordPress } from 'cdk-wordpress' 69 | 70 | WordPress.isConstruct(x: any) 71 | ``` 72 | 73 | Return whether the given object is a Construct. 74 | 75 | ###### `x`Required 76 | 77 | - *Type:* any 78 | 79 | --- 80 | 81 | #### Properties 82 | 83 | | **Name** | **Type** | **Description** | 84 | | --- | --- | --- | 85 | | node | @aws-cdk/core.ConstructNode | The construct tree node associated with this construct. | 86 | | endpoint | string | *No description.* | 87 | 88 | --- 89 | 90 | ##### `node`Required 91 | 92 | ```typescript 93 | public readonly node: ConstructNode; 94 | ``` 95 | 96 | - *Type:* @aws-cdk/core.ConstructNode 97 | 98 | The construct tree node associated with this construct. 99 | 100 | --- 101 | 102 | ##### `endpoint`Required 103 | 104 | ```typescript 105 | public readonly endpoint: string; 106 | ``` 107 | 108 | - *Type:* string 109 | 110 | --- 111 | 112 | 113 | ## Structs 114 | 115 | ### WordPressProps 116 | 117 | The interface for all wordpress. 118 | 119 | #### Initializer 120 | 121 | ```typescript 122 | import { WordPressProps } from 'cdk-wordpress' 123 | 124 | const wordPressProps: WordPressProps = { ... } 125 | ``` 126 | 127 | #### Properties 128 | 129 | | **Name** | **Type** | **Description** | 130 | | --- | --- | --- | 131 | | cluster | @aws-cdk/aws-ecs.Cluster | The WordPress cluster. | 132 | | rdsInstance | @aws-cdk/aws-rds.DatabaseInstance | The WordPress RDS. | 133 | | vpc | @aws-cdk/aws-ec2.IVpc | The WordPress VPC. | 134 | 135 | --- 136 | 137 | ##### `cluster`Optional 138 | 139 | ```typescript 140 | public readonly cluster: Cluster; 141 | ``` 142 | 143 | - *Type:* @aws-cdk/aws-ecs.Cluster 144 | 145 | The WordPress cluster. 146 | 147 | --- 148 | 149 | ##### `rdsInstance`Optional 150 | 151 | ```typescript 152 | public readonly rdsInstance: DatabaseInstance; 153 | ``` 154 | 155 | - *Type:* @aws-cdk/aws-rds.DatabaseInstance 156 | 157 | The WordPress RDS. 158 | 159 | --- 160 | 161 | ##### `vpc`Optional 162 | 163 | ```typescript 164 | public readonly vpc: IVpc; 165 | ``` 166 | 167 | - *Type:* @aws-cdk/aws-ec2.IVpc 168 | 169 | The WordPress VPC. 170 | 171 | --- 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [0.0.6](https://github.com/clarencetw/cdk-wordpress/compare/v0.0.5...v0.0.6) (2020-10-31) 6 | 7 | ### [0.0.5](https://github.com/clarencetw/cdk-wordpress/compare/v0.0.4...v0.0.5) (2020-10-31) 8 | 9 | ### [0.0.4](https://github.com/clarencetw/cdk-wordpress/compare/v0.0.3...v0.0.4) (2020-10-31) 10 | 11 | ### [0.0.3](https://github.com/clarencetw/cdk-wordpress/compare/v0.0.2...v0.0.3) (2020-10-31) 12 | 13 | ### [0.0.2](https://github.com/clarencetw/cdk-wordpress/compare/v0.0.1...v0.0.2) (2020-10-31) 14 | 15 | ### 0.0.1 (2020-10-30) 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cdk-wordpress 2 | 3 | [![NPM version](https://badge.fury.io/js/cdk-wordpress.svg)](https://www.npmjs.com/package/cdk-wordpress) 4 | [![PyPI version](https://badge.fury.io/py/cdk-wordpress.svg)](https://pypi.org/project/cdk-wordpress) 5 | ![Release](https://github.com/clarencetw/cdk-wordpress/workflows/Release/badge.svg) 6 | 7 | ![npm](https://img.shields.io/npm/dt/cdk-wordpress?label=npm&color=orange) 8 | ![PyPI](https://img.shields.io/pypi/dm/cdk-wordpress?label=pypi&color=blue) 9 | 10 | A CDK construct library to deploy WordPress 11 | 12 | ## How do use 13 | 14 | Install your package manager: 15 | 16 | ```sh 17 | yarn add cdk-wordpress 18 | ``` 19 | 20 | ### TypeScript Sample 21 | 22 | ```ts 23 | import { WordPress } from "cdk-wordpress"; 24 | 25 | const wordpress = new WordPress(stack, "WordPressEcs"); 26 | 27 | // Get WordPress endpoint 28 | new CfnOutput(stack, "Endpoint", { value: wordpress.endpoint }); 29 | ``` 30 | 31 | ### To deploy 32 | 33 | ```bash 34 | cdk deploy 35 | ``` 36 | 37 | ### To destroy 38 | 39 | ```bash 40 | cdk destroy 41 | ``` 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk-wordpress", 3 | "repository": { 4 | "type": "git", 5 | "url": "https://github.com/clarencetw/cdk-wordpress.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 | "package": "npx projen package", 18 | "package-all": "npx projen package-all", 19 | "package:js": "npx projen package:js", 20 | "package:python": "npx projen package:python", 21 | "post-compile": "npx projen post-compile", 22 | "post-upgrade": "npx projen post-upgrade", 23 | "pre-compile": "npx projen pre-compile", 24 | "release": "npx projen release", 25 | "test": "npx projen test", 26 | "test:watch": "npx projen test:watch", 27 | "unbump": "npx projen unbump", 28 | "upgrade": "npx projen upgrade", 29 | "watch": "npx projen watch", 30 | "projen": "npx projen" 31 | }, 32 | "author": { 33 | "name": "Clarence Lin", 34 | "email": "mr.lin.clarence@gmail.com", 35 | "organization": false 36 | }, 37 | "devDependencies": { 38 | "@aws-cdk/assert": "^1.71.0", 39 | "@types/jest": "^27", 40 | "@types/node": "^16 <= 16.18.78", 41 | "@typescript-eslint/eslint-plugin": "^7", 42 | "@typescript-eslint/parser": "^7", 43 | "commit-and-tag-version": "^12", 44 | "eslint": "^8", 45 | "eslint-import-resolver-typescript": "^2.7.1", 46 | "eslint-plugin-import": "^2.29.1", 47 | "jest": "^27", 48 | "jest-junit": "^15", 49 | "jsii": "1.x", 50 | "jsii-diff": "^1.103.1", 51 | "jsii-docgen": "^10.5.0", 52 | "jsii-pacmak": "^1.103.1", 53 | "jsii-rosetta": "1.x", 54 | "projen": "^0.86.6", 55 | "ts-jest": "^27", 56 | "typescript": "^4.9.5" 57 | }, 58 | "peerDependencies": { 59 | "@aws-cdk/aws-ec2": "^1.71.0", 60 | "@aws-cdk/aws-ecs": "^1.71.0", 61 | "@aws-cdk/aws-ecs-patterns": "^1.71.0", 62 | "@aws-cdk/aws-efs": "^1.71.0", 63 | "@aws-cdk/aws-rds": "^1.71.0", 64 | "@aws-cdk/core": "^1.71.0", 65 | "constructs": "^3.2.27" 66 | }, 67 | "dependencies": { 68 | "@aws-cdk/aws-ec2": "^1.71.0", 69 | "@aws-cdk/aws-ecs": "^1.71.0", 70 | "@aws-cdk/aws-ecs-patterns": "^1.71.0", 71 | "@aws-cdk/aws-efs": "^1.71.0", 72 | "@aws-cdk/aws-rds": "^1.71.0", 73 | "@aws-cdk/core": "^1.71.0" 74 | }, 75 | "resolutions": { 76 | "@types/babel__traverse": "7.18.2", 77 | "@types/prettier": "2.6.0" 78 | }, 79 | "keywords": [ 80 | "aws", 81 | "cdk", 82 | "wordpress" 83 | ], 84 | "main": "lib/index.js", 85 | "license": "Apache-2.0", 86 | "publishConfig": { 87 | "access": "public" 88 | }, 89 | "version": "0.0.0", 90 | "jest": { 91 | "coverageProvider": "v8", 92 | "testMatch": [ 93 | "/@(src|test)/**/*(*.)@(spec|test).ts?(x)", 94 | "/@(src|test)/**/__tests__/**/*.ts?(x)" 95 | ], 96 | "clearMocks": true, 97 | "collectCoverage": true, 98 | "coverageReporters": [ 99 | "json", 100 | "lcov", 101 | "clover", 102 | "cobertura", 103 | "text" 104 | ], 105 | "coverageDirectory": "coverage", 106 | "coveragePathIgnorePatterns": [ 107 | "/node_modules/" 108 | ], 109 | "testPathIgnorePatterns": [ 110 | "/node_modules/" 111 | ], 112 | "watchPathIgnorePatterns": [ 113 | "/node_modules/" 114 | ], 115 | "reporters": [ 116 | "default", 117 | [ 118 | "jest-junit", 119 | { 120 | "outputDirectory": "test-reports" 121 | } 122 | ] 123 | ], 124 | "preset": "ts-jest", 125 | "globals": { 126 | "ts-jest": { 127 | "tsconfig": "tsconfig.dev.json" 128 | } 129 | } 130 | }, 131 | "types": "lib/index.d.ts", 132 | "stability": "stable", 133 | "jsii": { 134 | "outdir": "dist", 135 | "targets": { 136 | "python": { 137 | "distName": "cdk-wordpress", 138 | "module": "cdk_wordpress" 139 | } 140 | }, 141 | "tsc": { 142 | "outDir": "lib", 143 | "rootDir": "src" 144 | } 145 | }, 146 | "awscdkio": { 147 | "twitter": "Clarence_Lin", 148 | "announce": false 149 | }, 150 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 151 | } 152 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as ec2 from '@aws-cdk/aws-ec2'; 2 | import * as ecs from '@aws-cdk/aws-ecs'; 3 | import * as ecsPatterns from '@aws-cdk/aws-ecs-patterns'; 4 | import * as efs from '@aws-cdk/aws-efs'; 5 | import * as rds from '@aws-cdk/aws-rds'; 6 | import * as cdk from '@aws-cdk/core'; 7 | 8 | /** 9 | * The interface for all wordpress. 10 | */ 11 | export interface WordPressProps { 12 | /** 13 | * The WordPress VPC 14 | */ 15 | readonly vpc?: ec2.IVpc; 16 | 17 | /** 18 | * The WordPress RDS 19 | */ 20 | readonly rdsInstance?: rds.DatabaseInstance; 21 | 22 | /** 23 | * The WordPress cluster 24 | */ 25 | readonly cluster?: ecs.Cluster; 26 | } 27 | 28 | export class WordPress extends cdk.Construct { 29 | readonly endpoint: string; 30 | constructor(scope: cdk.Construct, id: string, props: WordPressProps = {}) { 31 | super(scope, id); 32 | 33 | const vpc = props.vpc ?? new ec2.Vpc(this, 'Vpc', { maxAzs: 3, natGateways: 1 }); 34 | 35 | const rdsInstance = props.rdsInstance ?? new rds.DatabaseInstance(this, 'Database', { 36 | engine: rds.DatabaseInstanceEngine.mysql({ 37 | version: rds.MysqlEngineVersion.VER_8_0_21, 38 | }), 39 | vpc, 40 | deleteAutomatedBackups: true, 41 | instanceType: ec2.InstanceType.of( 42 | ec2.InstanceClass.BURSTABLE3, 43 | ec2.InstanceSize.MICRO, 44 | ), 45 | allocatedStorage: 10, 46 | }); 47 | 48 | const cluster = props.cluster ?? new ecs.Cluster(this, 'EcsCluster', { vpc }); 49 | 50 | const autoScalingGroup = cluster.addCapacity('ASG', { 51 | instanceType: ec2.InstanceType.of( 52 | ec2.InstanceClass.T3A, 53 | ec2.InstanceSize.SMALL, 54 | ), 55 | maxCapacity: 3, 56 | machineImage: ecs.EcsOptimizedImage.amazonLinux2(), 57 | }); 58 | autoScalingGroup.scaleOnCpuUtilization('KeepCpuHalfwayLoaded', { 59 | targetUtilizationPercent: 50, 60 | }); 61 | 62 | const loadBalancedEcsService = new ecsPatterns.ApplicationLoadBalancedEc2Service( 63 | this, 64 | 'Service', 65 | { 66 | cluster, 67 | memoryLimitMiB: 512, 68 | taskImageOptions: { 69 | image: ecs.ContainerImage.fromRegistry('wordpress'), 70 | environment: { 71 | WORDPRESS_DB_NAME: 'wordpress', 72 | }, 73 | secrets: { 74 | WORDPRESS_DB_HOST: ecs.Secret.fromSecretsManager( 75 | rdsInstance.secret!, 76 | 'host', 77 | ), 78 | WORDPRESS_DB_USER: ecs.Secret.fromSecretsManager( 79 | rdsInstance.secret!, 80 | 'username', 81 | ), 82 | WORDPRESS_DB_PASSWORD: ecs.Secret.fromSecretsManager( 83 | rdsInstance.secret!, 84 | 'password', 85 | ), 86 | }, 87 | }, 88 | desiredCount: 2, 89 | }, 90 | ); 91 | const scaling = loadBalancedEcsService.service.autoScaleTaskCount({ 92 | maxCapacity: 6, 93 | }); 94 | scaling.scaleOnCpuUtilization('CpuScaling', { 95 | targetUtilizationPercent: 50, 96 | }); 97 | loadBalancedEcsService.targetGroup.healthCheck = { 98 | path: '/wp-includes/images/blank.gif', 99 | interval: cdk.Duration.minutes(1), 100 | }; 101 | 102 | rdsInstance.connections.allowFrom( 103 | loadBalancedEcsService.cluster.connections, 104 | ec2.Port.tcp(3306), 105 | ); 106 | 107 | const fileSystem = new efs.FileSystem(this, 'FileSystem', { 108 | vpc, 109 | encrypted: true, 110 | }); 111 | fileSystem.connections.allowFrom( 112 | autoScalingGroup.connections.connections, 113 | ec2.Port.tcp(2049), 114 | ); 115 | 116 | const volumeName = 'efs'; 117 | loadBalancedEcsService.taskDefinition.addVolume({ 118 | name: volumeName, 119 | efsVolumeConfiguration: { 120 | fileSystemId: fileSystem.fileSystemId, 121 | }, 122 | }); 123 | 124 | loadBalancedEcsService.taskDefinition.defaultContainer?.addMountPoints({ 125 | containerPath: '/var/www/html', 126 | readOnly: false, 127 | sourceVolume: volumeName, 128 | }); 129 | 130 | this.endpoint = `http://${loadBalancedEcsService.loadBalancer.loadBalancerDnsName}`; 131 | } 132 | } -------------------------------------------------------------------------------- /src/integ.default.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from '@aws-cdk/core'; 2 | import { WordPress } from './index'; 3 | 4 | export class IntegTesting { 5 | readonly stack: cdk.Stack[]; 6 | 7 | constructor() { 8 | const app = new cdk.App(); 9 | 10 | const env = { 11 | account: process.env.CDK_DEFAULT_ACCOUNT, 12 | region: process.env.CDK_DEFAULT_REGION, 13 | }; 14 | 15 | const stack = new cdk.Stack(app, 'testing-stack', { env }); 16 | 17 | const wordpress = new WordPress(stack, 'WordPressEcs'); 18 | 19 | new cdk.CfnOutput(stack, 'Endpoint', { 20 | value: wordpress.endpoint, 21 | }); 22 | this.stack = [stack]; 23 | } 24 | } 25 | 26 | new IntegTesting(); -------------------------------------------------------------------------------- /test/__snapshots__/integ.snapshot.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`integ snapshot validation 1`] = ` 4 | Object { 5 | "Outputs": Object { 6 | "Endpoint": Object { 7 | "Value": Object { 8 | "Fn::Join": Array [ 9 | "", 10 | Array [ 11 | "http://", 12 | Object { 13 | "Fn::GetAtt": Array [ 14 | "WordPressEcsServiceLB538A1BD5", 15 | "DNSName", 16 | ], 17 | }, 18 | ], 19 | ], 20 | }, 21 | }, 22 | "WordPressEcsServiceLoadBalancerDNSD66F7753": Object { 23 | "Value": Object { 24 | "Fn::GetAtt": Array [ 25 | "WordPressEcsServiceLB538A1BD5", 26 | "DNSName", 27 | ], 28 | }, 29 | }, 30 | "WordPressEcsServiceServiceURLDC074258": Object { 31 | "Value": Object { 32 | "Fn::Join": Array [ 33 | "", 34 | Array [ 35 | "http://", 36 | Object { 37 | "Fn::GetAtt": Array [ 38 | "WordPressEcsServiceLB538A1BD5", 39 | "DNSName", 40 | ], 41 | }, 42 | ], 43 | ], 44 | }, 45 | }, 46 | }, 47 | "Parameters": Object { 48 | "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": Object { 49 | "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id", 50 | "Type": "AWS::SSM::Parameter::Value", 51 | }, 52 | }, 53 | "Resources": Object { 54 | "WordPressEcsDatabaseBC5843FB": Object { 55 | "DeletionPolicy": "Snapshot", 56 | "Properties": Object { 57 | "AllocatedStorage": "10", 58 | "CopyTagsToSnapshot": true, 59 | "DBInstanceClass": "db.t3.micro", 60 | "DBSubnetGroupName": Object { 61 | "Ref": "WordPressEcsDatabaseSubnetGroup4A8A0A61", 62 | }, 63 | "DeleteAutomatedBackups": true, 64 | "Engine": "mysql", 65 | "EngineVersion": "8.0.21", 66 | "MasterUserPassword": Object { 67 | "Fn::Join": Array [ 68 | "", 69 | Array [ 70 | "{{resolve:secretsmanager:", 71 | Object { 72 | "Ref": "WordPressEcsDatabaseSecret86C748B7", 73 | }, 74 | ":SecretString:password::}}", 75 | ], 76 | ], 77 | }, 78 | "MasterUsername": Object { 79 | "Fn::Join": Array [ 80 | "", 81 | Array [ 82 | "{{resolve:secretsmanager:", 83 | Object { 84 | "Ref": "WordPressEcsDatabaseSecret86C748B7", 85 | }, 86 | ":SecretString:username::}}", 87 | ], 88 | ], 89 | }, 90 | "StorageType": "gp2", 91 | "VPCSecurityGroups": Array [ 92 | Object { 93 | "Fn::GetAtt": Array [ 94 | "WordPressEcsDatabaseSecurityGroupDB784C4B", 95 | "GroupId", 96 | ], 97 | }, 98 | ], 99 | }, 100 | "Type": "AWS::RDS::DBInstance", 101 | "UpdateReplacePolicy": "Snapshot", 102 | }, 103 | "WordPressEcsDatabaseSecret86C748B7": Object { 104 | "DeletionPolicy": "Delete", 105 | "Properties": Object { 106 | "Description": Object { 107 | "Fn::Join": Array [ 108 | "", 109 | Array [ 110 | "Generated by the CDK for stack: ", 111 | Object { 112 | "Ref": "AWS::StackName", 113 | }, 114 | ], 115 | ], 116 | }, 117 | "GenerateSecretString": Object { 118 | "ExcludeCharacters": " %+~\`#$&*()|[]{}:;<>?!'/@\\"\\\\", 119 | "GenerateStringKey": "password", 120 | "PasswordLength": 30, 121 | "SecretStringTemplate": "{\\"username\\":\\"admin\\"}", 122 | }, 123 | }, 124 | "Type": "AWS::SecretsManager::Secret", 125 | "UpdateReplacePolicy": "Delete", 126 | }, 127 | "WordPressEcsDatabaseSecretAttachment89768602": Object { 128 | "Properties": Object { 129 | "SecretId": Object { 130 | "Ref": "WordPressEcsDatabaseSecret86C748B7", 131 | }, 132 | "TargetId": Object { 133 | "Ref": "WordPressEcsDatabaseBC5843FB", 134 | }, 135 | "TargetType": "AWS::RDS::DBInstance", 136 | }, 137 | "Type": "AWS::SecretsManager::SecretTargetAttachment", 138 | }, 139 | "WordPressEcsDatabaseSecurityGroupDB784C4B": Object { 140 | "Properties": Object { 141 | "GroupDescription": "Security group for Database database", 142 | "SecurityGroupEgress": Array [ 143 | Object { 144 | "CidrIp": "0.0.0.0/0", 145 | "Description": "Allow all outbound traffic by default", 146 | "IpProtocol": "-1", 147 | }, 148 | ], 149 | "VpcId": Object { 150 | "Ref": "WordPressEcsVpc532EC8A6", 151 | }, 152 | }, 153 | "Type": "AWS::EC2::SecurityGroup", 154 | }, 155 | "WordPressEcsDatabaseSecurityGroupfromtestingstackWordPressEcsEcsClusterASGInstanceSecurityGroup81EA60CD330689875EEA": Object { 156 | "Properties": Object { 157 | "Description": "from testingstackWordPressEcsEcsClusterASGInstanceSecurityGroup81EA60CD:3306", 158 | "FromPort": 3306, 159 | "GroupId": Object { 160 | "Fn::GetAtt": Array [ 161 | "WordPressEcsDatabaseSecurityGroupDB784C4B", 162 | "GroupId", 163 | ], 164 | }, 165 | "IpProtocol": "tcp", 166 | "SourceSecurityGroupId": Object { 167 | "Fn::GetAtt": Array [ 168 | "WordPressEcsEcsClusterASGInstanceSecurityGroup47B2EB77", 169 | "GroupId", 170 | ], 171 | }, 172 | "ToPort": 3306, 173 | }, 174 | "Type": "AWS::EC2::SecurityGroupIngress", 175 | }, 176 | "WordPressEcsDatabaseSubnetGroup4A8A0A61": Object { 177 | "Properties": Object { 178 | "DBSubnetGroupDescription": "Subnet group for Database database", 179 | "SubnetIds": Array [ 180 | Object { 181 | "Ref": "WordPressEcsVpcPrivateSubnet1Subnet8A2983A9", 182 | }, 183 | Object { 184 | "Ref": "WordPressEcsVpcPrivateSubnet2SubnetDA96ABD4", 185 | }, 186 | ], 187 | }, 188 | "Type": "AWS::RDS::DBSubnetGroup", 189 | }, 190 | "WordPressEcsEcsClusterASGDrainECSHookFunctionAllowInvoketestingstackWordPressEcsEcsClusterASGLifecycleHookDrainHookTopic0D92F521298AF9B4": Object { 191 | "Properties": Object { 192 | "Action": "lambda:InvokeFunction", 193 | "FunctionName": Object { 194 | "Fn::GetAtt": Array [ 195 | "WordPressEcsEcsClusterASGDrainECSHookFunctionF292A64B", 196 | "Arn", 197 | ], 198 | }, 199 | "Principal": "sns.amazonaws.com", 200 | "SourceArn": Object { 201 | "Ref": "WordPressEcsEcsClusterASGLifecycleHookDrainHookTopicF8DF4F0A", 202 | }, 203 | }, 204 | "Type": "AWS::Lambda::Permission", 205 | }, 206 | "WordPressEcsEcsClusterASGDrainECSHookFunctionF292A64B": Object { 207 | "DependsOn": Array [ 208 | "WordPressEcsEcsClusterASGDrainECSHookFunctionServiceRoleDefaultPolicy1D1C4E1F", 209 | "WordPressEcsEcsClusterASGDrainECSHookFunctionServiceRole4373C7F5", 210 | ], 211 | "Properties": Object { 212 | "Code": Object { 213 | "ZipFile": "import boto3, json, os, time 214 | 215 | ecs = boto3.client('ecs') 216 | autoscaling = boto3.client('autoscaling') 217 | 218 | 219 | def lambda_handler(event, context): 220 | print(json.dumps(dict(event, ResponseURL='...'))) 221 | cluster = os.environ['CLUSTER'] 222 | snsTopicArn = event['Records'][0]['Sns']['TopicArn'] 223 | lifecycle_event = json.loads(event['Records'][0]['Sns']['Message']) 224 | instance_id = lifecycle_event.get('EC2InstanceId') 225 | if not instance_id: 226 | print('Got event without EC2InstanceId: %s', json.dumps(dict(event, ResponseURL='...'))) 227 | return 228 | 229 | instance_arn = container_instance_arn(cluster, instance_id) 230 | print('Instance %s has container instance ARN %s' % (lifecycle_event['EC2InstanceId'], instance_arn)) 231 | 232 | if not instance_arn: 233 | return 234 | 235 | task_arns = container_instance_task_arns(cluster, instance_arn) 236 | 237 | if task_arns: 238 | print('Instance ARN %s has task ARNs %s' % (instance_arn, ', '.join(task_arns))) 239 | 240 | while has_tasks(cluster, instance_arn, task_arns): 241 | time.sleep(10) 242 | 243 | try: 244 | print('Terminating instance %s' % instance_id) 245 | autoscaling.complete_lifecycle_action( 246 | LifecycleActionResult='CONTINUE', 247 | **pick(lifecycle_event, 'LifecycleHookName', 'LifecycleActionToken', 'AutoScalingGroupName')) 248 | except Exception as e: 249 | # Lifecycle action may have already completed. 250 | print(str(e)) 251 | 252 | 253 | def container_instance_arn(cluster, instance_id): 254 | \\"\\"\\"Turn an instance ID into a container instance ARN.\\"\\"\\" 255 | arns = ecs.list_container_instances(cluster=cluster, filter='ec2InstanceId==' + instance_id)['containerInstanceArns'] 256 | if not arns: 257 | return None 258 | return arns[0] 259 | 260 | def container_instance_task_arns(cluster, instance_arn): 261 | \\"\\"\\"Fetch tasks for a container instance ARN.\\"\\"\\" 262 | arns = ecs.list_tasks(cluster=cluster, containerInstance=instance_arn)['taskArns'] 263 | return arns 264 | 265 | def has_tasks(cluster, instance_arn, task_arns): 266 | \\"\\"\\"Return True if the instance is running tasks for the given cluster.\\"\\"\\" 267 | instances = ecs.describe_container_instances(cluster=cluster, containerInstances=[instance_arn])['containerInstances'] 268 | if not instances: 269 | return False 270 | instance = instances[0] 271 | 272 | if instance['status'] == 'ACTIVE': 273 | # Start draining, then try again later 274 | set_container_instance_to_draining(cluster, instance_arn) 275 | return True 276 | 277 | task_count = None 278 | 279 | if task_arns: 280 | # Fetch details for tasks running on the container instance 281 | tasks = ecs.describe_tasks(cluster=cluster, tasks=task_arns)['tasks'] 282 | if tasks: 283 | # Consider any non-stopped tasks as running 284 | task_count = sum(task['lastStatus'] != 'STOPPED' for task in tasks) + instance['pendingTasksCount'] 285 | 286 | if not task_count: 287 | # Fallback to instance task counts if detailed task information is unavailable 288 | task_count = instance['runningTasksCount'] + instance['pendingTasksCount'] 289 | 290 | print('Instance %s has %s tasks' % (instance_arn, task_count)) 291 | 292 | return task_count > 0 293 | 294 | def set_container_instance_to_draining(cluster, instance_arn): 295 | ecs.update_container_instances_state( 296 | cluster=cluster, 297 | containerInstances=[instance_arn], status='DRAINING') 298 | 299 | 300 | def pick(dct, *keys): 301 | \\"\\"\\"Pick a subset of a dict.\\"\\"\\" 302 | return {k: v for k, v in dct.items() if k in keys} 303 | ", 304 | }, 305 | "Environment": Object { 306 | "Variables": Object { 307 | "CLUSTER": Object { 308 | "Ref": "WordPressEcsEcsClusterB051F2EF", 309 | }, 310 | }, 311 | }, 312 | "Handler": "index.lambda_handler", 313 | "Role": Object { 314 | "Fn::GetAtt": Array [ 315 | "WordPressEcsEcsClusterASGDrainECSHookFunctionServiceRole4373C7F5", 316 | "Arn", 317 | ], 318 | }, 319 | "Runtime": "python3.9", 320 | "Tags": Array [ 321 | Object { 322 | "Key": "Name", 323 | "Value": "testing-stack/WordPressEcs/EcsCluster/ASG", 324 | }, 325 | ], 326 | "Timeout": 310, 327 | }, 328 | "Type": "AWS::Lambda::Function", 329 | }, 330 | "WordPressEcsEcsClusterASGDrainECSHookFunctionServiceRole4373C7F5": Object { 331 | "Properties": Object { 332 | "AssumeRolePolicyDocument": Object { 333 | "Statement": Array [ 334 | Object { 335 | "Action": "sts:AssumeRole", 336 | "Effect": "Allow", 337 | "Principal": Object { 338 | "Service": "lambda.amazonaws.com", 339 | }, 340 | }, 341 | ], 342 | "Version": "2012-10-17", 343 | }, 344 | "ManagedPolicyArns": Array [ 345 | Object { 346 | "Fn::Join": Array [ 347 | "", 348 | Array [ 349 | "arn:", 350 | Object { 351 | "Ref": "AWS::Partition", 352 | }, 353 | ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", 354 | ], 355 | ], 356 | }, 357 | ], 358 | "Tags": Array [ 359 | Object { 360 | "Key": "Name", 361 | "Value": "testing-stack/WordPressEcs/EcsCluster/ASG", 362 | }, 363 | ], 364 | }, 365 | "Type": "AWS::IAM::Role", 366 | }, 367 | "WordPressEcsEcsClusterASGDrainECSHookFunctionServiceRoleDefaultPolicy1D1C4E1F": Object { 368 | "Properties": Object { 369 | "PolicyDocument": Object { 370 | "Statement": Array [ 371 | Object { 372 | "Action": Array [ 373 | "ec2:DescribeInstances", 374 | "ec2:DescribeInstanceAttribute", 375 | "ec2:DescribeInstanceStatus", 376 | "ec2:DescribeHosts", 377 | ], 378 | "Effect": "Allow", 379 | "Resource": "*", 380 | }, 381 | Object { 382 | "Action": "autoscaling:CompleteLifecycleAction", 383 | "Effect": "Allow", 384 | "Resource": Object { 385 | "Fn::Join": Array [ 386 | "", 387 | Array [ 388 | "arn:", 389 | Object { 390 | "Ref": "AWS::Partition", 391 | }, 392 | ":autoscaling:", 393 | Object { 394 | "Ref": "AWS::Region", 395 | }, 396 | ":", 397 | Object { 398 | "Ref": "AWS::AccountId", 399 | }, 400 | ":autoScalingGroup:*:autoScalingGroupName/", 401 | Object { 402 | "Ref": "WordPressEcsEcsClusterASGF425BB11", 403 | }, 404 | ], 405 | ], 406 | }, 407 | }, 408 | Object { 409 | "Action": Array [ 410 | "ecs:DescribeContainerInstances", 411 | "ecs:DescribeTasks", 412 | ], 413 | "Condition": Object { 414 | "ArnEquals": Object { 415 | "ecs:cluster": Object { 416 | "Fn::GetAtt": Array [ 417 | "WordPressEcsEcsClusterB051F2EF", 418 | "Arn", 419 | ], 420 | }, 421 | }, 422 | }, 423 | "Effect": "Allow", 424 | "Resource": "*", 425 | }, 426 | Object { 427 | "Action": Array [ 428 | "ecs:ListContainerInstances", 429 | "ecs:SubmitContainerStateChange", 430 | "ecs:SubmitTaskStateChange", 431 | ], 432 | "Effect": "Allow", 433 | "Resource": Object { 434 | "Fn::GetAtt": Array [ 435 | "WordPressEcsEcsClusterB051F2EF", 436 | "Arn", 437 | ], 438 | }, 439 | }, 440 | Object { 441 | "Action": Array [ 442 | "ecs:UpdateContainerInstancesState", 443 | "ecs:ListTasks", 444 | ], 445 | "Condition": Object { 446 | "ArnEquals": Object { 447 | "ecs:cluster": Object { 448 | "Fn::GetAtt": Array [ 449 | "WordPressEcsEcsClusterB051F2EF", 450 | "Arn", 451 | ], 452 | }, 453 | }, 454 | }, 455 | "Effect": "Allow", 456 | "Resource": "*", 457 | }, 458 | ], 459 | "Version": "2012-10-17", 460 | }, 461 | "PolicyName": "WordPressEcsEcsClusterASGDrainECSHookFunctionServiceRoleDefaultPolicy1D1C4E1F", 462 | "Roles": Array [ 463 | Object { 464 | "Ref": "WordPressEcsEcsClusterASGDrainECSHookFunctionServiceRole4373C7F5", 465 | }, 466 | ], 467 | }, 468 | "Type": "AWS::IAM::Policy", 469 | }, 470 | "WordPressEcsEcsClusterASGDrainECSHookFunctionTopicC611ECFB": Object { 471 | "Properties": Object { 472 | "Endpoint": Object { 473 | "Fn::GetAtt": Array [ 474 | "WordPressEcsEcsClusterASGDrainECSHookFunctionF292A64B", 475 | "Arn", 476 | ], 477 | }, 478 | "Protocol": "lambda", 479 | "TopicArn": Object { 480 | "Ref": "WordPressEcsEcsClusterASGLifecycleHookDrainHookTopicF8DF4F0A", 481 | }, 482 | }, 483 | "Type": "AWS::SNS::Subscription", 484 | }, 485 | "WordPressEcsEcsClusterASGF425BB11": Object { 486 | "Properties": Object { 487 | "LaunchConfigurationName": Object { 488 | "Ref": "WordPressEcsEcsClusterASGLaunchConfigDE0C2F3F", 489 | }, 490 | "MaxSize": "3", 491 | "MinSize": "1", 492 | "Tags": Array [ 493 | Object { 494 | "Key": "Name", 495 | "PropagateAtLaunch": true, 496 | "Value": "testing-stack/WordPressEcs/EcsCluster/ASG", 497 | }, 498 | ], 499 | "VPCZoneIdentifier": Array [ 500 | Object { 501 | "Ref": "WordPressEcsVpcPrivateSubnet1Subnet8A2983A9", 502 | }, 503 | Object { 504 | "Ref": "WordPressEcsVpcPrivateSubnet2SubnetDA96ABD4", 505 | }, 506 | ], 507 | }, 508 | "Type": "AWS::AutoScaling::AutoScalingGroup", 509 | "UpdatePolicy": Object { 510 | "AutoScalingReplacingUpdate": Object { 511 | "WillReplace": true, 512 | }, 513 | "AutoScalingScheduledAction": Object { 514 | "IgnoreUnmodifiedGroupSizeProperties": true, 515 | }, 516 | }, 517 | }, 518 | "WordPressEcsEcsClusterASGInstanceProfile22BE408A": Object { 519 | "Properties": Object { 520 | "Roles": Array [ 521 | Object { 522 | "Ref": "WordPressEcsEcsClusterASGInstanceRole6B0483FC", 523 | }, 524 | ], 525 | }, 526 | "Type": "AWS::IAM::InstanceProfile", 527 | }, 528 | "WordPressEcsEcsClusterASGInstanceRole6B0483FC": Object { 529 | "Properties": Object { 530 | "AssumeRolePolicyDocument": Object { 531 | "Statement": Array [ 532 | Object { 533 | "Action": "sts:AssumeRole", 534 | "Effect": "Allow", 535 | "Principal": Object { 536 | "Service": Object { 537 | "Fn::Join": Array [ 538 | "", 539 | Array [ 540 | "ec2.", 541 | Object { 542 | "Ref": "AWS::URLSuffix", 543 | }, 544 | ], 545 | ], 546 | }, 547 | }, 548 | }, 549 | ], 550 | "Version": "2012-10-17", 551 | }, 552 | "Tags": Array [ 553 | Object { 554 | "Key": "Name", 555 | "Value": "testing-stack/WordPressEcs/EcsCluster/ASG", 556 | }, 557 | ], 558 | }, 559 | "Type": "AWS::IAM::Role", 560 | }, 561 | "WordPressEcsEcsClusterASGInstanceRoleDefaultPolicy9002C9CC": Object { 562 | "Properties": Object { 563 | "PolicyDocument": Object { 564 | "Statement": Array [ 565 | Object { 566 | "Action": Array [ 567 | "ecs:DeregisterContainerInstance", 568 | "ecs:RegisterContainerInstance", 569 | "ecs:Submit*", 570 | ], 571 | "Effect": "Allow", 572 | "Resource": Object { 573 | "Fn::GetAtt": Array [ 574 | "WordPressEcsEcsClusterB051F2EF", 575 | "Arn", 576 | ], 577 | }, 578 | }, 579 | Object { 580 | "Action": Array [ 581 | "ecs:Poll", 582 | "ecs:StartTelemetrySession", 583 | ], 584 | "Condition": Object { 585 | "ArnEquals": Object { 586 | "ecs:cluster": Object { 587 | "Fn::GetAtt": Array [ 588 | "WordPressEcsEcsClusterB051F2EF", 589 | "Arn", 590 | ], 591 | }, 592 | }, 593 | }, 594 | "Effect": "Allow", 595 | "Resource": "*", 596 | }, 597 | Object { 598 | "Action": Array [ 599 | "ecs:DiscoverPollEndpoint", 600 | "ecr:GetAuthorizationToken", 601 | "logs:CreateLogStream", 602 | "logs:PutLogEvents", 603 | ], 604 | "Effect": "Allow", 605 | "Resource": "*", 606 | }, 607 | ], 608 | "Version": "2012-10-17", 609 | }, 610 | "PolicyName": "WordPressEcsEcsClusterASGInstanceRoleDefaultPolicy9002C9CC", 611 | "Roles": Array [ 612 | Object { 613 | "Ref": "WordPressEcsEcsClusterASGInstanceRole6B0483FC", 614 | }, 615 | ], 616 | }, 617 | "Type": "AWS::IAM::Policy", 618 | }, 619 | "WordPressEcsEcsClusterASGInstanceSecurityGroup47B2EB77": Object { 620 | "Properties": Object { 621 | "GroupDescription": "testing-stack/WordPressEcs/EcsCluster/ASG/InstanceSecurityGroup", 622 | "SecurityGroupEgress": Array [ 623 | Object { 624 | "CidrIp": "0.0.0.0/0", 625 | "Description": "Allow all outbound traffic by default", 626 | "IpProtocol": "-1", 627 | }, 628 | ], 629 | "Tags": Array [ 630 | Object { 631 | "Key": "Name", 632 | "Value": "testing-stack/WordPressEcs/EcsCluster/ASG", 633 | }, 634 | ], 635 | "VpcId": Object { 636 | "Ref": "WordPressEcsVpc532EC8A6", 637 | }, 638 | }, 639 | "Type": "AWS::EC2::SecurityGroup", 640 | }, 641 | "WordPressEcsEcsClusterASGInstanceSecurityGroupfromtestingstackWordPressEcsServiceLBSecurityGroupF32BB12032768655357788818D": Object { 642 | "Properties": Object { 643 | "Description": "Load balancer to target", 644 | "FromPort": 32768, 645 | "GroupId": Object { 646 | "Fn::GetAtt": Array [ 647 | "WordPressEcsEcsClusterASGInstanceSecurityGroup47B2EB77", 648 | "GroupId", 649 | ], 650 | }, 651 | "IpProtocol": "tcp", 652 | "SourceSecurityGroupId": Object { 653 | "Fn::GetAtt": Array [ 654 | "WordPressEcsServiceLBSecurityGroup59F9C55A", 655 | "GroupId", 656 | ], 657 | }, 658 | "ToPort": 65535, 659 | }, 660 | "Type": "AWS::EC2::SecurityGroupIngress", 661 | }, 662 | "WordPressEcsEcsClusterASGLaunchConfigDE0C2F3F": Object { 663 | "DependsOn": Array [ 664 | "WordPressEcsEcsClusterASGInstanceRoleDefaultPolicy9002C9CC", 665 | "WordPressEcsEcsClusterASGInstanceRole6B0483FC", 666 | ], 667 | "Properties": Object { 668 | "IamInstanceProfile": Object { 669 | "Ref": "WordPressEcsEcsClusterASGInstanceProfile22BE408A", 670 | }, 671 | "ImageId": Object { 672 | "Ref": "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter", 673 | }, 674 | "InstanceType": "t3a.small", 675 | "SecurityGroups": Array [ 676 | Object { 677 | "Fn::GetAtt": Array [ 678 | "WordPressEcsEcsClusterASGInstanceSecurityGroup47B2EB77", 679 | "GroupId", 680 | ], 681 | }, 682 | ], 683 | "UserData": Object { 684 | "Fn::Base64": Object { 685 | "Fn::Join": Array [ 686 | "", 687 | Array [ 688 | "#!/bin/bash 689 | echo ECS_CLUSTER=", 690 | Object { 691 | "Ref": "WordPressEcsEcsClusterB051F2EF", 692 | }, 693 | " >> /etc/ecs/ecs.config 694 | sudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP 695 | sudo service iptables save 696 | echo ECS_AWSVPC_BLOCK_IMDS=true >> /etc/ecs/ecs.config", 697 | ], 698 | ], 699 | }, 700 | }, 701 | }, 702 | "Type": "AWS::AutoScaling::LaunchConfiguration", 703 | }, 704 | "WordPressEcsEcsClusterASGLifecycleHookDrainHookD7966898": Object { 705 | "DependsOn": Array [ 706 | "WordPressEcsEcsClusterASGLifecycleHookDrainHookRoleDefaultPolicy291CEBE9", 707 | "WordPressEcsEcsClusterASGLifecycleHookDrainHookRole0D8CC841", 708 | ], 709 | "Properties": Object { 710 | "AutoScalingGroupName": Object { 711 | "Ref": "WordPressEcsEcsClusterASGF425BB11", 712 | }, 713 | "DefaultResult": "CONTINUE", 714 | "HeartbeatTimeout": 300, 715 | "LifecycleTransition": "autoscaling:EC2_INSTANCE_TERMINATING", 716 | "NotificationTargetARN": Object { 717 | "Ref": "WordPressEcsEcsClusterASGLifecycleHookDrainHookTopicF8DF4F0A", 718 | }, 719 | "RoleARN": Object { 720 | "Fn::GetAtt": Array [ 721 | "WordPressEcsEcsClusterASGLifecycleHookDrainHookRole0D8CC841", 722 | "Arn", 723 | ], 724 | }, 725 | }, 726 | "Type": "AWS::AutoScaling::LifecycleHook", 727 | }, 728 | "WordPressEcsEcsClusterASGLifecycleHookDrainHookRole0D8CC841": Object { 729 | "Properties": Object { 730 | "AssumeRolePolicyDocument": Object { 731 | "Statement": Array [ 732 | Object { 733 | "Action": "sts:AssumeRole", 734 | "Effect": "Allow", 735 | "Principal": Object { 736 | "Service": "autoscaling.amazonaws.com", 737 | }, 738 | }, 739 | ], 740 | "Version": "2012-10-17", 741 | }, 742 | "Tags": Array [ 743 | Object { 744 | "Key": "Name", 745 | "Value": "testing-stack/WordPressEcs/EcsCluster/ASG", 746 | }, 747 | ], 748 | }, 749 | "Type": "AWS::IAM::Role", 750 | }, 751 | "WordPressEcsEcsClusterASGLifecycleHookDrainHookRoleDefaultPolicy291CEBE9": Object { 752 | "Properties": Object { 753 | "PolicyDocument": Object { 754 | "Statement": Array [ 755 | Object { 756 | "Action": "sns:Publish", 757 | "Effect": "Allow", 758 | "Resource": Object { 759 | "Ref": "WordPressEcsEcsClusterASGLifecycleHookDrainHookTopicF8DF4F0A", 760 | }, 761 | }, 762 | ], 763 | "Version": "2012-10-17", 764 | }, 765 | "PolicyName": "WordPressEcsEcsClusterASGLifecycleHookDrainHookRoleDefaultPolicy291CEBE9", 766 | "Roles": Array [ 767 | Object { 768 | "Ref": "WordPressEcsEcsClusterASGLifecycleHookDrainHookRole0D8CC841", 769 | }, 770 | ], 771 | }, 772 | "Type": "AWS::IAM::Policy", 773 | }, 774 | "WordPressEcsEcsClusterASGLifecycleHookDrainHookTopicF8DF4F0A": Object { 775 | "Properties": Object { 776 | "Tags": Array [ 777 | Object { 778 | "Key": "Name", 779 | "Value": "testing-stack/WordPressEcs/EcsCluster/ASG", 780 | }, 781 | ], 782 | }, 783 | "Type": "AWS::SNS::Topic", 784 | }, 785 | "WordPressEcsEcsClusterASGScalingPolicyKeepCpuHalfwayLoaded51DA1CCE": Object { 786 | "Properties": Object { 787 | "AutoScalingGroupName": Object { 788 | "Ref": "WordPressEcsEcsClusterASGF425BB11", 789 | }, 790 | "PolicyType": "TargetTrackingScaling", 791 | "TargetTrackingConfiguration": Object { 792 | "PredefinedMetricSpecification": Object { 793 | "PredefinedMetricType": "ASGAverageCPUUtilization", 794 | }, 795 | "TargetValue": 50, 796 | }, 797 | }, 798 | "Type": "AWS::AutoScaling::ScalingPolicy", 799 | }, 800 | "WordPressEcsEcsClusterB051F2EF": Object { 801 | "Type": "AWS::ECS::Cluster", 802 | }, 803 | "WordPressEcsFileSystem0FE21389": Object { 804 | "DeletionPolicy": "Retain", 805 | "Properties": Object { 806 | "Encrypted": true, 807 | "FileSystemTags": Array [ 808 | Object { 809 | "Key": "Name", 810 | "Value": "testing-stack/WordPressEcs/FileSystem", 811 | }, 812 | ], 813 | }, 814 | "Type": "AWS::EFS::FileSystem", 815 | "UpdateReplacePolicy": "Retain", 816 | }, 817 | "WordPressEcsFileSystemEfsMountTarget151790379": Object { 818 | "Properties": Object { 819 | "FileSystemId": Object { 820 | "Ref": "WordPressEcsFileSystem0FE21389", 821 | }, 822 | "SecurityGroups": Array [ 823 | Object { 824 | "Fn::GetAtt": Array [ 825 | "WordPressEcsFileSystemEfsSecurityGroupE1ACC1C0", 826 | "GroupId", 827 | ], 828 | }, 829 | ], 830 | "SubnetId": Object { 831 | "Ref": "WordPressEcsVpcPrivateSubnet1Subnet8A2983A9", 832 | }, 833 | }, 834 | "Type": "AWS::EFS::MountTarget", 835 | }, 836 | "WordPressEcsFileSystemEfsMountTarget24EDF0CBB": Object { 837 | "Properties": Object { 838 | "FileSystemId": Object { 839 | "Ref": "WordPressEcsFileSystem0FE21389", 840 | }, 841 | "SecurityGroups": Array [ 842 | Object { 843 | "Fn::GetAtt": Array [ 844 | "WordPressEcsFileSystemEfsSecurityGroupE1ACC1C0", 845 | "GroupId", 846 | ], 847 | }, 848 | ], 849 | "SubnetId": Object { 850 | "Ref": "WordPressEcsVpcPrivateSubnet2SubnetDA96ABD4", 851 | }, 852 | }, 853 | "Type": "AWS::EFS::MountTarget", 854 | }, 855 | "WordPressEcsFileSystemEfsSecurityGroupE1ACC1C0": Object { 856 | "Properties": Object { 857 | "GroupDescription": "testing-stack/WordPressEcs/FileSystem/EfsSecurityGroup", 858 | "SecurityGroupEgress": Array [ 859 | Object { 860 | "CidrIp": "0.0.0.0/0", 861 | "Description": "Allow all outbound traffic by default", 862 | "IpProtocol": "-1", 863 | }, 864 | ], 865 | "Tags": Array [ 866 | Object { 867 | "Key": "Name", 868 | "Value": "testing-stack/WordPressEcs/FileSystem", 869 | }, 870 | ], 871 | "VpcId": Object { 872 | "Ref": "WordPressEcsVpc532EC8A6", 873 | }, 874 | }, 875 | "Type": "AWS::EC2::SecurityGroup", 876 | }, 877 | "WordPressEcsFileSystemEfsSecurityGroupfromtestingstackWordPressEcsEcsClusterASGInstanceSecurityGroup81EA60CD204976A257F1": Object { 878 | "Properties": Object { 879 | "Description": "from testingstackWordPressEcsEcsClusterASGInstanceSecurityGroup81EA60CD:2049", 880 | "FromPort": 2049, 881 | "GroupId": Object { 882 | "Fn::GetAtt": Array [ 883 | "WordPressEcsFileSystemEfsSecurityGroupE1ACC1C0", 884 | "GroupId", 885 | ], 886 | }, 887 | "IpProtocol": "tcp", 888 | "SourceSecurityGroupId": Object { 889 | "Fn::GetAtt": Array [ 890 | "WordPressEcsEcsClusterASGInstanceSecurityGroup47B2EB77", 891 | "GroupId", 892 | ], 893 | }, 894 | "ToPort": 2049, 895 | }, 896 | "Type": "AWS::EC2::SecurityGroupIngress", 897 | }, 898 | "WordPressEcsService756743EE": Object { 899 | "DependsOn": Array [ 900 | "WordPressEcsServiceLBPublicListenerECSGroup6008A666", 901 | "WordPressEcsServiceLBPublicListenerD9840C9A", 902 | ], 903 | "Properties": Object { 904 | "Cluster": Object { 905 | "Ref": "WordPressEcsEcsClusterB051F2EF", 906 | }, 907 | "DeploymentConfiguration": Object { 908 | "MaximumPercent": 200, 909 | "MinimumHealthyPercent": 50, 910 | }, 911 | "DesiredCount": 2, 912 | "EnableECSManagedTags": false, 913 | "HealthCheckGracePeriodSeconds": 60, 914 | "LaunchType": "EC2", 915 | "LoadBalancers": Array [ 916 | Object { 917 | "ContainerName": "web", 918 | "ContainerPort": 80, 919 | "TargetGroupArn": Object { 920 | "Ref": "WordPressEcsServiceLBPublicListenerECSGroup6008A666", 921 | }, 922 | }, 923 | ], 924 | "SchedulingStrategy": "REPLICA", 925 | "TaskDefinition": Object { 926 | "Ref": "WordPressEcsServiceTaskDef5E2B238A", 927 | }, 928 | }, 929 | "Type": "AWS::ECS::Service", 930 | }, 931 | "WordPressEcsServiceLB538A1BD5": Object { 932 | "DependsOn": Array [ 933 | "WordPressEcsVpcPublicSubnet1DefaultRoute302AD6DA", 934 | "WordPressEcsVpcPublicSubnet2DefaultRoute1B791D0C", 935 | ], 936 | "Properties": Object { 937 | "LoadBalancerAttributes": Array [ 938 | Object { 939 | "Key": "deletion_protection.enabled", 940 | "Value": "false", 941 | }, 942 | ], 943 | "Scheme": "internet-facing", 944 | "SecurityGroups": Array [ 945 | Object { 946 | "Fn::GetAtt": Array [ 947 | "WordPressEcsServiceLBSecurityGroup59F9C55A", 948 | "GroupId", 949 | ], 950 | }, 951 | ], 952 | "Subnets": Array [ 953 | Object { 954 | "Ref": "WordPressEcsVpcPublicSubnet1Subnet45F30A18", 955 | }, 956 | Object { 957 | "Ref": "WordPressEcsVpcPublicSubnet2SubnetDB529F9E", 958 | }, 959 | ], 960 | "Type": "application", 961 | }, 962 | "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", 963 | }, 964 | "WordPressEcsServiceLBPublicListenerD9840C9A": Object { 965 | "Properties": Object { 966 | "DefaultActions": Array [ 967 | Object { 968 | "TargetGroupArn": Object { 969 | "Ref": "WordPressEcsServiceLBPublicListenerECSGroup6008A666", 970 | }, 971 | "Type": "forward", 972 | }, 973 | ], 974 | "LoadBalancerArn": Object { 975 | "Ref": "WordPressEcsServiceLB538A1BD5", 976 | }, 977 | "Port": 80, 978 | "Protocol": "HTTP", 979 | }, 980 | "Type": "AWS::ElasticLoadBalancingV2::Listener", 981 | }, 982 | "WordPressEcsServiceLBPublicListenerECSGroup6008A666": Object { 983 | "Properties": Object { 984 | "HealthCheckIntervalSeconds": 60, 985 | "HealthCheckPath": "/wp-includes/images/blank.gif", 986 | "Port": 80, 987 | "Protocol": "HTTP", 988 | "TargetGroupAttributes": Array [ 989 | Object { 990 | "Key": "stickiness.enabled", 991 | "Value": "false", 992 | }, 993 | ], 994 | "TargetType": "instance", 995 | "VpcId": Object { 996 | "Ref": "WordPressEcsVpc532EC8A6", 997 | }, 998 | }, 999 | "Type": "AWS::ElasticLoadBalancingV2::TargetGroup", 1000 | }, 1001 | "WordPressEcsServiceLBSecurityGroup59F9C55A": Object { 1002 | "Properties": Object { 1003 | "GroupDescription": "Automatically created Security Group for ELB testingstackWordPressEcsServiceLB2F1A441B", 1004 | "SecurityGroupIngress": Array [ 1005 | Object { 1006 | "CidrIp": "0.0.0.0/0", 1007 | "Description": "Allow from anyone on port 80", 1008 | "FromPort": 80, 1009 | "IpProtocol": "tcp", 1010 | "ToPort": 80, 1011 | }, 1012 | ], 1013 | "VpcId": Object { 1014 | "Ref": "WordPressEcsVpc532EC8A6", 1015 | }, 1016 | }, 1017 | "Type": "AWS::EC2::SecurityGroup", 1018 | }, 1019 | "WordPressEcsServiceLBSecurityGrouptotestingstackWordPressEcsEcsClusterASGInstanceSecurityGroup81EA60CD32768655357ED5E462": Object { 1020 | "Properties": Object { 1021 | "Description": "Load balancer to target", 1022 | "DestinationSecurityGroupId": Object { 1023 | "Fn::GetAtt": Array [ 1024 | "WordPressEcsEcsClusterASGInstanceSecurityGroup47B2EB77", 1025 | "GroupId", 1026 | ], 1027 | }, 1028 | "FromPort": 32768, 1029 | "GroupId": Object { 1030 | "Fn::GetAtt": Array [ 1031 | "WordPressEcsServiceLBSecurityGroup59F9C55A", 1032 | "GroupId", 1033 | ], 1034 | }, 1035 | "IpProtocol": "tcp", 1036 | "ToPort": 65535, 1037 | }, 1038 | "Type": "AWS::EC2::SecurityGroupEgress", 1039 | }, 1040 | "WordPressEcsServiceTaskCountTarget82DE54D8": Object { 1041 | "Properties": Object { 1042 | "MaxCapacity": 6, 1043 | "MinCapacity": 1, 1044 | "ResourceId": Object { 1045 | "Fn::Join": Array [ 1046 | "", 1047 | Array [ 1048 | "service/", 1049 | Object { 1050 | "Ref": "WordPressEcsEcsClusterB051F2EF", 1051 | }, 1052 | "/", 1053 | Object { 1054 | "Fn::GetAtt": Array [ 1055 | "WordPressEcsService756743EE", 1056 | "Name", 1057 | ], 1058 | }, 1059 | ], 1060 | ], 1061 | }, 1062 | "RoleARN": Object { 1063 | "Fn::Join": Array [ 1064 | "", 1065 | Array [ 1066 | "arn:", 1067 | Object { 1068 | "Ref": "AWS::Partition", 1069 | }, 1070 | ":iam::", 1071 | Object { 1072 | "Ref": "AWS::AccountId", 1073 | }, 1074 | ":role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService", 1075 | ], 1076 | ], 1077 | }, 1078 | "ScalableDimension": "ecs:service:DesiredCount", 1079 | "ServiceNamespace": "ecs", 1080 | }, 1081 | "Type": "AWS::ApplicationAutoScaling::ScalableTarget", 1082 | }, 1083 | "WordPressEcsServiceTaskCountTargetCpuScaling1E3F84D5": Object { 1084 | "Properties": Object { 1085 | "PolicyName": "testingstackWordPressEcsServiceTaskCountTargetCpuScalingB6EEC98E", 1086 | "PolicyType": "TargetTrackingScaling", 1087 | "ScalingTargetId": Object { 1088 | "Ref": "WordPressEcsServiceTaskCountTarget82DE54D8", 1089 | }, 1090 | "TargetTrackingScalingPolicyConfiguration": Object { 1091 | "PredefinedMetricSpecification": Object { 1092 | "PredefinedMetricType": "ECSServiceAverageCPUUtilization", 1093 | }, 1094 | "TargetValue": 50, 1095 | }, 1096 | }, 1097 | "Type": "AWS::ApplicationAutoScaling::ScalingPolicy", 1098 | }, 1099 | "WordPressEcsServiceTaskDef5E2B238A": Object { 1100 | "Properties": Object { 1101 | "ContainerDefinitions": Array [ 1102 | Object { 1103 | "Environment": Array [ 1104 | Object { 1105 | "Name": "WORDPRESS_DB_NAME", 1106 | "Value": "wordpress", 1107 | }, 1108 | ], 1109 | "Essential": true, 1110 | "Image": "wordpress", 1111 | "LogConfiguration": Object { 1112 | "LogDriver": "awslogs", 1113 | "Options": Object { 1114 | "awslogs-group": Object { 1115 | "Ref": "WordPressEcsServiceTaskDefwebLogGroup69746206", 1116 | }, 1117 | "awslogs-region": Object { 1118 | "Ref": "AWS::Region", 1119 | }, 1120 | "awslogs-stream-prefix": "Service", 1121 | }, 1122 | }, 1123 | "Memory": 512, 1124 | "MountPoints": Array [ 1125 | Object { 1126 | "ContainerPath": "/var/www/html", 1127 | "ReadOnly": false, 1128 | "SourceVolume": "efs", 1129 | }, 1130 | ], 1131 | "Name": "web", 1132 | "PortMappings": Array [ 1133 | Object { 1134 | "ContainerPort": 80, 1135 | "HostPort": 0, 1136 | "Protocol": "tcp", 1137 | }, 1138 | ], 1139 | "Secrets": Array [ 1140 | Object { 1141 | "Name": "WORDPRESS_DB_HOST", 1142 | "ValueFrom": Object { 1143 | "Fn::Join": Array [ 1144 | "", 1145 | Array [ 1146 | Object { 1147 | "Ref": "WordPressEcsDatabaseSecretAttachment89768602", 1148 | }, 1149 | ":host::", 1150 | ], 1151 | ], 1152 | }, 1153 | }, 1154 | Object { 1155 | "Name": "WORDPRESS_DB_USER", 1156 | "ValueFrom": Object { 1157 | "Fn::Join": Array [ 1158 | "", 1159 | Array [ 1160 | Object { 1161 | "Ref": "WordPressEcsDatabaseSecretAttachment89768602", 1162 | }, 1163 | ":username::", 1164 | ], 1165 | ], 1166 | }, 1167 | }, 1168 | Object { 1169 | "Name": "WORDPRESS_DB_PASSWORD", 1170 | "ValueFrom": Object { 1171 | "Fn::Join": Array [ 1172 | "", 1173 | Array [ 1174 | Object { 1175 | "Ref": "WordPressEcsDatabaseSecretAttachment89768602", 1176 | }, 1177 | ":password::", 1178 | ], 1179 | ], 1180 | }, 1181 | }, 1182 | ], 1183 | }, 1184 | ], 1185 | "ExecutionRoleArn": Object { 1186 | "Fn::GetAtt": Array [ 1187 | "WordPressEcsServiceTaskDefExecutionRole90934E6B", 1188 | "Arn", 1189 | ], 1190 | }, 1191 | "Family": "testingstackWordPressEcsServiceTaskDefC3B4611C", 1192 | "NetworkMode": "bridge", 1193 | "RequiresCompatibilities": Array [ 1194 | "EC2", 1195 | ], 1196 | "TaskRoleArn": Object { 1197 | "Fn::GetAtt": Array [ 1198 | "WordPressEcsServiceTaskDefTaskRole15E267C6", 1199 | "Arn", 1200 | ], 1201 | }, 1202 | "Volumes": Array [ 1203 | Object { 1204 | "EFSVolumeConfiguration": Object { 1205 | "FilesystemId": Object { 1206 | "Ref": "WordPressEcsFileSystem0FE21389", 1207 | }, 1208 | }, 1209 | "Name": "efs", 1210 | }, 1211 | ], 1212 | }, 1213 | "Type": "AWS::ECS::TaskDefinition", 1214 | }, 1215 | "WordPressEcsServiceTaskDefExecutionRole90934E6B": Object { 1216 | "Properties": Object { 1217 | "AssumeRolePolicyDocument": Object { 1218 | "Statement": Array [ 1219 | Object { 1220 | "Action": "sts:AssumeRole", 1221 | "Effect": "Allow", 1222 | "Principal": Object { 1223 | "Service": "ecs-tasks.amazonaws.com", 1224 | }, 1225 | }, 1226 | ], 1227 | "Version": "2012-10-17", 1228 | }, 1229 | }, 1230 | "Type": "AWS::IAM::Role", 1231 | }, 1232 | "WordPressEcsServiceTaskDefExecutionRoleDefaultPolicy49FFB904": Object { 1233 | "Properties": Object { 1234 | "PolicyDocument": Object { 1235 | "Statement": Array [ 1236 | Object { 1237 | "Action": Array [ 1238 | "logs:CreateLogStream", 1239 | "logs:PutLogEvents", 1240 | ], 1241 | "Effect": "Allow", 1242 | "Resource": Object { 1243 | "Fn::GetAtt": Array [ 1244 | "WordPressEcsServiceTaskDefwebLogGroup69746206", 1245 | "Arn", 1246 | ], 1247 | }, 1248 | }, 1249 | Object { 1250 | "Action": Array [ 1251 | "secretsmanager:GetSecretValue", 1252 | "secretsmanager:DescribeSecret", 1253 | ], 1254 | "Effect": "Allow", 1255 | "Resource": Object { 1256 | "Ref": "WordPressEcsDatabaseSecretAttachment89768602", 1257 | }, 1258 | }, 1259 | ], 1260 | "Version": "2012-10-17", 1261 | }, 1262 | "PolicyName": "WordPressEcsServiceTaskDefExecutionRoleDefaultPolicy49FFB904", 1263 | "Roles": Array [ 1264 | Object { 1265 | "Ref": "WordPressEcsServiceTaskDefExecutionRole90934E6B", 1266 | }, 1267 | ], 1268 | }, 1269 | "Type": "AWS::IAM::Policy", 1270 | }, 1271 | "WordPressEcsServiceTaskDefTaskRole15E267C6": Object { 1272 | "Properties": Object { 1273 | "AssumeRolePolicyDocument": Object { 1274 | "Statement": Array [ 1275 | Object { 1276 | "Action": "sts:AssumeRole", 1277 | "Effect": "Allow", 1278 | "Principal": Object { 1279 | "Service": "ecs-tasks.amazonaws.com", 1280 | }, 1281 | }, 1282 | ], 1283 | "Version": "2012-10-17", 1284 | }, 1285 | }, 1286 | "Type": "AWS::IAM::Role", 1287 | }, 1288 | "WordPressEcsServiceTaskDefwebLogGroup69746206": Object { 1289 | "DeletionPolicy": "Retain", 1290 | "Type": "AWS::Logs::LogGroup", 1291 | "UpdateReplacePolicy": "Retain", 1292 | }, 1293 | "WordPressEcsVpc532EC8A6": Object { 1294 | "Properties": Object { 1295 | "CidrBlock": "10.0.0.0/16", 1296 | "EnableDnsHostnames": true, 1297 | "EnableDnsSupport": true, 1298 | "InstanceTenancy": "default", 1299 | "Tags": Array [ 1300 | Object { 1301 | "Key": "Name", 1302 | "Value": "testing-stack/WordPressEcs/Vpc", 1303 | }, 1304 | ], 1305 | }, 1306 | "Type": "AWS::EC2::VPC", 1307 | }, 1308 | "WordPressEcsVpcIGWF8916901": Object { 1309 | "Properties": Object { 1310 | "Tags": Array [ 1311 | Object { 1312 | "Key": "Name", 1313 | "Value": "testing-stack/WordPressEcs/Vpc", 1314 | }, 1315 | ], 1316 | }, 1317 | "Type": "AWS::EC2::InternetGateway", 1318 | }, 1319 | "WordPressEcsVpcPrivateSubnet1DefaultRoute9F4F024F": Object { 1320 | "Properties": Object { 1321 | "DestinationCidrBlock": "0.0.0.0/0", 1322 | "NatGatewayId": Object { 1323 | "Ref": "WordPressEcsVpcPublicSubnet1NATGatewayD1A35C79", 1324 | }, 1325 | "RouteTableId": Object { 1326 | "Ref": "WordPressEcsVpcPrivateSubnet1RouteTable978C9236", 1327 | }, 1328 | }, 1329 | "Type": "AWS::EC2::Route", 1330 | }, 1331 | "WordPressEcsVpcPrivateSubnet1RouteTable978C9236": Object { 1332 | "Properties": Object { 1333 | "Tags": Array [ 1334 | Object { 1335 | "Key": "Name", 1336 | "Value": "testing-stack/WordPressEcs/Vpc/PrivateSubnet1", 1337 | }, 1338 | ], 1339 | "VpcId": Object { 1340 | "Ref": "WordPressEcsVpc532EC8A6", 1341 | }, 1342 | }, 1343 | "Type": "AWS::EC2::RouteTable", 1344 | }, 1345 | "WordPressEcsVpcPrivateSubnet1RouteTableAssociationFAC63540": Object { 1346 | "Properties": Object { 1347 | "RouteTableId": Object { 1348 | "Ref": "WordPressEcsVpcPrivateSubnet1RouteTable978C9236", 1349 | }, 1350 | "SubnetId": Object { 1351 | "Ref": "WordPressEcsVpcPrivateSubnet1Subnet8A2983A9", 1352 | }, 1353 | }, 1354 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 1355 | }, 1356 | "WordPressEcsVpcPrivateSubnet1Subnet8A2983A9": Object { 1357 | "Properties": Object { 1358 | "AvailabilityZone": Object { 1359 | "Fn::Select": Array [ 1360 | 0, 1361 | Object { 1362 | "Fn::GetAZs": "", 1363 | }, 1364 | ], 1365 | }, 1366 | "CidrBlock": "10.0.128.0/18", 1367 | "MapPublicIpOnLaunch": false, 1368 | "Tags": Array [ 1369 | Object { 1370 | "Key": "aws-cdk:subnet-name", 1371 | "Value": "Private", 1372 | }, 1373 | Object { 1374 | "Key": "aws-cdk:subnet-type", 1375 | "Value": "Private", 1376 | }, 1377 | Object { 1378 | "Key": "Name", 1379 | "Value": "testing-stack/WordPressEcs/Vpc/PrivateSubnet1", 1380 | }, 1381 | ], 1382 | "VpcId": Object { 1383 | "Ref": "WordPressEcsVpc532EC8A6", 1384 | }, 1385 | }, 1386 | "Type": "AWS::EC2::Subnet", 1387 | }, 1388 | "WordPressEcsVpcPrivateSubnet2DefaultRouteFDFD67D3": Object { 1389 | "Properties": Object { 1390 | "DestinationCidrBlock": "0.0.0.0/0", 1391 | "NatGatewayId": Object { 1392 | "Ref": "WordPressEcsVpcPublicSubnet1NATGatewayD1A35C79", 1393 | }, 1394 | "RouteTableId": Object { 1395 | "Ref": "WordPressEcsVpcPrivateSubnet2RouteTable0F058C18", 1396 | }, 1397 | }, 1398 | "Type": "AWS::EC2::Route", 1399 | }, 1400 | "WordPressEcsVpcPrivateSubnet2RouteTable0F058C18": Object { 1401 | "Properties": Object { 1402 | "Tags": Array [ 1403 | Object { 1404 | "Key": "Name", 1405 | "Value": "testing-stack/WordPressEcs/Vpc/PrivateSubnet2", 1406 | }, 1407 | ], 1408 | "VpcId": Object { 1409 | "Ref": "WordPressEcsVpc532EC8A6", 1410 | }, 1411 | }, 1412 | "Type": "AWS::EC2::RouteTable", 1413 | }, 1414 | "WordPressEcsVpcPrivateSubnet2RouteTableAssociation7D58EE9D": Object { 1415 | "Properties": Object { 1416 | "RouteTableId": Object { 1417 | "Ref": "WordPressEcsVpcPrivateSubnet2RouteTable0F058C18", 1418 | }, 1419 | "SubnetId": Object { 1420 | "Ref": "WordPressEcsVpcPrivateSubnet2SubnetDA96ABD4", 1421 | }, 1422 | }, 1423 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 1424 | }, 1425 | "WordPressEcsVpcPrivateSubnet2SubnetDA96ABD4": Object { 1426 | "Properties": Object { 1427 | "AvailabilityZone": Object { 1428 | "Fn::Select": Array [ 1429 | 1, 1430 | Object { 1431 | "Fn::GetAZs": "", 1432 | }, 1433 | ], 1434 | }, 1435 | "CidrBlock": "10.0.192.0/18", 1436 | "MapPublicIpOnLaunch": false, 1437 | "Tags": Array [ 1438 | Object { 1439 | "Key": "aws-cdk:subnet-name", 1440 | "Value": "Private", 1441 | }, 1442 | Object { 1443 | "Key": "aws-cdk:subnet-type", 1444 | "Value": "Private", 1445 | }, 1446 | Object { 1447 | "Key": "Name", 1448 | "Value": "testing-stack/WordPressEcs/Vpc/PrivateSubnet2", 1449 | }, 1450 | ], 1451 | "VpcId": Object { 1452 | "Ref": "WordPressEcsVpc532EC8A6", 1453 | }, 1454 | }, 1455 | "Type": "AWS::EC2::Subnet", 1456 | }, 1457 | "WordPressEcsVpcPublicSubnet1DefaultRoute302AD6DA": Object { 1458 | "DependsOn": Array [ 1459 | "WordPressEcsVpcVPCGWF055B850", 1460 | ], 1461 | "Properties": Object { 1462 | "DestinationCidrBlock": "0.0.0.0/0", 1463 | "GatewayId": Object { 1464 | "Ref": "WordPressEcsVpcIGWF8916901", 1465 | }, 1466 | "RouteTableId": Object { 1467 | "Ref": "WordPressEcsVpcPublicSubnet1RouteTable752D3055", 1468 | }, 1469 | }, 1470 | "Type": "AWS::EC2::Route", 1471 | }, 1472 | "WordPressEcsVpcPublicSubnet1EIP1CAB09E2": Object { 1473 | "Properties": Object { 1474 | "Domain": "vpc", 1475 | "Tags": Array [ 1476 | Object { 1477 | "Key": "Name", 1478 | "Value": "testing-stack/WordPressEcs/Vpc/PublicSubnet1", 1479 | }, 1480 | ], 1481 | }, 1482 | "Type": "AWS::EC2::EIP", 1483 | }, 1484 | "WordPressEcsVpcPublicSubnet1NATGatewayD1A35C79": Object { 1485 | "Properties": Object { 1486 | "AllocationId": Object { 1487 | "Fn::GetAtt": Array [ 1488 | "WordPressEcsVpcPublicSubnet1EIP1CAB09E2", 1489 | "AllocationId", 1490 | ], 1491 | }, 1492 | "SubnetId": Object { 1493 | "Ref": "WordPressEcsVpcPublicSubnet1Subnet45F30A18", 1494 | }, 1495 | "Tags": Array [ 1496 | Object { 1497 | "Key": "Name", 1498 | "Value": "testing-stack/WordPressEcs/Vpc/PublicSubnet1", 1499 | }, 1500 | ], 1501 | }, 1502 | "Type": "AWS::EC2::NatGateway", 1503 | }, 1504 | "WordPressEcsVpcPublicSubnet1RouteTable752D3055": Object { 1505 | "Properties": Object { 1506 | "Tags": Array [ 1507 | Object { 1508 | "Key": "Name", 1509 | "Value": "testing-stack/WordPressEcs/Vpc/PublicSubnet1", 1510 | }, 1511 | ], 1512 | "VpcId": Object { 1513 | "Ref": "WordPressEcsVpc532EC8A6", 1514 | }, 1515 | }, 1516 | "Type": "AWS::EC2::RouteTable", 1517 | }, 1518 | "WordPressEcsVpcPublicSubnet1RouteTableAssociation51510531": Object { 1519 | "Properties": Object { 1520 | "RouteTableId": Object { 1521 | "Ref": "WordPressEcsVpcPublicSubnet1RouteTable752D3055", 1522 | }, 1523 | "SubnetId": Object { 1524 | "Ref": "WordPressEcsVpcPublicSubnet1Subnet45F30A18", 1525 | }, 1526 | }, 1527 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 1528 | }, 1529 | "WordPressEcsVpcPublicSubnet1Subnet45F30A18": Object { 1530 | "Properties": Object { 1531 | "AvailabilityZone": Object { 1532 | "Fn::Select": Array [ 1533 | 0, 1534 | Object { 1535 | "Fn::GetAZs": "", 1536 | }, 1537 | ], 1538 | }, 1539 | "CidrBlock": "10.0.0.0/18", 1540 | "MapPublicIpOnLaunch": true, 1541 | "Tags": Array [ 1542 | Object { 1543 | "Key": "aws-cdk:subnet-name", 1544 | "Value": "Public", 1545 | }, 1546 | Object { 1547 | "Key": "aws-cdk:subnet-type", 1548 | "Value": "Public", 1549 | }, 1550 | Object { 1551 | "Key": "Name", 1552 | "Value": "testing-stack/WordPressEcs/Vpc/PublicSubnet1", 1553 | }, 1554 | ], 1555 | "VpcId": Object { 1556 | "Ref": "WordPressEcsVpc532EC8A6", 1557 | }, 1558 | }, 1559 | "Type": "AWS::EC2::Subnet", 1560 | }, 1561 | "WordPressEcsVpcPublicSubnet2DefaultRoute1B791D0C": Object { 1562 | "DependsOn": Array [ 1563 | "WordPressEcsVpcVPCGWF055B850", 1564 | ], 1565 | "Properties": Object { 1566 | "DestinationCidrBlock": "0.0.0.0/0", 1567 | "GatewayId": Object { 1568 | "Ref": "WordPressEcsVpcIGWF8916901", 1569 | }, 1570 | "RouteTableId": Object { 1571 | "Ref": "WordPressEcsVpcPublicSubnet2RouteTable49ECA83F", 1572 | }, 1573 | }, 1574 | "Type": "AWS::EC2::Route", 1575 | }, 1576 | "WordPressEcsVpcPublicSubnet2RouteTable49ECA83F": Object { 1577 | "Properties": Object { 1578 | "Tags": Array [ 1579 | Object { 1580 | "Key": "Name", 1581 | "Value": "testing-stack/WordPressEcs/Vpc/PublicSubnet2", 1582 | }, 1583 | ], 1584 | "VpcId": Object { 1585 | "Ref": "WordPressEcsVpc532EC8A6", 1586 | }, 1587 | }, 1588 | "Type": "AWS::EC2::RouteTable", 1589 | }, 1590 | "WordPressEcsVpcPublicSubnet2RouteTableAssociationA78B3CDF": Object { 1591 | "Properties": Object { 1592 | "RouteTableId": Object { 1593 | "Ref": "WordPressEcsVpcPublicSubnet2RouteTable49ECA83F", 1594 | }, 1595 | "SubnetId": Object { 1596 | "Ref": "WordPressEcsVpcPublicSubnet2SubnetDB529F9E", 1597 | }, 1598 | }, 1599 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 1600 | }, 1601 | "WordPressEcsVpcPublicSubnet2SubnetDB529F9E": Object { 1602 | "Properties": Object { 1603 | "AvailabilityZone": Object { 1604 | "Fn::Select": Array [ 1605 | 1, 1606 | Object { 1607 | "Fn::GetAZs": "", 1608 | }, 1609 | ], 1610 | }, 1611 | "CidrBlock": "10.0.64.0/18", 1612 | "MapPublicIpOnLaunch": true, 1613 | "Tags": Array [ 1614 | Object { 1615 | "Key": "aws-cdk:subnet-name", 1616 | "Value": "Public", 1617 | }, 1618 | Object { 1619 | "Key": "aws-cdk:subnet-type", 1620 | "Value": "Public", 1621 | }, 1622 | Object { 1623 | "Key": "Name", 1624 | "Value": "testing-stack/WordPressEcs/Vpc/PublicSubnet2", 1625 | }, 1626 | ], 1627 | "VpcId": Object { 1628 | "Ref": "WordPressEcsVpc532EC8A6", 1629 | }, 1630 | }, 1631 | "Type": "AWS::EC2::Subnet", 1632 | }, 1633 | "WordPressEcsVpcVPCGWF055B850": Object { 1634 | "Properties": Object { 1635 | "InternetGatewayId": Object { 1636 | "Ref": "WordPressEcsVpcIGWF8916901", 1637 | }, 1638 | "VpcId": Object { 1639 | "Ref": "WordPressEcsVpc532EC8A6", 1640 | }, 1641 | }, 1642 | "Type": "AWS::EC2::VPCGatewayAttachment", 1643 | }, 1644 | }, 1645 | } 1646 | `; 1647 | -------------------------------------------------------------------------------- /test/integ.snapshot.test.ts: -------------------------------------------------------------------------------- 1 | import '@aws-cdk/assert/jest'; 2 | import { SynthUtils } from '@aws-cdk/assert'; 3 | import { IntegTesting } from '../src/integ.default'; 4 | 5 | test('integ snapshot validation', () => { 6 | const integ = new IntegTesting(); 7 | integ.stack.forEach(stack => { 8 | expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); 9 | }); 10 | }); -------------------------------------------------------------------------------- /tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | // ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | { 3 | "compilerOptions": { 4 | "alwaysStrict": true, 5 | "declaration": true, 6 | "esModuleInterop": true, 7 | "experimentalDecorators": true, 8 | "inlineSourceMap": true, 9 | "inlineSources": true, 10 | "lib": [ 11 | "es2019" 12 | ], 13 | "module": "CommonJS", 14 | "noEmitOnError": false, 15 | "noFallthroughCasesInSwitch": true, 16 | "noImplicitAny": true, 17 | "noImplicitReturns": true, 18 | "noImplicitThis": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "resolveJsonModule": true, 22 | "strict": true, 23 | "strictNullChecks": true, 24 | "strictPropertyInitialization": true, 25 | "stripInternal": true, 26 | "target": "ES2019" 27 | }, 28 | "include": [ 29 | "src/**/*.ts", 30 | "test/**/*.ts", 31 | ".projenrc.js" 32 | ], 33 | "exclude": [ 34 | "node_modules" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.6" 3 | } 4 | --------------------------------------------------------------------------------