Policy

A policy is a configurable behavior that you attach to a stack. Policies let you define lifecycle rules like automatic cleanup after a time period (TTL) or automatic enforcement of declared state (auto-reconcile).

formae provides two built-in policy types:

  • TTL Policy: Automatically destroys a stack and its resources after a specified duration
  • Auto-Reconcile Policy: Automatically re-applies the declared state at regular intervals, undoing any out-of-band and incremental changes

Inline vs Reusable Policies

Policies can be defined in two ways:

Inline Policies

Inline policies are defined directly within a stack definition. They are owned by the stack and automatically deleted when the stack is destroyed.

new formae.Stack {
  label = "dev-environment"
  policies = new Listing {
    new formae.TTLPolicy {
      ttl = 4.h
      onDependents = "cascade"
    }
  }
}

Inline policies:

  • Do not have a label
  • Have their lifecycle tied to the owning stack
  • Cannot be shared across stacks

Reusable Policies

Reusable policies are defined as standalone objects and referenced by one or more stacks using the .res reference. This lets you define a policy once and apply it consistently across multiple stacks.

// Define a reusable policy
local ephemeralPolicy = new formae.TTLPolicy {
  label = "ephemeral-1h"
  ttl = 1.h
  onDependents = "abort"
}

forma {
  // Add the policy to the forma
  ephemeralPolicy

  // Reference it from multiple stacks
  new formae.Stack {
    label = "dev-stack-1"
    policies = new Listing { ephemeralPolicy.res }
  }

  new formae.Stack {
    label = "dev-stack-2"
    policies = new Listing { ephemeralPolicy.res }
  }
}

Reusable policies:

  • Require an explicit label
  • Have an independent lifecycle (must be explicitly deleted)
  • Can be shared across multiple stacks via .res reference
  • Changes propagate to all referencing stacks

Comparison

Aspect Inline Reusable
Label None Required
Lifecycle Deleted with stack Independent
Sharing Single stack only Multiple stacks
Use case One-off behaviors Consistent policies across stacks

Querying Policies

Use formae inventory policies to list all reusable policies:

formae inventory policies

Use formae inventory stacks to see which policies are attached to each stack:

formae inventory stacks

Examples

Development environment with TTL:

// Short-lived dev stack that auto-destroys after 8 hours
new formae.Stack {
  label = "feature-dev"
  description = "Ephemeral development environment"
  policies = new Listing {
    new formae.TTLPolicy {
      ttl = 8.h
      onDependents = "cascade"
    }
  }
}

Production stack with auto-reconcile:

// Production stack that undoes out-of-band and incremental changes every 5 minutes
new formae.Stack {
  label = "production-api"
  description = "Production API infrastructure"
  policies = new Listing {
    new formae.AutoReconcilePolicy {
      interval = 5.min
    }
  }
}

Shared policies across environments:

// Reusable policies for consistency
local devTTL = new formae.TTLPolicy {
  label = "dev-ttl-24h"
  ttl = 24.h
  onDependents = "cascade"
}

local prodReconcile = new formae.AutoReconcilePolicy {
  label = "prod-reconcile-5m"
  interval = 5.min
}

forma {
  devTTL
  prodReconcile

  new formae.Stack {
    label = "dev-us-east"
    policies = new Listing { devTTL.res }
  }

  new formae.Stack {
    label = "dev-eu-west"
    policies = new Listing { devTTL.res }
  }

  new formae.Stack {
    label = "prod-api"
    policies = new Listing { prodReconcile.res }
  }
}