Skip to main content

CloudFormation Templates

carlin uses CloudFormation templates to define AWS infrastructure. Templates can be written in TypeScript, YAML, or JSON, giving you flexibility in how you define resources.

Template Formats​

TypeScript templates provide type safety, IDE autocomplete, and dynamic generation:

// cloudformation.ts
export const template = {
Resources: {
MyBucket: {
Type: 'AWS::S3::Bucket',
Properties: {
BucketName: 'my-app-bucket',
VersioningConfiguration: {
Status: 'Enabled',
},
},
},
MyTable: {
Type: 'AWS::DynamoDB::Table',
Properties: {
TableName: 'my-app-table',
BillingMode: 'PAY_PER_REQUEST',
AttributeDefinitions: [{ AttributeName: 'id', AttributeType: 'S' }],
KeySchema: [{ AttributeName: 'id', KeyType: 'HASH' }],
},
},
},
Outputs: {
BucketName: {
Value: { Ref: 'MyBucket' },
Export: { Name: 'MyAppBucketName' },
},
TableName: {
Value: { Ref: 'MyTable' },
},
},
};

YAML Templates​

Standard CloudFormation YAML syntax:

# cloudformation.yml
Resources:
MyBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-app-bucket
VersioningConfiguration:
Status: Enabled

Outputs:
BucketName:
Value: !Ref MyBucket
Export:
Name: MyAppBucketName

JSON Templates​

Standard CloudFormation JSON syntax:

{
"Resources": {
"MyBucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketName": "my-app-bucket"
}
}
},
"Outputs": {
"BucketName": {
"Value": { "Ref": "MyBucket" }
}
}
}

Template Discovery​

carlin automatically searches for templates in this order:

  1. ./src/cloudformation.ts
  2. ./src/cloudformation.js
  3. ./src/cloudformation.yaml
  4. ./src/cloudformation.yml
  5. ./src/cloudformation.json

Override with --template-path:

carlin deploy --template-path infrastructure/stack.ts

TypeScript Template Benefits​

1. Type Safety​

import { CloudFormationTemplate } from '@ttoss/cloudformation';

export const template: CloudFormationTemplate = {
Resources: {
Bucket: {
Type: 'AWS::S3::Bucket',
Properties: {
// TypeScript validates property names
BucketName: 'my-bucket',
// Error: 'InvalidProperty' doesn't exist
// InvalidProperty: 'value',
},
},
},
};

2. Dynamic Generation​

const environments = ['dev', 'staging', 'prod'];

export const template = {
Resources: environments.reduce(
(acc, env) => ({
...acc,
[`${env}Bucket`]: {
Type: 'AWS::S3::Bucket',
Properties: {
BucketName: `my-app-${env}-bucket`,
},
},
}),
{}
),
};

3. Code Reusability​

// utils/resources.ts
export const createBucket = (name: string) => ({
Type: 'AWS::S3::Bucket',
Properties: {
BucketName: name,
VersioningConfiguration: { Status: 'Enabled' },
PublicAccessBlockConfiguration: {
BlockPublicAcls: true,
BlockPublicPolicy: true,
IgnorePublicAcls: true,
RestrictPublicBuckets: true,
},
},
});

// cloudformation.ts
import { createBucket } from './utils/resources';

export const template = {
Resources: {
AssetsBucket: createBucket('my-app-assets'),
BackupsBucket: createBucket('my-app-backups'),
},
};

4. Environment Variables​

export const template = {
Parameters: {
Environment: {
Type: 'String',
Default: process.env.NODE_ENV || 'development',
},
},
Resources: {
Database: {
Type: 'AWS::RDS::DBInstance',
Properties: {
DBInstanceClass:
process.env.NODE_ENV === 'production' ? 'db.r5.large' : 'db.t3.micro',
},
},
},
};

Template Structure​

Templates follow the standard AWS CloudFormation template structure with Parameters, Resources, Outputs, and Conditions sections.

Pass parameters during deployment:

carlin deploy --parameters '{"DomainName":"example.com","InstanceType":"t3.small"}'

Lambda Function Handling​

carlin automatically builds and deploys Lambda functions when detected in templates.

Auto-Detected Lambda​

export const template = {
Resources: {
MyFunction: {
Type: 'AWS::Lambda::Function',
Properties: {
Runtime: 'nodejs20.x',
Handler: 'handler.handler', // Points to src/handler.ts
Code: {
S3Bucket: { Ref: 'LambdaS3Bucket' },
S3Key: { Ref: 'LambdaS3Key' },
S3ObjectVersion: { Ref: 'LambdaS3ObjectVersion' },
},
},
},
},
};

Create src/handler.ts:

export const handler = async (event: any) => {
return {
statusCode: 200,
body: JSON.stringify({ message: 'Hello from Lambda!' }),
};
};

carlin automatically:

  1. Builds src/handler.ts using esbuild
  2. Uploads bundle to S3
  3. Injects S3 parameters into template
  4. Deploys Lambda function

Custom Lambda Entry Point​

carlin deploy --lambda-entry-points-base-dir functions

Template references functions/handler.ts instead of src/handler.ts.

Template Size Limits​

CloudFormation templates have a 51,200-byte body limit. carlin automatically uploads large templates to S3.

Automatic handling:

No configuration needed—carlin handles this automatically.

Template Validation​

Validate templates before deployment:

aws cloudformation validate-template --template-body file://cloudformation.yml

Or deploy in validation mode:

carlin deploy --dry-run # (if supported)

Best Practices​

1. Use Parameters for Environment-Specific Values​

// ✅ Good
export const template = {
Parameters: {
DomainName: { Type: 'String' },
},
Resources: {
Distribution: {
Properties: {
DomainName: { Ref: 'DomainName' },
},
},
},
};

// ❌ Bad - hardcoded values
export const template = {
Resources: {
Distribution: {
Properties: {
DomainName: 'production.example.com',
},
},
},
};

2. Export Important Outputs​

export const template = {
Outputs: {
ApiUrl: {
Value: { 'Fn::GetAtt': ['Api', 'Url'] },
Export: { Name: 'MyApiUrl' }, // Other stacks can import this
},
},
};

3. Use TypeScript for Complex Logic​

const regions = ['us-east-1', 'eu-west-1'];

export const template = {
Resources: Object.fromEntries(
regions.map((region) => [
`Bucket${region.replace(/-/g, '')}`,
{
Type: 'AWS::S3::Bucket',
Properties: {
BucketName: `my-app-${region}`,
},
},
])
),
};

4. Organize Large Templates​

// infrastructure/buckets.ts
export const buckets = {
AssetsBucket: {
/* ... */
},
BackupsBucket: {
/* ... */
},
};

// infrastructure/databases.ts
export const databases = {
MainTable: {
/* ... */
},
CacheTable: {
/* ... */
},
};

// cloudformation.ts
import { buckets } from './infrastructure/buckets';
import { databases } from './infrastructure/databases';

export const template = {
Resources: {
...buckets,
...databases,
},
};

5. Add Resource Dependencies​

export const template = {
Resources: {
Bucket: {
Type: 'AWS::S3::Bucket',
},
BucketPolicy: {
Type: 'AWS::S3::BucketPolicy',
DependsOn: 'Bucket', // Wait for bucket creation
Properties: {
Bucket: { Ref: 'Bucket' },
PolicyDocument: {
/* ... */
},
},
},
},
};

Troubleshooting​

Template Validation Errors​

Error: Template format error: ...

Solution: Validate syntax:

aws cloudformation validate-template --template-body file://cloudformation.yml

Parameters Not Working​

Error: Parameters not being passed to stack

Solution: Ensure correct JSON format:

carlin deploy --parameters '{"Key":"Value"}'

Resource Creation Failed​

Error: CloudFormation resource creation failed

Solution: Check CloudFormation console for detailed error messages and stack events.