Learn
M3: Infrastructure

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

Apply

M3.1 - Architecture Diagram

Generate an architecture diagram of your web app with cdk-dia (opens in a new tab)

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
  • Run pnpm deploy:pipeline within infra/ 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 to false in infra/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 the data 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 of DbIamCluster
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 in infra/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 in infra/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 and stageRegions, of infra/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.