Auto-Reconcile Policy

An auto-reconcile policy automatically enforces a stack's declared state at regular intervals. When attached to a stack, formae periodically re-applies the last reconciled configuration, undoing any out-of-band changes or patches made since the last reconciliation.

How It Works

When you attach an auto-reconcile policy to a stack:

  1. formae records the declared state after each successful reconcile
  2. At the configured interval, it compares the current state to the last reconciled state
  3. If these deviate is detected, formae automatically applies a reconcile command to restore the declared state

The interval timer starts after each reconcile completes, ensuring consistent spacing between reconciliations regardless of how long each one takes.

Configuration

An auto-reconcile policy has one setting:

Property Type Description
interval Duration How often to reconcile (e.g., 5.min, 1.h)

Out-of-band And Incremental Changes and Auto-Reconcile

formae detects changes to your infrastructure through synchronization. These are changes made through the cloud console or other tools (out-of-band changes) or through formae patches (incremental changes).

Without auto-reconcile, you control what happens to these changes on your next apply:

  • Keep the changes: Accept the out-of-band and incremental changes and update your infrastructure code
  • Overwrite them: Revert to your declared state

With auto-reconcile, such changes are automatically undone at the configured interval. This is useful when you want to enforce strict compliance with your declared configuration.

Tip: Auto-reconcile works well for production stacks where configuration consistency is critical. For development environments where you want flexibility for experimentation, consider not using auto-reconcile.

Examples

Inline Auto-Reconcile Policy

Define the policy directly in the stack:

amends "@formae/forma.pkl"

import "@formae/formae.pkl"
import "@aws/aws.pkl"
import "@aws/ec2/securitygroup.pkl"

local prodTarget = new formae.Target {
  label = "prod-target"
  config = new aws.Config {
    region = "us-east-1"
  }
}

forma {
  prodTarget

  new formae.Stack {
    label = "production-networking"
    description = "Production network security - auto-enforced"
    policies = new Listing {
      new formae.AutoReconcilePolicy {
        interval = 5.min  // Re-enforce every 5 minutes
      }
    }
  }

  new securitygroup.SecurityGroup {
    label = "api-sg"
    stack = "production-networking"
    target = prodTarget.res
    groupDescription = "API security group - managed by formae"
    securityGroupIngress = new Listing {
      new {
        ipProtocol = "tcp"
        fromPort = 443
        toPort = 443
        cidrIp = "0.0.0.0/0"
      }
    }
  }
}

Reusable Auto-Reconcile Policy

Share the same reconciliation interval across multiple production stacks:

amends "@formae/forma.pkl"

import "@formae/formae.pkl"
import "@aws/aws.pkl"
import "@aws/s3/bucket.pkl"

// Reusable policy for all production stacks
local prodReconcile = new formae.AutoReconcilePolicy {
  label = "prod-reconcile-5m"
  interval = 5.min
}

local prodTarget = new formae.Target {
  label = "prod-target"
  config = new aws.Config {
    region = "us-east-1"
  }
}

forma {
  prodReconcile
  prodTarget

  new formae.Stack {
    label = "prod-storage"
    description = "Production storage infrastructure"
    policies = new Listing { prodReconcile.res }
  }

  new bucket.Bucket {
    label = "prod-data"
    stack = "prod-storage"
    target = prodTarget.res
    bucketName = "company-prod-data"
  }

  new formae.Stack {
    label = "prod-logging"
    description = "Production logging infrastructure"
    policies = new Listing { prodReconcile.res }
  }

  new bucket.Bucket {
    label = "prod-logs"
    stack = "prod-logging"
    target = prodTarget.res
    bucketName = "company-prod-logs"
  }
}

Combining TTL and Auto-Reconcile

Use both policies together for controlled, consistent environments:

amends "@formae/forma.pkl"

import "@formae/formae.pkl"
import "@aws/aws.pkl"
import "@aws/logs/loggroup.pkl"

local stagingTarget = new formae.Target {
  label = "staging-target"
  config = new aws.Config {
    region = "us-east-1"
  }
}

forma {
  stagingTarget

  new formae.Stack {
    label = "staging-environment"
    description = "Staging env - auto-reconciled, expires in 7 days"
    policies = new Listing {
      // Auto-reconcile every hour
      new formae.AutoReconcilePolicy {
        interval = 1.h
      }
      // Auto-destroy after 7 days
      new formae.TTLPolicy {
        ttl = 7.d
        onDependents = "cascade"
      }
    }
  }

  new loggroup.LogGroup {
    label = "staging-logs"
    stack = "staging-environment"
    target = stagingTarget.res
    logGroupName = "/staging/logs"
    retentionInDays = 7
  }
}

Use Cases

Security compliance: Ensure security groups and IAM policies always match declared configuration, automatically reverting unauthorized changes.

Production stability: Enforce desired state in critical infrastructure.

Multi-team environments: When multiple teams access the same infrastructure, auto-reconcile ensures changes made outside of version-controlled IaC are reverted.

Audit and compliance: Maintain consistent, auditable infrastructure by automatically correcting deviations from approved configurations.

Monitoring Auto-Reconcile

View auto-reconcile commands in the status output:

# List recent auto-reconcile commands
formae status --query "source:policy:auto-reconcile"

Check which stacks have auto-reconcile policies:

formae inventory stacks

Considerations

Interval selection: Choose an interval that balances responsiveness with API rate limits. Very short intervals (< 1 minute) may trigger rate limiting from cloud providers.

Emergency changes: If you need to make an emergency change that should persist, either temporarily remove the auto-reconcile policy or update your infrastructure code before the next reconciliation.

Interaction with patches: Patch mode changes are reverted by auto-reconcile. If you apply a patch for an urgent fix, the auto-reconciler will revert it on the next cycle unless you also update the declared state.