diff --git a/typescript/ec2-instance/.env.example b/typescript/ec2-instance/.env.example new file mode 100644 index 0000000000..ca7c698dd4 --- /dev/null +++ b/typescript/ec2-instance/.env.example @@ -0,0 +1,11 @@ +# Logging level (INFO, DEBUG, etc.) +LOG_LEVEL=INFO + +# Your SSH public key for instance access +SSH_PUB_KEY=ssh-rsa AAAAB3NzaC1yc2E... + +# CPU architecture (ARM64 or X86) +CPU_TYPE=ARM64 + +# Instance size (LARGE, XLARGE, XLARGE2, XLARGE4) +INSTANCE_SIZE=LARGE diff --git a/typescript/ec2-instance/.eslintrc.json b/typescript/ec2-instance/.eslintrc.json deleted file mode 100644 index 07259f95e7..0000000000 --- a/typescript/ec2-instance/.eslintrc.json +++ /dev/null @@ -1,228 +0,0 @@ -// ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -{ - "env": { - "jest": true, - "node": true - }, - "root": true, - "plugins": [ - "@typescript-eslint", - "import" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module", - "project": "./tsconfig.dev.json" - }, - "extends": [ - "plugin:import/typescript" - ], - "settings": { - "import/parsers": { - "@typescript-eslint/parser": [ - ".ts", - ".tsx" - ] - }, - "import/resolver": { - "node": {}, - "typescript": { - "project": "./tsconfig.dev.json", - "alwaysTryTypes": true - } - } - }, - "ignorePatterns": [ - "resources/**", - "!.projenrc.js" - ], - "rules": { - "indent": [ - "off" - ], - "@typescript-eslint/indent": [ - "error", - 2 - ], - "quotes": [ - "error", - "single", - { - "avoidEscape": true - } - ], - "comma-dangle": [ - "error", - "always-multiline" - ], - "comma-spacing": [ - "error", - { - "before": false, - "after": true - } - ], - "no-multi-spaces": [ - "error", - { - "ignoreEOLComments": false - } - ], - "array-bracket-spacing": [ - "error", - "never" - ], - "array-bracket-newline": [ - "error", - "consistent" - ], - "object-curly-spacing": [ - "error", - "always" - ], - "object-curly-newline": [ - "error", - { - "multiline": true, - "consistent": true - } - ], - "object-property-newline": [ - "error", - { - "allowAllPropertiesOnSameLine": true - } - ], - "keyword-spacing": [ - "error" - ], - "brace-style": [ - "error", - "1tbs", - { - "allowSingleLine": true - } - ], - "space-before-blocks": [ - "error" - ], - "curly": [ - "error", - "multi-line", - "consistent" - ], - "@typescript-eslint/member-delimiter-style": [ - "error" - ], - "semi": [ - "error", - "always" - ], - "max-len": [ - "error", - { - "code": 150, - "ignoreUrls": true, - "ignoreStrings": true, - "ignoreTemplateLiterals": true, - "ignoreComments": true, - "ignoreRegExpLiterals": true - } - ], - "quote-props": [ - "error", - "consistent-as-needed" - ], - "@typescript-eslint/no-require-imports": [ - "error" - ], - "import/no-extraneous-dependencies": [ - "error", - { - "devDependencies": [ - "**/test/**", - "**/build-tools/**" - ], - "optionalDependencies": false, - "peerDependencies": true - } - ], - "import/no-unresolved": [ - "error" - ], - "import/order": [ - "warn", - { - "groups": [ - "builtin", - "external" - ], - "alphabetize": { - "order": "asc", - "caseInsensitive": true - } - } - ], - "no-duplicate-imports": [ - "error" - ], - "no-shadow": [ - "off" - ], - "@typescript-eslint/no-shadow": [ - "error" - ], - "key-spacing": [ - "error" - ], - "no-multiple-empty-lines": [ - "error" - ], - "@typescript-eslint/no-floating-promises": [ - "error" - ], - "no-return-await": [ - "off" - ], - "@typescript-eslint/return-await": [ - "error" - ], - "no-trailing-spaces": [ - "error" - ], - "dot-notation": [ - "error" - ], - "no-bitwise": [ - "error" - ], - "@typescript-eslint/member-ordering": [ - "error", - { - "default": [ - "public-static-field", - "public-static-method", - "protected-static-field", - "protected-static-method", - "private-static-field", - "private-static-method", - "field", - "constructor", - "method" - ] - } - ] - }, - "overrides": [ - { - "files": [ - ".projenrc.js" - ], - "rules": { - "@typescript-eslint/no-require-imports": "off", - "import/no-extraneous-dependencies": "off" - } - } - ] -} diff --git a/typescript/ec2-instance/.gitattributes b/typescript/ec2-instance/.gitattributes deleted file mode 100644 index 37f8188eea..0000000000 --- a/typescript/ec2-instance/.gitattributes +++ /dev/null @@ -1,23 +0,0 @@ -# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". - -*.snap linguist-generated -/.eslintrc.json linguist-generated -/.gitattributes linguist-generated -/.github/pull_request_template.md linguist-generated -/.github/workflows/build.yml linguist-generated -/.github/workflows/pull-request-lint.yml linguist-generated -/.github/workflows/upgrade.yml linguist-generated -/.gitignore linguist-generated -/.mergify.yml linguist-generated -/.npmignore linguist-generated -/.npmrc linguist-generated -/.projen/** linguist-generated -/.projen/deps.json linguist-generated -/.projen/files.json linguist-generated -/.projen/tasks.json linguist-generated -/cdk.json linguist-generated -/LICENSE linguist-generated -/package.json linguist-generated -/tsconfig.dev.json linguist-generated -/tsconfig.json linguist-generated -/yarn.lock linguist-generated \ No newline at end of file diff --git a/typescript/ec2-instance/.github/pull_request_template.md b/typescript/ec2-instance/.github/pull_request_template.md deleted file mode 100644 index 11d479bcae..0000000000 --- a/typescript/ec2-instance/.github/pull_request_template.md +++ /dev/null @@ -1 +0,0 @@ -Fixes # \ No newline at end of file diff --git a/typescript/ec2-instance/.github/workflows/build.yml b/typescript/ec2-instance/.github/workflows/build.yml deleted file mode 100644 index 574ad74475..0000000000 --- a/typescript/ec2-instance/.github/workflows/build.yml +++ /dev/null @@ -1,73 +0,0 @@ -# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". - -name: build -on: - pull_request: {} - workflow_dispatch: {} -jobs: - build: - runs-on: ubuntu-latest - permissions: - contents: write - outputs: - self_mutation_happened: ${{ steps.self_mutation.outputs.self_mutation_happened }} - env: - CI: "true" - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.ref }} - repository: ${{ github.event.pull_request.head.repo.full_name }} - - name: Install dependencies - run: yarn install --check-files - - name: build - run: npx projen build - - name: Find mutations - id: self_mutation - run: |- - git add . - git diff --staged --patch --exit-code > .repo.patch || echo "self_mutation_happened=true" >> $GITHUB_OUTPUT - - name: Upload patch - if: steps.self_mutation.outputs.self_mutation_happened - uses: actions/upload-artifact@v3 - with: - name: .repo.patch - path: .repo.patch - - name: Fail build on mutation - if: steps.self_mutation.outputs.self_mutation_happened - run: |- - echo "::error::Files were changed during build (see build log). If this was triggered from a fork, you will need to update your branch." - cat .repo.patch - exit 1 - self-mutation: - needs: build - runs-on: ubuntu-latest - permissions: - contents: write - if: always() && needs.build.outputs.self_mutation_happened && !(github.event.pull_request.head.repo.full_name != github.repository) - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - token: ${{ secrets.PROJEN_GITHUB_TOKEN }} - ref: ${{ github.event.pull_request.head.ref }} - repository: ${{ github.event.pull_request.head.repo.full_name }} - - name: Download patch - uses: actions/download-artifact@v3 - with: - name: .repo.patch - path: ${{ runner.temp }} - - name: Apply patch - run: '[ -s ${{ runner.temp }}/.repo.patch ] && git apply ${{ runner.temp }}/.repo.patch || echo "Empty patch. Skipping."' - - name: Set git identity - run: |- - git config user.name "github-actions" - git config user.email "github-actions@github.com" - - name: Push changes - env: - PULL_REQUEST_REF: ${{ github.event.pull_request.head.ref }} - run: |- - git add . - git commit -s -m "chore: self mutation" - git push origin HEAD:$PULL_REQUEST_REF diff --git a/typescript/ec2-instance/.github/workflows/pull-request-lint.yml b/typescript/ec2-instance/.github/workflows/pull-request-lint.yml deleted file mode 100644 index 6758951b5f..0000000000 --- a/typescript/ec2-instance/.github/workflows/pull-request-lint.yml +++ /dev/null @@ -1,29 +0,0 @@ -# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". - -name: pull-request-lint -on: - pull_request_target: - types: - - labeled - - opened - - synchronize - - reopened - - ready_for_review - - edited -jobs: - validate: - name: Validate PR title - runs-on: ubuntu-latest - permissions: - pull-requests: write - steps: - - uses: amannn/action-semantic-pull-request@v5.0.2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - types: |- - feat - fix - chore - requireScope: false - githubBaseUrl: ${{ github.api_url }} diff --git a/typescript/ec2-instance/.github/workflows/upgrade.yml b/typescript/ec2-instance/.github/workflows/upgrade.yml deleted file mode 100644 index cb5ff07110..0000000000 --- a/typescript/ec2-instance/.github/workflows/upgrade.yml +++ /dev/null @@ -1,83 +0,0 @@ -# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". - -name: upgrade -on: - workflow_dispatch: {} - schedule: - - cron: 0 0 * * * -jobs: - upgrade: - name: Upgrade - runs-on: ubuntu-latest - permissions: - contents: read - outputs: - patch_created: ${{ steps.create_patch.outputs.patch_created }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Install dependencies - run: yarn install --check-files --frozen-lockfile - - name: Upgrade dependencies - run: npx projen upgrade - - name: Find mutations - id: create_patch - run: |- - git add . - git diff --staged --patch --exit-code > .repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT - - name: Upload patch - if: steps.create_patch.outputs.patch_created - uses: actions/upload-artifact@v3 - with: - name: .repo.patch - path: .repo.patch - pr: - name: Create Pull Request - needs: upgrade - runs-on: ubuntu-latest - permissions: - contents: read - if: ${{ needs.upgrade.outputs.patch_created }} - steps: - - name: Checkout - uses: actions/checkout@v3 - with: {} - - name: Download patch - uses: actions/download-artifact@v3 - with: - name: .repo.patch - path: ${{ runner.temp }} - - name: Apply patch - run: '[ -s ${{ runner.temp }}/.repo.patch ] && git apply ${{ runner.temp }}/.repo.patch || echo "Empty patch. Skipping."' - - name: Set git identity - run: |- - git config user.name "github-actions" - git config user.email "github-actions@github.com" - - name: Create Pull Request - id: create-pr - uses: peter-evans/create-pull-request@v4 - with: - token: ${{ secrets.PROJEN_GITHUB_TOKEN }} - commit-message: |- - chore(deps): upgrade dependencies - - Upgrades project dependencies. See details in [workflow run]. - - [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - - ------ - - *Automatically created by projen via the "upgrade" workflow* - branch: github-actions/upgrade - title: "chore(deps): upgrade dependencies" - body: |- - Upgrades project dependencies. See details in [workflow run]. - - [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - - ------ - - *Automatically created by projen via the "upgrade" workflow* - author: github-actions - committer: github-actions - signoff: true diff --git a/typescript/ec2-instance/.gitignore b/typescript/ec2-instance/.gitignore index aa5c82deaf..c0ed8424a2 100644 --- a/typescript/ec2-instance/.gitignore +++ b/typescript/ec2-instance/.gitignore @@ -1,58 +1,19 @@ -# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -!/.gitattributes -!/.projen/tasks.json -!/.projen/deps.json -!/.projen/files.json -!/.github/workflows/pull-request-lint.yml -!/package.json -!/LICENSE -!/.npmignore -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json -pids -*.pid -*.seed -*.pid.lock -lib-cov +*.js +!jest.config.js +*.d.ts +node_modules +.env + +# CDK asset staging directory +.cdk.staging +cdk.out + +# Parcel default cache directory +.parcel-cache + +# Build output +lib coverage -*.lcov .nyc_output -build/Release -node_modules/ -jspm_packages/ -*.tsbuildinfo -.eslintcache -*.tgz -.yarn-integrity -.cache -!/.projenrc.js -/test-reports/ -junit.xml -/coverage/ -!/.github/workflows/build.yml -!/.mergify.yml -!/.github/workflows/upgrade.yml -!/.github/pull_request_template.md -!/.npmrc -!/test/ -!/tsconfig.json -!/tsconfig.dev.json -!/src/ -/lib -/dist/ -!/.eslintrc.json -/assets/ -!/cdk.json -/cdk.out/ -.cdk.staging/ -.parcel-cache/ -cdk.out -cdk.context.json -yarn-error.log -dependabot.yml +dist .DS_Store diff --git a/typescript/ec2-instance/.mergify.yml b/typescript/ec2-instance/.mergify.yml deleted file mode 100644 index d0f90113d9..0000000000 --- a/typescript/ec2-instance/.mergify.yml +++ /dev/null @@ -1,24 +0,0 @@ -# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". - -queue_rules: - - name: default - update_method: merge - conditions: - - "#approved-reviews-by>=1" - - -label~=(do-not-merge) - - status-success=build -pull_request_rules: - - name: Automatic merge on approval and successful build - actions: - delete_head_branch: {} - queue: - method: squash - name: default - commit_message_template: |- - {{ title }} (#{{ number }}) - - {{ body }} - conditions: - - "#approved-reviews-by>=1" - - -label~=(do-not-merge) - - status-success=build diff --git a/typescript/ec2-instance/.npmignore b/typescript/ec2-instance/.npmignore deleted file mode 100644 index c3a92f2374..0000000000 --- a/typescript/ec2-instance/.npmignore +++ /dev/null @@ -1,24 +0,0 @@ -# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -/.projen/ -/test-reports/ -junit.xml -/coverage/ -permissions-backup.acl -/.mergify.yml -/test/ -/tsconfig.dev.json -/src/ -!/lib/ -!/lib/**/*.js -!/lib/**/*.d.ts -dist -/tsconfig.json -/.github/ -/.vscode/ -/.idea/ -/.projenrc.js -tsconfig.tsbuildinfo -/.eslintrc.json -!/assets/ -cdk.out/ -.cdk.staging/ diff --git a/typescript/ec2-instance/.projen/deps.json b/typescript/ec2-instance/.projen/deps.json deleted file mode 100644 index 5fe1e1397f..0000000000 --- a/typescript/ec2-instance/.projen/deps.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "dependencies": [ - { - "name": "@types/jest", - "type": "build" - }, - { - "name": "@types/node", - "version": "^16", - "type": "build" - }, - { - "name": "@typescript-eslint/eslint-plugin", - "version": "^5", - "type": "build" - }, - { - "name": "@typescript-eslint/parser", - "version": "^5", - "type": "build" - }, - { - "name": "aws-cdk", - "version": "^2.83.0", - "type": "build" - }, - { - "name": "esbuild", - "type": "build" - }, - { - "name": "eslint-import-resolver-node", - "type": "build" - }, - { - "name": "eslint-import-resolver-typescript", - "type": "build" - }, - { - "name": "eslint-plugin-import", - "type": "build" - }, - { - "name": "eslint", - "version": "^8", - "type": "build" - }, - { - "name": "jest", - "type": "build" - }, - { - "name": "jest-junit", - "version": "^15", - "type": "build" - }, - { - "name": "npm-check-updates", - "version": "^16", - "type": "build" - }, - { - "name": "projen", - "type": "build" - }, - { - "name": "ts-jest", - "type": "build" - }, - { - "name": "ts-node", - "type": "build" - }, - { - "name": "typescript", - "type": "build" - }, - { - "name": "aws-cdk-lib", - "version": "^2.83.0", - "type": "runtime" - }, - { - "name": "constructs", - "version": "^10.0.5", - "type": "runtime" - }, - { - "name": "dotenv", - "type": "runtime" - } - ], - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." -} diff --git a/typescript/ec2-instance/.projen/files.json b/typescript/ec2-instance/.projen/files.json deleted file mode 100644 index ce0b501142..0000000000 --- a/typescript/ec2-instance/.projen/files.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "files": [ - ".eslintrc.json", - ".gitattributes", - ".github/pull_request_template.md", - ".github/workflows/build.yml", - ".github/workflows/pull-request-lint.yml", - ".github/workflows/upgrade.yml", - ".gitignore", - ".mergify.yml", - ".npmignore", - ".npmrc", - ".projen/deps.json", - ".projen/files.json", - ".projen/tasks.json", - "cdk.json", - "LICENSE", - "tsconfig.dev.json", - "tsconfig.json" - ], - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." -} diff --git a/typescript/ec2-instance/.projen/tasks.json b/typescript/ec2-instance/.projen/tasks.json deleted file mode 100644 index eb86dc14f1..0000000000 --- a/typescript/ec2-instance/.projen/tasks.json +++ /dev/null @@ -1,269 +0,0 @@ -{ - "tasks": { - "build": { - "name": "build", - "description": "Full release build", - "steps": [ - { - "spawn": "default" - }, - { - "spawn": "pre-compile" - }, - { - "spawn": "compile" - }, - { - "spawn": "post-compile" - }, - { - "spawn": "test" - }, - { - "spawn": "package" - } - ] - }, - "bundle": { - "name": "bundle", - "description": "Prepare assets" - }, - "clobber": { - "name": "clobber", - "description": "hard resets to HEAD of origin and cleans the local repo", - "env": { - "BRANCH": "$(git branch --show-current)" - }, - "steps": [ - { - "exec": "git checkout -b scratch", - "name": "save current HEAD in \"scratch\" branch" - }, - { - "exec": "git checkout $BRANCH" - }, - { - "exec": "git fetch origin", - "name": "fetch latest changes from origin" - }, - { - "exec": "git reset --hard origin/$BRANCH", - "name": "hard reset to origin commit" - }, - { - "exec": "git clean -fdx", - "name": "clean all untracked files" - }, - { - "say": "ready to rock! (unpushed commits are under the \"scratch\" branch)" - } - ], - "condition": "git diff --exit-code > /dev/null" - }, - "compile": { - "name": "compile", - "description": "Only compile" - }, - "default": { - "name": "default", - "description": "Synthesize project files", - "steps": [ - { - "exec": "node .projenrc.js" - } - ] - }, - "deploy": { - "name": "deploy", - "description": "Deploys your CDK app to the AWS cloud", - "steps": [ - { - "exec": "cdk deploy", - "receiveArgs": true - } - ] - }, - "destroy": { - "name": "destroy", - "description": "Destroys your cdk app in the AWS cloud", - "steps": [ - { - "exec": "cdk destroy", - "receiveArgs": true - } - ] - }, - "diff": { - "name": "diff", - "description": "Diffs the currently deployed app against your code", - "steps": [ - { - "exec": "cdk diff" - } - ] - }, - "eject": { - "name": "eject", - "description": "Remove projen from the project", - "env": { - "PROJEN_EJECTING": "true" - }, - "steps": [ - { - "spawn": "default" - } - ] - }, - "eslint": { - "name": "eslint", - "description": "Runs eslint against the codebase", - "steps": [ - { - "exec": "eslint --ext .ts,.tsx --fix --no-error-on-unmatched-pattern src test build-tools .projenrc.js" - } - ] - }, - "install": { - "name": "install", - "description": "Install project dependencies and update lockfile (non-frozen)", - "steps": [ - { - "exec": "yarn install --check-files" - } - ] - }, - "install:ci": { - "name": "install:ci", - "description": "Install project dependencies using frozen lockfile", - "steps": [ - { - "exec": "yarn install --check-files --frozen-lockfile" - } - ] - }, - "launch": { - "name": "launch", - "steps": [ - { - "exec": "yarn && yarn projen && yarn build && yarn cdk bootstrap && yarn cdk deploy --require-approval never" - } - ] - }, - "package": { - "name": "package", - "description": "Creates the distribution package" - }, - "post-compile": { - "name": "post-compile", - "description": "Runs after successful compilation", - "steps": [ - { - "spawn": "synth:silent" - } - ] - }, - "post-upgrade": { - "name": "post-upgrade", - "description": "Runs after upgrading dependencies" - }, - "pre-compile": { - "name": "pre-compile", - "description": "Prepare the project for compilation" - }, - "synth": { - "name": "synth", - "description": "Synthesizes your cdk app into cdk.out", - "steps": [ - { - "exec": "cdk synth" - } - ] - }, - "synth:silent": { - "name": "synth:silent", - "description": "Synthesizes your cdk app into cdk.out and suppresses the template in stdout (part of \"yarn build\")", - "steps": [ - { - "exec": "cdk synth -q" - } - ] - }, - "test": { - "name": "test", - "description": "Run tests", - "steps": [ - { - "exec": "jest --passWithNoTests --updateSnapshot", - "receiveArgs": true - }, - { - "spawn": "eslint" - } - ] - }, - "test:watch": { - "name": "test:watch", - "description": "Run jest in watch mode", - "steps": [ - { - "exec": "jest --watch" - } - ] - }, - "upgrade": { - "name": "upgrade", - "description": "upgrade dependencies", - "env": { - "CI": "0" - }, - "steps": [ - { - "exec": "yarn upgrade npm-check-updates" - }, - { - "exec": "npm-check-updates --dep dev --upgrade --target=minor" - }, - { - "exec": "npm-check-updates --dep bundle --upgrade --target=minor" - }, - { - "exec": "npm-check-updates --dep peer --upgrade --target=minor" - }, - { - "exec": "npm-check-updates --dep prod --upgrade --target=minor" - }, - { - "exec": "npm-check-updates --dep optional --upgrade --target=minor" - }, - { - "exec": "yarn install --check-files" - }, - { - "exec": "yarn upgrade @types/jest @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser aws-cdk esbuild eslint-import-resolver-node eslint-import-resolver-typescript eslint-plugin-import eslint jest jest-junit npm-check-updates projen ts-jest ts-node typescript aws-cdk-lib constructs dotenv" - }, - { - "exec": "npx projen" - }, - { - "spawn": "post-upgrade" - } - ] - }, - "watch": { - "name": "watch", - "description": "Watches changes in your source code and rebuilds and deploys to the current account", - "steps": [ - { - "exec": "cdk deploy --hotswap" - }, - { - "exec": "cdk watch" - } - ] - } - }, - "env": { - "PATH": "$(npx -c \"node --print process.env.PATH\")" - }, - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." -} diff --git a/typescript/ec2-instance/.projenrc.js b/typescript/ec2-instance/.projenrc.js deleted file mode 100644 index b9dae9e1ef..0000000000 --- a/typescript/ec2-instance/.projenrc.js +++ /dev/null @@ -1,27 +0,0 @@ -const { awscdk } = require('projen'); -const project = new awscdk.AwsCdkTypeScriptApp({ - cdkVersion: '2.83.0', - license: 'MIT-0', - author: 'Court Schuett', - copyrightOwner: 'Amazon.com, Inc.', - authorAddress: 'https://aws.amazon.com', - appEntrypoint: 'ec2-instance.ts', - name: 'ec2-instance', - eslintOptions: { ignorePatterns: ['resources/**'] }, - devDeps: ['esbuild'], - defaultReleaseBranch: 'master', - deps: ['dotenv'], -}); - -const common_exclude = [ - 'cdk.out', - 'cdk.context.json', - 'yarn-error.log', - 'dependabot.yml', - '.DS_Store', -]; -project.addTask('launch', { - exec: 'yarn && yarn projen && yarn build && yarn cdk bootstrap && yarn cdk deploy --require-approval never', -}); -project.gitignore.exclude(...common_exclude); -project.synth(); diff --git a/typescript/ec2-instance/LICENSE b/typescript/ec2-instance/LICENSE deleted file mode 100644 index 5a26b5925e..0000000000 --- a/typescript/ec2-instance/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -Copyright 2023 Amazon.com, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/typescript/ec2-instance/README.md b/typescript/ec2-instance/README.md index 2aeed5f057..2631d4de95 100644 --- a/typescript/ec2-instance/README.md +++ b/typescript/ec2-instance/README.md @@ -1,149 +1,68 @@ -# EC2 Instance Creation with CDK +# EC2 Instance CDK Example -This example will create: +This project demonstrates how to create an EC2 instance with AWS CDK, including: -- A new VPC -- Two public subnets -- A security group -- An EC2 instance in one of the subnets -- CloudWatch logs for the instance +- VPC with public subnets +- Security groups for SSH access +- EC2 instance with Amazon Linux 2023 +- CloudFormation Init for instance configuration +- Asset deployment via S3 +- CloudWatch integration -## Run Commands at Launch +## Prerequisites -This example shows a variety of ways to configure and build your EC2 Instance using AWS CDK. We will [run commands on your Linux instance at launch](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html) using these methods. +- AWS CLI configured with appropriate credentials +- Node.js 16 or later +- TypeScript -### UserData +## Environment Variables -```typescript -const userData = UserData.forLinux(); -userData.addCommands( - 'yum update -y', - 'curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo', - 'curl -sL https://rpm.nodesource.com/setup_18.x | sudo -E bash - ', - 'yum install -y amazon-cloudwatch-agent nodejs python3-pip zip unzip docker yarn', - 'sudo systemctl enable docker', - 'sudo systemctl start docker', -); -``` +You can customize the deployment with these environment variables: -Here we see [UserData](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-add-user-data.html) being added to EC2 instance and used to set up a variety of services that we can use once the instance has been deployed. These commands will be run as root user and not run interactively. - -### CloudInit - -```typescript - init: CloudFormationInit.fromConfigSets({ - configSets: { - default: ['config'], - }, - configs: { - config: new InitConfig([ - InitFile.fromObject('/etc/config.json', { - STACK_ID: Stack.of(this).artifactId, - }), - InitFile.fromFileInline( - '/tmp/amazon-cloudwatch-agent.json', - './src/resources/server/config/amazon-cloudwatch-agent.json', - ), - InitFile.fromFileInline( - '/etc/config.sh', - 'src/resources/server/config/config.sh', - ), - InitFile.fromString( - '/home/ec2-user/.ssh/authorized_keys', - props.sshPubKey + '\n', - ), - InitCommand.shellCommand('chmod +x /etc/config.sh'), - InitCommand.shellCommand('/etc/config.sh'), - ]), - }, - }), -``` +| Variable | Description | Default | +|----------|-------------|---------| +| `LOG_LEVEL` | Logging level | `INFO` | +| `SSH_PUB_KEY` | Your SSH public key for instance access | ` ` (empty) | +| `CPU_TYPE` | CPU architecture (`ARM64` or `X86`) | `ARM64` | +| `INSTANCE_SIZE` | Instance size (`LARGE`, `XLARGE`, `XLARGE2`, `XLARGE4`) | `LARGE` | -With cloud-init directives, we can copy files to the instance, create new files from strings or objects, and run commands on the instance. In this case, we are configuring our cloudwatch-agent and installing an optional SSH pub key. - -### S3 Bucket - -```typescript -const assetBucket = new Bucket(this, 'assetBucket', { - publicReadAccess: false, - removalPolicy: RemovalPolicy.DESTROY, - objectOwnership: ObjectOwnership.BUCKET_OWNER_PREFERRED, - autoDeleteObjects: true, -}); - -new BucketDeployment(this, 'assetBucketDeployment', { - sources: [Source.asset('src/resources/server/assets')], - destinationBucket: assetBucket, - retainOnDelete: false, - exclude: ['**/node_modules/**', '**/dist/**'], - memoryLimit: 512, -}); -``` +## Getting Started -This demo also includes an S3 bucket that can be accessed by the instance when it is deployed. +```bash +# Install dependencies +npm install -This can be used to download files to the instance using UserData: +# Build the project +npm run build -```typescript - 'mkdir -p /home/ec2-user/sample', - 'aws s3 cp s3://' + - assetBucket.bucketName + - '/sample /home/ec2-user/sample --recursive', +# Deploy the stack +npx cdk deploy ``` -## Cloudwatch Logs - -As part of the deployment, [CloudWatch Agent](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Install-CloudWatch-Agent.html) is installed, configured, and run. This will allow you to see logs from your EC2 instance in CloudWatch log groups. These logs will be available in the `/ec2/log/ec2-example/` log group. - -## Configuration - -Three options are available in the `.env` file to customize your instance: - -- SSH_PUB_KEY -- CPU_TYPE -- INSTANCE_SIZE - -#### SSH_PUB_KEY - -If included, this public key will be added to the `/home/ec2-user/.ssh/authorized_keys` file to allow you to SSH to this instance. +## Connecting to the Instance -#### CPU_TYPE +After deployment, the CDK will output commands to connect to your instance: -Valid options are `ARM64` and `X86` and will be used to determine the instance type deployed. `ARM64` will be used as the default if nothing is included. +- Using SSH: `ssh ec2-user@` +- Using SSM: `aws ssm start-session --target ` -#### INSTANCE_SIZE - -Valid options are `LARGE`, `XLARGE`, `XLARGE2`, and `XLARGE4`. This will be used to determine the size of the instance deployed. `LARGE` will be used if nothing is included. - -## Connecting To - -Two options are available to connect to the created instance. Once deployed, the CDK will output two commands: +## Testing ```bash -Outputs: -EC2Example.sshCommand = ssh ec2-user@ec2-192-0-2-161.compute-1.amazonaws.com -EC2Example.ssmCommand = aws ssm start-session --target i-043xxxxxxxxxxxxxx +npm test ``` -#### SSH - -If you had configured a public key in your `.env` file, you can use SSH to connect to the instance. - -#### SSM - -Alternatively, you can use [SSM](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-sessions-start.html#sessions-start-cli) to connect to the instance. This commands requires AWS CLI and credentials, but does not require a public key to be loaded on the instance. - -## To Deploy - -1. Configure `.env` file (optional) -2. Install [yarn](https://yarnpkg.com/getting-started/install) +## Clean Up ```bash -yarn launch +npx cdk destroy ``` -## To Destroy +## Project Structure -```bash -yarn cdk destroy -``` +- `bin/app.ts` - Entry point for CDK application +- `lib/ec2-stack.ts` - Main stack definition +- `lib/constructs/` - CDK constructs for VPC and EC2 server +- `lib/utils/` - Utility functions and validators +- `lib/resources/` - Configuration files and assets for the EC2 instance +- `test/` - Jest tests for the CDK constructs diff --git a/typescript/ec2-instance/bin/app.ts b/typescript/ec2-instance/bin/app.ts new file mode 100644 index 0000000000..f34efb0ef1 --- /dev/null +++ b/typescript/ec2-instance/bin/app.ts @@ -0,0 +1,26 @@ +#!/usr/bin/env node +import 'dotenv/config'; +import { App } from 'aws-cdk-lib'; +import { EC2Stack } from '../lib/ec2-stack'; + +const app = new App(); + +const devEnv = { + account: process.env.CDK_DEFAULT_ACCOUNT, + region: process.env.CDK_DEFAULT_REGION || 'us-east-1', +}; + +const stackProps = { + logLevel: process.env.LOG_LEVEL || 'INFO', + sshPubKey: process.env.SSH_PUB_KEY || ' ', + cpuType: process.env.CPU_TYPE || 'ARM64', + instanceSize: process.env.INSTANCE_SIZE || 'LARGE', +}; + +new EC2Stack(app, 'EC2Example', { + ...stackProps, + env: devEnv, + description: 'EC2 Instance Example with CDK', +}); + +app.synth(); diff --git a/typescript/ec2-instance/cdk.json b/typescript/ec2-instance/cdk.json index e1f07214b7..79e4652326 100644 --- a/typescript/ec2-instance/cdk.json +++ b/typescript/ec2-instance/cdk.json @@ -1,11 +1,8 @@ { - "app": "npx ts-node -P tsconfig.json --prefer-ts-exts src/ec2-instance.ts", - "output": "cdk.out", - "build": "npx projen bundle", + "app": "npx ts-node --prefer-ts-exts bin/app.ts", "watch": { "include": [ - "src/**/*.ts", - "test/**/*.ts" + "**" ], "exclude": [ "README.md", @@ -15,8 +12,39 @@ "tsconfig.json", "package*.json", "yarn.lock", - "node_modules" + "node_modules", + "test" ] }, - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProposedMajorVersionUpgrade": true, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, + "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, + "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, + "@aws-cdk/aws-kinesis:enableAutoScaling": true, + "@aws-cdk/aws-cloudfront:crossAccountKeyAliasStackSafeResourceName": true + } } diff --git a/typescript/ec2-instance/jest.config.js b/typescript/ec2-instance/jest.config.js new file mode 100644 index 0000000000..08263b8954 --- /dev/null +++ b/typescript/ec2-instance/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; diff --git a/typescript/ec2-instance/package.json b/typescript/ec2-instance/package.json index 59803b8804..188d49bf15 100644 --- a/typescript/ec2-instance/package.json +++ b/typescript/ec2-instance/package.json @@ -1,94 +1,28 @@ { - "name": "ec2-instance", + "name": "ec2-instance-cdk", + "version": "0.1.0", + "bin": { + "ec2-instance-cdk": "bin/app.js" + }, "scripts": { - "build": "npx projen build", - "bundle": "npx projen bundle", - "clobber": "npx projen clobber", - "compile": "npx projen compile", - "default": "npx projen default", - "deploy": "npx projen deploy", - "destroy": "npx projen destroy", - "diff": "npx projen diff", - "eject": "npx projen eject", - "eslint": "npx projen eslint", - "launch": "npx projen launch", - "package": "npx projen package", - "post-compile": "npx projen post-compile", - "post-upgrade": "npx projen post-upgrade", - "pre-compile": "npx projen pre-compile", - "synth": "npx projen synth", - "synth:silent": "npx projen synth:silent", - "test": "npx projen test", - "test:watch": "npx projen test:watch", - "upgrade": "npx projen upgrade", - "watch": "npx projen watch", - "projen": "npx projen" + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk" }, "devDependencies": { "@types/jest": "^29.5.3", "@types/node": "^16", - "@typescript-eslint/eslint-plugin": "^5", - "@typescript-eslint/parser": "^5", - "aws-cdk": "^2.83.0", - "esbuild": "^0.18.17", - "eslint": "^8", - "eslint-import-resolver-node": "^0.3.7", - "eslint-import-resolver-typescript": "^3.5.5", - "eslint-plugin-import": "^2.28.0", + "aws-cdk": "2.1010.0", "jest": "^29.6.2", - "jest-junit": "^15", - "npm-check-updates": "^16", - "projen": "^0.71.158", "ts-jest": "^29.1.1", "ts-node": "^10.9.1", "typescript": "^5.1.6" }, "dependencies": { - "aws-cdk-lib": "^2.83.0", + "aws-cdk-lib": "2.189.0", "constructs": "^10.0.5", "dotenv": "^16.3.1" }, - "license": "MIT-0", - "version": "0.0.0", - "jest": { - "testMatch": [ - "/src/**/__tests__/**/*.ts?(x)", - "/(test|src)/**/*(*.)@(spec|test).ts?(x)" - ], - "clearMocks": true, - "collectCoverage": true, - "coverageReporters": [ - "json", - "lcov", - "clover", - "cobertura", - "text" - ], - "coverageDirectory": "coverage", - "coveragePathIgnorePatterns": [ - "/node_modules/" - ], - "testPathIgnorePatterns": [ - "/node_modules/" - ], - "watchPathIgnorePatterns": [ - "/node_modules/" - ], - "reporters": [ - "default", - [ - "jest-junit", - { - "outputDirectory": "test-reports" - } - ] - ], - "preset": "ts-jest", - "globals": { - "ts-jest": { - "tsconfig": "tsconfig.dev.json" - } - } - }, - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." + "license": "MIT-0" } diff --git a/typescript/ec2-instance/src/ec2-instance.ts b/typescript/ec2-instance/src/ec2-instance.ts deleted file mode 100644 index 23fda55272..0000000000 --- a/typescript/ec2-instance/src/ec2-instance.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import { App, Stack, StackProps, CfnOutput } from 'aws-cdk-lib'; -import { Construct } from 'constructs'; -import { config } from 'dotenv'; -import { VPCResources, ServerResources } from '.'; -import { envValidator } from './envValidator'; - -config(); - -export interface EC2ExampleProps extends StackProps { - logLevel: string; - sshPubKey: string; - cpuType: string; - instanceSize: string; -} - -export class EC2Example extends Stack { - constructor(scope: Construct, id: string, props: EC2ExampleProps) { - super(scope, id, props); - - const { logLevel, sshPubKey, cpuType, instanceSize } = props; - - envValidator(props); - - // Create VPC and Security Group - const vpcResources = new VPCResources(this, 'VPC'); - - // Create EC2 Instance - // We will pass props to ServerResources to create the EC2 instance - const serverResources = new ServerResources(this, 'EC2', { - vpc: vpcResources.vpc, - sshSecurityGroup: vpcResources.sshSecurityGroup, - logLevel: logLevel, - sshPubKey: sshPubKey, - cpuType: cpuType, - instanceSize: instanceSize.toLowerCase(), - }); - - // SSM Command to start a session - new CfnOutput(this, 'ssmCommand', { - value: `aws ssm start-session --target ${serverResources.instance.instanceId}`, - }); - - // SSH Command to connect to the EC2 Instance - new CfnOutput(this, 'sshCommand', { - value: `ssh ec2-user@${serverResources.instance.instancePublicDnsName}`, - }); - } -} - -const devEnv = { - account: process.env.CDK_DEFAULT_ACCOUNT, - region: 'us-east-1', -}; - -const stackProps = { - logLevel: process.env.LOG_LEVEL || 'INFO', - sshPubKey: process.env.SSH_PUB_KEY || ' ', - cpuType: process.env.CPU_TYPE || 'ARM64', - instanceSize: process.env.INSTANCE_SIZE || 'LARGE', -}; - -const app = new App(); - -new EC2Example(app, 'EC2Example', { - ...stackProps, - env: devEnv, -}); - -app.synth(); diff --git a/typescript/ec2-instance/src/envValidator.ts b/typescript/ec2-instance/src/envValidator.ts deleted file mode 100644 index 602e082581..0000000000 --- a/typescript/ec2-instance/src/envValidator.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { EC2ExampleProps } from './ec2-instance'; - -export enum InstanceSize { - 'LARGE' = 'large', - 'XLARGE' = 'xlarge', - 'XLARGE2' = 'xlarge2', - 'XLARGE4' = 'xlarge4', -} -export enum CPUTypes { - 'X86' = 'x86', - 'ARM64' = 'arm64', -} - -export function envValidator(props: EC2ExampleProps) { - const validCpuTypes = Object.keys(CPUTypes).join(', '); - if (props.cpuType) { - if (props.cpuType !== 'X86' && props.cpuType !== 'ARM64') { - throw new Error( - `Invalid CPU type. Valid CPU Types are ${validCpuTypes}`, - ); - } - } - - if (props.instanceSize) { - const validSizes = Object.keys(InstanceSize).join(', '); - if ( - !Object.values(InstanceSize).includes( - props.instanceSize.toLowerCase() as InstanceSize, - ) - ) { - throw new Error(`Invalid instance size. Valid sizes are: ${validSizes}`); - } - } -} diff --git a/typescript/ec2-instance/src/index.ts b/typescript/ec2-instance/src/index.ts deleted file mode 100644 index 6b3c08fecb..0000000000 --- a/typescript/ec2-instance/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './server'; -export * from './vpc'; diff --git a/typescript/ec2-instance/src/resources/server/assets/sample/sample.txt b/typescript/ec2-instance/src/resources/server/assets/sample/sample.txt deleted file mode 100644 index 8a6a68e83d..0000000000 --- a/typescript/ec2-instance/src/resources/server/assets/sample/sample.txt +++ /dev/null @@ -1 +0,0 @@ -Sample file that can be downloaded during deploy. diff --git a/typescript/ec2-instance/src/resources/server/config/amazon-cloudwatch-agent.json b/typescript/ec2-instance/src/resources/server/config/amazon-cloudwatch-agent.json deleted file mode 100644 index eff36a2c2f..0000000000 --- a/typescript/ec2-instance/src/resources/server/config/amazon-cloudwatch-agent.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "agent": { - "run_as_user": "root" - }, - "logs": { - "logs_collected": { - "files": { - "collect_list": [ - { - "file_path": "/var/log/cloud-init-output.log", - "log_group_name": "/ec2/log/ec2-example/", - "log_stream_name": "{instance_id}-cloud-init-output", - "retention_in_days": 7 - }, - { - "file_path": "/var/log/cloud-init.log", - "log_group_name": "/ec2/log/ec2-example/", - "log_stream_name": "{instance_id}-cloud-init", - "retention_in_days": 7 - } - ] - } - } - } -} \ No newline at end of file diff --git a/typescript/ec2-instance/src/resources/server/config/config.sh b/typescript/ec2-instance/src/resources/server/config/config.sh deleted file mode 100644 index d88b37ed38..0000000000 --- a/typescript/ec2-instance/src/resources/server/config/config.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -xe -/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/tmp/amazon-cloudwatch-agent.json diff --git a/typescript/ec2-instance/src/server.ts b/typescript/ec2-instance/src/server.ts deleted file mode 100644 index 2659afa2de..0000000000 --- a/typescript/ec2-instance/src/server.ts +++ /dev/null @@ -1,189 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import { RemovalPolicy, Duration, Stack } from 'aws-cdk-lib'; -import { - Vpc, - SecurityGroup, - Instance, - InstanceType, - InstanceClass, - InstanceSize, - CloudFormationInit, - InitConfig, - InitFile, - InitCommand, - UserData, - MachineImage, - AmazonLinuxCpuType, -} from 'aws-cdk-lib/aws-ec2'; -import { - Role, - ServicePrincipal, - ManagedPolicy, - PolicyDocument, - PolicyStatement, -} from 'aws-cdk-lib/aws-iam'; -import { Bucket, ObjectOwnership } from 'aws-cdk-lib/aws-s3'; -import { Source, BucketDeployment } from 'aws-cdk-lib/aws-s3-deployment'; -import { Construct } from 'constructs'; - -interface ServerProps { - vpc: Vpc; - sshSecurityGroup: SecurityGroup; - logLevel: string; - sshPubKey: string; - cpuType: string; - instanceSize: string; -} - -let cpuType: AmazonLinuxCpuType; -let instanceClass: InstanceClass; -let instanceSize: InstanceSize; - -export class ServerResources extends Construct { - public instance: Instance; - - constructor(scope: Construct, id: string, props: ServerProps) { - super(scope, id); - - // Create an Asset Bucket for the Instance. Assets in this bucket will be downloaded to the EC2 during deployment - const assetBucket = new Bucket(this, 'assetBucket', { - publicReadAccess: false, - removalPolicy: RemovalPolicy.DESTROY, - objectOwnership: ObjectOwnership.BUCKET_OWNER_PREFERRED, - autoDeleteObjects: true, - }); - - // Deploy the local assets to the Asset Bucket during the CDK deployment - new BucketDeployment(this, 'assetBucketDeployment', { - sources: [Source.asset('src/resources/server/assets')], - destinationBucket: assetBucket, - retainOnDelete: false, - exclude: ['**/node_modules/**', '**/dist/**'], - memoryLimit: 512, - }); - - // Create a role for the EC2 instance to assume. This role will allow the instance to put log events to CloudWatch Logs - const serverRole = new Role(this, 'serverEc2Role', { - assumedBy: new ServicePrincipal('ec2.amazonaws.com'), - inlinePolicies: { - ['RetentionPolicy']: new PolicyDocument({ - statements: [ - new PolicyStatement({ - resources: ['*'], - actions: ['logs:PutRetentionPolicy'], - }), - ], - }), - }, - managedPolicies: [ - ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'), - ManagedPolicy.fromAwsManagedPolicyName('CloudWatchAgentServerPolicy'), - ], - }); - - // Grant the EC2 role access to the bucket - assetBucket.grantReadWrite(serverRole); - - const userData = UserData.forLinux(); - - // Add user data that is used to configure the EC2 instance - userData.addCommands( - 'yum update -y', - 'curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo', - 'curl -sL https://rpm.nodesource.com/setup_18.x | sudo -E bash - ', - 'yum install -y amazon-cloudwatch-agent nodejs python3-pip zip unzip docker yarn', - 'sudo systemctl enable docker', - 'sudo systemctl start docker', - 'mkdir -p /home/ec2-user/sample', - 'aws s3 cp s3://' + - assetBucket.bucketName + - '/sample /home/ec2-user/sample --recursive', - ); - - // Create a Security Group for the EC2 instance. This group will allow SSH access to the EC2 instance - const ec2InstanceSecurityGroup = new SecurityGroup( - this, - 'ec2InstanceSecurityGroup', - { vpc: props.vpc, allowAllOutbound: true }, - ); - - // Determine the correct CPUType and Instance Class based on the props passed in - if (props.cpuType == 'ARM64') { - cpuType = AmazonLinuxCpuType.ARM_64; - instanceClass = InstanceClass.M7G; - } else { - cpuType = AmazonLinuxCpuType.X86_64; - instanceClass = InstanceClass.M5; - } - - // Determine the correct InstanceSize based on the props passed in - switch (props.instanceSize) { - case 'large': - instanceSize = InstanceSize.LARGE; - break; - case 'xlarge': - instanceSize = InstanceSize.XLARGE; - break; - case 'xlarge2': - instanceSize = InstanceSize.XLARGE2; - break; - case 'xlarge4': - instanceSize = InstanceSize.XLARGE4; - break; - default: - instanceSize = InstanceSize.LARGE; - } - - // Create the EC2 instance - this.instance = new Instance(this, 'Instance', { - vpc: props.vpc, - instanceType: InstanceType.of(instanceClass, instanceSize), - machineImage: MachineImage.latestAmazonLinux2023({ - cachedInContext: false, - cpuType: cpuType, - }), - userData: userData, - securityGroup: ec2InstanceSecurityGroup, - init: CloudFormationInit.fromConfigSets({ - configSets: { - default: ['config'], - }, - configs: { - config: new InitConfig([ - InitFile.fromObject('/etc/config.json', { - // Use CloudformationInit to create an object on the EC2 instance - STACK_ID: Stack.of(this).artifactId, - }), - InitFile.fromFileInline( - // Use CloudformationInit to copy a file to the EC2 instance - '/tmp/amazon-cloudwatch-agent.json', - './src/resources/server/config/amazon-cloudwatch-agent.json', - ), - InitFile.fromFileInline( - '/etc/config.sh', - 'src/resources/server/config/config.sh', - ), - InitFile.fromString( - // Use CloudformationInit to write a string to the EC2 instance - '/home/ec2-user/.ssh/authorized_keys', - props.sshPubKey + '\n', - ), - InitCommand.shellCommand('chmod +x /etc/config.sh'), // Use CloudformationInit to run a shell command on the EC2 instance - InitCommand.shellCommand('/etc/config.sh'), - ]), - }, - }), - - initOptions: { - timeout: Duration.minutes(10), - includeUrl: true, - includeRole: true, - printLog: true, - }, - role: serverRole, - }); - - // Add the SSH Security Group to the EC2 instance - this.instance.addSecurityGroup(props.sshSecurityGroup); - } -} diff --git a/typescript/ec2-instance/src/vpc.ts b/typescript/ec2-instance/src/vpc.ts deleted file mode 100644 index ce5948c9ca..0000000000 --- a/typescript/ec2-instance/src/vpc.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { - SecurityGroup, - Peer, - Port, - SubnetType, - Vpc, -} from 'aws-cdk-lib/aws-ec2'; -import { Construct } from 'constructs'; - -export class VPCResources extends Construct { - public sshSecurityGroup: SecurityGroup; - public vpc: Vpc; - - constructor(scope: Construct, id: string) { - super(scope, id); - - // Create a VPC with public subnets in 2 AZs - this.vpc = new Vpc(this, 'VPC', { - natGateways: 0, - subnetConfiguration: [ - { - cidrMask: 24, - name: 'ServerPublic', - subnetType: SubnetType.PUBLIC, - mapPublicIpOnLaunch: true, - }, - ], - maxAzs: 2, - }); - - // Create a security group for SSH - this.sshSecurityGroup = new SecurityGroup(this, 'SSHSecurityGroup', { - vpc: this.vpc, - description: 'Security Group for SSH', - allowAllOutbound: true, - }); - - // Allow SSH inbound traffic on TCP port 22 - this.sshSecurityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(22)); - } -} diff --git a/typescript/ec2-instance/test/__snapshots__/ec2-stack.test.ts.snap b/typescript/ec2-instance/test/__snapshots__/ec2-stack.test.ts.snap new file mode 100644 index 0000000000..9dd1214dae --- /dev/null +++ b/typescript/ec2-instance/test/__snapshots__/ec2-stack.test.ts.snap @@ -0,0 +1,985 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EC2Stack Snapshot test 1`] = ` +{ + "Outputs": { + "sshCommand": { + "Value": { + "Fn::Join": [ + "", + [ + "ssh ec2-user@", + { + "Fn::GetAtt": [ + "EC2Instance1F00751C93ecacd2478e7d47", + "PublicDnsName", + ], + }, + ], + ], + }, + }, + "ssmCommand": { + "Value": { + "Fn::Join": [ + "", + [ + "aws ssm start-session --target ", + { + "Ref": "EC2Instance1F00751C93ecacd2478e7d47", + }, + ], + ], + }, + }, + }, + "Parameters": { + "BootstrapVersion": { + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]", + "Type": "AWS::SSM::Parameter::Value", + }, + "SsmParameterValueawsserviceamiamazonlinuxlatestal2023amikernel61arm64C96584B6F00A464EAD1953AFF4B05118Parameter": { + "Default": "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-arm64", + "Type": "AWS::SSM::Parameter::Value", + }, + }, + "Resources": { + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C512MiB6723FB92": { + "DependsOn": [ + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C512MiBServiceRoleDefaultPolicy96C3E726", + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C512MiBServiceRoleBA21DBC1", + ], + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-us-east-1", + }, + "S3Key": "NORMALIZED_ASSET_HASH.zip", + }, + "Environment": { + "Variables": { + "AWS_CA_BUNDLE": "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", + }, + }, + "Handler": "index.handler", + "Layers": [ + { + "Ref": "EC2assetBucketDeploymentAwsCliLayerFF55632D", + }, + ], + "MemorySize": 512, + "Role": { + "Fn::GetAtt": [ + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C512MiBServiceRoleBA21DBC1", + "Arn", + ], + }, + "Runtime": "python3.11", + "Timeout": 900, + }, + "Type": "AWS::Lambda::Function", + }, + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C512MiBServiceRoleBA21DBC1": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + ], + ], + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C512MiBServiceRoleDefaultPolicy96C3E726": { + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":s3:::", + { + "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-us-east-1", + }, + ], + ], + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":s3:::", + { + "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-us-east-1", + }, + "/*", + ], + ], + }, + ], + }, + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", + "s3:Abort*", + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "EC2assetBucketC584B4AB", + "Arn", + ], + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "EC2assetBucketC584B4AB", + "Arn", + ], + }, + "/*", + ], + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C512MiBServiceRoleDefaultPolicy96C3E726", + "Roles": [ + { + "Ref": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C512MiBServiceRoleBA21DBC1", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": { + "DependsOn": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + ], + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-us-east-1", + }, + "S3Key": "NORMALIZED_ASSET_HASH.zip", + }, + "Description": { + "Fn::Join": [ + "", + [ + "Lambda function for auto-deleting objects in ", + { + "Ref": "EC2assetBucketC584B4AB", + }, + " S3 bucket.", + ], + ], + }, + "Handler": "index.handler", + "MemorySize": 128, + "Role": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn", + ], + }, + "Runtime": "nodejs20.x", + "Timeout": 900, + }, + "Type": "AWS::Lambda::Function", + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:\${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + "EC2Instance1F00751C93ecacd2478e7d47": { + "CreationPolicy": { + "ResourceSignal": { + "Count": 1, + "Timeout": "PT10M", + }, + }, + "DependsOn": [ + "EC2serverEc2RoleDefaultPolicy34EE5F1D", + "EC2serverEc2Role6775A3D4", + ], + "Metadata": { + "AWS::CloudFormation::Init": { + "config": { + "commands": { + "000": { + "command": "chmod +x /etc/config.sh", + }, + "001": { + "command": "/etc/config.sh", + }, + }, + "files": { + "/etc/config.json": { + "content": { + "STACK_ID": "TestStack", + }, + "group": "root", + "mode": "000644", + "owner": "root", + }, + "/etc/config.sh": { + "content": "#!/bin/bash -xe +/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/tmp/amazon-cloudwatch-agent.json +", + "encoding": "plain", + "group": "root", + "mode": "000644", + "owner": "root", + }, + "/home/ec2-user/.ssh/authorized_keys": { + "content": " +", + "encoding": "plain", + "group": "root", + "mode": "000644", + "owner": "root", + }, + "/tmp/amazon-cloudwatch-agent.json": { + "content": "{ + "agent": { + "run_as_user": "root" + }, + "logs": { + "logs_collected": { + "files": { + "collect_list": [ + { + "file_path": "/var/log/cloud-init-output.log", + "log_group_name": "/ec2/log/ec2-example/", + "log_stream_name": "{instance_id}-cloud-init-output", + "retention_in_days": 7 + }, + { + "file_path": "/var/log/cloud-init.log", + "log_group_name": "/ec2/log/ec2-example/", + "log_stream_name": "{instance_id}-cloud-init", + "retention_in_days": 7 + } + ] + } + } + } +}", + "encoding": "plain", + "group": "root", + "mode": "000644", + "owner": "root", + }, + }, + }, + "configSets": { + "default": [ + "config", + ], + }, + }, + }, + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "", + }, + ], + }, + "IamInstanceProfile": { + "Ref": "EC2InstanceInstanceProfile2CAA3051", + }, + "ImageId": { + "Ref": "SsmParameterValueawsserviceamiamazonlinuxlatestal2023amikernel61arm64C96584B6F00A464EAD1953AFF4B05118Parameter", + }, + "InstanceType": "m7g.large", + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "EC2ec2InstanceSecurityGroupD268D496", + "GroupId", + ], + }, + { + "Fn::GetAtt": [ + "VPCSSHSecurityGroup0495A24F", + "GroupId", + ], + }, + ], + "SubnetId": { + "Ref": "VPCServerPublicSubnet1SubnetF3987995", + }, + "Tags": [ + { + "Key": "Name", + "Value": "TestStack/EC2/Instance", + }, + ], + "UserData": { + "Fn::Base64": { + "Fn::Join": [ + "", + [ + "#!/bin/bash +yum update -y +curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo +curl -sL https://rpm.nodesource.com/setup_18.x | sudo -E bash - +yum install -y amazon-cloudwatch-agent nodejs python3-pip zip unzip docker yarn +sudo systemctl enable docker +sudo systemctl start docker +mkdir -p /home/ec2-user/sample +aws s3 cp s3://", + { + "Ref": "EC2assetBucketC584B4AB", + }, + "/sample /home/ec2-user/sample --recursive +# fingerprint: 32d640a23cf42eea +( + set +e + /opt/aws/bin/cfn-init -v --region ", + { + "Ref": "AWS::Region", + }, + " --stack ", + { + "Ref": "AWS::StackName", + }, + " --resource EC2Instance1F00751C93ecacd2478e7d47 --url https://cloudformation.", + { + "Ref": "AWS::Region", + }, + ".", + { + "Ref": "AWS::URLSuffix", + }, + " --role ", + { + "Ref": "EC2serverEc2Role6775A3D4", + }, + " -c default + /opt/aws/bin/cfn-signal -e $? --region ", + { + "Ref": "AWS::Region", + }, + " --stack ", + { + "Ref": "AWS::StackName", + }, + " --resource EC2Instance1F00751C93ecacd2478e7d47 --url https://cloudformation.", + { + "Ref": "AWS::Region", + }, + ".", + { + "Ref": "AWS::URLSuffix", + }, + " --role ", + { + "Ref": "EC2serverEc2Role6775A3D4", + }, + " + cat /var/log/cfn-init.log >&2 +)", + ], + ], + }, + }, + }, + "Type": "AWS::EC2::Instance", + }, + "EC2InstanceInstanceProfile2CAA3051": { + "Properties": { + "Roles": [ + { + "Ref": "EC2serverEc2Role6775A3D4", + }, + ], + }, + "Type": "AWS::IAM::InstanceProfile", + }, + "EC2assetBucketAutoDeleteObjectsCustomResource48CBA772": { + "DeletionPolicy": "Delete", + "DependsOn": [ + "EC2assetBucketPolicy31C0B372", + ], + "Properties": { + "BucketName": { + "Ref": "EC2assetBucketC584B4AB", + }, + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn", + ], + }, + }, + "Type": "Custom::S3AutoDeleteObjects", + "UpdateReplacePolicy": "Delete", + }, + "EC2assetBucketC584B4AB": { + "DeletionPolicy": "Delete", + "Properties": { + "OwnershipControls": { + "Rules": [ + { + "ObjectOwnership": "BucketOwnerPreferred", + }, + ], + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true", + }, + { + "Key": "aws-cdk:cr-owned:566cfaa5", + "Value": "true", + }, + ], + }, + "Type": "AWS::S3::Bucket", + "UpdateReplacePolicy": "Delete", + }, + "EC2assetBucketDeploymentAwsCliLayerFF55632D": { + "Properties": { + "Content": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-us-east-1", + }, + "S3Key": "NORMALIZED_ASSET_HASH.zip", + }, + "Description": "/opt/awscli/aws", + }, + "Type": "AWS::Lambda::LayerVersion", + }, + "EC2assetBucketDeploymentCustomResource512MiB0663CD81": { + "DeletionPolicy": "Delete", + "Properties": { + "DestinationBucketName": { + "Ref": "EC2assetBucketC584B4AB", + }, + "Exclude": [ + "**/node_modules/**", + "**/dist/**", + ], + "OutputObjectKeys": true, + "Prune": true, + "RetainOnDelete": false, + "ServiceToken": { + "Fn::GetAtt": [ + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C512MiB6723FB92", + "Arn", + ], + }, + "SourceBucketNames": [ + { + "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-us-east-1", + }, + ], + "SourceObjectKeys": [ + "906c472a7a70d98f9ddd319c5286b06cc53be23c17afba3dba925f37cd89ba0a.zip", + ], + }, + "Type": "Custom::CDKBucketDeployment", + "UpdateReplacePolicy": "Delete", + }, + "EC2assetBucketPolicy31C0B372": { + "Properties": { + "Bucket": { + "Ref": "EC2assetBucketC584B4AB", + }, + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn", + ], + }, + }, + "Resource": [ + { + "Fn::GetAtt": [ + "EC2assetBucketC584B4AB", + "Arn", + ], + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "EC2assetBucketC584B4AB", + "Arn", + ], + }, + "/*", + ], + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + }, + "Type": "AWS::S3::BucketPolicy", + }, + "EC2ec2InstanceSecurityGroupD268D496": { + "Properties": { + "GroupDescription": "TestStack/EC2/ec2InstanceSecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1", + }, + ], + "VpcId": { + "Ref": "VPC61AD6880", + }, + }, + "Type": "AWS::EC2::SecurityGroup", + }, + "EC2serverEc2Role6775A3D4": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":iam::aws:policy/AmazonSSMManagedInstanceCore", + ], + ], + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":iam::aws:policy/CloudWatchAgentServerPolicy", + ], + ], + }, + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "logs:PutRetentionPolicy", + "Effect": "Allow", + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "RetentionPolicy", + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + "EC2serverEc2RoleDefaultPolicy34EE5F1D": { + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", + "s3:Abort*", + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "EC2assetBucketC584B4AB", + "Arn", + ], + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "EC2assetBucketC584B4AB", + "Arn", + ], + }, + "/*", + ], + ], + }, + ], + }, + { + "Action": [ + "cloudformation:DescribeStackResource", + "cloudformation:SignalResource", + ], + "Effect": "Allow", + "Resource": { + "Ref": "AWS::StackId", + }, + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "EC2serverEc2RoleDefaultPolicy34EE5F1D", + "Roles": [ + { + "Ref": "EC2serverEc2Role6775A3D4", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "VPC61AD6880": { + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "TestStack/VPC/VPC", + }, + ], + }, + "Type": "AWS::EC2::VPC", + }, + "VPCIGWE1DD60CF": { + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "TestStack/VPC/VPC", + }, + ], + }, + "Type": "AWS::EC2::InternetGateway", + }, + "VPCSSHSecurityGroup0495A24F": { + "Properties": { + "GroupDescription": "Security Group for SSH", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1", + }, + ], + "SecurityGroupIngress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "from 0.0.0.0/0:22", + "FromPort": 22, + "IpProtocol": "tcp", + "ToPort": 22, + }, + ], + "VpcId": { + "Ref": "VPC61AD6880", + }, + }, + "Type": "AWS::EC2::SecurityGroup", + }, + "VPCServerPublicSubnet1DefaultRoute0DEC8857": { + "DependsOn": [ + "VPCVPCGW3AFA48F6", + ], + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWE1DD60CF", + }, + "RouteTableId": { + "Ref": "VPCServerPublicSubnet1RouteTable140320E2", + }, + }, + "Type": "AWS::EC2::Route", + }, + "VPCServerPublicSubnet1RouteTable140320E2": { + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "TestStack/VPC/VPC/ServerPublicSubnet1", + }, + ], + "VpcId": { + "Ref": "VPC61AD6880", + }, + }, + "Type": "AWS::EC2::RouteTable", + }, + "VPCServerPublicSubnet1RouteTableAssociationBCA9EE21": { + "Properties": { + "RouteTableId": { + "Ref": "VPCServerPublicSubnet1RouteTable140320E2", + }, + "SubnetId": { + "Ref": "VPCServerPublicSubnet1SubnetF3987995", + }, + }, + "Type": "AWS::EC2::SubnetRouteTableAssociation", + }, + "VPCServerPublicSubnet1SubnetF3987995": { + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "", + }, + ], + }, + "CidrBlock": "10.0.0.0/24", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "ServerPublic", + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public", + }, + { + "Key": "Name", + "Value": "TestStack/VPC/VPC/ServerPublicSubnet1", + }, + ], + "VpcId": { + "Ref": "VPC61AD6880", + }, + }, + "Type": "AWS::EC2::Subnet", + }, + "VPCServerPublicSubnet2DefaultRoute6A434666": { + "DependsOn": [ + "VPCVPCGW3AFA48F6", + ], + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWE1DD60CF", + }, + "RouteTableId": { + "Ref": "VPCServerPublicSubnet2RouteTableD6971BF3", + }, + }, + "Type": "AWS::EC2::Route", + }, + "VPCServerPublicSubnet2RouteTableAssociationE261CDCA": { + "Properties": { + "RouteTableId": { + "Ref": "VPCServerPublicSubnet2RouteTableD6971BF3", + }, + "SubnetId": { + "Ref": "VPCServerPublicSubnet2SubnetDB508B90", + }, + }, + "Type": "AWS::EC2::SubnetRouteTableAssociation", + }, + "VPCServerPublicSubnet2RouteTableD6971BF3": { + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "TestStack/VPC/VPC/ServerPublicSubnet2", + }, + ], + "VpcId": { + "Ref": "VPC61AD6880", + }, + }, + "Type": "AWS::EC2::RouteTable", + }, + "VPCServerPublicSubnet2SubnetDB508B90": { + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "", + }, + ], + }, + "CidrBlock": "10.0.1.0/24", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "ServerPublic", + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public", + }, + { + "Key": "Name", + "Value": "TestStack/VPC/VPC/ServerPublicSubnet2", + }, + ], + "VpcId": { + "Ref": "VPC61AD6880", + }, + }, + "Type": "AWS::EC2::Subnet", + }, + "VPCVPCGW3AFA48F6": { + "Properties": { + "InternetGatewayId": { + "Ref": "VPCIGWE1DD60CF", + }, + "VpcId": { + "Ref": "VPC61AD6880", + }, + }, + "Type": "AWS::EC2::VPCGatewayAttachment", + }, + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5", + ], + { + "Ref": "BootstrapVersion", + }, + ], + }, + ], + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.", + }, + ], + }, + }, +} +`; diff --git a/typescript/ec2-instance/test/__snapshots__/main.test.ts.snap b/typescript/ec2-instance/test/__snapshots__/main.test.ts.snap deleted file mode 100644 index 96daa5fa95..0000000000 --- a/typescript/ec2-instance/test/__snapshots__/main.test.ts.snap +++ /dev/null @@ -1,40 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Snapshot 1`] = ` -{ - "Parameters": { - "BootstrapVersion": { - "Default": "/cdk-bootstrap/hnb659fds/version", - "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]", - "Type": "AWS::SSM::Parameter::Value", - }, - }, - "Rules": { - "CheckBootstrapVersion": { - "Assertions": [ - { - "Assert": { - "Fn::Not": [ - { - "Fn::Contains": [ - [ - "1", - "2", - "3", - "4", - "5", - ], - { - "Ref": "BootstrapVersion", - }, - ], - }, - ], - }, - "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.", - }, - ], - }, - }, -} -`; diff --git a/typescript/ec2-instance/test/ec2-stack.test.ts b/typescript/ec2-instance/test/ec2-stack.test.ts new file mode 100644 index 0000000000..2dd4497680 --- /dev/null +++ b/typescript/ec2-instance/test/ec2-stack.test.ts @@ -0,0 +1,81 @@ +import { App } from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import { EC2Stack } from '../lib/ec2-stack'; +import { InstanceSize, CPUTypes } from '../lib/utils/env-validator'; +import { normalizeTemplate } from '../../test-utils/normalize-template'; + +const devEnv = { + account: process.env.CDK_DEFAULT_ACCOUNT, + region: 'us-east-1', +}; + +const stackProps = { + logLevel: 'INFO', + sshPubKey: '', + cpuType: 'ARM64', + instanceSize: 'LARGE', +}; + +describe('EC2Stack', () => { + test('Snapshot test', () => { + const app = new App(); + const stack = new EC2Stack(app, 'TestStack', { + ...stackProps, + env: devEnv, + }); + + const template = Template.fromStack(stack); + const normalizedTemplate = normalizeTemplate(template.toJSON()); + expect(normalizedTemplate).toMatchSnapshot(); + }); + + test('Instance size validation', () => { + const app = new App(); + + // Valid sizes should not throw errors + Object.keys(InstanceSize).forEach(size => { + expect(() => { + new EC2Stack(app, `Test-${size}`, { + ...stackProps, + instanceSize: size, + env: devEnv, + }); + }).not.toThrow(); + }); + + // Invalid size should throw error + const validSizes = Object.keys(InstanceSize).join(', '); + expect(() => { + new EC2Stack(app, 'TestInvalidSize', { + ...stackProps, + instanceSize: 'BAD_SIZE', + env: devEnv, + }); + }).toThrowError(`Invalid instance size. Valid sizes are: ${validSizes}`); + }); + + test('CPU type validation', () => { + const app = new App(); + + // Valid CPU types should not throw errors + Object.keys(CPUTypes).forEach(cpuType => { + expect(() => { + new EC2Stack(app, `Test-${cpuType}`, { + ...stackProps, + cpuType: cpuType, + env: devEnv, + }); + }).not.toThrow(); + }); + + // Invalid CPU type should throw error + const validCpuTypes = Object.keys(CPUTypes).join(', '); + expect(() => { + new EC2Stack(app, 'TestInvalidCPU', { + ...stackProps, + cpuType: 'BAD_CPU', + env: devEnv, + }); + }).toThrowError(`Invalid CPU type. Valid CPU Types are ${validCpuTypes}`); + }); +}); diff --git a/typescript/ec2-instance/test/main.test.ts b/typescript/ec2-instance/test/main.test.ts deleted file mode 100644 index f07b6e448b..0000000000 --- a/typescript/ec2-instance/test/main.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { App, Stack } from 'aws-cdk-lib'; -import { Template } from 'aws-cdk-lib/assertions'; -import { EC2Example } from '../src/ec2-instance'; -import { InstanceSize, CPUTypes } from '../src/envValidator'; - -const devEnv = { - account: process.env.CDK_DEFAULT_ACCOUNT, - region: 'us-east-1', -}; - -const stackProps = { - logLevel: '', - sshPubKey: '', - cpuType: '', - instanceSize: '', -}; - -const app = new App(); - -const stack = new Stack(app, 'testing-stack', { - ...stackProps, - env: devEnv, -}); - -test('Snapshot', () => { - new EC2Example(stack, 'EC2Example', { - ...stackProps, - }); - - const template = Template.fromStack(stack); - expect(template.toJSON()).toMatchSnapshot(); -}); - -test('LARGE', () => { - new EC2Example(stack, 'LARGE_Test', { - ...stackProps, - instanceSize: 'LARGE', - }); -}); - -test('XLARGE', () => { - new EC2Example(stack, 'XLARGE_Test', { - ...stackProps, - instanceSize: 'XLARGE', - }); -}); - -test('XLARGE2', () => { - new EC2Example(stack, 'XLARGE2_Test', { - ...stackProps, - instanceSize: 'XLARGE2', - }); -}); - -test('XLARGE4', () => { - new EC2Example(stack, 'XLARGE4_Test', { - ...stackProps, - instanceSize: 'XLARGE4', - }); -}); - -test('ARM64', () => { - new EC2Example(stack, 'ARM64_Test', { - ...stackProps, - cpuType: 'ARM64', - }); -}); - -test('X86', () => { - new EC2Example(stack, 'X86_Test', { - ...stackProps, - cpuType: 'X86', - }); -}); - -test('BadSize', () => { - const validSizes = Object.keys(InstanceSize).join(', '); - expect( - () => - new EC2Example(stack, 'BadSize', { - ...stackProps, - instanceSize: 'BAD_SIZE', - }), - ).toThrowError(`Invalid instance size. Valid sizes are: ${validSizes}`); -}); - -test('BadCPU', () => { - const validCpuTypes = Object.keys(CPUTypes).join(', '); - expect( - () => - new EC2Example(stack, 'BadCPU', { - ...stackProps, - cpuType: 'BAD_CPU', - }), - ).toThrowError(`Invalid CPU type. Valid CPU Types are ${validCpuTypes}`); -}); diff --git a/typescript/ec2-instance/tsconfig.dev.json b/typescript/ec2-instance/tsconfig.dev.json index 3bd3671661..f560eba0bd 100644 --- a/typescript/ec2-instance/tsconfig.dev.json +++ b/typescript/ec2-instance/tsconfig.dev.json @@ -1,36 +1,14 @@ -// ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". { + "extends": "./tsconfig.json", "compilerOptions": { - "alwaysStrict": true, - "declaration": true, - "esModuleInterop": true, - "experimentalDecorators": true, - "inlineSourceMap": true, - "inlineSources": true, - "lib": [ - "es2019" - ], - "module": "CommonJS", - "noEmitOnError": false, - "noFallthroughCasesInSwitch": true, - "noImplicitAny": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "resolveJsonModule": true, - "strict": true, - "strictNullChecks": true, - "strictPropertyInitialization": true, - "stripInternal": true, - "target": "ES2019" + "rootDir": "../..", + "baseUrl": ".", + "paths": { + "../../../test-utils/*": ["../../../test-utils/*"] + } }, "include": [ - ".projenrc.js", - "src/**/*.ts", - "test/**/*.ts" - ], - "exclude": [ - "node_modules" + "**/*.ts", + "../../../test-utils/**/*.ts" ] } diff --git a/typescript/ec2-instance/tsconfig.json b/typescript/ec2-instance/tsconfig.json index c96331d0d5..ebe7df3d7e 100644 --- a/typescript/ec2-instance/tsconfig.json +++ b/typescript/ec2-instance/tsconfig.json @@ -1,36 +1,37 @@ -// ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". { "compilerOptions": { - "rootDir": "src", - "outDir": "lib", - "alwaysStrict": true, - "declaration": true, - "esModuleInterop": true, - "experimentalDecorators": true, - "inlineSourceMap": true, - "inlineSources": true, + "target": "ES2018", + "module": "commonjs", "lib": [ - "es2019" + "es2018" ], - "module": "CommonJS", - "noEmitOnError": false, - "noFallthroughCasesInSwitch": true, + "declaration": true, + "strict": true, "noImplicitAny": true, - "noImplicitReturns": true, + "strictNullChecks": true, "noImplicitThis": true, + "alwaysStrict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "resolveJsonModule": true, - "strict": true, - "strictNullChecks": true, - "strictPropertyInitialization": true, - "stripInternal": true, - "target": "ES2019" + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "./node_modules/@types" + ], + "outDir": "lib", + "rootDir": "../.." }, - "include": [ - "src/**/*.ts" - ], "exclude": [ - "cdk.out" + "node_modules", + "cdk.out", + "lib" + ], + "include": [ + "**/*.ts", + "../../test-utils/**/*.ts" ] }