Chapter 5: AWS CDK Fundamentals
Chapter 5: AWS CDK Fundamentals
This chapter introduces the fundamental concepts of AWS Cloud Development Kit (CDK), installation and configuration methods, and how to perform basic development using the Python SDK. CDK is an Infrastructure as Code (IaC) tool provided by AWS that allows you to define cloud resources using familiar programming languages.
Learning Objectives
- Understand AWS CDK core concepts and architecture
- Master CDK installation and configuration processes
- Learn to perform basic development using Python CDK SDK
- Understand CDK project structure and best practices
- Be able to create and deploy your first CDK application
5.1 AWS CDK Overview
5.1.1 What is AWS CDK
AWS Cloud Development Kit (CDK) is an open-source software development framework for defining cloud infrastructure using familiar programming languages. CDK compiles your code into CloudFormation templates.
- Infrastructure as Code: Use familiar programming languages
- IDE Support: Complete auto-completion and type checking
- Component Reusability: Code reuse through functions and classes
- High-level Abstraction: Provides high-level constructs to simplify complex configurations
5.1.2 CDK Core Concepts
| Concept | Description | Example |
|---|---|---|
| App | Root of CDK application, contains one or more Stacks | cdk.App() |
| Stack | Deployment unit, corresponds to a CloudFormation template | MyStack(app, "MyStack") |
| Construct | Basic building block, represents cloud components | aws_lambda.Function() |
| Resource | Lowest-level construct, directly maps to CloudFormation resources | CfnFunction |
5.1.3 CDK Architecture Model
- L1 Constructs: Direct mapping to CloudFormation resources
- L2 Constructs: High-level constructs with sensible defaults
- L3 Constructs: Pattern constructs that implement common architecture patterns
5.2 Environment Setup and Installation
5.2.1 Prerequisites
# Check Node.js version (required by CDK CLI)
node --version # Requires >= 14.x
# Check Python version
python --version # Recommended >= 3.8
# Check AWS CLI configuration
aws configure list
5.2.2 Installing CDK CLI
# Install CDK CLI globally
npm install -g aws-cdk
# Verify installation
cdk --version
# Initialize CDK environment (first-time use)
cdk bootstrap
cdk bootstrap will create necessary resources in your AWS account (S3 buckets, IAM roles, etc.), and only needs to be executed once per region.
5.2.3 Creating a Python CDK Project
# Create new CDK project
mkdir my-lambda-cdk
cd my-lambda-cdk
# Initialize Python CDK project
cdk init app --language python
# Activate virtual environment
source .venv/bin/activate # Linux/Mac
# .venv\Scripts\activate.bat # Windows
# Install dependencies
pip install -r requirements.txt
5.2.4 Project Structure Breakdown
my-lambda-cdk/
├── app.py # CDK application entry point
├── my_lambda_cdk/ # Main code directory
│ ├── __init__.py
│ └── my_lambda_cdk_stack.py # Stack definition
├── tests/ # Test directory
├── requirements.txt # Python dependencies
├── cdk.json # CDK configuration file
└── .venv/ # Virtual environment
5.3 Python CDK SDK Basics
5.3.1 Basic Imports and Setup
# app.py
import aws_cdk as cdk
from my_lambda_cdk.my_lambda_cdk_stack import MyLambdaCdkStack
app = cdk.App()
MyLambdaCdkStack(app, "MyLambdaCdkStack",
env=cdk.Environment(
account='123456789012', # Replace with your account ID
region='us-east-1' # Replace with your region
)
)
app.synth()
5.3.2 Basic Stack Structure
# my_lambda_cdk/my_lambda_cdk_stack.py
from aws_cdk import (
Stack,
aws_lambda as _lambda,
aws_iam as iam,
Duration,
CfnOutput
)
from constructs import Construct
class MyLambdaCdkStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# Define your resources here
self.create_lambda_function()
def create_lambda_function(self):
"""Example method to create Lambda function"""
# Will be implemented in later chapters
pass
5.3.3 Common CDK Patterns
Resource Naming Conventions
class MyLambdaCdkStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# Use consistent naming conventions
self.lambda_function = self._create_lambda_function()
self.api_gateway = self._create_api_gateway()
def _create_lambda_function(self):
"""Private method to create Lambda function"""
return _lambda.Function(
self, "MyFunction", # Construct ID
# Configuration parameters...
)
Environment Variables and Configuration
import os
from aws_cdk import aws_lambda as _lambda
# Get configuration from environment variables
ENVIRONMENT = os.getenv('ENVIRONMENT', 'dev')
REGION = os.getenv('AWS_REGION', 'us-east-1')
class MyLambdaCdkStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# Environment-based resource naming
function_name = f"my-function-{ENVIRONMENT}"
self.lambda_function = _lambda.Function(
self, "MyFunction",
function_name=function_name,
# Other configurations...
)
5.4 CDK Command Line Tools
5.4.1 Common CDK Commands
| Command | Function | Example |
|---|---|---|
cdk list | List all Stacks | cdk list |
cdk synth | Synthesize CloudFormation template | cdk synth MyStack |
cdk diff | Show differences from deployed version | cdk diff MyStack |
cdk deploy | Deploy Stack | cdk deploy MyStack |
cdk destroy | Delete Stack | cdk destroy MyStack |
5.4.2 Development Workflow
# 1. After developing code, first check syntax
python -m py_compile my_lambda_cdk/*.py
# 2. Synthesize template for verification
cdk synth
# 3. View changes
cdk diff
# 4. Deploy to development environment
cdk deploy --profile dev
# 5. After testing, deploy to production
cdk deploy --profile prod
5.4.3 Debugging and Troubleshooting
# Enable debug mode
import aws_cdk as cdk
app = cdk.App()
# Add tags for resource management
cdk.Tags.of(app).add("Project", "MyLambdaProject")
cdk.Tags.of(app).add("Environment", "Development")
# Output important information
class MyLambdaCdkStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# Create resources...
# Output useful information
CfnOutput(self, "LambdaFunctionArn",
value=self.lambda_function.function_arn,
description="Lambda Function ARN")
5.5 CDK Best Practices
5.5.1 Project Organization
# Recommended project structure
my_lambda_cdk/
├── stacks/ # Stack definitions
│ ├── __init__.py
│ ├── lambda_stack.py # Lambda resources
│ ├── api_stack.py # API Gateway related
│ └── database_stack.py # Database related
├── constructs/ # Custom constructs
│ ├── __init__.py
│ └── lambda_construct.py
├── lambda_functions/ # Lambda function code
│ ├── function1/
│ └── function2/
└── tests/ # Test code
5.5.2 Configuration Management
# config.py
import os
from dataclasses import dataclass
from typing import Dict, Any
@dataclass
class EnvironmentConfig:
environment: str
lambda_memory: int
lambda_timeout: int
log_level: str
@classmethod
def from_environment(cls) -> 'EnvironmentConfig':
env = os.getenv('ENVIRONMENT', 'dev')
configs = {
'dev': cls(
environment='dev',
lambda_memory=128,
lambda_timeout=30,
log_level='DEBUG'
),
'prod': cls(
environment='prod',
lambda_memory=256,
lambda_timeout=60,
log_level='INFO'
)
}
return configs.get(env, configs['dev'])
5.5.3 Security Best Practices
- Never hardcode sensitive information in code
- Use AWS Secrets Manager or Parameter Store to store secrets
- Follow the principle of least privilege
- Regularly review IAM permissions
from aws_cdk import aws_secretsmanager as secretsmanager
class SecureStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# Create secret
db_secret = secretsmanager.Secret(
self, "DatabaseSecret",
description="Database credentials",
generate_secret_string=secretsmanager.SecretStringGenerator(
secret_string_template='{"username": "admin"}',
generate_string_key="password",
exclude_characters=" %+~`#$&*()|[]{}:;<>?!'/\\\"",
password_length=32
)
)
# Lambda function using secret
lambda_function = _lambda.Function(
self, "SecureFunction",
# ... other configurations
environment={
"DB_SECRET_ARN": db_secret.secret_arn
}
)
# Grant Lambda permission to access secret
db_secret.grant_read(lambda_function)
5.6 Practical Exercises
5.6.1 Creating Your First CDK Application
Create a simple CDK application with basic Stack structure:
# exercises/basic_app.py
import aws_cdk as cdk
from aws_cdk import Stack
from constructs import Construct
class BasicStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# Add tags
cdk.Tags.of(self).add("Project", "CDK-Learning")
# Output Stack information
cdk.CfnOutput(self, "StackName",
value=self.stack_name,
description="The name of this stack")
app = cdk.App()
BasicStack(app, "BasicStack")
app.synth()
5.6.2 Exercise Problems
- Basic Exercise: Create a CDK project containing two Stacks
- Intermediate Exercise: Implement configuration management supporting different environments
- Advanced Exercise: Create custom Constructs to encapsulate common functionality
5.7 Chapter Summary
- CDK is a powerful tool for defining AWS infrastructure using code
- Python CDK provides complete type support and IDE integration
- Following best practices improves code quality and maintainability
- Security should be considered from the start of the project
In the next chapter, we will learn how to create and deploy Lambda functions using CDK, including function configuration, permission management, and deployment strategies.