diff --git a/.gitignore b/.gitignore index 9b8e2c6..b7dab5e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -cdk.out -node_modules \ No newline at end of file +node_modules +build \ No newline at end of file diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..b6a7d89 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +16 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ddbb447 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Symphonia LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 5cebb91..59e8ce6 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,10 @@ This demo shows various techniques: ## How to use -This is not a "200 level" example. :) I'm assuming that if you're going to use this example then you understand CDK, deploying to AWS, and the basics of websites, certificates, and DNS. +This is a somewhat advanced example. Prerequisites are as follows: + +* You should already be able to deploy CDK stacks to AWS. For more details on how to do this, see my ["bare bones CDK" example](https://github.com/symphoniacloud/cdk-bare-bones). +* You should understand the basics of deploying websites, certificates, and DNS. Overall I recommend you review the entire contents of this repo. Particularly relevant files are: @@ -30,16 +33,16 @@ Overall I recommend you review the entire contents of this repo. Particularly re Many of the specific elements in this repo are custom, since it is an actual deployed site. Elements that you will likely want to change if you are copying it are: -* The [per-environment properties](src/cdk/envProps.ts) will drastically change. +* The [per-environment properties](src/cdk/envProps.ts) will change. * Note the main `CoffeeStoreWebDemoStackPropsPerEnv` constant in that file is keyed by account ID, so these will have to change. * Also you'll want to change custom domain names, certificate ARN import details, and hosted zone names (assuming you want to automatically update DNS) * For more on this, see the [documentation for the `cdk-website` construct](https://github.com/symphoniacloud/cdk-website) -* You will want to change `DEFAULT_STACK_NAME` in [cdkApp.ts](src/cdk/cdkApp.ts) . You may also want to be able to change the stack name outside of that file, but that's beyond the scope of this demo +* You will want to change `DEFAULT_STACK_NAME` in [App.ts](src/cdk/App.ts) . You may also want to be able to change the stack name outside of that file, but that's beyond the scope of this demo * If you are generating any content before deployment you will either want to update [deploy.sh](deploy.sh), or update the [custom Github Actions action](.github/actions/deploy/action.yaml) to also build and not just deploy. - * You'll probably also want to change `content` -> `path` in [cdkApp.ts](src/cdk/cdkApp.ts) + * You'll probably also want to change `content` -> `path` in [App.ts](src/cdk/App.ts) * If you are just using static content in the repo, then change the contents of [src/site](src/site) * Consider the ["pre process" CloudFront function](src/cloudfront/preProcessFunction.js): - * Either delete it, and remove the `preProcessFunctionCode` property in [cdkApp.ts](src/cdk/cdkApp.ts) ... + * Either delete it, and remove the `preProcessFunctionCode` property in [App.ts](src/cdk/App.ts) ... * ... or update the `manualRedirects` value in [preProcessFunction.js](src/cloudfront/preProcessFunction.js) * If you want to use the Github Actions workflows: * You'll need to deploy the Github OIDC resources if you haven't done so already. See [here](https://github.com/symphoniacloud/coffee-store-v2/tree/main/github-actions-prereqs) for an example. diff --git a/cdk.json b/cdk.json index 07bb7d9..a384a08 100644 --- a/cdk.json +++ b/cdk.json @@ -1,3 +1,3 @@ { - "app": "npx ts-node --prefer-ts-exts src/cdk/cdkApp.ts" + "app": "npx ts-node --prefer-ts-exts src/cdk/App.ts" } diff --git a/deploy.sh b/deploy.sh index 3b56bc8..80571d1 100755 --- a/deploy.sh +++ b/deploy.sh @@ -5,5 +5,7 @@ set -euo pipefail # Assumes CDK has been bootstrapped in the current account + region # As is, this example uses the default stack name (DEFAULT_STACK_NAME) in the CDK App source -# If you want to override the stack name, then add `--context stackName=YOUR_STACK_NAME` here -npx cdk deploy --require-approval never +# If you want to override the stack name, then add `-- --context stackName=YOUR_STACK_NAME` here +# e.g. the following will deploy a stack named myWebStack: +# npm run deploy -- --context stackName=myWebStack +npm run deploy diff --git a/package.json b/package.json index 582f040..550b93a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,13 @@ { - "name": "cdk-website-basic-example", - "version": "2022.1.0", - "private": true, + "name": "coffee-store-web-demo", + "version": "2022.1.1", + "homepage": "https://github.com/symphoniacloud/coffee-store-web-demo", + "license": "MIT", + "author": { + "email": "mike@symphonia.io", + "name": "Mike Roberts", + "url": "https://symphonia.io" + }, "devDependencies": { "@tsconfig/node16": "1.x", "@types/node": "^16.x", @@ -13,6 +19,10 @@ "typescript": "4.x" }, "scripts": { - "deploy": "npx cdk deploy --require-approval never" + "deploy": "npx cdk deploy --output build/cdk.out --require-approval never", + "cdk-diff" : "npx cdk diff --output build/cdk.out --require-approval never", + "cdk-destroy" : "npx cdk destroy --output build/cdk.out --require-approval never", + "precdk-command": "echo \"Running CDK with command: $npm_config_command\"", + "cdk-command": "npx cdk $npm_config_command --output build/cdk.out --require-approval never" } } diff --git a/src/cdk/cdkApp.ts b/src/cdk/App.ts similarity index 78% rename from src/cdk/cdkApp.ts rename to src/cdk/App.ts index e1b9a8d..128bf02 100644 --- a/src/cdk/cdkApp.ts +++ b/src/cdk/App.ts @@ -3,7 +3,7 @@ import 'source-map-support/register'; import {App, Stack} from 'aws-cdk-lib'; import {Construct} from 'constructs'; import {Website} from "@symphoniacloud/cdk-website"; -import {CoffeeStoreWebDemoStackProps, getPropsForDefaultAWSEnvironment} from "./envProps"; +import {CoffeeStoreWebDemoStackProps, createCoffeeStoreWebDemoStackProps} from "./envProps"; const DEFAULT_STACK_NAME = 'coffee-store-web-demo' @@ -25,6 +25,4 @@ class CoffeeStoreWebDemo extends Stack { } const app = new App(); -const stackName = app.node.tryGetContext('stackName') || DEFAULT_STACK_NAME - -new CoffeeStoreWebDemo(app, 'CoffeeStoreWebDemo', getPropsForDefaultAWSEnvironment(stackName)); \ No newline at end of file +new CoffeeStoreWebDemo(app, 'CoffeeStoreWebDemo', createCoffeeStoreWebDemoStackProps(app, DEFAULT_STACK_NAME)); \ No newline at end of file diff --git a/src/cdk/envProps.ts b/src/cdk/envProps.ts index 0562c89..f4e21c9 100644 --- a/src/cdk/envProps.ts +++ b/src/cdk/envProps.ts @@ -1,6 +1,7 @@ -import {Environment, Fn, StackProps} from "aws-cdk-lib"; +import {App, Fn, StackProps} from "aws-cdk-lib"; import {BehaviorOptions, CachePolicy} from "aws-cdk-lib/aws-cloudfront"; import {WebsiteCustomDomain} from "@symphoniacloud/cdk-website"; +import {createStackProps} from "./initSupport"; export interface CoffeeStoreWebDemoStackProps extends StackProps { customDomain: WebsiteCustomDomain @@ -53,24 +54,13 @@ const CoffeeStoreWebDemoStackPropsPerEnv: Record { - const account = process.env.CDK_DEFAULT_ACCOUNT - const region = process.env.CDK_DEFAULT_REGION - - if (account && region) - return {account, region} - - throw new Error('Unable to read CDK_DEFAULT_ACCOUNT or CDK_DEFAULT_REGION') + return { ...baseStackProps, ...appProps } } \ No newline at end of file diff --git a/src/cdk/initSupport.ts b/src/cdk/initSupport.ts new file mode 100644 index 0000000..d518c4e --- /dev/null +++ b/src/cdk/initSupport.ts @@ -0,0 +1,40 @@ +import {App, Environment, StackProps} from "aws-cdk-lib"; + +// Generic functions to help with stack initialization + +interface StackPropsWithAccountRegionAndStackName extends StackProps { + env: Required + stackName: string +} + +/** + * Generates stack properties based on a default stack name and current AWS environment + * @param app The CDK app instance + * @param defaultStackName Will be used as stackName, unless a stackName property is specified in the app context + * @return valid StackProps, with env and stackName specified + */ +export function createStackProps(app: App, defaultStackName: string): StackPropsWithAccountRegionAndStackName { + return {env: calcEnvironment(), stackName: calcStackName(app, defaultStackName)} +} + +/** + * @param app The CDK app instance + * @param defaultStackName default stack name + * @return defaultStackName, unless stackName is specified in app context, in which case that is returned instead + */ +export function calcStackName(app: App, defaultStackName: string) { + return app.node.tryGetContext('stackName') || defaultStackName +} + +/** + * @return An Environment with both account and region set according to the current AWS environment (see https://docs.aws.amazon.com/cdk/v2/guide/environments.html) + */ +export function calcEnvironment(): Required { + const account = process.env.CDK_DEFAULT_ACCOUNT + const region = process.env.CDK_DEFAULT_REGION + + if (account && region) + return {account, region} + + throw new Error('Unable to read CDK_DEFAULT_ACCOUNT or CDK_DEFAULT_REGION') +} \ No newline at end of file