├── cdk.out ├── cdk.out ├── SingleEc2Stack.template.json ├── tree.json └── manifest.json ├── bin ├── single-ec2.d.ts ├── single-ec2.ts └── single-ec2.js ├── scripts ├── versions ├── clean ├── ssh ├── creds ├── copykeys ├── ec2 └── setssh ├── jest.config.js ├── .gitignore ├── docker ├── Dockerfile └── DevWithDocker.md ├── userdata ├── example └── user_script.sh ├── test └── simple-ec2.test.ts ├── configs ├── config.json.example └── config.json.template ├── tsconfig.json ├── cdk.json ├── LICENSE ├── SETUP-DEPS.md ├── package.json ├── lib └── single-ec2-stack.ts └── README.md /cdk.out/cdk.out: -------------------------------------------------------------------------------- 1 | {"version":"16.0.0"} -------------------------------------------------------------------------------- /bin/single-ec2.d.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | -------------------------------------------------------------------------------- /scripts/versions: -------------------------------------------------------------------------------- 1 | set -x 2 | npm -v 3 | node -v 4 | aws --version 5 | cdk --version 6 | npm list --depth=0 7 | 8 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.js 3 | !jest.config.js 4 | *.d.ts 5 | node_modules 6 | .env 7 | .envrc 8 | cdk.context.json 9 | package-lock.json 10 | configs/config.json 11 | yarn.lock 12 | cdk.context.json 13 | 14 | # CDK asset staging directory 15 | .cdk.staging 16 | cdk.out 17 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amazonlinux:latest 2 | RUN yum update && yum install -y \ 3 | shadow-utils sudo tar unzip util-linux wget 4 | 5 | COPY ./userdata/user_script.sh / 6 | RUN chmod ago+x ./user_script.sh 7 | 8 | RUN cd /root && \ 9 | /user_script.sh; \ 10 | exit 0 11 | -------------------------------------------------------------------------------- /scripts/clean: -------------------------------------------------------------------------------- 1 | echo "cleaning up..." 2 | rm -Rf *~ 3 | rm -Rf cdk-outputs.json 4 | rm -Rf lib/*.js lib/*.d.ts 5 | rm -Rf test/*.js test/*.d.ts 6 | rm -Rf package-lock.json yarn.lock 7 | rm -Rf src/*~ lib/*~ test/*~ userdata/*~ scripts/*~ configs/*~ 8 | echo "removing node_modules, this takes a moment..." 9 | rm -Rf node_modules 10 | echo "all done!" 11 | -------------------------------------------------------------------------------- /scripts/ssh: -------------------------------------------------------------------------------- 1 | CONFIG=./configs/config.json 2 | CDKOUT=./cdk-outputs.json 3 | INSTANCEID=`jq -r .[].ec2instanceid $CDKOUT` 4 | KEYFILE=`jq -r .keyFile $CONFIG` 5 | IP_ADDRESS=`aws ec2 describe-instances --filters "Name=instance-id,Values=$INSTANCEID" | jq -r .Reservations[].Instances[].NetworkInterfaces[].Association.PublicIp` 6 | ssh -i $KEYFILE ec2-user@$IP_ADDRESS 7 | 8 | -------------------------------------------------------------------------------- /userdata/example: -------------------------------------------------------------------------------- 1 | useradd username 2 | usermod -G wheel username 3 | # change to allow sudo in the install scripts 4 | echo "username ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers 5 | su -c 'cd ~username;git clone https://github.com/username/examplerepo' username 6 | su -c 'cd ~username/examplerepo;./install' username 7 | # change it back for safety later 8 | sed -i.bak '/username/d' /etc/sudoers 9 | 10 | 11 | -------------------------------------------------------------------------------- /scripts/creds: -------------------------------------------------------------------------------- 1 | CONFIG=./configs/config.json 2 | AWSCREDSVAR=`jq -r .awsConfig $CONFIG` 3 | AWSCREDS="${AWSCREDSVAR//\~/$HOME}" 4 | TARGET=`jq -r .nickName $CONFIG`":" 5 | echo $AWSCREDS $TARGET 6 | if [ $AWSCREDS == "" ]; then 7 | echo "copying credentials is disabled - please set awsConfig in configs/config.json" 8 | exit -1 9 | fi 10 | scp -r "$AWSCREDSVAR" $TARGET 11 | scp -r "$AWSCREDS" $TARGET 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /bin/single-ec2.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from '@aws-cdk/core'; 4 | import { SingleEc2Stack } from '../lib/single-ec2-stack'; 5 | 6 | const app = new cdk.App(); 7 | new SingleEc2Stack(app, 'SingleEc2Stack', { 8 | env: { 9 | account: process.env.CDK_DEFAULT_ACCOUNT, 10 | region: process.env.CDK_DEFAULT_REGION 11 | }, 12 | stackName: process.env.STACKNAME 13 | }); 14 | -------------------------------------------------------------------------------- /test/simple-ec2.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as SingleEc2 from '../lib/single-ec2-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new SingleEc2.SingleEc2Stack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /configs/config.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "stackName": "SingleEc2Stack-dev", 3 | "ec2Name": "dev", 4 | "nickName": "dev", 5 | "ec2Class": "t3", 6 | "ec2Size": "micro", 7 | "keyName": "", 8 | "keyFile": "", 9 | "hostedZoneID": "", 10 | "domainName": "", 11 | "userDataFile": " $PUB 8 | TARGET=`jq -r .nickName configs/config.json`:.ssh/ 9 | echo "" 10 | echo "copying $KEYFILE and $PUB to $TARGET" 11 | scp $KEYFILE $TARGET 12 | scp $PUB $TARGET 13 | echo "copying $PUB to all AWS regions" 14 | AWS_REGIONS="$(aws ec2 describe-regions --query 'Regions[].RegionName' --output text)" 15 | for each_region in ${AWS_REGIONS} ; do aws ec2 import-key-pair --key-name $KEYNAME --public-key-material fileb://$PUB --region $each_region ; done 16 | 17 | 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2018" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "resolveJsonModule": true, 23 | "typeRoots": [ 24 | "./node_modules/@types" 25 | ] 26 | }, 27 | "exclude": [ 28 | "node_modules", 29 | "cdk.out" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/single-ec2.ts", 3 | "context": { 4 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 5 | "@aws-cdk/core:enableStackNameDuplicates": "true", 6 | "aws-cdk:enableDiffNoFail": "true", 7 | "@aws-cdk/core:stackRelativeExports": "true", 8 | "@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true, 9 | "@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true, 10 | "@aws-cdk/aws-kms:defaultKeyPolicies": true, 11 | "@aws-cdk/aws-s3:grantWriteWithoutAcl": true, 12 | "@aws-cdk/aws-ecs-patterns:removeDefaultDesiredCount": true, 13 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 14 | "@aws-cdk/aws-efs:defaultEncryptionAtRest": true, 15 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 16 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /configs/config.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "stackName": "", 5 | "ec2Class": "", 6 | "ec2Size": "", 8 | "keyFile": "", 9 | "hostedZoneID": "", 10 | "domainName": "", 12 | "cdkOut": "cdk-outputs.json", 13 | "timeBomb": "0", 14 | "awsConfig": "~/.aws" 15 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /scripts/ec2: -------------------------------------------------------------------------------- 1 | CDKOUT=./cdk-outputs.json 2 | INSTANCEID=`jq -r .[].ec2instanceid $CDKOUT` 3 | RUNSTATE=`aws ec2 describe-instances --filters "Name=instance-id,Values=$INSTANCEID" | jq -r .Reservations[].Instances[].State.Name` 4 | IP_ADDRESS=`aws ec2 describe-instances --filters "Name=instance-id,Values=$INSTANCEID" | jq -r .Reservations[].Instances[].NetworkInterfaces[].Association.PublicIp` 5 | NICKNAME=`jq -r .nickName configs/config.json` 6 | 7 | if [ $1 == "status" ]; then 8 | echo "ec2 instance $INSTANCEID nicknamed $NICKNAME is $RUNSTATE" at IP Address $IP_ADDRESS 9 | exit 0 10 | fi 11 | if [ $1 == "start" ]; then 12 | if [ $RUNSTATE == "stopped" ]; then 13 | aws ec2 start-instances --instance-ids "$INSTANCEID" 14 | exit 0 15 | else 16 | echo "ec2 instance $INSTANCEID nicknamed $NICKNAME is not in stopped state so cannot be started (check status first)" 17 | exit 0 18 | fi 19 | fi 20 | if [ $1 == "stop" ]; then 21 | if [ $RUNSTATE == "running" ]; then 22 | aws ec2 stop-instances --instance-ids "$INSTANCEID" 23 | exit 0 24 | else 25 | echo "ec2 instance $INSTANCEID nicknamed $NICKNAME is not in running so cannot be stopped (check status first)" 26 | exit 0 27 | fi 28 | fi 29 | 30 | -------------------------------------------------------------------------------- /docker/DevWithDocker.md: -------------------------------------------------------------------------------- 1 | # Using Docker for development 2 | 3 | As an alternative to setting up an [Amazon Elastic Compute Cloud (EC2)](https://aws.amazon.com/pm/ec2/) instance, a [Docker](https://www.docker.com/) container can be created to provide a repeatable environment. 4 | 5 | The provided [Dockerfile](./Dockerfile) uses [Amazon Linux 2](https://aws.amazon.com/amazon-linux-2) and builds on it with the provided [user script](../userdata/user_script.sh) to create an equivalent environment on your local desktop or any Docker host. 6 | 7 | 8 | ### Build the Docker image 9 | 10 | ```bash 11 | cd 12 | 13 | docker build --rm -t chime-ws-dev -f docker/Dockerfile . 14 | ``` 15 | 16 | _NOTE_: `user_script.sh` will error in setting some of the local variables and DNS entries. However, this may not be needed for all development tasks. If this is important for your work, you may wish to modify `user_script.sh` or simply deploy to EC2. 17 | 18 | ### Run Container with interactive terminal 19 | 20 | ```bash 21 | docker run -v $HOME/.aws:/root/.aws:rw -it --entrypoint /bin/bash chime-ws-dev 22 | ``` 23 | 24 | _NOTE_: the AWS credentials and configuration are shared from the host to the guest container. If this is not desired, you may adjust the `-v` mount or otherwise configure the Command Line Interface (CLI). 25 | -------------------------------------------------------------------------------- /SETUP-DEPS.md: -------------------------------------------------------------------------------- 1 | ## Installing Dependencies 2 | 3 | If you are using a yum-based distribution of Linux, you can create a script from the following shell commands that should install the dependencies. 4 | 5 | ```bash 6 | curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" 7 | unzip awscliv2.zip 8 | sudo ./aws/install 9 | rm -Rf aws 10 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash 11 | cat << EOF >> /home/ec2-user/.bash_profile 12 | export NVM_DIR="$HOME/.nvm" 13 | [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm 14 | [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" 15 | EOF 16 | source /home/ec2-user/.bash_profile 17 | nvm install 16 18 | nvm use 16 19 | npm install -g npm nodejs typescript aws-sdk aws-cdk 20 | ``` 21 | 22 | Please refer to the documentation for all relevant packages for installation instructions if you are not running a version of Linux that uses "yum" for installs. 23 | ## Disclaimer 24 | 25 | Deploying the Amazon Chime SDK demo application contained in this repository will cause your AWS Account to be billed for services, including the Amazon Chime SDK, used by the application. 26 | ## Security 27 | 28 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 29 | 30 | ## License 31 | 32 | This library is licensed under the MIT-0 License. See the LICENSE file. 33 | 34 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 35 | SPDX-License-Identifier: MIT-0 36 | -------------------------------------------------------------------------------- /scripts/setssh: -------------------------------------------------------------------------------- 1 | CDKOUT=./cdk-outputs.json 2 | INSTANCEID=`jq -r .[].ec2instanceid $CDKOUT` 3 | if [ -z $INSTANCEID ]; then 4 | echo "no running instance $INSTANCEID found" 5 | exit 6 | else 7 | echo "instance $INSTANCEID is running" 8 | fi 9 | IP_ADDRESS=`aws ec2 describe-instances --filters "Name=instance-id,Values=$INSTANCEID" | jq -r .Reservations[].Instances[].NetworkInterfaces[].Association.PublicIp` 10 | KEYFILE=`jq -r .keyFile configs/config.json` 11 | NICKNAME=`jq -r .nickName configs/config.json` 12 | EC2NAME=`jq -r .ec2Name configs/config.json` 13 | DOMAIN=`aws route53 get-hosted-zone --id Z00609272L7MZI1OMD0IL | jq -r .HostedZone.Name | sed 's/\.$//'` 14 | FQDN=$EC2NAME.$DOMAIN 15 | echo "alias IP address: $IP_ADDRESS" 16 | echo "ec2 hostname $EC2NAME" 17 | echo "ssh nickname: $NICKNAME" 18 | echo "ssh keyFile: $KEYFILE" 19 | echo "FQDN:" $FQDN 20 | cp ~/.ssh/config ~/.ssh/config.bak 21 | OLDALIAS=`ssh -G $NICKNAME |grep ^hostname | sed -n -e 's/hostname //p' | xargs` # xargs strips spaces (neat hack) 22 | echo "oldAlias: " $OLDALIAS 23 | EXISTS=`grep $NICKNAME ~/.ssh/config | sed -n -e 's/Host //p' | xargs` # xargs strips spaces (neat hack) 24 | echo "Existing host: " $EXISTS 25 | if [[ $EXISTS == $NICKNAME ]]; then 26 | echo "Host $EXISTS record exists" 27 | sed -i -e "s/$OLDALIAS/$IP_ADDRESS/g" ~/.ssh/config 28 | ssh-keygen -R $OLDALIAS 29 | ssh-keygen -R $FQDN 30 | else 31 | cat << EOF >> ~/.ssh/config 32 | Host $NICKNAME 33 | HostName $IP_ADDRESS 34 | IdentityFile $KEYFILE 35 | 36 | EOF 37 | fi 38 | echo "new ~/.ssh/config file:" 39 | cat ~/.ssh/config 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "single-ec2", 3 | "version": "0.1.0", 4 | "bin": { 5 | "single-ec2": "bin/single-ec2.js" 6 | }, 7 | "scripts": { 8 | "build": "yarn install && tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk", 12 | "clean": "scripts/clean", 13 | "deploy": "yarn build && STACKNAME=`jq -r .stackName configs/config.json` cdk deploy --outputs-file ./cdk-outputs.json --require-approval never && scripts/setssh", 14 | "prep": "yarn creds && yarn copykeys", 15 | "destroy": "yarn build && STACKNAME=`jq -r .stackName configs/config.json` cdk destroy --force", 16 | "setssh": "scripts/setssh", 17 | "ssh": "scripts/ssh", 18 | "stop": "scripts/ec2 stop", 19 | "start": "scripts/ec2 start", 20 | "status": "scripts/ec2 status", 21 | "creds": "scripts/creds", 22 | "copykeys": "scripts/copykeys" 23 | }, 24 | "devDependencies": { 25 | "@aws-cdk/assert": "1.138.2", 26 | "@types/jest": "^26.0.10", 27 | "@types/node": "^10.17.27", 28 | "jest": "^26.4.2", 29 | "ts-jest": "^26.2.0", 30 | "ts-node": "^9.0.0", 31 | "typescript": "^4.5.4" 32 | }, 33 | "dependencies": { 34 | "@aws-cdk/aws-ec2": "^1.153.0", 35 | "@aws-cdk/aws-iam": "^1.153.0", 36 | "@aws-cdk/core": "^1.153.0", 37 | "@aws-sdk/client-route-53": "^3.45.0", 38 | "aws-cdk": "^2.21.0", 39 | "aws-sdk": "^2.1049.0", 40 | "cdk-time-bomb": "^1.53.0", 41 | "dotenv": "^11.0.0", 42 | "process": "^0.11.10", 43 | "source-map-support": "^0.5.16", 44 | "untildify": "^4.0.0", 45 | "yarn": "^1.22.17" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /bin/single-ec2.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | require("source-map-support/register"); 5 | const cdk = require("@aws-cdk/core"); 6 | const single_ec2_stack_1 = require("../lib/single-ec2-stack"); 7 | const app = new cdk.App(); 8 | new single_ec2_stack_1.SingleEc2Stack(app, 'SingleEc2Stack', { 9 | env: { 10 | account: process.env.CDK_DEFAULT_ACCOUNT, 11 | region: process.env.CDK_DEFAULT_REGION 12 | }, 13 | stackName: process.env.STACKNAME 14 | }); 15 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2luZ2xlLWVjMi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInNpbmdsZS1lYzIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQ0EsdUNBQXFDO0FBQ3JDLHFDQUFxQztBQUNyQyw4REFBeUQ7QUFFekQsTUFBTSxHQUFHLEdBQUcsSUFBSSxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUM7QUFDMUIsSUFBSSxpQ0FBYyxDQUFDLEdBQUcsRUFBRSxnQkFBZ0IsRUFBRTtJQUN4QyxHQUFHLEVBQUU7UUFDSCxPQUFPLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxtQkFBbUI7UUFDeEMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsa0JBQWtCO0tBQ3ZDO0lBQ0QsU0FBUyxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsU0FBUztDQUNqQyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIjIS91c3IvYmluL2VudiBub2RlXG5pbXBvcnQgJ3NvdXJjZS1tYXAtc3VwcG9ydC9yZWdpc3Rlcic7XG5pbXBvcnQgKiBhcyBjZGsgZnJvbSAnQGF3cy1jZGsvY29yZSc7XG5pbXBvcnQgeyBTaW5nbGVFYzJTdGFjayB9IGZyb20gJy4uL2xpYi9zaW5nbGUtZWMyLXN0YWNrJztcblxuY29uc3QgYXBwID0gbmV3IGNkay5BcHAoKTtcbm5ldyBTaW5nbGVFYzJTdGFjayhhcHAsICdTaW5nbGVFYzJTdGFjaycsIHtcbiAgZW52OiB7XG4gICAgYWNjb3VudDogcHJvY2Vzcy5lbnYuQ0RLX0RFRkFVTFRfQUNDT1VOVCxcbiAgICByZWdpb246IHByb2Nlc3MuZW52LkNES19ERUZBVUxUX1JFR0lPTlxuICB9LFxuICBzdGFja05hbWU6IHByb2Nlc3MuZW52LlNUQUNLTkFNRVxufSk7XG4iXX0= -------------------------------------------------------------------------------- /userdata/user_script.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash -x 2 | # install critial tools 3 | sudo yum -y update 4 | sudo yum remove -y awscli # remove v1 to make way to install v2 5 | sudo yum -y install expect jq curl git 6 | # install AWS CLI 7 | wget "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -O "awscliv2.zip" 8 | unzip -o awscliv2.zip 9 | sudo ./aws/install --bin-dir /usr/local/bin --install-dir /usr/local/aws-cli --update 10 | rm -Rf aws awscliv2.zip 11 | # install SAM 12 | wget https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip -O sam.zip 13 | unzip sam.zip -o -d sam-installation 14 | sudo ./sam-installation/install 15 | sam --version 16 | rm -Rf ./sam-installation sam.zip 17 | # install NVM 18 | export HOME=/usr/local/bin 19 | cd /usr/local/bin 20 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash 21 | cat << "EOF" >> /etc/profile 22 | NVM_DIR=/usr/local/bin/.nvm 23 | [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm 24 | [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" 25 | eval `ssh-agent -s` 26 | for i in $HOME/.ssh/*.pem; 27 | do 28 | [ -f "$i" ] || break 29 | ssh-add $i 30 | done 31 | EOF 32 | 33 | export HOME=/root 34 | cat << "EOF" >> $HOME/.bashrc 35 | source /etc/profile 36 | source $NVM_DIR/nvm.sh 37 | EOF 38 | source $HOME/.bashrc 39 | 40 | nvm install 16 41 | nvm use 16 42 | npm install -g npm nodejs typescript aws-sdk aws-cdk yarn 43 | 44 | # set local variables 45 | INSTANCE_ID=$(wget -qO- http://instance-data/latest/meta-data/instance-id) 46 | HOST=`aws ec2 describe-tags --filters "Name=resource-id,Values=$INSTANCE_ID" "Name=key,Values=nickName" | jq -r .Tags[].Value` 47 | DOMAIN=`aws ec2 describe-tags --filters "Name=resource-id,Values=$INSTANCE_ID" "Name=key,Values=domainName" | jq -r .Tags[].Value` 48 | echo "preserve_hostname: true" >> /etc/cloud/cloud.cfg 49 | hostnamectl set-hostname $HOST.$DOMAIN 50 | echo "ssh-add $KEYFILE" >> /etc/profile 51 | # configure DNS 52 | touch /var/lib/cloud/scripts/per-boot/set-dns 53 | chmod +x /var/lib/cloud/scripts/per-boot/set-dns 54 | cat << "EOF" > /var/lib/cloud/scripts/per-boot/set-dns 55 | #!/bin/bash 56 | INSTANCE_ID=$(wget -qO- http://instance-data/latest/meta-data/instance-id) 57 | MY_IP=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4/) 58 | DOMAIN=`aws ec2 describe-tags --filters "Name=resource-id,Values=$INSTANCE_ID" "Name=key,Values=domainName" | jq -r .Tags[].Value` 59 | HOST=`aws ec2 describe-tags --filters "Name=resource-id,Values=$INSTANCE_ID" "Name=key,Values=nickName" | jq -r .Tags[].Value` 60 | FQDN=$HOST.$DOMAIN 61 | /usr/local/bin/aws route53 change-resource-record-sets --hosted-zone-id ZONE_ID --change-batch '{ 62 | "Changes":[{ 63 | "Action":"UPSERT", 64 | "ResourceRecordSet":{ 65 | "Name": "'$FQDN'", 66 | "Type":"A", 67 | "TTL":10, 68 | "ResourceRecords":[ 69 | { 70 | "Value": "'$MY_IP'" 71 | } 72 | ] 73 | } 74 | }] 75 | }' 76 | EOF 77 | var/lib/cloud/scripts/per-boot/set-dns 78 | 79 | # to inspect what this script did, inspect /var/log/cloud-init-output.log 80 | 81 | 82 | -------------------------------------------------------------------------------- /lib/single-ec2-stack.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from '@aws-cdk/core'; 2 | import * as ec2 from '@aws-cdk/aws-ec2'; // import ec2 library 3 | import * as iam from '@aws-cdk/aws-iam'; // import iam library for permissions 4 | import * as r53 from "@aws-sdk/client-route-53"; 5 | import * as aws from "aws-sdk"; 6 | 7 | import * as fs from 'fs' 8 | import { InstanceClass, InstanceSize } from '@aws-cdk/aws-ec2'; 9 | import { Tags } from '@aws-cdk/core'; 10 | import * as os from 'os'; 11 | import { GetHostedZoneCommand, GetHostedZoneCommandInput } from '@aws-sdk/client-route-53'; 12 | import { SelfDestruct } from 'cdk-time-bomb'; 13 | 14 | interface Config { 15 | stackName: string, 16 | ec2Name: string, 17 | nickName: string, 18 | ec2Class: string, 19 | ec2Size: string, 20 | keyName: string, 21 | keyFile: string, 22 | hostedZoneID: string, 23 | domainName: string, 24 | userDataFile: string 25 | cdkOut: string, 26 | timeBomb: string 27 | } 28 | 29 | const config: Config = require('../configs/config.json'); 30 | const defaultUserData: string = "./userdata/user_script.sh"; 31 | config.userDataFile = config.userDataFile.replace(/^~/, os.homedir()); 32 | console.log("using configuration: ", config); 33 | 34 | export class SingleEc2Stack extends cdk.Stack { 35 | constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { 36 | super(scope, id, props); 37 | 38 | console.log("keyName: ", config.keyName); 39 | console.log("ec2Name: ", config.ec2Name); 40 | 41 | const defaultVpc = ec2.Vpc.fromLookup(this, 'VPC', { isDefault: true }); 42 | 43 | // OPTIONAL self destruct - disabled if config.timeBomb is 0, otherwise in minutes 44 | if (config.timeBomb != "0") { 45 | 46 | const selfDestruct = new SelfDestruct(this, "selfDestructor", { 47 | timeToLive: cdk.Duration.minutes(60) 48 | }); 49 | 50 | defaultVpc.node.addDependency(selfDestruct); 51 | } 52 | 53 | const role = new iam.Role( 54 | this, 55 | config.ec2Name + '-role', 56 | { assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com') } 57 | ) 58 | const dnsPolicyDoc = new iam.PolicyDocument({ 59 | statements: [ 60 | new iam.PolicyStatement({ 61 | effect: iam.Effect.ALLOW, 62 | actions: ["route53:GetChange"], // needed by the set-dns script we install 63 | resources: ["arn:aws:route53:::hostedzone/" + config.hostedZoneID], 64 | }), 65 | new iam.PolicyStatement({ 66 | effect: iam.Effect.ALLOW, 67 | actions: ["route53:ChangeResourceRecordSets"], // needed by the set-dns script we install 68 | resources: ["arn:aws:route53:::hostedzone/" + config.hostedZoneID], 69 | }), 70 | new iam.PolicyStatement({ 71 | effect: iam.Effect.ALLOW, 72 | actions: ["route53:GetHostedZone"], // needed to validate the zone at creation time 73 | resources: ["arn:aws:route53:::hostedzone/" + config.hostedZoneID], 74 | }), 75 | ], 76 | }); 77 | const dnsPolicy = new iam.Policy(this, 'dnsPolicy', { 78 | document: dnsPolicyDoc 79 | }); 80 | role.attachInlinePolicy(dnsPolicy); 81 | 82 | const ssmPolicyDoc = new iam.PolicyDocument({ 83 | statements: [ 84 | new iam.PolicyStatement({ 85 | effect: iam.Effect.ALLOW, 86 | actions: ["ssm:UpdateInstanceInformation", 87 | "ssmmessages:CreateControlChannel", 88 | "ssmmessages:CreateDataChannel", 89 | "ssmmessages:OpenControlChannel", 90 | "ssmmessages:OpenDataChannel"], 91 | resources: ["*"], 92 | }), 93 | ], 94 | }); 95 | const ssmPolicy = new iam.Policy(this, 'ssmPolicy', { 96 | document: ssmPolicyDoc 97 | }); 98 | role.attachInlinePolicy(ssmPolicy); 99 | 100 | const securityGroup = new ec2.SecurityGroup(this, config.ec2Name + 'sg', 101 | { 102 | vpc: defaultVpc, 103 | allowAllOutbound: true, // will let your instance send outboud traffic 104 | securityGroupName: config.ec2Name + '-sg', 105 | } 106 | ) 107 | 108 | // open the SSH port 109 | securityGroup.addIngressRule( 110 | ec2.Peer.anyIpv4(), 111 | ec2.Port.tcp(22), 112 | ) 113 | /* Uncomment this block if you plan on exposing any standard web server 114 | securityGroup.addIngressRule( 115 | ec2.Peer.anyIpv4(), 116 | ec2.Port.tcp(8080), 117 | ) 118 | securityGroup.addIngressRule( 119 | ec2.Peer.anyIpv4(), 120 | ec2.Port.tcp(80), 121 | ) 122 | 123 | securityGroup.addIngressRule( 124 | ec2.Peer.anyIpv4(), 125 | ec2.Port.tcp(443), 126 | ) 127 | */ 128 | const instance = new ec2.Instance(this, config.ec2Name as string, { 129 | vpc: defaultVpc, 130 | role: role, 131 | securityGroup: securityGroup, 132 | instanceName: config.ec2Name, 133 | instanceType: ec2.InstanceType.of( 134 | config.ec2Class as InstanceClass, 135 | config.ec2Size as InstanceSize, 136 | ), 137 | machineImage: ec2.MachineImage.latestAmazonLinux({ 138 | generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2, 139 | }), 140 | keyName: config.keyName, 141 | blockDevices: [ 142 | { 143 | deviceName: '/dev/xvda', 144 | volume: ec2.BlockDeviceVolume.ebs(100), 145 | }, 146 | ], 147 | }); 148 | 149 | const arn: string = "arn:aws:ec2:" + process.env.CDK_DEFAULT_REGION + ":" + process.env.CDK_DEFAULT_ACCOUNT + ":instance/" + instance.instanceId; 150 | const tagPolicyDoc = new iam.PolicyDocument({ 151 | statements: [ 152 | new iam.PolicyStatement({ 153 | effect: iam.Effect.ALLOW, 154 | actions: ["ec2:DescribeTags"], // needed by the set-dns script we install 155 | resources: [arn], 156 | }), 157 | ], 158 | }); 159 | const tagPolicy = new iam.Policy(this, 'tagPollicy', { 160 | document: tagPolicyDoc 161 | }); 162 | role.attachInlinePolicy(tagPolicy); 163 | 164 | 165 | 166 | // add all our configs as tags 167 | Tags.of(instance).add('ec2Name', config.ec2Name); 168 | Tags.of(instance).add('nickName', config.nickName); 169 | Tags.of(instance).add('keyName', config.keyName); 170 | Tags.of(instance).add('keyFile', config.keyFile); 171 | Tags.of(instance).add('hostedZoneID', config.hostedZoneID); 172 | Tags.of(instance).add('domainName', config.domainName); 173 | Tags.of(instance).add('timeBomb', config.timeBomb); 174 | Tags.of(instance).add('ec2Name', config.ec2Name); 175 | 176 | new cdk.CfnOutput(this, 'ec2-instance-ip-address', { 177 | value: instance.instancePublicIp 178 | }) 179 | new cdk.CfnOutput(this, 'ec2-instance-id', { 180 | value: instance.instanceId 181 | }) 182 | new cdk.CfnOutput(this, 'ec2-instance-public-dnsname', { 183 | value: instance.instancePublicDnsName 184 | }) 185 | 186 | let localUserData: string = fs.readFileSync(defaultUserData, 'utf8'); 187 | 188 | // handle DNS, if and only if the config file specifies a zone 189 | if (config.hostedZoneID) { 190 | const zoneName = verifyHostedZone(config.hostedZoneID, config.ec2Name); 191 | if (zoneName) { 192 | localUserData = localUserData.replace("ZONE_ID", config.hostedZoneID); 193 | localUserData = localUserData.replace("ZONE_NAME", config.ec2Name + zoneName); 194 | } else { 195 | console.log("DNS name not set due to error obtaining hosted zone information"); 196 | } 197 | } else { 198 | console.log("DNS name not set"); 199 | } 200 | var userData: string = ""; 201 | if (config.userDataFile) { 202 | userData = fs.readFileSync(config.userDataFile, 'utf8'); 203 | } 204 | const totalUserData: string = localUserData + userData; 205 | console.log("creating userdata script: "); 206 | console.log(totalUserData); 207 | instance.addUserData(totalUserData); 208 | 209 | } 210 | } 211 | 212 | async function verifyHostedZone(hostedZoneID: string, ec2Name: string) { 213 | const config = '{}'; 214 | const client = new r53.Route53Client(config); 215 | const input: GetHostedZoneCommandInput = { Id: hostedZoneID }; 216 | const command = new r53.GetHostedZoneCommand(input); 217 | try { 218 | const response = await client.send(command); 219 | console.log(response); 220 | return response; 221 | } catch (error) { 222 | console.log(error); 223 | return null; 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /cdk.out/SingleEc2Stack.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Resources": { 3 | "devrole61AE7F72": { 4 | "Type": "AWS::IAM::Role", 5 | "Properties": { 6 | "AssumeRolePolicyDocument": { 7 | "Statement": [ 8 | { 9 | "Action": "sts:AssumeRole", 10 | "Effect": "Allow", 11 | "Principal": { 12 | "Service": "ec2.amazonaws.com" 13 | } 14 | } 15 | ], 16 | "Version": "2012-10-17" 17 | } 18 | }, 19 | "Metadata": { 20 | "aws:cdk:path": "SingleEc2Stack/dev-role/Resource" 21 | } 22 | }, 23 | "dnsPolicy90BE57CE": { 24 | "Type": "AWS::IAM::Policy", 25 | "Properties": { 26 | "PolicyDocument": { 27 | "Statement": [ 28 | { 29 | "Action": "route53:GetChange", 30 | "Effect": "Allow", 31 | "Resource": "arn:aws:route53:::hostedzone/Z00609272L7MZI1OMD0IL" 32 | }, 33 | { 34 | "Action": "route53:ChangeResourceRecordSets", 35 | "Effect": "Allow", 36 | "Resource": "arn:aws:route53:::hostedzone/Z00609272L7MZI1OMD0IL" 37 | }, 38 | { 39 | "Action": "route53:GetHostedZone", 40 | "Effect": "Allow", 41 | "Resource": "arn:aws:route53:::hostedzone/Z00609272L7MZI1OMD0IL" 42 | } 43 | ], 44 | "Version": "2012-10-17" 45 | }, 46 | "PolicyName": "dnsPolicy90BE57CE", 47 | "Roles": [ 48 | { 49 | "Ref": "devrole61AE7F72" 50 | } 51 | ] 52 | }, 53 | "Metadata": { 54 | "aws:cdk:path": "SingleEc2Stack/dnsPolicy/Resource" 55 | } 56 | }, 57 | "ssmPolicy178E7BD0": { 58 | "Type": "AWS::IAM::Policy", 59 | "Properties": { 60 | "PolicyDocument": { 61 | "Statement": [ 62 | { 63 | "Action": [ 64 | "ssm:UpdateInstanceInformation", 65 | "ssmmessages:CreateControlChannel", 66 | "ssmmessages:CreateDataChannel", 67 | "ssmmessages:OpenControlChannel", 68 | "ssmmessages:OpenDataChannel" 69 | ], 70 | "Effect": "Allow", 71 | "Resource": "*" 72 | } 73 | ], 74 | "Version": "2012-10-17" 75 | }, 76 | "PolicyName": "ssmPolicy178E7BD0", 77 | "Roles": [ 78 | { 79 | "Ref": "devrole61AE7F72" 80 | } 81 | ] 82 | }, 83 | "Metadata": { 84 | "aws:cdk:path": "SingleEc2Stack/ssmPolicy/Resource" 85 | } 86 | }, 87 | "devsg25C5920B": { 88 | "Type": "AWS::EC2::SecurityGroup", 89 | "Properties": { 90 | "GroupDescription": "SingleEc2Stack/devsg", 91 | "GroupName": "dev-sg", 92 | "SecurityGroupEgress": [ 93 | { 94 | "CidrIp": "0.0.0.0/0", 95 | "Description": "Allow all outbound traffic by default", 96 | "IpProtocol": "-1" 97 | } 98 | ], 99 | "SecurityGroupIngress": [ 100 | { 101 | "CidrIp": "0.0.0.0/0", 102 | "Description": "from 0.0.0.0/0:22", 103 | "FromPort": 22, 104 | "IpProtocol": "tcp", 105 | "ToPort": 22 106 | } 107 | ], 108 | "VpcId": "vpc-34c1924c" 109 | }, 110 | "Metadata": { 111 | "aws:cdk:path": "SingleEc2Stack/devsg/Resource" 112 | } 113 | }, 114 | "devInstanceProfile2C058DB1": { 115 | "Type": "AWS::IAM::InstanceProfile", 116 | "Properties": { 117 | "Roles": [ 118 | { 119 | "Ref": "devrole61AE7F72" 120 | } 121 | ] 122 | }, 123 | "Metadata": { 124 | "aws:cdk:path": "SingleEc2Stack/dev/InstanceProfile" 125 | } 126 | }, 127 | "devF7B884FF": { 128 | "Type": "AWS::EC2::Instance", 129 | "Properties": { 130 | "AvailabilityZone": "us-west-2a", 131 | "BlockDeviceMappings": [ 132 | { 133 | "DeviceName": "/dev/xvda", 134 | "Ebs": { 135 | "VolumeSize": 100 136 | } 137 | } 138 | ], 139 | "IamInstanceProfile": { 140 | "Ref": "devInstanceProfile2C058DB1" 141 | }, 142 | "ImageId": { 143 | "Ref": "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter" 144 | }, 145 | "InstanceType": "t3.2xlarge", 146 | "KeyName": "gherlein-ec2", 147 | "SecurityGroupIds": [ 148 | { 149 | "Fn::GetAtt": [ 150 | "devsg25C5920B", 151 | "GroupId" 152 | ] 153 | } 154 | ], 155 | "SubnetId": "subnet-b342eecb", 156 | "Tags": [ 157 | { 158 | "Key": "domainName", 159 | "Value": "herlein.dev" 160 | }, 161 | { 162 | "Key": "ec2Name", 163 | "Value": "dev" 164 | }, 165 | { 166 | "Key": "hostedZoneID", 167 | "Value": "Z00609272L7MZI1OMD0IL" 168 | }, 169 | { 170 | "Key": "keyFile", 171 | "Value": "~/keys/gherlein-ec2.pem" 172 | }, 173 | { 174 | "Key": "keyName", 175 | "Value": "gherlein-ec2" 176 | }, 177 | { 178 | "Key": "Name", 179 | "Value": "dev" 180 | }, 181 | { 182 | "Key": "nickName", 183 | "Value": "dev" 184 | }, 185 | { 186 | "Key": "timeBomb", 187 | "Value": "0" 188 | } 189 | ], 190 | "UserData": { 191 | "Fn::Base64": "#!/bin/bash\n#! /bin/bash -x\n# install critial tools\nsudo yum -y update\nsudo yum remove -y awscli # remove v1 to make way to install v2\nsudo yum -y install expect jq curl git\n# install AWS CLI\nwget \"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip\" -O \"awscliv2.zip\"\nunzip awscliv2.zip\nsudo ./aws/install --bin-dir /usr/local/bin --install-dir /usr/local/aws-cli --update\nrm -Rf aws awscliv2.zip\n# install SAM\nwget https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip -O sam.zip\nunzip sam.zip -d sam-installation\nsudo ./sam-installation/install\nsam --version\nrm -Rf ./sam-installation sam.zip\n# install NVM\nexport HOME=/usr/local/bin\ncd /usr/local/bin\ncurl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash\ncat << \"EOF\" >> /etc/profile\nNVM_DIR=/usr/local/bin/.nvm\n[ -s \"$NVM_DIR/nvm.sh\" ] && \\. \"$NVM_DIR/nvm.sh\" # This loads nvm\n[ -s \"$NVM_DIR/bash_completion\" ] && \\. \"$NVM_DIR/bash_completion\"\nfor i in $(ls $HOME/.ssh/*.pem);\ndo\n [ -f \"$i\" ] || break\n ssh-add $i\ndone\nEOF\nsource /etc/profile\nsource $NVM_DIR/nvm.sh\nnvm install 16\nnvm use 16\nnpm install -g npm nodejs typescript aws-sdk aws-cdk yarn\nexport HOME=/root\n# set local variables\nINSTANCE_ID=$(wget -qO- http://instance-data/latest/meta-data/instance-id)\nHOST=`aws ec2 describe-tags --filters \"Name=resource-id,Values=$INSTANCE_ID\" \"Name=key,Values=nickName\" | jq -r .Tags[].Value`\nDOMAIN=`aws ec2 describe-tags --filters \"Name=resource-id,Values=$INSTANCE_ID\" \"Name=key,Values=domainName\" | jq -r .Tags[].Value`\necho \"preserve_hostname: true\" >> /etc/cloud/cloud.cfg\nhostnamectl set-hostname $HOST.$DOMAIN\necho \"ssh-add $KEYFILE\" >> /etc/profile\n# configure DNS\ntouch /var/lib/cloud/scripts/per-boot/set-dns\nchmod +x /var/lib/cloud/scripts/per-boot/set-dns\ncat << \"EOF\" > /var/lib/cloud/scripts/per-boot/set-dns\n#!/bin/bash\nINSTANCE_ID=$(wget -qO- http://instance-data/latest/meta-data/instance-id)\nMY_IP=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4/)\nDOMAIN=`aws ec2 describe-tags --filters \"Name=resource-id,Values=$INSTANCE_ID\" \"Name=key,Values=domainName\" | jq -r .Tags[].Value`\nHOST=`aws ec2 describe-tags --filters \"Name=resource-id,Values=$INSTANCE_ID\" \"Name=key,Values=nickName\" | jq -r .Tags[].Value`\nFQDN=$HOST.$DOMAIN\n/usr/local/bin/aws route53 change-resource-record-sets --hosted-zone-id Z00609272L7MZI1OMD0IL --change-batch '{\n \"Changes\":[{\n \"Action\":\"UPSERT\",\n \"ResourceRecordSet\":{\n \"Name\": \"'$FQDN'\",\n \"Type\":\"A\",\n \"TTL\":10,\n \"ResourceRecords\":[\n {\n \"Value\": \"'$MY_IP'\"\n }\n ]\n }\n }]\n}'\nEOF\nvar/lib/cloud/scripts/per-boot/set-dns\n\n# to inspect what this script did, inspect /var/log/cloud-init-output.log\n\n\nuseradd gherlein\nusermod -G wheel gherlein\necho \"gherlein:none\" | chpasswd\nyum install -y emacs-nox\n# change to allow sudo in the install scripts\necho \"gherlein ALL=(ALL) NOPASSWD: ALL\" >> /etc/sudoers \nsu -c 'cd ~gherlein;git clone https://github.com/gherlein/gch' gherlein\nsu -c 'cd ~gherlein/gch/;./install' gherlein\n# change it back for safety later\nsed -i.bak '/gherlein/d' /etc/sudoers\nsu -c 'echo \"~/keychain/keychain --eval --agents ssh gherlein-ec2.pem >> ~/.bash_profile\"' gherlein\nsu -c 'echo \"ssh-add ~/keys/gherlein-ec2.pem >> ~/.bash_profile\"' gherlein\n" 192 | } 193 | }, 194 | "DependsOn": [ 195 | "devrole61AE7F72" 196 | ], 197 | "Metadata": { 198 | "aws:cdk:path": "SingleEc2Stack/dev/Resource" 199 | } 200 | }, 201 | "tagPollicy47586843": { 202 | "Type": "AWS::IAM::Policy", 203 | "Properties": { 204 | "PolicyDocument": { 205 | "Statement": [ 206 | { 207 | "Action": "ec2:DescribeTags", 208 | "Effect": "Allow", 209 | "Resource": { 210 | "Fn::Join": [ 211 | "", 212 | [ 213 | "arn:aws:ec2:us-west-2:497939524935:instance/", 214 | { 215 | "Ref": "devF7B884FF" 216 | } 217 | ] 218 | ] 219 | } 220 | } 221 | ], 222 | "Version": "2012-10-17" 223 | }, 224 | "PolicyName": "tagPollicy47586843", 225 | "Roles": [ 226 | { 227 | "Ref": "devrole61AE7F72" 228 | } 229 | ] 230 | }, 231 | "Metadata": { 232 | "aws:cdk:path": "SingleEc2Stack/tagPollicy/Resource" 233 | } 234 | }, 235 | "CDKMetadata": { 236 | "Type": "AWS::CDK::Metadata", 237 | "Properties": { 238 | "Analytics": "v2:deflate64:H4sIAAAAAAAA/1WNzQrCMBCEn6X3NDVaPAs9iCdLfYKw3UL6k5RNopSQd7epVPQ0u8PMN4KLsuSH7CJfNod2KAIYQh4eTsLAGrTGEyCrOl1LkhM6pPTcvZu9Y5XR1pEHl7w9HFliBSUnHhozbuVNazMqWDbW97qtAKkBazKdGtcuwnFdR/Ck3HIl4+cU+zf20i8gxsi0aZH3tniKMxcnLrLeKpWT105NyJuPvgGQ/dHf8gAAAA==" 239 | }, 240 | "Metadata": { 241 | "aws:cdk:path": "SingleEc2Stack/CDKMetadata/Default" 242 | } 243 | } 244 | }, 245 | "Parameters": { 246 | "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter": { 247 | "Type": "AWS::SSM::Parameter::Value", 248 | "Default": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" 249 | } 250 | }, 251 | "Outputs": { 252 | "ec2instanceipaddress": { 253 | "Value": { 254 | "Fn::GetAtt": [ 255 | "devF7B884FF", 256 | "PublicIp" 257 | ] 258 | } 259 | }, 260 | "ec2instanceid": { 261 | "Value": { 262 | "Ref": "devF7B884FF" 263 | } 264 | }, 265 | "ec2instancepublicdnsname": { 266 | "Value": { 267 | "Fn::GetAtt": [ 268 | "devF7B884FF", 269 | "PublicDnsName" 270 | ] 271 | } 272 | } 273 | } 274 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Single EC2 CDK Script 2 | 3 | This is a simple CDK project that creates a single EC2 instance and copies a common set of tools needed for doing cloud development. This script handles the undifferentiated heavy lifting of creating a development environment. While other methods 4 | exist that give similar results from [AWS Cloud9](https://aws.amazon.com/cloud9/) and [Amazon WorkSpaces](https://www.amazonaws.cn/en/workspaces/), neither of those automatically installs the entire development environment and pre-configures it for access with a remote editor. This solution fully automates the creation of your entire development environment. This solution makes creation of a new AWS Elastic Computing (EC2) instance easy and fully repeatable: you can dispose of and re-create your development environment on demand. 5 | 6 | The example config (in 'configs/config.json.example') uses a t3 micro instance type by default and installs a userdata script at userdata/user_script.sh. You can add user-specific commands to that by adding a pointer to another file. 7 | 8 | You do need to specify an SSH key name and pem file. You can optionally set the DNS name and Amazon Route53 ZoneID and AWS Cloud Development Kit (CDK) will update that for you on every EC2 boot. 9 | 10 | You should realize that deploying this will incur charges for the EC2 instance from your AWS account. 11 | 12 | ## Why Would You Need This? 13 | 14 | It is especially useful to have an easily available linux server on demand in the cloud. When you don't need it, you can stop it or even delete it, and recreate it whenever needed. This is especially useful to to rapid testing on a "clean" linux box, perhaps to ensure that your "new install scripts" work properly, or perhaps because software you installed for another project is not compatible with the work you need to do next. Or maybe you are working through how to learn something and you don't want to risk polluting your primary development machine. Or maybe 15 | you want to follow the steps in an AWS blog or workshop and you want to make sure your own development tools don't conflict. Any time you need a "clean" linux box for something! You can even have multiple EC2 instances, one per project, all set up exactly how you need for a specific project. Just clone this repo into different sub-directories and customize each one. 16 | 17 | The challenge of just starting an EC2 server is the initial installation and configuration of basic tools. This project enables full automation of that. The default userdata script installs the most up to date AWS Command Line Interface (CLI) and a common set of tools. You can easily extend that script to create users and install softare in that users home directory. Please see 'userdata/example' for a template. Both of these are easily extensible. A full description of this is included below. 18 | 19 | Please note that if you just need a development machine at a low cost, or, you want/need to have a very strong security posture, it is possible to run an EC2 instance without a public IP address and connect only over Amazon System Manager (SSM). [Here is a good blog about it](https://pub.towardsai.net/how-to-do-remote-development-with-vs-code-using-aws-ssm-415881d249f3). This tool could be modified to support that. This tool does set the IAM policy to enable the use of SSM. However, this tool assumes that you want/need to be able to connect to the host over the internet. 20 | 21 | ## Software Installed by Default 22 | 23 | In addition to several basic tools, the following software will be installed on the target EC2 host: 24 | 25 | * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) - AWS Command Line Tools 26 | * [AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) - AWS Cloud Development Kit 27 | * [AWS Serverless Application Model (SAM)](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) - Serverless Application Model command line tools 28 | * [jq](https://stedolan.github.io/jq/) - command line JSON processor 29 | * [nvm](https://github.com/nvm-sh/nvm) - Node Version Manager (llows you to quickly install and use different versions of node) 30 | * [nodejs](https://nodejs.dev/) - Node.js 31 | * [npm](https://www.npmjs.com/) - Node Package Manager 32 | * [TypeScript](https://www.typescriptlang.org/) - Typescript 33 | * [yarn](https://classic.yarnpkg.com/lang/en/docs/install/) - Yarn, a modern high performance Node package management tool 34 | 35 | All the software installed is open source with details provided on the listed web sites, or are installed by "yum" from the standard repositories. 36 | 37 | You can easily expand what is installed by writing a script and setting the userDataFile config variable to point to that file. 38 | 39 | ## Why Not Use Cloud9 or Amazon WorkSpaces? 40 | 41 | [Cloud9](https://aws.amazon.com/cloud9/) is a great tool. So is [Amazon WorkSpaces](https://www.amazonaws.cn/en/workspaces/). However, some customers prefer to use tools like Visual Studio Code, or to ssh to a host and use command line tools. Or they want to easily change 42 | the IAM permissions for the host. Or most importantly, they want to create a "clean" new development environment to start a new project, or to test that their code will work properly on a clean new environment, and they don't want to manually set up that environment each time. Netiher Cloud9 nor the Amazon WorkSpaces make that easy to do. 43 | 44 | ## Using Local Docker Instead of an EC2 45 | 46 | You don't need a separate computer to get a consistent development environment. You can install Docker and use a container to get the same effect. There are reports that Docker has issues on some MacOS machines, so this may not be a solution for you. However, if Docker works for you this can be a very easy alternative. Read [instructions here](docker/DevWithDocker.md) on how to set this up. 47 | 48 | ## Security Improvements 49 | 50 | This script opens port 22 so that you can ssh to the host, from anywhere by default. You may want to limit the IP addresses that can connect inbound. You can read about [SSH best practices](https://aws.amazon.com/premiumsupport/knowledge-center/ec2-ssh-best-practices/). 51 | 52 | Alternately you could disable port 22 and use SSM (see above). 53 | 54 | ## Using this Solution with Visual Studio Code 55 | 56 | Visual Studio Code has an extension that makes it easy to [do remote development over ssh](https://code.visualstudio.com/docs/remote/ssh). This tool will automatically add information to the SSH config file for the created EC2 server. You can open VS Code and immediately 57 | see your new host in the list of available targets. 58 | 59 | # Installation Instructions 60 | ## AWS Account (Launch Host) 61 | 62 | To run this CDK script to create an EC2 instance, you need to configure your AWS Account parameters to enable deploying the application. The easiest way to ensure that you have it configured properly do this: 63 | 64 | ```bash 65 | aws sts get-caller-identity 66 | ``` 67 | 68 | You should get information about your valid AWS account if it is configured properly. 69 | 70 | ## Installing Application Dependencies (Launch Host) 71 | 72 | You need to install the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html), [jq](https://stedolan.github.io/jq/download/) and 73 | the [Node Version Manager (nvm)](https://github.com/nvm-sh/nvm). You can then use nvm to install the other dependendencies, like this: 74 | 75 | ```bash 76 | nvm install 16 # installs Nodejs 16 77 | nvm use 16 # selects it 78 | npm install -g npm nodejs typescript aws-sdk aws-cdk yarn # installs the necessary modules 79 | ``` 80 | 81 | An example of the commands to install using the yum package manager on linux is [here](SETUP-DEPS.md). However, please always reference the tool's installation instructions since installers do mature and change over time. 82 | 83 | ## Create and Download an EC2 Key Pair/PEM file 84 | 85 | Using the AWS console, you will need to create an [Amazon EC2 Key Pair PEM file](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html) and download the pem file. Save it to a safe place on your launch host. Some folks store it in ~/keys and others store it 86 | in the ~/.ssh directory. Note the entire file path for that pem file. You will need to the provide the filename/path in the configuration below. 87 | 88 | If you also want this CDK to automatically set a DNS record for you, you need to create a [Route53 Public Hosted Zone](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/CreatingHostedZone.html) and create a host record in that zone. The CDK script will configure the EC2 instance userdata script to automatically set the IP address for that record every time the instance boots. You will need to add configuration items (see below). 89 | 90 | ## Configuration 91 | 92 | Copy the file 'configs/config.json.template' to 'configs/config.json and edit it with your favorite editor: 93 | 94 | ```json 95 | { 96 | "stackName": "make this what you want your stack to be named - it needs to be unique to you, per region", 97 | "ec2Name": "the hostname for the EC2 instance", 98 | "nickName": "", 99 | "ec2Class": "t3 (or whatever you want)", 100 | "ec2Size": "micro (or whatever you want)", 101 | "keyName": "the name of the keypair in the EC2 console", 102 | "keyFile": "the path/name of the pem file for the EC2 keypair", 103 | "hostedZoneID": "the Hosted Zone ID from the Route53 control panel", 104 | "domainName": "the domain name for the Hosted Zone ID", 105 | "userDataFile": "a path/name of a file to an additional file to append to the standard user data file", 106 | "cdkOut": "cdk-outputs.json", 107 | "timeBomb": "optional timebomb - any non-zero number is the number of minutes the EC2 should exist before automatically getting destroyed", 108 | "awsConfig": "folder where your AWS commmand line credentials are stored, ~/.aws by default" 109 | } 110 | ``` 111 | 112 | The cdkOut file can be named anything, but it should have a json extension since it will be a json data file. CDK writes data there that we use to automate other operations. It is recommended to leave it to the default value. The tool "jq" reads from that file to get data for various other commands built into this tool. 113 | 114 | ## Extra Userdata to Install Your Stuff 115 | 116 | Take a look at userdata/example: 117 | 118 | ```bash 119 | useradd username 120 | usermod -G wheel username 121 | # change to allow sudo in the install scripts 122 | echo "username ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers 123 | su -c 'cd ~username;git clone https://github.com/username/examplerepo' username 124 | su -c 'cd ~username/examplerepo;./install' username 125 | # change it back for safety later 126 | sed -i.bak '/username/d' /etc/sudoers 127 | ``` 128 | 129 | If you make a file like this and swap "username" for your own username then the CDK project will append this onto the userdata script. This example adds a specific user, adds them to the wheel group (so the have sudo access on Amazon Linux) and 130 | then grants temporary no passwd sudo access and runs an install script from a cloned git repo. This example grants no passwd access to enable the script to "sudo" without prompting for a password. The script removes that setting when done per best practice. 131 | 132 | # Usage 133 | 134 | Just type 'yarn install' then 'yarn deploy' to deploy the solution and add the host to your .ssh/config file to make it easy to connect. Once deployed, "yarn prep" will copy your AWS and SSH credentials to the new instance. You can then just ssh to the "nickname" you set in the config file. 135 | 136 | ## Deploy 137 | 138 | ```bash 139 | yarn depoy 140 | ``` 141 | 142 | Builds and deploys the instance stack and configures your SSH config to know about the instance via the "nickname" you set. 143 | 144 | ## SSH to Host 145 | 146 | You can just "ssh " and SSH will pull the target IP and keyfile out of the .ssh/config file. No more messing with typing out the keyfile name! 147 | 148 | If your EC2 host was restarted for any reason, it will get a new IP address and your SSH config file will be incorrect. This will manifest as the ssh command hanging and failing to connect. You can verify that the instance is available with "yarn status" and if it seems that it rebooted you can easily fix your config. To rewrite your ssh config file, you can just run this command: 149 | 150 | ```bash 151 | yarn setssh 152 | ``` 153 | 154 | ## Install AWS Credentials 155 | 156 | By default the EC2 host will have very limited AWS credentials. To do cloud development you will need to set your AWS credentials on the EC2 host. To make that simple, you can run this command: 157 | 158 | ```bash 159 | yarn creds 160 | ``` 161 | 162 | This will copy your AWS credentials to the host. If your credentials are not in the default location, you can point to the folder where they reside using the awsConfig parameter in the configs/config.json file. This command is included in the "yarn prep" script. 163 | 164 | ## Copying the SSH Keys (to enable using git, etc) 165 | 166 | There's a helper script to copy your pem file to the destination host. You should have already deployed the EC2 and run "yarn setssh" for this to work. Just: 167 | 168 | ```bash 169 | yarn copykeys 170 | ``` 171 | 172 | The helper will also extract your public key from the pem file and write it to your .ssh directory. You will need to copy that public key to the service you wish to connect to from your EC2 (such as GitHub or GitLab). The default userdata script will add a command to the /etc/profile file to load the pem file into the SSH agent when you log in. This script is also included as part of the "yarn prep" script. 173 | 174 | Because EC2 keypairs are specific to a region, this script will also copy the key to every region. This enables you to deploy the EC2 instance in every region. 175 | 176 | 177 | ## Handling an Error When Using SSH 178 | 179 | If something fails and you cannot connect using "ssh " you can easily ssh to the host using: 180 | 181 | ```bash 182 | yarn ssh 183 | ``` 184 | 185 | This will connect you to the host by automatically setting the command line parameters for your SSH key and will connect you as "ec2-user" instead of your normal username. This may be necessary, for example, if the extra script you configured in the userDataFile 186 | parameter had some kind of error that rendered your user inoperable. 187 | 188 | ## Destroy Stack 189 | 190 | If you want to completely tear down the EC2 instance and all associated resources, use this command: 191 | 192 | ```bash 193 | yarn destroy 194 | ``` 195 | which basically runs "cdk destroy" for you. 196 | 197 | ## Stop and Start the Instance (to save money) 198 | 199 | You can start and stop the instance with the following commands: 200 | 201 | ```bash 202 | yarn stop 203 | yarn run v1.22.17 204 | $ scripts/ec2 stop 205 | { 206 | "StoppingInstances": [ 207 | { 208 | "CurrentState": { 209 | "Code": 64, 210 | "Name": "stopping" 211 | }, 212 | "InstanceId": "i-08025470eb8dabb7a", 213 | "PreviousState": { 214 | "Code": 16, 215 | "Name": "running" 216 | } 217 | } 218 | ] 219 | } 220 | ✨ Done in 2.58s. 221 | ``` 222 | You can check the running state of your instance with: 223 | 224 | ```bash 225 | yarn status 226 | yarn run v1.22.17 227 | $ scripts/ec2 status 228 | ec2 instance i-08025470eb8dabb7a is stopped 229 | ✨ Done in 1.37s. 230 | ``` 231 | 232 | You can restart your instance with: 233 | 234 | ```bash 235 | yarn start 236 | yarn run v1.22.17 237 | $ scripts/ec2 start 238 | { 239 | "StartingInstances": [ 240 | { 241 | "CurrentState": { 242 | "Code": 0, 243 | "Name": "pending" 244 | }, 245 | "InstanceId": "i-08025470eb8dabb7a", 246 | "PreviousState": { 247 | "Code": 80, 248 | "Name": "stopped" 249 | } 250 | } 251 | ] 252 | } 253 | ✨ Done in 2.50s. 254 | 88665a1e55c0:~/src/cdk/single-ec2-dev> yarn status 255 | yarn run v1.22.17 256 | $ scripts/ec2 status 257 | ec2 instance i-08025470eb8dabb7a is running 258 | ✨ Done in 1.15s. 259 | ``` 260 | 261 | NOTE: after restarting, your EC2 instance will have a new IP address. To correct your ssh config and clear the old info from the SSH known hosts file. 262 | 263 | ## Cleaning Up the Folder 264 | 265 | ```bash 266 | yarn clean 267 | ``` 268 | 269 | Cleans up all the build files. NOTE: this will delete the stack state data files. It's really only meant for development to test a clean install. 270 | 271 | # Disclaimer 272 | 273 | Deploying the the CDK will build a CloudFormation stack that will cause your AWS Account to be billed for the use of the EC2 instance that is created. 274 | 275 | # Security 276 | 277 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 278 | 279 | # License 280 | 281 | This code is licensed under the MIT-0 License. See the [LICENSE file](https://github.com/aws-samples/single-ec2-cdk/blob/main/LICENSE). 282 | -------------------------------------------------------------------------------- /cdk.out/tree.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "tree-0.1", 3 | "tree": { 4 | "id": "App", 5 | "path": "", 6 | "children": { 7 | "Tree": { 8 | "id": "Tree", 9 | "path": "Tree", 10 | "constructInfo": { 11 | "fqn": "@aws-cdk/core.Construct", 12 | "version": "1.144.0" 13 | } 14 | }, 15 | "SingleEc2Stack": { 16 | "id": "SingleEc2Stack", 17 | "path": "SingleEc2Stack", 18 | "children": { 19 | "VPC": { 20 | "id": "VPC", 21 | "path": "SingleEc2Stack/VPC", 22 | "children": { 23 | "PublicSubnet1": { 24 | "id": "PublicSubnet1", 25 | "path": "SingleEc2Stack/VPC/PublicSubnet1", 26 | "constructInfo": { 27 | "fqn": "@aws-cdk/core.Resource", 28 | "version": "1.144.0" 29 | } 30 | }, 31 | "PublicSubnet2": { 32 | "id": "PublicSubnet2", 33 | "path": "SingleEc2Stack/VPC/PublicSubnet2", 34 | "constructInfo": { 35 | "fqn": "@aws-cdk/core.Resource", 36 | "version": "1.144.0" 37 | } 38 | }, 39 | "PublicSubnet3": { 40 | "id": "PublicSubnet3", 41 | "path": "SingleEc2Stack/VPC/PublicSubnet3", 42 | "constructInfo": { 43 | "fqn": "@aws-cdk/core.Resource", 44 | "version": "1.144.0" 45 | } 46 | }, 47 | "PublicSubnet4": { 48 | "id": "PublicSubnet4", 49 | "path": "SingleEc2Stack/VPC/PublicSubnet4", 50 | "constructInfo": { 51 | "fqn": "@aws-cdk/core.Resource", 52 | "version": "1.144.0" 53 | } 54 | } 55 | }, 56 | "constructInfo": { 57 | "fqn": "@aws-cdk/core.Resource", 58 | "version": "1.144.0" 59 | } 60 | }, 61 | "dev-role": { 62 | "id": "dev-role", 63 | "path": "SingleEc2Stack/dev-role", 64 | "children": { 65 | "Resource": { 66 | "id": "Resource", 67 | "path": "SingleEc2Stack/dev-role/Resource", 68 | "attributes": { 69 | "aws:cdk:cloudformation:type": "AWS::IAM::Role", 70 | "aws:cdk:cloudformation:props": { 71 | "assumeRolePolicyDocument": { 72 | "Statement": [ 73 | { 74 | "Action": "sts:AssumeRole", 75 | "Effect": "Allow", 76 | "Principal": { 77 | "Service": "ec2.amazonaws.com" 78 | } 79 | } 80 | ], 81 | "Version": "2012-10-17" 82 | } 83 | } 84 | }, 85 | "constructInfo": { 86 | "fqn": "@aws-cdk/aws-iam.CfnRole", 87 | "version": "1.144.0" 88 | } 89 | } 90 | }, 91 | "constructInfo": { 92 | "fqn": "@aws-cdk/aws-iam.Role", 93 | "version": "1.144.0" 94 | } 95 | }, 96 | "dnsPolicy": { 97 | "id": "dnsPolicy", 98 | "path": "SingleEc2Stack/dnsPolicy", 99 | "children": { 100 | "Resource": { 101 | "id": "Resource", 102 | "path": "SingleEc2Stack/dnsPolicy/Resource", 103 | "attributes": { 104 | "aws:cdk:cloudformation:type": "AWS::IAM::Policy", 105 | "aws:cdk:cloudformation:props": { 106 | "policyDocument": { 107 | "Statement": [ 108 | { 109 | "Action": "route53:GetChange", 110 | "Effect": "Allow", 111 | "Resource": "arn:aws:route53:::hostedzone/Z00609272L7MZI1OMD0IL" 112 | }, 113 | { 114 | "Action": "route53:ChangeResourceRecordSets", 115 | "Effect": "Allow", 116 | "Resource": "arn:aws:route53:::hostedzone/Z00609272L7MZI1OMD0IL" 117 | }, 118 | { 119 | "Action": "route53:GetHostedZone", 120 | "Effect": "Allow", 121 | "Resource": "arn:aws:route53:::hostedzone/Z00609272L7MZI1OMD0IL" 122 | } 123 | ], 124 | "Version": "2012-10-17" 125 | }, 126 | "policyName": "dnsPolicy90BE57CE", 127 | "roles": [ 128 | { 129 | "Ref": "devrole61AE7F72" 130 | } 131 | ] 132 | } 133 | }, 134 | "constructInfo": { 135 | "fqn": "@aws-cdk/aws-iam.CfnPolicy", 136 | "version": "1.144.0" 137 | } 138 | } 139 | }, 140 | "constructInfo": { 141 | "fqn": "@aws-cdk/aws-iam.Policy", 142 | "version": "1.144.0" 143 | } 144 | }, 145 | "ssmPolicy": { 146 | "id": "ssmPolicy", 147 | "path": "SingleEc2Stack/ssmPolicy", 148 | "children": { 149 | "Resource": { 150 | "id": "Resource", 151 | "path": "SingleEc2Stack/ssmPolicy/Resource", 152 | "attributes": { 153 | "aws:cdk:cloudformation:type": "AWS::IAM::Policy", 154 | "aws:cdk:cloudformation:props": { 155 | "policyDocument": { 156 | "Statement": [ 157 | { 158 | "Action": [ 159 | "ssm:UpdateInstanceInformation", 160 | "ssmmessages:CreateControlChannel", 161 | "ssmmessages:CreateDataChannel", 162 | "ssmmessages:OpenControlChannel", 163 | "ssmmessages:OpenDataChannel" 164 | ], 165 | "Effect": "Allow", 166 | "Resource": "*" 167 | } 168 | ], 169 | "Version": "2012-10-17" 170 | }, 171 | "policyName": "ssmPolicy178E7BD0", 172 | "roles": [ 173 | { 174 | "Ref": "devrole61AE7F72" 175 | } 176 | ] 177 | } 178 | }, 179 | "constructInfo": { 180 | "fqn": "@aws-cdk/aws-iam.CfnPolicy", 181 | "version": "1.144.0" 182 | } 183 | } 184 | }, 185 | "constructInfo": { 186 | "fqn": "@aws-cdk/aws-iam.Policy", 187 | "version": "1.144.0" 188 | } 189 | }, 190 | "devsg": { 191 | "id": "devsg", 192 | "path": "SingleEc2Stack/devsg", 193 | "children": { 194 | "Resource": { 195 | "id": "Resource", 196 | "path": "SingleEc2Stack/devsg/Resource", 197 | "attributes": { 198 | "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", 199 | "aws:cdk:cloudformation:props": { 200 | "groupDescription": "SingleEc2Stack/devsg", 201 | "groupName": "dev-sg", 202 | "securityGroupEgress": [ 203 | { 204 | "cidrIp": "0.0.0.0/0", 205 | "description": "Allow all outbound traffic by default", 206 | "ipProtocol": "-1" 207 | } 208 | ], 209 | "securityGroupIngress": [ 210 | { 211 | "cidrIp": "0.0.0.0/0", 212 | "ipProtocol": "tcp", 213 | "fromPort": 22, 214 | "toPort": 22, 215 | "description": "from 0.0.0.0/0:22" 216 | } 217 | ], 218 | "vpcId": "vpc-34c1924c" 219 | } 220 | }, 221 | "constructInfo": { 222 | "fqn": "@aws-cdk/aws-ec2.CfnSecurityGroup", 223 | "version": "1.144.0" 224 | } 225 | } 226 | }, 227 | "constructInfo": { 228 | "fqn": "@aws-cdk/aws-ec2.SecurityGroup", 229 | "version": "1.144.0" 230 | } 231 | }, 232 | "dev": { 233 | "id": "dev", 234 | "path": "SingleEc2Stack/dev", 235 | "children": { 236 | "InstanceProfile": { 237 | "id": "InstanceProfile", 238 | "path": "SingleEc2Stack/dev/InstanceProfile", 239 | "attributes": { 240 | "aws:cdk:cloudformation:type": "AWS::IAM::InstanceProfile", 241 | "aws:cdk:cloudformation:props": { 242 | "roles": [ 243 | { 244 | "Ref": "devrole61AE7F72" 245 | } 246 | ] 247 | } 248 | }, 249 | "constructInfo": { 250 | "fqn": "@aws-cdk/aws-iam.CfnInstanceProfile", 251 | "version": "1.144.0" 252 | } 253 | }, 254 | "Resource": { 255 | "id": "Resource", 256 | "path": "SingleEc2Stack/dev/Resource", 257 | "attributes": { 258 | "aws:cdk:cloudformation:type": "AWS::EC2::Instance", 259 | "aws:cdk:cloudformation:props": { 260 | "availabilityZone": "us-west-2a", 261 | "blockDeviceMappings": [ 262 | { 263 | "deviceName": "/dev/xvda", 264 | "ebs": { 265 | "volumeSize": 100 266 | } 267 | } 268 | ], 269 | "iamInstanceProfile": { 270 | "Ref": "devInstanceProfile2C058DB1" 271 | }, 272 | "imageId": { 273 | "Ref": "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter" 274 | }, 275 | "instanceType": "t3.2xlarge", 276 | "keyName": "gherlein-ec2", 277 | "securityGroupIds": [ 278 | { 279 | "Fn::GetAtt": [ 280 | "devsg25C5920B", 281 | "GroupId" 282 | ] 283 | } 284 | ], 285 | "subnetId": "subnet-b342eecb", 286 | "tags": [ 287 | { 288 | "key": "domainName", 289 | "value": "herlein.dev" 290 | }, 291 | { 292 | "key": "ec2Name", 293 | "value": "dev" 294 | }, 295 | { 296 | "key": "hostedZoneID", 297 | "value": "Z00609272L7MZI1OMD0IL" 298 | }, 299 | { 300 | "key": "keyFile", 301 | "value": "~/keys/gherlein-ec2.pem" 302 | }, 303 | { 304 | "key": "keyName", 305 | "value": "gherlein-ec2" 306 | }, 307 | { 308 | "key": "Name", 309 | "value": "dev" 310 | }, 311 | { 312 | "key": "nickName", 313 | "value": "dev" 314 | }, 315 | { 316 | "key": "timeBomb", 317 | "value": "0" 318 | } 319 | ], 320 | "userData": { 321 | "Fn::Base64": "#!/bin/bash\n#! /bin/bash -x\n# install critial tools\nsudo yum -y update\nsudo yum remove -y awscli # remove v1 to make way to install v2\nsudo yum -y install expect jq curl git\n# install AWS CLI\nwget \"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip\" -O \"awscliv2.zip\"\nunzip awscliv2.zip\nsudo ./aws/install --bin-dir /usr/local/bin --install-dir /usr/local/aws-cli --update\nrm -Rf aws awscliv2.zip\n# install SAM\nwget https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip -O sam.zip\nunzip sam.zip -d sam-installation\nsudo ./sam-installation/install\nsam --version\nrm -Rf ./sam-installation sam.zip\n# install NVM\nexport HOME=/usr/local/bin\ncd /usr/local/bin\ncurl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash\ncat << \"EOF\" >> /etc/profile\nNVM_DIR=/usr/local/bin/.nvm\n[ -s \"$NVM_DIR/nvm.sh\" ] && \\. \"$NVM_DIR/nvm.sh\" # This loads nvm\n[ -s \"$NVM_DIR/bash_completion\" ] && \\. \"$NVM_DIR/bash_completion\"\nfor i in $(ls $HOME/.ssh/*.pem);\ndo\n [ -f \"$i\" ] || break\n ssh-add $i\ndone\nEOF\nsource /etc/profile\nsource $NVM_DIR/nvm.sh\nnvm install 16\nnvm use 16\nnpm install -g npm nodejs typescript aws-sdk aws-cdk yarn\nexport HOME=/root\n# set local variables\nINSTANCE_ID=$(wget -qO- http://instance-data/latest/meta-data/instance-id)\nHOST=`aws ec2 describe-tags --filters \"Name=resource-id,Values=$INSTANCE_ID\" \"Name=key,Values=nickName\" | jq -r .Tags[].Value`\nDOMAIN=`aws ec2 describe-tags --filters \"Name=resource-id,Values=$INSTANCE_ID\" \"Name=key,Values=domainName\" | jq -r .Tags[].Value`\necho \"preserve_hostname: true\" >> /etc/cloud/cloud.cfg\nhostnamectl set-hostname $HOST.$DOMAIN\necho \"ssh-add $KEYFILE\" >> /etc/profile\n# configure DNS\ntouch /var/lib/cloud/scripts/per-boot/set-dns\nchmod +x /var/lib/cloud/scripts/per-boot/set-dns\ncat << \"EOF\" > /var/lib/cloud/scripts/per-boot/set-dns\n#!/bin/bash\nINSTANCE_ID=$(wget -qO- http://instance-data/latest/meta-data/instance-id)\nMY_IP=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4/)\nDOMAIN=`aws ec2 describe-tags --filters \"Name=resource-id,Values=$INSTANCE_ID\" \"Name=key,Values=domainName\" | jq -r .Tags[].Value`\nHOST=`aws ec2 describe-tags --filters \"Name=resource-id,Values=$INSTANCE_ID\" \"Name=key,Values=nickName\" | jq -r .Tags[].Value`\nFQDN=$HOST.$DOMAIN\n/usr/local/bin/aws route53 change-resource-record-sets --hosted-zone-id Z00609272L7MZI1OMD0IL --change-batch '{\n \"Changes\":[{\n \"Action\":\"UPSERT\",\n \"ResourceRecordSet\":{\n \"Name\": \"'$FQDN'\",\n \"Type\":\"A\",\n \"TTL\":10,\n \"ResourceRecords\":[\n {\n \"Value\": \"'$MY_IP'\"\n }\n ]\n }\n }]\n}'\nEOF\nvar/lib/cloud/scripts/per-boot/set-dns\n\n# to inspect what this script did, inspect /var/log/cloud-init-output.log\n\n\nuseradd gherlein\nusermod -G wheel gherlein\necho \"gherlein:none\" | chpasswd\nyum install -y emacs-nox\n# change to allow sudo in the install scripts\necho \"gherlein ALL=(ALL) NOPASSWD: ALL\" >> /etc/sudoers \nsu -c 'cd ~gherlein;git clone https://github.com/gherlein/gch' gherlein\nsu -c 'cd ~gherlein/gch/;./install' gherlein\n# change it back for safety later\nsed -i.bak '/gherlein/d' /etc/sudoers\nsu -c 'echo \"~/keychain/keychain --eval --agents ssh gherlein-ec2.pem >> ~/.bash_profile\"' gherlein\nsu -c 'echo \"ssh-add ~/keys/gherlein-ec2.pem >> ~/.bash_profile\"' gherlein\n" 322 | } 323 | } 324 | }, 325 | "constructInfo": { 326 | "fqn": "@aws-cdk/aws-ec2.CfnInstance", 327 | "version": "1.144.0" 328 | } 329 | } 330 | }, 331 | "constructInfo": { 332 | "fqn": "@aws-cdk/aws-ec2.Instance", 333 | "version": "1.144.0" 334 | } 335 | }, 336 | "SsmParameterValue:--aws--service--ami-amazon-linux-latest--amzn2-ami-hvm-x86_64-gp2:C96584B6-F00A-464E-AD19-53AFF4B05118.Parameter": { 337 | "id": "SsmParameterValue:--aws--service--ami-amazon-linux-latest--amzn2-ami-hvm-x86_64-gp2:C96584B6-F00A-464E-AD19-53AFF4B05118.Parameter", 338 | "path": "SingleEc2Stack/SsmParameterValue:--aws--service--ami-amazon-linux-latest--amzn2-ami-hvm-x86_64-gp2:C96584B6-F00A-464E-AD19-53AFF4B05118.Parameter", 339 | "constructInfo": { 340 | "fqn": "@aws-cdk/core.CfnParameter", 341 | "version": "1.144.0" 342 | } 343 | }, 344 | "SsmParameterValue:--aws--service--ami-amazon-linux-latest--amzn2-ami-hvm-x86_64-gp2:C96584B6-F00A-464E-AD19-53AFF4B05118": { 345 | "id": "SsmParameterValue:--aws--service--ami-amazon-linux-latest--amzn2-ami-hvm-x86_64-gp2:C96584B6-F00A-464E-AD19-53AFF4B05118", 346 | "path": "SingleEc2Stack/SsmParameterValue:--aws--service--ami-amazon-linux-latest--amzn2-ami-hvm-x86_64-gp2:C96584B6-F00A-464E-AD19-53AFF4B05118", 347 | "constructInfo": { 348 | "fqn": "@aws-cdk/core.Resource", 349 | "version": "1.144.0" 350 | } 351 | }, 352 | "tagPollicy": { 353 | "id": "tagPollicy", 354 | "path": "SingleEc2Stack/tagPollicy", 355 | "children": { 356 | "Resource": { 357 | "id": "Resource", 358 | "path": "SingleEc2Stack/tagPollicy/Resource", 359 | "attributes": { 360 | "aws:cdk:cloudformation:type": "AWS::IAM::Policy", 361 | "aws:cdk:cloudformation:props": { 362 | "policyDocument": { 363 | "Statement": [ 364 | { 365 | "Action": "ec2:DescribeTags", 366 | "Effect": "Allow", 367 | "Resource": { 368 | "Fn::Join": [ 369 | "", 370 | [ 371 | "arn:aws:ec2:us-west-2:497939524935:instance/", 372 | { 373 | "Ref": "devF7B884FF" 374 | } 375 | ] 376 | ] 377 | } 378 | } 379 | ], 380 | "Version": "2012-10-17" 381 | }, 382 | "policyName": "tagPollicy47586843", 383 | "roles": [ 384 | { 385 | "Ref": "devrole61AE7F72" 386 | } 387 | ] 388 | } 389 | }, 390 | "constructInfo": { 391 | "fqn": "@aws-cdk/aws-iam.CfnPolicy", 392 | "version": "1.144.0" 393 | } 394 | } 395 | }, 396 | "constructInfo": { 397 | "fqn": "@aws-cdk/aws-iam.Policy", 398 | "version": "1.144.0" 399 | } 400 | }, 401 | "ec2-instance-ip-address": { 402 | "id": "ec2-instance-ip-address", 403 | "path": "SingleEc2Stack/ec2-instance-ip-address", 404 | "constructInfo": { 405 | "fqn": "@aws-cdk/core.CfnOutput", 406 | "version": "1.144.0" 407 | } 408 | }, 409 | "ec2-instance-id": { 410 | "id": "ec2-instance-id", 411 | "path": "SingleEc2Stack/ec2-instance-id", 412 | "constructInfo": { 413 | "fqn": "@aws-cdk/core.CfnOutput", 414 | "version": "1.144.0" 415 | } 416 | }, 417 | "ec2-instance-public-dnsname": { 418 | "id": "ec2-instance-public-dnsname", 419 | "path": "SingleEc2Stack/ec2-instance-public-dnsname", 420 | "constructInfo": { 421 | "fqn": "@aws-cdk/core.CfnOutput", 422 | "version": "1.144.0" 423 | } 424 | }, 425 | "CDKMetadata": { 426 | "id": "CDKMetadata", 427 | "path": "SingleEc2Stack/CDKMetadata", 428 | "children": { 429 | "Default": { 430 | "id": "Default", 431 | "path": "SingleEc2Stack/CDKMetadata/Default", 432 | "constructInfo": { 433 | "fqn": "@aws-cdk/core.CfnResource", 434 | "version": "1.144.0" 435 | } 436 | } 437 | }, 438 | "constructInfo": { 439 | "fqn": "@aws-cdk/core.Construct", 440 | "version": "1.144.0" 441 | } 442 | } 443 | }, 444 | "constructInfo": { 445 | "fqn": "@aws-cdk/core.Stack", 446 | "version": "1.144.0" 447 | } 448 | } 449 | }, 450 | "constructInfo": { 451 | "fqn": "@aws-cdk/core.App", 452 | "version": "1.144.0" 453 | } 454 | } 455 | } -------------------------------------------------------------------------------- /cdk.out/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "16.0.0", 3 | "artifacts": { 4 | "Tree": { 5 | "type": "cdk:tree", 6 | "properties": { 7 | "file": "tree.json" 8 | } 9 | }, 10 | "SingleEc2Stack": { 11 | "type": "aws:cloudformation:stack", 12 | "environment": "aws://497939524935/us-west-2", 13 | "properties": { 14 | "templateFile": "SingleEc2Stack.template.json", 15 | "validateOnSynth": false, 16 | "stackName": "SingleEc2Stack-dev" 17 | }, 18 | "metadata": { 19 | "/SingleEc2Stack/dev-role/Resource": [ 20 | { 21 | "type": "aws:cdk:logicalId", 22 | "data": "devrole61AE7F72", 23 | "trace": [ 24 | "new Role (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/aws-iam/lib/role.ts:201:18)", 25 | "new SingleEc2Stack (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/lib/single-ec2-stack.ts:53:18)", 26 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/bin/single-ec2.ts:7:1)", 27 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 28 | "Module.m._compile (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1056:23)", 29 | "Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 30 | "Object.require.extensions. [as .ts] (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1059:12)", 31 | "Module.load (node:internal/modules/cjs/loader:981:32)", 32 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 33 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 34 | "main (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:198:14)", 35 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:288:3)", 36 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 37 | "Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 38 | "Module.load (node:internal/modules/cjs/loader:981:32)", 39 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 40 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 41 | "node:internal/main/run_main_module:17:47" 42 | ] 43 | } 44 | ], 45 | "/SingleEc2Stack/dnsPolicy/Resource": [ 46 | { 47 | "type": "aws:cdk:logicalId", 48 | "data": "dnsPolicy90BE57CE", 49 | "trace": [ 50 | "new Policy (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/aws-iam/lib/policy.ts:89:22)", 51 | "new SingleEc2Stack (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/lib/single-ec2-stack.ts:77:23)", 52 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/bin/single-ec2.ts:7:1)", 53 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 54 | "Module.m._compile (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1056:23)", 55 | "Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 56 | "Object.require.extensions. [as .ts] (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1059:12)", 57 | "Module.load (node:internal/modules/cjs/loader:981:32)", 58 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 59 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 60 | "main (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:198:14)", 61 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:288:3)", 62 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 63 | "Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 64 | "Module.load (node:internal/modules/cjs/loader:981:32)", 65 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 66 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 67 | "node:internal/main/run_main_module:17:47" 68 | ] 69 | } 70 | ], 71 | "/SingleEc2Stack/ssmPolicy/Resource": [ 72 | { 73 | "type": "aws:cdk:logicalId", 74 | "data": "ssmPolicy178E7BD0", 75 | "trace": [ 76 | "new Policy (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/aws-iam/lib/policy.ts:89:22)", 77 | "new SingleEc2Stack (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/lib/single-ec2-stack.ts:95:23)", 78 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/bin/single-ec2.ts:7:1)", 79 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 80 | "Module.m._compile (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1056:23)", 81 | "Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 82 | "Object.require.extensions. [as .ts] (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1059:12)", 83 | "Module.load (node:internal/modules/cjs/loader:981:32)", 84 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 85 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 86 | "main (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:198:14)", 87 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:288:3)", 88 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 89 | "Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 90 | "Module.load (node:internal/modules/cjs/loader:981:32)", 91 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 92 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 93 | "node:internal/main/run_main_module:17:47" 94 | ] 95 | } 96 | ], 97 | "/SingleEc2Stack/devsg/Resource": [ 98 | { 99 | "type": "aws:cdk:logicalId", 100 | "data": "devsg25C5920B", 101 | "trace": [ 102 | "new SecurityGroup (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/aws-ec2/lib/security-group.ts:272:26)", 103 | "new SingleEc2Stack (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/lib/single-ec2-stack.ts:100:27)", 104 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/bin/single-ec2.ts:7:1)", 105 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 106 | "Module.m._compile (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1056:23)", 107 | "Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 108 | "Object.require.extensions. [as .ts] (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1059:12)", 109 | "Module.load (node:internal/modules/cjs/loader:981:32)", 110 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 111 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 112 | "main (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:198:14)", 113 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:288:3)", 114 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 115 | "Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 116 | "Module.load (node:internal/modules/cjs/loader:981:32)", 117 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 118 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 119 | "node:internal/main/run_main_module:17:47" 120 | ] 121 | } 122 | ], 123 | "/SingleEc2Stack/dev/InstanceProfile": [ 124 | { 125 | "type": "aws:cdk:logicalId", 126 | "data": "devInstanceProfile2C058DB1", 127 | "trace": [ 128 | "new Instance (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/aws-ec2/lib/instance.ts:167:24)", 129 | "new SingleEc2Stack (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/lib/single-ec2-stack.ts:128:22)", 130 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/bin/single-ec2.ts:7:1)", 131 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 132 | "Module.m._compile (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1056:23)", 133 | "Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 134 | "Object.require.extensions. [as .ts] (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1059:12)", 135 | "Module.load (node:internal/modules/cjs/loader:981:32)", 136 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 137 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 138 | "main (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:198:14)", 139 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:288:3)", 140 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 141 | "Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 142 | "Module.load (node:internal/modules/cjs/loader:981:32)", 143 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 144 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 145 | "node:internal/main/run_main_module:17:47" 146 | ] 147 | } 148 | ], 149 | "/SingleEc2Stack/dev/Resource": [ 150 | { 151 | "type": "aws:cdk:logicalId", 152 | "data": "devF7B884FF", 153 | "trace": [ 154 | "new Instance (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/aws-ec2/lib/instance.ts:202:21)", 155 | "new SingleEc2Stack (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/lib/single-ec2-stack.ts:128:22)", 156 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/bin/single-ec2.ts:7:1)", 157 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 158 | "Module.m._compile (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1056:23)", 159 | "Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 160 | "Object.require.extensions. [as .ts] (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1059:12)", 161 | "Module.load (node:internal/modules/cjs/loader:981:32)", 162 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 163 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 164 | "main (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:198:14)", 165 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:288:3)", 166 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 167 | "Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 168 | "Module.load (node:internal/modules/cjs/loader:981:32)", 169 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 170 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 171 | "node:internal/main/run_main_module:17:47" 172 | ] 173 | } 174 | ], 175 | "/SingleEc2Stack/SsmParameterValue:--aws--service--ami-amazon-linux-latest--amzn2-ami-hvm-x86_64-gp2:C96584B6-F00A-464E-AD19-53AFF4B05118.Parameter": [ 176 | { 177 | "type": "aws:cdk:logicalId", 178 | "data": "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter", 179 | "trace": [ 180 | "Function.fromStringParameterAttributes (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/aws-ssm/lib/parameter.ts:194:9)", 181 | "Function.valueForTypedStringParameter (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/aws-ssm/lib/parameter.ts:246:17)", 182 | "lookupImage (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/aws-ec2/lib/machine-image.ts:435:27)", 183 | "AmazonLinuxImage.getImage (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/aws-ec2/lib/machine-image.ts:229:21)", 184 | "new Instance (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/aws-ec2/lib/instance.ts:172:44)", 185 | "new SingleEc2Stack (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/lib/single-ec2-stack.ts:128:22)", 186 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/bin/single-ec2.ts:7:1)", 187 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 188 | "Module.m._compile (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1056:23)", 189 | "Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 190 | "Object.require.extensions. [as .ts] (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1059:12)", 191 | "Module.load (node:internal/modules/cjs/loader:981:32)", 192 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 193 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 194 | "main (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:198:14)", 195 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:288:3)", 196 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 197 | "Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 198 | "Module.load (node:internal/modules/cjs/loader:981:32)", 199 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 200 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 201 | "node:internal/main/run_main_module:17:47" 202 | ] 203 | } 204 | ], 205 | "/SingleEc2Stack/tagPollicy/Resource": [ 206 | { 207 | "type": "aws:cdk:logicalId", 208 | "data": "tagPollicy47586843", 209 | "trace": [ 210 | "new Policy (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/aws-iam/lib/policy.ts:89:22)", 211 | "new SingleEc2Stack (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/lib/single-ec2-stack.ts:159:23)", 212 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/bin/single-ec2.ts:7:1)", 213 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 214 | "Module.m._compile (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1056:23)", 215 | "Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 216 | "Object.require.extensions. [as .ts] (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1059:12)", 217 | "Module.load (node:internal/modules/cjs/loader:981:32)", 218 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 219 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 220 | "main (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:198:14)", 221 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:288:3)", 222 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 223 | "Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 224 | "Module.load (node:internal/modules/cjs/loader:981:32)", 225 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 226 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 227 | "node:internal/main/run_main_module:17:47" 228 | ] 229 | } 230 | ], 231 | "/SingleEc2Stack/ec2-instance-ip-address": [ 232 | { 233 | "type": "aws:cdk:logicalId", 234 | "data": "ec2instanceipaddress", 235 | "trace": [ 236 | "new SingleEc2Stack (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/lib/single-ec2-stack.ts:176:5)", 237 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/bin/single-ec2.ts:7:1)", 238 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 239 | "Module.m._compile (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1056:23)", 240 | "Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 241 | "Object.require.extensions. [as .ts] (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1059:12)", 242 | "Module.load (node:internal/modules/cjs/loader:981:32)", 243 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 244 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 245 | "main (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:198:14)", 246 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:288:3)", 247 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 248 | "Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 249 | "Module.load (node:internal/modules/cjs/loader:981:32)", 250 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 251 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 252 | "node:internal/main/run_main_module:17:47" 253 | ] 254 | } 255 | ], 256 | "/SingleEc2Stack/ec2-instance-id": [ 257 | { 258 | "type": "aws:cdk:logicalId", 259 | "data": "ec2instanceid", 260 | "trace": [ 261 | "new SingleEc2Stack (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/lib/single-ec2-stack.ts:179:5)", 262 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/bin/single-ec2.ts:7:1)", 263 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 264 | "Module.m._compile (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1056:23)", 265 | "Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 266 | "Object.require.extensions. [as .ts] (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1059:12)", 267 | "Module.load (node:internal/modules/cjs/loader:981:32)", 268 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 269 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 270 | "main (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:198:14)", 271 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:288:3)", 272 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 273 | "Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 274 | "Module.load (node:internal/modules/cjs/loader:981:32)", 275 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 276 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 277 | "node:internal/main/run_main_module:17:47" 278 | ] 279 | } 280 | ], 281 | "/SingleEc2Stack/ec2-instance-public-dnsname": [ 282 | { 283 | "type": "aws:cdk:logicalId", 284 | "data": "ec2instancepublicdnsname", 285 | "trace": [ 286 | "new SingleEc2Stack (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/lib/single-ec2-stack.ts:182:5)", 287 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/bin/single-ec2.ts:7:1)", 288 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 289 | "Module.m._compile (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1056:23)", 290 | "Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 291 | "Object.require.extensions. [as .ts] (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/index.ts:1059:12)", 292 | "Module.load (node:internal/modules/cjs/loader:981:32)", 293 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 294 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 295 | "main (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:198:14)", 296 | "Object. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/ts-node/src/bin.ts:288:3)", 297 | "Module._compile (node:internal/modules/cjs/loader:1101:14)", 298 | "Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)", 299 | "Module.load (node:internal/modules/cjs/loader:981:32)", 300 | "Function.Module._load (node:internal/modules/cjs/loader:822:12)", 301 | "Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)", 302 | "node:internal/main/run_main_module:17:47" 303 | ] 304 | } 305 | ], 306 | "/SingleEc2Stack/CDKMetadata/Default": [ 307 | { 308 | "type": "aws:cdk:logicalId", 309 | "data": "CDKMetadata", 310 | "trace": [ 311 | "new MetadataResource (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/core/lib/private/metadata-resource.ts:22:24)", 312 | "/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/core/lib/private/synthesis.ts:166:5", 313 | "visit (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/core/lib/private/synthesis.ts:231:5)", 314 | "visit (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/core/lib/private/synthesis.ts:227:5)", 315 | "injectMetadataResources (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/core/lib/private/synthesis.ts:157:3)", 316 | "Object.synthesize (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/core/lib/private/synthesis.ts:18:3)", 317 | "App.synth (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/core/lib/stage.ts:94:23)", 318 | "process. (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/@aws-cdk/core/lib/app.ts:64:45)", 319 | "Object.onceWrapper (node:events:510:26)", 320 | "process.emit (node:events:390:28)", 321 | "process.emit (node:domain:475:12)", 322 | "process.emit (/Users/gherlein/src/chime/dev/us-west-2/single-ec2-cdk/node_modules/source-map-support/source-map-support.js:516:21)" 323 | ] 324 | } 325 | ] 326 | }, 327 | "displayName": "SingleEc2Stack" 328 | } 329 | } 330 | } --------------------------------------------------------------------------------