Quay lại TIL
2023-04-05Devopsadvanced7 min

Infrastructure as Code with AWS CDK


# Infrastructure as Code with AWS CDK

Today I learned how to use AWS Cloud Development Kit (CDK) to define cloud infrastructure using TypeScript, which provides significant advantages over traditional CloudFormation templates.

## The Evolution of IaC

Infrastructure as Code has evolved from manual scripts to declarative templates (CloudFormation, Terraform) and now to programming language constructs with AWS CDK. This evolution brings several benefits:

## Key Advantages of CDK

- **Type Safety**: Compile-time checks catch errors before deployment
- **Abstraction**: High-level constructs simplify complex infrastructure patterns
- **Reusability**: Create custom constructs for organization-specific patterns
- **Familiar Tools**: Use IDEs, testing frameworks, and version control as with application code
- **Logic & Loops**: Leverage programming language features for dynamic infrastructure

## Practical Implementation

I migrated our microservices infrastructure from CloudFormation to CDK, creating:

1. A base stack with shared VPC and security groups
2. Reusable constructs for standard microservice patterns
3. Environment-specific configurations using context variables
4. Custom aspects for enforcing security and compliance policies

## Results

The migration reduced our infrastructure code by approximately 60% while making it more maintainable and less error-prone. New service deployments that previously took days to configure now take hours, significantly accelerating our delivery pipeline.

Code Example

import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';

export class MicroserviceStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Create VPC and Fargate Cluster
    const vpc = new ec2.Vpc(this, 'ServiceVPC', { maxAzs: 2 });
    const cluster = new ecs.Cluster(this, 'Cluster', { vpc });
    
    // Define the Fargate Service
    const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef');
    taskDefinition.addContainer('AppContainer', {
      image: ecs.ContainerImage.fromRegistry('my-app:latest'),
      memoryLimitMiB: 512,
      cpu: 256,
      portMappings: [{ containerPort: 80 }]
    });
    
    // Create the service
    const service = new ecs.FargateService(this, 'Service', {
      cluster,
      taskDefinition,
      desiredCount: 2
    });
    
    // Set up load balancing
    const lb = new elbv2.ApplicationLoadBalancer(this, 'LB', { vpc, internetFacing: true });
    const listener = lb.addListener('Listener', { port: 80 });
    listener.addTargets('Target', {
      port: 80,
      targets: [service]
    });
    
    // Output the load balancer URL
    new cdk.CfnOutput(this, 'LoadBalancerDNS', { value: lb.loadBalancerDnsName });
  }
}