Module 3: Infrastructure
To define AWS infrastructure as code, Green Boost uses the AWS Cloud Development Kit (CDK) because it allows developers to use familiar programming languages like TypeScript to define AWS infrastructure. Additionally, the AWS CDK enables easy customization, sharing, and reusing constructs or units of infrastructure.
Learn
- AWS CDK
- Read through the developer guide (opens in a new tab)
- Do the AWS CDK TypeScript Workshop (opens in a new tab)
- cdk-nag: scan the README (opens in a new tab) and AWS Solutions Rule Pack (opens in a new tab)
- Review the
infra/
folder scaffolded by the GB CLI
Apply
M3.1 - Architecture Diagram
Generate an architecture diagram of your web app with cdk-dia (opens in a new tab)
- Install Graphviz (opens in a new tab) with instructions here (opens in a new tab).
- Within the
infra/
folder runpnpm cdk-dia
- Checkout your diagram.png!
M3.2 - Deploy the CDK Pipeline
- You've already deployed locally your web app which is ideal for development, but for test and prod environments you'll want deployment to be automated via a Continuous Integration/Continuous Deployment (CDI/CD) Pipeline triggered by a commit to a repository. We'll use CodeCommit but keep in mind you can also use git services.
- First, create a CodeCommit repository in your AWS account with the same name as your
appId
. - Commit code to the repository
git add -A git commit -m "initial commit" git remote add origin <your-code-commit-repo-url> git push --set-upstream origin main
- If you don’t have a workflow setup for connecting to AWS CodeCommit, try this: update your
~/.gitconfig
with the below code block to use IAM credentials to authenticate to CodeCommit. For more info see here (opens in a new tab). Note: make sure to change the region in below code block if you’re not using us-east-1.
.gitconfig[credential "https://git-codecommit.us-east-1.amazonaws.com/v1/repos"] helper = # reset helper helper = !aws codecommit credential-helper $@ useHttpPath = true
- If you don’t have a workflow setup for connecting to AWS CodeCommit, try this: update your
- Run
pnpm deploy:pipeline
withininfra/
folder. - Checkout the pipeline deployed in the CodePipeline AWS Console.
- For now, you should only see a "dev" stage deployed.
AWS has a soft limit of 5 Elastic IPs (EIPs). Each stage/environment deployed uses 2 public subnets which include a NAT Gateway. Each NAT Gateway requires an EIP. Therefore, if you attempt to deploy 3 or more stages (6 EIPs) you'll surpass the limit and the deployment will fail. You can either request an increase in your EIP quota or do multi-account deployments.
M3.3 - cdk deploy
Options
In order to deploy the Aurora PostgreSQL web app in module 1, you ran the command: pnpm deploy:local
. This command is simply an alias for the command: cdk deploy "**" --require-approval never --no-rollback
. You can find the alias mapping defined in infra/package.json#scripts
. When developing your web app further, you'll likely only need to deploy updates to stacks one at a time. The "**"
in cdk deploy "**"
tells the CDK CLI to deploy all stacks. To only deploy updates to one stack, you can run cdk deploy "*/<stack-id>" --exclusively
. Try exclusively deploying your ui stack:
cdk deploy "*/ui" --exclusively
Other stack IDs include: data, monitor, and waf. These IDs are defined in infra/src/app-stage.ts
. We specify a */
before the <stack-id>
because in the construct tree (opens in a new tab) above each of the stacks is a Stage (opens in a new tab). Stages are helpful because they allow us to encapsulate our application's stacks and deploy them locally in infra/src/local-app.ts
or via a pipeline in infra/src/pipeline/pipeline-app.ts
. You can specify <stage-id>/<stack-id>
where stage-id
is defined in infra/src/config/config.ts
but we use a *
because it's more concise and we're only deploying 1 stage.
Exclusively deploying stacks is faster, but what if we're only changing a lambda function or another commonly changed resource and want to quickly update it without going through CloudFormation? To do this, we can use cdk deploy
's --hotswap (opens in a new tab) option. This option attempts to update your AWS resources directly (like through AWS Lambda's UpdateFunctionCode
API). These updates typically take under 10 seconds 🚀. Try this out by updating a route in your Next.js app (runs as Lambda function) within the ui/src/app
folder then run:
cdk deploy "*/ui" --exclusively --hotswap
You'll notice the open-next build
process still takes a while, but the update of the Lambda function code is much faster with --hotswap
.
M3.4 - cdk-nag
cdk-nag checks CDK applications for best practices using a combination of available rule packs. GB templates use the AWS Solutions by default which enforces 130+ best practices. Take a moment to scan through those rules (opens in a new tab). Now, let's see cdk-nag
in action.
- Change
storageEncrypted: true
tofalse
ininfra/src/app/stateful/data-stack.ts
. - Run
cdk synth "**" --exclusively
and you should see a build error,AwsSolutions-RDS2: The RDS instance or Aurora DB cluster does not have storage encryption enabled.
, because thedata
stack is violating the rule. This is good!cdk-nag
is a mechanism for preventing security misconfiguration, an OWASP Top 10 category (opens in a new tab). - Let's say you have a requirement that necessitates having unencrypted DB storage. You can suppress a rule through several different methods documented here (opens in a new tab). In order to suppress
AwsSolutions-RDS2
add the following code below the instantiation ofDbIamCluster
NagSuppressions.addResourceSuppressions(
cluster.node.findChild("Resource"),
[
{
id: "AwsSolutions-RDS2",
reason:
"Customer requirement necessitates having unencrypted DB storage",
},
],
);
- Now run
cdk synth "**"
again and your CDK app should successfully synthesize. You can view all suppressed rules ininfra/cdk.out/assembly-<appId>-<stageName>/AwsSolutions-<appId>-<stageName>-${stackId}-NagReport.csv
. This can be sent to your security team to ensure only acceptable rule suppressions are used.
M3.5 - Multi-account CDK Pipeline Deployment
It's best practice (opens in a new tab) to deploy stages/environments into separate AWS Accounts. In order to do this, follow the below steps.
- Bootstrap the additional AWS account you'll deploy to with the command:
cdk bootstrap aws://<new-aws-account-number>/<new-region> --trust <original-aws-account-number>
replacing the angle brackets with denoted values. See here (opens in a new tab) for more info.- Make sure you have the correct AWS credentials in your shell to bootstrap the account.
- Uncomment the line
// crossAccountKeys:true
ininfra/src/pipeline/pipeline-stack.ts
. - Uncomment either the test or prod stage at the bottom of
infra/src/pipeline/pipeline-stack.ts
. - Update the static members,
stageAccounts
andstageRegions
, ofinfra/src/config/config.ts
with your new AWS account number and region. - Run
pnpm deploy:pipeline
again and watch progress of pipeline as it deploys to the new AWS account. - Find the CloudFront Domain of your app deployed in your new AWS account and ensure your web app is working correctly.