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 typedProps class. Each member’s name
becomes a --flag CLI option automatically. A member with a default is
optional; a member without one is required.
--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.Map choices to infrastructure decisions
This is where your platform expertise lives: translating a small vocabulary likesmall/medium/large into concrete instance classes, storage sizes, and
policies. Read the property values and derive the real configuration.
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..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.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 The output includes a
--help lists the property flags, their defaults,
and which are required:Properties: section, for example:Properties:
—env property: env [default: “dev”]
—size property: size [default: “small”]
—team property: team [required]
—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:Pass
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
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
+ 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
--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
.opaqueand.setOncemodifiers for secrets and stable generated values. - Write your first forma: a worked example of turning a hardcoded value into a property end to end.

