Claude Skills Guide

Claude Code for CDK Aspects Workflow Tutorial

AWS CDK Aspects are one of the most powerful yet underutilized features in the CDK ecosystem. They enable you to apply cross-cutting concerns across your entire infrastructure stack during synthesis, making them ideal for enforcement, validation, and governance. Combined with Claude Code’s AI-assisted development capabilities, you can build robust CDK projects with automated compliance checks and consistent infrastructure patterns.

This tutorial walks you through practical workflows for creating, testing, and maintaining CDK Aspects using Claude Code.

Understanding CDK Aspects

Before diving into the workflow, it’s essential to understand what Aspects do and why they matter for your CDK projects.

How Aspects Work

Aspects operate at synthesis time, traversing the CDK construct tree and applying modifications or validations to constructs. Unlike CloudFormation guards or policies that run after deployment, Aspects intervene during the synthesis phase, allowing you to catch issues before they reach AWS.

The core interface consists of visit() method that receives each construct in the tree:

import { IAspect, IConstruct } from 'constructs';

export class MyAspect implements IAspect {
  visit(node: IConstruct): void {
    // Apply logic to each construct
    console.log(`Visiting: ${node.node.id}`);
  }
}

When you apply an aspect to a stack, CDK automatically traverses all children and invokes your visitor on each:

import { App, Stack, Aspects } from 'aws-cdk-lib';

const app = new App();
const myStack = new Stack(app, 'MyStack');

// Apply aspect to entire stack
Aspects.of(myStack).add(new MyAspect());

Common Use Cases

CDK Aspects excel at several scenarios:

Setting Up Claude Code for CDK Projects

Start by ensuring Claude Code understands your CDK project structure and organizational standards.

Creating Project Context

When you begin a CDK project session with Claude Code, provide a CLAUDE.md file with your infrastructure standards:

# CDK Project Context

## Naming Conventions
- All stack names: {Project}-{Environment}-{Component}
- Resource names: PascalCase with descriptive purpose
- Tags: Environment, CostCenter, Owner required on all resources

## Compliance Requirements
- All S3 buckets must have versioning enabled
- All EC2 instances require specific IAM role
- VPCs must flow logs enabled
- RDS must have deletion protection in prod

## Common Patterns
- Use standardized VPC construct from ./lib/common/
- All buckets use server-side encryption
- ALB redirect HTTP to HTTPS always

This context helps Claude Code generate Aspects that align with your existing infrastructure.

Building Your First CDK Aspect

Let’s create a practical aspect that enforces tagging requirements across your stack.

Tag Enforcement Aspect

Create a new file for your aspect:

// lib/aspects/tag-enforcement.ts
import { IAspect, IConstruct, TagManager, TagType } from 'constructs';
import { CfnResource } from 'aws-cdk-lib';

export interface TagRule {
  key: string;
  allowedValues?: string[];
  required?: boolean;
}

export class TagEnforcement implements IAspect {
  private rules: TagRule[];

  constructor(rules: TagRule[]) {
    this.rules = rules;
  }

  visit(node: IConstruct): void {
    // Only apply to CloudFormation resources
    if (!(node instanceof CfnResource)) {
      return;
    }

    const tags = node.cfnProperties.Tags || [];
    const tagMap = new Map(tags.map((t: any) => [t.Key, t.Value]));

    // Check required tags
    for (const rule of this.rules) {
      if (rule.required && !tagMap.has(rule.key)) {
        console.error(
          `Missing required tag '${rule.key}' on ${node.node.path}`
        );
      }

      // Check allowed values
      if (rule.allowedValues && tagMap.has(rule.key)) {
        const value = tagMap.get(rule.key)!;
        if (!rule.allowedValues.includes(value)) {
          console.error(
            `Invalid tag value '${value}' for '${rule.key}' on ${node.node.path}. ` +
            `Allowed values: ${rule.allowedValues.join(', ')}`
          );
        }
      }
    }
  }
}

Applying the Aspect

Use the aspect in your stack:

import { Stack, App, Aspects } from 'aws-cdk-lib';
import { TagEnforcement, TagRule } from './aspects/tag-enforcement';

const app = new App();

const stack = new Stack(app, 'ProductionStack', {
  env: { region: 'us-east-1', account: '123456789012' }
});

// Define your tagging rules
const requiredTags: TagRule[] = [
  { key: 'Environment', required: true, allowedValues: ['dev', 'staging', 'prod'] },
  { key: 'CostCenter', required: true },
  { key: 'Owner', required: true },
  { key: 'ComplianceLevel', allowedValues: ['low', 'medium', 'high', 'critical'] }
];

// Apply the aspect
Aspects.of(stack).add(new TagEnforcement(requiredTags));

When you run cdk synth, the aspect validates tags and reports violations.

Advanced Aspect: Security Compliance Checker

Build a more sophisticated aspect that checks for security configurations.

S3 Bucket Security Aspect

// lib/aspects/security-checker.ts
import { IAspect, IConstruct } from 'constructs';
import { CfnResource, CfnBucket } from 'aws-cdk-lib';

interface SecurityViolation {
  resource: string;
  issue: string;
  severity: 'high' | 'medium' | 'low';
}

export class SecurityChecker implements IAspect {
  private violations: SecurityViolation[] = [];

  visit(node: IConstruct): void {
    if (node instanceof CfnBucket) {
      this.checkS3Bucket(node);
    }
  }

  private checkS3Bucket(bucket: CfnBucket): void {
    // Check versioning
    if (!bucket.versioningStatus) {
      this.violations.push({
        resource: bucket.node.path,
        issue: 'S3 bucket versioning not enabled',
        severity: 'high'
      });
    }

    // Check encryption
    if (!bucket.bucketEncryption) {
      this.violations.push({
        resource: bucket.node.path,
        issue: 'S3 bucket encryption not configured',
        severity: 'high'
      });
    }

    // Check public access block
    const publicAccessBlock = bucket.publicAccessBlockConfiguration;
    if (!publicAccessBlock || 
        !publicAccessBlock.blockPublicAcls ||
        !publicAccessBlock.blockPublicPolicy) {
      this.violations.push({
        resource: bucket.node.path,
        issue: 'S3 bucket public access not fully blocked',
        severity: 'high'
      });
    }
  }

  report(): SecurityViolation[] {
    return this.violations;
  }
}

Using with Claude Code

When working with Claude Code, you can describe your security requirements in natural language and have it generate the appropriate aspect:

Prompt to Claude Code:

Create a CDK aspect that validates:
1. All EC2 instances have IAM roles attached
2. All RDS instances have deletion protection enabled
3. All security groups have descriptive names
4. All ALBs redirect HTTP to HTTPS
5. VPCs have flow logs enabled

Report violations at different severity levels.

Claude Code will generate a comprehensive security checker aspect matching your requirements.

CI/CD Integration Workflow

Integrate Aspects into your continuous deployment pipeline.

GitHub Actions Example

name: CDK Deploy

on:
  push:
    branches: [main]

jobs:
  synth:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
      
      - name: Install dependencies
        run: |
          npm ci
      
      - name: Run CDK synth
        run: npx cdk synth
        env:
          AWS_REGION: us-east-1
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
      
      - name: Check for Aspect violations
        run: |
          # Aspects should have already reported violations during synth
          # Exit with error if there were critical issues
          if grep -q "CRITICAL" cdk.out/aspect-report.txt; then
            echo "Critical compliance violations detected"
            cat cdk.out/aspect-report.txt
            exit 1
          fi

Creating a Synth Hook

For more control, create a synth step that runs Aspects explicitly:

import { App, Aspects, SynthesisMessage } from 'aws-cdk-lib';

const app = new App();

// Apply aspects
Aspects.of(app).add(new TagEnforcement(rules));
Aspects.of(app).add(new SecurityChecker());

// Hook into synth to capture messages
app.synth({
  skipValidation: false,
  strict: true,
  onSynthesis: (session) => {
    const messages = session.messages;
    const errors = messages.filter(m => m.level === 'error');
    
    if (errors.length > 0) {
      console.error('Aspect validation errors:');
      errors.forEach(e => console.error(e.message));
      throw new Error('Aspect validation failed');
    }
  }
});

Best Practices for CDK Aspects

Follow these guidelines when building and maintaining Aspects with Claude Code.

Keep Aspects Focused

Each aspect should handle one concern. Don’t try to do everything in a single aspect:

// ❌ Don't: One aspect doing too much
class EverythingAspect implements IAspect {
  visit(node: IConstruct): void {
    // Tagging
    // Security checks
    // Naming validation
    // Cost tracking
    // ... 500 lines later
  }
}

// ✅ Do: Focused, composable aspects
class TagEnforcement implements IAspect { /* tagging only */ }
class SecurityChecker implements IAspect { /* security only */ }
class NamingValidator implements IAspect { /* naming only */ }
class CostTagger implements IAspect { /* cost only */ }

// Apply individually
Aspects.of(stack)
  .add(new TagEnforcement(tags))
  .add(new SecurityChecker())
  .add(new NamingValidator())
  .add(new CostTagger());

Test Your Aspects

Claude Code can help generate tests for your aspects:

// test/tag-enforcement.test.ts
import { Stack } from 'aws-cdk-lib';
import { Template } from 'aws-cdk-lib/assertions';
import { TagEnforcement } from '../lib/aspects/tag-enforcement';
import { CfnBucket } from 'aws-cdk-lib';

describe('TagEnforcement', () => {
  test('reports missing required tags', () => {
    const stack = new Stack();
    
    // Create bucket without tags
    new CfnBucket(stack, 'TestBucket', {
      bucketName: 'test-bucket'
    });
    
    const aspect = new TagEnforcement([
      { key: 'Environment', required: true }
    ]);
    
    Aspects.of(stack).add(aspect);
    
    // Synth will trigger the aspect
    expect(() => stack.synth()).toThrow();
  });
});

Conclusion

CDK Aspects combined with Claude Code create a powerful workflow for infrastructure governance. By automating compliance checks, tagging enforcement, and security validation at synthesis time, you catch issues before deployment and maintain consistent infrastructure standards across your organization.

Start with simple tag enforcement, then gradually add more sophisticated Aspects as your governance needs grow. Claude Code can help you build and maintain these aspects efficiently, allowing you to focus on your application logic while ensuring your infrastructure meets organizational standards.

Built by theluckystrike — More at zovo.one