Skip to content

Practical example: Lifeline

The Lifeline example demonstrates a complete Day-0 to Day-N infrastructure lifecycle. It shows how different teams collaborate, how to use modular architecture patterns, and how apply modes support different workflows.

This example is included with formae and can be found in /opt/pel/formae/examples/lifeline/.

Prerequisites: Understand Fundamentals, Modular infrastructure, and Workflows before diving into this example.

What you'll learn

  • How to organize complex infrastructure into focused modules
  • When to use reconcile vs patch mode
  • How different teams can work on the same infrastructure
  • The Day-0 to Day-N progression pattern

The Lifeline progression

Project structure

Lifeline organizes infrastructure into focused modules, each with a specific responsibility:

lifeline/
├── basic_infrastructure.pkl   # Main entry point
├── cross_cutting_change.pkl   # Cross-cutting updates
├── micro_change.pkl           # Application-specific changes
├── network.pkl                # Networking components
├── nvpc.pkl                   # VPC creation
├── security_groups.pkl        # Security configuration
└── vars.pkl                   # Shared configuration

Key architectural patterns

Function-based resources

Modules use functions to create reusable infrastructure components:

// network.pkl
function subnets(properties: Dynamic?): Listing = new Listing {
    new subnet.Subnet {
        label = "lifeline-public-subnet-1"
        vpcId = nvpc.nvpc(properties).res.id
        cidrBlock = properties.subnetCidr1.value
        availabilityZone = "\(properties.region.value)a"
        tags { new { key = "Name"; value = properties.name.value + "-public-subnet-1" } }
    }
    // More subnets...
}

Clean import strategy

The main forma imports modules and shared configuration:

// basic_infrastructure.pkl
amends "@formae/forma.pkl"
import "@formae/formae.pkl"

import "./vars.pkl"
import "./nvpc.pkl"
import "./network.pkl"
import "./security_groups.pkl"

import "@formae/ext/random.pkl"

Shared configuration

The vars.pkl file contains stack, target, and properties used across all formae:

// vars.pkl - Note the stack and target patterns
projectName = "lifeline"
stackName = "lifeline"
region = "us-east-1"

stack: formae.Stack = new {
    label = stackName
    description = "Stack for the lifeline showcase"
}

target: formae.Target = new formae.Target {
    label = "aws-target"
    config = new aws.Config {
        region = module.regionProp.value
    }
}

vpcLabel = "lifeline-vpc"
s3BucketSuffix = "-unique-bucket-id"
crossCuttingTag = "cross-cutting-tag"
crossCuttingTagValue = "cross-cutting-tag-value"

The complete workflow journey

This example demonstrates how different teams work on the same infrastructure over time using different apply modes.

Day 0: Platform engineers deploy foundation

formae apply --mode reconcile --watch basic_infrastructure.pkl

Platform engineers use reconcile mode to deploy the complete networking foundation. This creates VPC, subnets, internet gateway, route tables, and security groups.

Day 10: Platform engineers make structural changes

formae apply --mode reconcile --watch basic_infrastructure.pkl  # Updated version

Using reconcile mode again ensures the infrastructure matches the updated code exactly.

Day 50: Developers add application resources

formae apply --mode patch --watch micro_change.pkl

Developers use patch mode to add application-specific resources (like S3 buckets) without affecting the foundation infrastructure. The blast radius is minimal - only creates or updates, never destroys. This can be triggered from a GitHub workflow.

Day 90: Security team applies compliance update

formae apply --mode patch --watch cross_cutting_change.pkl

The security team uses patch mode to add compliance tags across multiple resources. While the blast radius in terms of resources touched can be large, the scope of changes is minimal and focused.

Day 100: Clean up everything

formae destroy --watch --query "stack:lifeline"

Working with ephemeral infrastructure or need to start over? Destroy all resources in the stack with a single command.

Understanding the forma files

Let's look at what each forma file does and why specific modes are used.

Foundation infrastructure (reconcile mode)

The basic_infrastructure.pkl creates core networking resources using reconcile mode:

forma {
    vars.stack
    vars.target

    // VPC
    nvpc.nvpc(properties)

    // gateways
    network.igw(properties)
    network.attachIgw(properties)

    // subnets
    for (subnet in network.subnets(properties)) {
        subnet
    }

    network.routeTable(properties)
    network.publicRoute(properties)

    for (subnetAssoc in network.subnetAssocs(properties)) {
        subnetAssoc
    }

    // security groups
    for (securityGroup in security_groups.securityGroups(properties)) {
        securityGroup
    }
}

Why reconcile mode? This is your infrastructure foundation. You want complete control - reconcile mode ensures the infrastructure matches your code exactly, removing anything not defined.

Micro changes (patch mode)

Applying micro_change.pkl adds application-specific resources:

forma {
    vars.stack.res
    vars.target.res

    new bucket.Bucket {
        label = "bucket-1"
        bucketName = vars.projectName + properties.s3BucketSuffix.value + "-1"
        versioningConfiguration = new bucket.BucketVersioningConfiguration {
            status = "Enabled"
        }
        tags {
            new {
                key = "Name"
                value = vars.projectName + "-bucket-1"
            }
            new {
                key = "Project"
                value = vars.projectName
            }
            new {
                key = "Environment"
                value = "Development"
            }
        }
        publicAccessBlockConfiguration = new bucket.BucketPublicAccessBlockConfiguration {
            blockPublicAcls = true
            blockPublicPolicy = true
            ignorePublicAcls = true
            restrictPublicBuckets = true
        }
        bucketEncryption = new bucket.BucketBucketEncryption {
            serverSideEncryptionConfiguration {
                new bucket.BucketServerSideEncryptionRule {
                    serverSideEncryptionByDefault = new bucket.BucketServerSideEncryptionByDefault {
                        sseAlgorithm = "AES256"
                    }
                }
            }
        }
    }
}

Why patch mode here? You're adding resources without changing the foundation. Minimal blast radius, minimal risk, fast deployment.

Cross-cutting changes (patch mode)

Applying cross_cutting_change.pkl updates security across existing resources:

forma {
    vars.stack.res
    vars.target.res

    new internetgateway.InternetGateway {
        label = "lifeline-igw"
        tags {
            new {
                key = vars.crossCuttingTag
                value = vars.crossCuttingTagValue
            }
        }
    }

    new securitygroup.SecurityGroup {
        label = "lifeline-alb-sg"
        groupDescription = "Allow HTTP traffic to ALB"
        tags {
            new {
                key = vars.crossCuttingTag
                value = vars.crossCuttingTagValue
            }
        }
    }

    new securitygroup.SecurityGroup {
        label = "lifeline-task-sg"
        groupDescription = "Allow HTTP traffic from ALB"
        tags {
            new {
                key = vars.crossCuttingTag
                value = vars.crossCuttingTagValue
            }
        }
    }
}

Why patch mode here? You're changing multiple resources that affect the foundation. There is minimal risk despite a potentially large blast radius, and no need to see unnecessary details in order to make orthogonal changes.

Takeaway Insight: Each apply mode serves different team workflows and risk profiles.

Try it yourself

You can run this example on your own AWS account:

  1. Copy the example:

    cd ~/projects
    cp -R /opt/pel/formae/examples/lifeline ./
    cd lifeline

  2. Review the code: Examine the module structure and see how functions create reusable components.

  3. Test with eval:

    # See what will be created
    formae eval basic_infrastructure.pkl
    
    # Test with different property values
    formae eval --region us-west-2 basic_infrastructure.pkl

  4. Deploy the foundation:

    formae apply --mode reconcile --watch basic_infrastructure.pkl

  5. Add application resources:

    formae apply --mode patch --watch micro_change.pkl

  6. Clean up when done:

    formae destroy --watch --query "stack:lifeline"

What's next

You've seen how to organize complex infrastructure using modules and apply modes. Continue learning: