Skip to main content

Templates and Functions

Comet uses Go templates with additional functions to enable dynamic configuration. This powerful feature allows you to reference variables, use conditional logic, and access cross-stack state.

Template Syntax

Templates use the {{ }} syntax:

const vpc = component('vpc', 'modules/vpc', {
name: 'vpc-{{ .stack }}', // Becomes 'vpc-dev', 'vpc-prod', etc.
region: '{{ .settings.region }}'
})

Built-in Variables

.stack

The current stack name:

backend('gcs', {
bucket: 'terraform-state',
prefix: '{{ .stack }}/{{ .component }}' // dev/vpc, prod/vpc, etc.
})

.component

The current component name:

const database = component('db', 'modules/cloudsql', {
instance_name: '{{ .stack }}-{{ .component }}' // dev-db, prod-db
})

.settings

Access stack settings:

stack('production', {
project_id: 'my-gcp-project',
region: 'us-central1',
db_tier: 'db-n1-standard-2'
})

const db = component('db', 'modules/cloudsql', {
project: '{{ .settings.project_id }}',
region: '{{ .settings.region }}',
tier: '{{ .settings.db_tier }}'
})

Template Functions

state - Cross-Stack References

Reference outputs from other stacks:

const app = component('app', 'modules/application', {
vpc_id: '{{ (state "infrastructure" "vpc").id }}',
db_host: '{{ (state "data" "database").connection_name }}'
})

See the Cross-Stack References page for more details.

secrets - Encrypted Secrets

Access SOPS-encrypted secrets:

const db = component('database', 'modules/cloudsql', {
password: '{{ secrets "sops://secrets.enc.yaml#/database/password" }}'
})

See the Secrets Management page for more details.

Conditional Logic

Use if, else, and end for conditional values:

const instance = component('vm', 'modules/compute', {
// Different machine types per environment
machine_type: '{{ if eq .stack "production" }}n1-standard-4{{ else }}n1-standard-1{{ end }}',

// Enable features only in production
deletion_protection: '{{ if eq .stack "production" }}true{{ else }}false{{ end }}'
})

Comparison Operators

  • eq - Equal to
  • ne - Not equal to
  • lt - Less than
  • le - Less than or equal
  • gt - Greater than
  • ge - Greater than or equal
const backup = component('backup', 'modules/backup', {
// Retention days based on environment
retention_days: '{{ if eq .stack "production" }}30{{ else if eq .stack "staging" }}7{{ else }}1{{ end }}'
})

String Functions

printf - String Formatting

const bucket = component('storage', 'modules/gcs', {
name: '{{ printf "%s-%s-data" .settings.project_id .stack }}'
// Results in: myproject-dev-data
})

lower / upper - Case Conversion

const resource = component('resource', 'modules/generic', {
name: '{{ .stack | lower }}', // dev, staging, production
label: '{{ .stack | upper }}' // DEV, STAGING, PRODUCTION
})

replace - String Replacement

const name = component('service', 'modules/app', {
// Replace underscores with hyphens
service_name: '{{ .settings.app_name | replace "_" "-" }}'
})

trim - Remove Whitespace

const config = component('config', 'modules/config', {
value: '{{ .settings.some_value | trim }}'
})

List and Object Functions

join - Join List Elements

const firewall = component('firewall', 'modules/firewall', {
// Join list of IPs
source_ranges: '{{ .settings.allowed_ips | join "," }}'
})

split - Split String

// In stack settings
stack('dev', {
regions_str: 'us-central1,us-east1,us-west1'
})

// Use in component (though JavaScript would be better for this)
const multi_region = component('app', 'modules/app', {
regions: '{{ .settings.regions_str | split "," }}'
})

Default Values

default - Provide Fallback

const vm = component('vm', 'modules/compute', {
// Use default if not set
machine_type: '{{ .settings.machine_type | default "n1-standard-1" }}',
zone: '{{ .settings.zone | default "us-central1-a" }}'
})

Nested Templates

Access nested settings:

stack('production', {
gcp: {
project_id: 'my-project',
region: 'us-central1'
},
aws: {
region: 'us-west-2',
account_id: '123456789'
}
})

const gcp_resource = component('gke', 'modules/gke', {
project: '{{ .settings.gcp.project_id }}',
region: '{{ .settings.gcp.region }}'
})

const aws_resource = component('eks', 'modules/eks', {
region: '{{ .settings.aws.region }}'
})

Combining JavaScript and Templates

You can combine JavaScript logic with template strings:

// JavaScript for complex logic
const environments = {
dev: { size: 'small', replicas: 1 },
staging: { size: 'medium', replicas: 2 },
production: { size: 'large', replicas: 5 }
}

stack('production', {
env_config: environments['production']
})

// Templates for dynamic values
const app = component('app', 'modules/k8s', {
replicas: '{{ .settings.env_config.replicas }}',
resources: {
size: '{{ .settings.env_config.size }}'
}
})

Common Patterns

Environment-Specific Configuration

const db = component('database', 'modules/cloudsql', {
tier: '{{ if eq .stack "production" }}db-n1-standard-4{{ else }}db-f1-micro{{ end }}',
backup_enabled: '{{ if eq .stack "production" }}true{{ else }}false{{ end }}',
high_availability: '{{ if eq .stack "production" }}REGIONAL{{ else }}ZONAL{{ end }}'
})

Resource Naming

const resources = component('app', 'modules/app', {
// Pattern: project-environment-component
name: '{{ printf "%s-%s-%s" .settings.project_name .stack .component }}',

// Pattern: component-environment
alt_name: '{{ .component }}-{{ .stack }}',

// Pattern: ENVIRONMENT_COMPONENT
env_var_name: '{{ printf "%s_%s" (.stack | upper) (.component | upper) }}'
})

Labels and Tags

const instance = component('vm', 'modules/compute', {
labels: {
environment: '{{ .stack }}',
component: '{{ .component }}',
managed_by: 'comet',
project: '{{ .settings.project_name }}'
}
})

Best Practices

  1. Use JavaScript for Complex Logic - Templates are great for simple substitutions, but use JavaScript for complex conditionals and transformations

  2. Keep Templates Readable - Break complex templates into multiple lines or use JavaScript variables

  3. Validate Template Output - Use comet export to see the generated Terraform files and verify template expansion

  4. Document Template Variables - Add comments explaining what template variables are used and where they come from

// Good: Clear and documented
stack('production', {
// GCP configuration
project_id: 'my-gcp-project', // GCP project ID
region: 'us-central1', // Primary region

// Database configuration
db_tier: 'db-n1-standard-4', // CloudSQL tier
db_version: 'POSTGRES_14' // PostgreSQL version
})