CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-cdktf

Cloud Development Kit for Terraform - programmatic infrastructure as code using familiar programming languages

Overview
Eval results
Files

annotations-aspects.mddocs/

Annotations and Aspects

Construct annotation system for adding metadata and implementing cross-cutting concerns through aspect-oriented programming patterns.

Capabilities

Annotations Class

System for adding informational messages, warnings, and errors to constructs for documentation and validation purposes.

/**
 * Annotation system for constructs
 */
class Annotations {
  /**
   * Get the annotations instance for a construct
   * @param scope - The construct to get annotations for
   * @returns Annotations instance
   */
  static of(scope: IConstruct): Annotations;

  /**
   * Add an informational message
   * @param message - Information message
   */
  addInfo(message: string): void;

  /**
   * Add a warning message
   * @param message - Warning message
   */
  addWarning(message: string): void;

  /**
   * Add an error message
   * @param message - Error message
   */
  addError(message: string): void;

  /**
   * Get all annotations for this construct
   */
  readonly all: AnnotationEntry[];
}

Usage Examples:

import { Annotations } from "cdktf";

class MyStack extends TerraformStack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    const instance = new AwsInstance(this, "web", {
      ami: "ami-12345678",
      instanceType: "t2.micro"
    });

    // Add informational annotation
    Annotations.of(instance).addInfo(
      "This instance is configured with default security settings"
    );

    // Add warning for potential issues
    if (!instance.keyName) {
      Annotations.of(instance).addWarning(
        "No SSH key specified - remote access will not be possible"
      );
    }

    // Add error for invalid configurations
    if (instance.instanceType.startsWith("t1.")) {
      Annotations.of(instance).addError(
        "t1 instance types are deprecated and should not be used"
      );
    }

    // Stack-level annotations
    Annotations.of(this).addInfo(
      "Stack created for development environment"
    );
  }
}

Aspects System

Aspect-oriented programming system for implementing cross-cutting concerns that apply to multiple constructs.

/**
 * Interface for implementing aspects
 */
interface IAspect {
  /**
   * Visit a construct and apply the aspect
   * @param node - The construct to visit
   */
  visit(node: IConstruct): void;
}

/**
 * Aspects management for constructs
 */
class Aspects {
  /**
   * Get the aspects instance for a construct
   * @param scope - The construct to get aspects for
   * @returns Aspects instance
   */
  static of(scope: IConstruct): Aspects;

  /**
   * Add an aspect to this construct
   * @param aspect - The aspect to add
   */
  add(aspect: IAspect): void;

  /**
   * Get all aspects applied to this construct
   */
  readonly all: IAspect[];
}

Usage Examples:

import { Aspects, IAspect, Annotations } from "cdktf";

// Security aspect for validating security groups
class SecurityGroupValidationAspect implements IAspect {
  visit(node: IConstruct): void {
    if (node instanceof AwsSecurityGroup) {
      const sg = node as AwsSecurityGroup;

      // Check for overly permissive rules
      if (sg.ingress?.some(rule =>
        rule.cidrBlocks?.includes("0.0.0.0/0") && rule.fromPort === 22
      )) {
        Annotations.of(node).addWarning(
          "Security group allows SSH access from anywhere (0.0.0.0/0)"
        );
      }

      // Check for missing egress rules
      if (!sg.egress || sg.egress.length === 0) {
        Annotations.of(node).addInfo(
          "No explicit egress rules defined - will use default allow-all"
        );
      }
    }
  }
}

// Tagging aspect for consistent resource tagging
class TaggingAspect implements IAspect {
  constructor(
    private readonly tags: {[key: string]: string}
  ) {}

  visit(node: IConstruct): void {
    // Apply to resources that support tags
    if (node instanceof TerraformResource) {
      const resource = node as any;
      if (resource.tags !== undefined) {
        resource.tags = {
          ...this.tags,
          ...resource.tags
        };

        Annotations.of(node).addInfo(
          `Applied standard tags: ${Object.keys(this.tags).join(", ")}`
        );
      }
    }
  }
}

// Cost optimization aspect
class CostOptimizationAspect implements IAspect {
  visit(node: IConstruct): void {
    if (node instanceof AwsInstance) {
      const instance = node as AwsInstance;

      // Recommend smaller instance types for development
      if (instance.instanceType?.startsWith("m5.")) {
        Annotations.of(node).addWarning(
          "Consider using t3 instance types for cost optimization"
        );
      }

      // Check for missing monitoring
      if (!instance.monitoring) {
        Annotations.of(node).addInfo(
          "Enable detailed monitoring for better cost visibility"
        );
      }
    }

    if (node instanceof AwsEbsVolume) {
      const volume = node as AwsEbsVolume;

      // Recommend gp3 over gp2
      if (volume.type === "gp2") {
        Annotations.of(node).addWarning(
          "Consider upgrading to gp3 volume type for better price/performance"
        );
      }
    }
  }
}

// Apply aspects to stack
class MyStack extends TerraformStack {
  constructor(scope: Construct, id: string, props: MyStackProps) {
    super(scope, id);

    // Apply tagging aspect to all resources in this stack
    Aspects.of(this).add(new TaggingAspect({
      Environment: props.environment,
      Project: props.project,
      ManagedBy: "terraform",
      CostCenter: props.costCenter
    }));

    // Apply security validation
    Aspects.of(this).add(new SecurityGroupValidationAspect());

    // Apply cost optimization recommendations
    Aspects.of(this).add(new CostOptimizationAspect());

    // Create resources - aspects will be applied automatically
    const vpc = new AwsVpc(this, "vpc", {
      cidrBlock: "10.0.0.0/16"
    });

    const securityGroup = new AwsSecurityGroup(this, "web-sg", {
      vpcId: vpc.id,
      ingress: [{
        fromPort: 80,
        toPort: 80,
        protocol: "tcp",
        cidrBlocks: ["0.0.0.0/0"]
      }]
    });

    const instance = new AwsInstance(this, "web", {
      ami: "ami-12345678",
      instanceType: "t3.micro",
      vpcSecurityGroupIds: [securityGroup.id]
    });
  }
}

Advanced Aspect Patterns

Conditional Aspects

class ConditionalSecurityAspect implements IAspect {
  constructor(
    private readonly environment: string
  ) {}

  visit(node: IConstruct): void {
    // Only apply strict security in production
    if (this.environment === "production") {
      if (node instanceof AwsInstance) {
        const instance = node as AwsInstance;

        if (!instance.keyName) {
          Annotations.of(node).addError(
            "Production instances must have SSH key configured"
          );
        }

        if (instance.associatePublicIpAddress) {
          Annotations.of(node).addError(
            "Production instances should not have public IP addresses"
          );
        }
      }
    }
  }
}

// Apply only to production stacks
if (environment === "production") {
  Aspects.of(stack).add(new ConditionalSecurityAspect(environment));
}

Resource Transformation Aspect

class EncryptionAspect implements IAspect {
  visit(node: IConstruct): void {
    // Automatically enable encryption for EBS volumes
    if (node instanceof AwsEbsVolume) {
      const volume = node as AwsEbsVolume;
      if (!volume.encrypted) {
        volume.encrypted = true;
        Annotations.of(node).addInfo("Encryption enabled automatically");
      }
    }

    // Enable encryption for S3 buckets
    if (node instanceof AwsS3Bucket) {
      const bucket = node as AwsS3Bucket;
      if (!bucket.serverSideEncryptionConfiguration) {
        bucket.serverSideEncryptionConfiguration = [{
          rule: [{
            applyServerSideEncryptionByDefault: [{
              sseAlgorithm: "AES256"
            }]
          }]
        }];
        Annotations.of(node).addInfo("Server-side encryption enabled automatically");
      }
    }
  }
}

Validation Aspect with Custom Logic

class NamingConventionAspect implements IAspect {
  constructor(
    private readonly prefix: string,
    private readonly environment: string
  ) {}

  visit(node: IConstruct): void {
    if (node instanceof TerraformResource) {
      const resource = node as any;

      // Check naming convention for resources with names
      if (resource.name && typeof resource.name === "string") {
        const expectedPrefix = `${this.prefix}-${this.environment}`;

        if (!resource.name.startsWith(expectedPrefix)) {
          Annotations.of(node).addWarning(
            `Resource name should start with "${expectedPrefix}" for consistency`
          );
        }
      }

      // Validate tag consistency
      if (resource.tags) {
        const requiredTags = ["Environment", "Project"];
        const missingTags = requiredTags.filter(tag => !resource.tags[tag]);

        if (missingTags.length > 0) {
          Annotations.of(node).addError(
            `Missing required tags: ${missingTags.join(", ")}`
          );
        }
      }
    }
  }
}

Built-in Aspects

CDKTF includes several built-in aspects for common use cases:

/**
 * Built-in aspect for validating resource configurations
 */
class ValidationAspect implements IAspect {
  visit(node: IConstruct): void;
}

/**
 * Built-in aspect for dependency validation
 */
class DependencyValidationAspect implements IAspect {
  visit(node: IConstruct): void;
}

Type Definitions

/**
 * Annotation entry with metadata
 */
interface AnnotationEntry {
  /**
   * Type of annotation
   */
  readonly type: AnnotationMetadataEntryType;

  /**
   * Annotation message
   */
  readonly message: string;

  /**
   * Stack trace where annotation was created
   */
  readonly trace: string[];

  /**
   * Additional metadata
   */
  readonly data?: any;
}

/**
 * Annotation types
 */
enum AnnotationMetadataEntryType {
  INFO = "info",
  WARN = "warn",
  ERROR = "error"
}

/**
 * Interface for constructs that can have annotations and aspects
 */
interface IConstruct {
  /**
   * Construct identifier
   */
  readonly node: ConstructNode;

  /**
   * Construct path in the tree
   */
  readonly constructPath: string;
}

/**
 * Construct node providing tree navigation
 */
interface ConstructNode {
  /**
   * All child constructs
   */
  readonly children: IConstruct[];

  /**
   * Parent construct
   */
  readonly scope?: IConstruct;

  /**
   * Construct ID
   */
  readonly id: string;

  /**
   * Full path from root
   */
  readonly path: string;
}

Install with Tessl CLI

npx tessl i tessl/npm-cdktf

docs

annotations-aspects.md

backend-configuration.md

core-infrastructure.md

index.md

iterators-dynamic.md

providers-modules.md

provisioners.md

resources-data-sources.md

terraform-functions.md

testing.md

tokens-expressions.md

variables-outputs.md

tile.json