Cloud Development Kit for Terraform - programmatic infrastructure as code using familiar programming languages
Construct annotation system for adding metadata and implementing cross-cutting concerns through aspect-oriented programming patterns.
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"
);
}
}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]
});
}
}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));
}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");
}
}
}
}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(", ")}`
);
}
}
}
}
}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;
}/**
* 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;
}