Skip to main content
In this final section we recap the project and show practical ways to extend the application: adding new stacks for features or environments, and controlling runtime behavior via environment variables. The patterns below focus on CDK for Terraform (CDKTF) with AWS: building reusable Constructs (LambdaFunction, LambdaRestApi), packaging Lambda assets correctly, moving Terraform state to a remote backend (S3 + DynamoDB), and importing Terraform modules via CDKTF. Key outcomes:
  • Reusable Constructs: LambdaFunction and LambdaRestApi.
  • Packaging strategy: prefer TerraformAsset over execSync for deterministic Lambda packaging.
  • Remote backend: transition local state to S3/DynamoDB for team collaboration.
  • CDKTF modules: import generated modules with cdktf get.
The deployed name-picker API returns a random family member to do chores — and the patterns here let Arthur add more features and environments safely.
A slide titled "Recap" with a blue gradient sidebar and a vertical list of colorful numbered markers: 01 Problem, 02 Manual process, 03 LambdaFunction construct, and 04 Packaging with execSync vs TerraformAsset. The slide includes a small © KodeKloud notice at the bottom.

Adding a new stack (WeekPlannerStack)

To add a separate feature as its own deployable unit, create a new stack class that extends your shared AwsBaseStack (this base stack centralizes provider/backend configuration). The WeekPlanner example below demonstrates a minimal stack that reuses the base stack and exposes an output.
Register the stack in your CDKTF app:
When multiple stacks exist the default CDKTF CLI needs an explicit target. Example CLI output when more than one stack is present:
Deploy a specific stack by name:
Or deploy multiple/all stacks using a quoted wildcard to prevent shell expansion:
When you deploy, CDKTF will prompt to confirm which stacks to deploy and then print outputs from each stack (for example, the Week Planner URL).

When to split functionality into separate stacks

A stack should map to a deployable unit of business functionality. Typical reasons to split into separate stacks: Below is the S3 console showing separate per-stack Terraform state files. Using a per-stack backend configuration results in distinct state objects per stack in your backend bucket.
A screenshot of the Amazon S3 console showing the bucket "cdktf-name-picker-prereq-992382811848" with three objects listed (cdktf-name-picker, cdktf-name-picker-prod, cdktf-week-planner) and controls like Upload, Create folder, and Copy URL. The page also shows object sizes, last-modified timestamps, and the S3 navigation sidebar.

Creating a prod stack (example)

A simple way to add a production environment is to instantiate the same stack class with a different ID. To avoid resource name collisions across stacks, use a helper that prefixes names with the stack identifier:
Instantiate dev and prod stacks in main.ts:
Deploying both stacks will create separate Terraform state files and outputs (for example, two API endpoints). Note: the StageName in the exercise example still shows /dev for both stacks — update stage naming if you want distinct stage paths for each environment.

Visual: multiple stacks and components

This diagram shows three stacks (dev/prod for the name picker and a WeekPlanner). Each stack can contain Constructs such as LambdaFunction and LambdaRestApi.
A presentation slide titled "Adding More Stacks" showing three colorful boxes labeled "Name Picker Stack (dev)", "Name Picker Stack (prod)", and "Week Planner (dev)", each containing components like "LambdaFunction Construct" and "LambdaRestApi Construct." A footer reads "Multiple stacks illustrate different app components."

Overriding runtime behavior with environment variables

Making Lambda runtime behavior configurable via environment variables is a practical way to change behavior without changing code. The name-picker Lambda supports a JSON array (NAMES) and a SHUFFLE flag. The handler below supports both modes:
  • Roulette (random): return a random name on each invocation.
  • Shuffle: maintain a shuffled in-memory list and return names sequentially; the in-memory state persists for the lifetime of the execution environment.
You can edit environment variables directly in the AWS Lambda console. The screenshot below shows the Lambda configuration page when no environment variables are set.
Screenshot of the AWS Lambda console on a function's Configuration > Environment variables tab, showing "No environment variables" with an Edit button. The left navigation menu and a tutorial panel are also visible.
Instead of manual edits, define default environment variables in your Lambda construct so they are deployed with the function. The project’s LambdaFunction construct forwards standard Lambda configuration (including environment) directly to the underlying AWS resource. Example:
Because the construct forwards arbitrary Lambda properties, you can add or change environment variables in your stack code and re-deploy — the deployment will overwrite manual console edits.

Hints and type-safety (as const)

TypeScript’s as const can help narrow literal types for compile-time checks. For example:
This pattern is optional but improves type-safety in code that branches on a small set of known values.

How to deploy the full project (quick start)

Include a README with the following step-by-step commands for a fresh checkout. This short sequence is the recommended quick-start for collaborators:
Run these commands after cloning to fetch providers/modules and deploy the backend and app stacks:
  • yarn install — install dependencies
  • yarn cdktf get — generate module bindings
  • yarn deploy:prereq — deploy the S3/DynamoDB remote backend
  • yarn deploy "*" — deploy all stacks (dev, prod, and feature stacks)
Notes:
  • yarn cdktf get generates the .gen folder used for imported Terraform modules.
  • yarn deploy:prereq deploys the prerequisite infrastructure (remote state bucket and locking table).

package.json scripts

Here are the useful npm/yarn scripts included in the project and what they do: Example package.json scripts snippet:

Cleaning up (destroying your infrastructure)

Destroying resources follows the reverse order of deployment. Important: destroy the application stacks first, then destroy the remote backend stack (the S3 bucket and DynamoDB table). If you destroy the backend first (remove the state bucket), Terraform will lose state and cannot reliably destroy the managed resources.
Warning: Always destroy application stacks before destroying the remote state backend. If the S3 bucket containing state is removed while resources still exist, Terraform cannot track or destroy those resources (you may encounter BucketNotEmpty or orphaned resources).
Steps to clean up:
  1. Destroy application stacks:
  2. Empty the backend S3 bucket if required (Terraform will fail with BucketNotEmpty if the bucket is not empty).
  3. Destroy the prereq backend stack:
The console shows an “Empty bucket” confirmation flow when you manually clear a bucket:
A screenshot of the Amazon S3 console showing an "Empty bucket" confirmation page. It displays warnings, a textbox requiring you to type "permanently delete," and an orange "Empty" button to confirm deletion.
After the backend is removed and everything is destroyed, the DynamoDB tables list should be empty:
A screenshot of the AWS DynamoDB console showing the Tables page with no tables in this region and a prominent "Create table" button. The left sidebar lists DynamoDB navigation items like Dashboard, Explore items, Backups, and Settings.
With these steps Arthur can fully tear down the application and avoid surprise cloud costs.
That concludes this lesson. The final summary covers everything learned across the course: problem definition, manual AWS deployment, reusable constructs, packaging strategies, remote backend setup, and importing modules via CDKTF — all combining to produce an automated, shareable, and maintainable infrastructure.

Watch Video

Practice Lab