Skip to main content
As a platform team you want developers to get production-ready infrastructure without touching the code that defines it. formae lets you do this with properties: you write one parameterized forma, expose only the inputs developers should choose, and every property becomes a CLI flag automatically. Developers run formae apply with a few flags, and your code turns those choices into compliant infrastructure. This guide walks through building such an offering: define the interface, map choices to real infrastructure, bake in your standards, and hand it to developers.

Design the developer interface

Properties are the contract between you and your developers. Declare only the inputs they need to vary as members of a typed Props class. Each member’s name becomes a --flag CLI option automatically. A member with a default is optional; a member without one is required.
Here --team is required, while --env and --size fall back to their defaults. Developers never open the Pkl file; they interact entirely through these flags.
Declaring properties as a typed class requires extends "@formae/forma.pkl" (not amends). See Properties for the typed form and the legacy properties {} block.
Use defaults generously. The fewer required flags, the easier the offering is to adopt. Make --team required because it has no sensible default, but let --size default to the common case.

Map choices to infrastructure decisions

This is where your platform expertise lives: translating a small vocabulary like small/medium/large into concrete instance classes, storage sizes, and policies. Read the property values and derive the real configuration.
You can also constrain a Props member directly so a bad input fails before any cloud API call: a closed enumeration (size: "small" | "medium" | "large") or a regex pattern rejects invalid values at evaluation time rather than mid-deploy. See Properties for the constraint patterns.

Bake your standards into the resources

Encode your organization’s requirements directly in the forma. Developers get compliant infrastructure without having to know the rules, because the rules are in the code, not in a wiki.
The generated password uses .opaque.setOnce: .opaque keeps it out of logs and output, and .setOnce fixes it on the first apply so it stays stable across later applies. See Values for both modifiers.
These examples use AWS resources, but the same pattern applies to any technology that has a formae plugin. Properties and standards are provider-agnostic.

Encapsulate larger offerings with modules

For anything bigger than a single resource, group the pieces into a Pkl class so the forma stays clean and the complexity lives behind an interface. A class takes the same property values as inputs and returns a listing of resources.
The forma then reduces to wiring the class to the properties and spreading its resources:

Hand it to developers

The properties you declared are already the whole interface. Developers discover them by passing the forma to --help, then deploy with the flags.
1

Discover the available flags

Passing the forma file to --help lists the property flags, their defaults, and which are required:
The output includes a Properties: section, for example:
Properties:
      —env                   property: env [default: “dev”]
      —size                  property: size [default: “small”]
      —team                  property: team [required]
2

Deploy with the chosen values

A developer supplies the flags and applies. Reconcile mode brings the team’s stack into being exactly as the forma (and their choices) describe it. formae shows the plan and asks for confirmation before making any changes, then streams the run in a live view:
  formae apply · reconcile                                                                       team-database.pkl
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  + 3 create

  ▌ Targets
  Operation ▲   Label
  + create      payments-target

  ▌ Stacks
  Operation ▲   Label
  + create      payments-staging

  ▌ Resources
  Operation ▲   Label                                                       Type
  + create      payments-database                                           AWS::RDS::DBInstance

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  ↑↓: select  space: expand  →←: column  s: sort  y: confirm  q: abort This operation will create 1 stack(s), create
1 target(s) and create 1 resource(s).  Do you want to continue? (y/N)  ?: help
Pass --yes to skip the confirmation prompt in a CI/CD job.
Treat property flags as a public API. Renaming a Props member renames its --flag and breaks every developer script and CI job that passes it. Keep flags stable: add new members rather than renaming, and deprecate old ones gradually.

See also

  • Properties: the full reference for typed, constrained property interfaces.
  • Apply modes: reconcile versus patch, and how each treats a stack.
  • Values: the .opaque and .setOnce modifiers for secrets and stable generated values.
  • Write your first forma: a worked example of turning a hardcoded value into a property end to end.