flow-as-code
Flow as code for Amazon Connect.
An Amazon Connect flow is a JSON document full of ARNs, and the ARNs differ per account, per region, per instance. So moving one flow from dev to prod means maintaining a mapping from "the queue I mean" to "the ARN it has in this environment", and something has to walk the flow and swap one for the other before deployment. That mapping table is the thing this tooling deletes.
Open the studio demo See it promote across environments Read the docs The demo needs a desktop-width screen. It is read-only, in your browser, no account and no network calls.
References are tokens
A flow refers to a queue by name, not by ARN:
new UpdateContactTargetQueue({
id: "set-working-queue",
queue: Refs.queue("appointments"),
next: "look-up-appointment",
onError: "apologize",
});
Refs.queue("appointments") becomes ${cdref:queue:appointments} in
the document. It stays a token through authoring, lint, version control and review. It
becomes an ARN at exactly one moment: when your IaC tool resolves it, per environment. Under
CDK the binding is a CloudFormation intrinsic, a Fn::GetAtt at a resource the
stack creates or an Fn::ImportValue of one it does not. Under Terraform it is a
templatefile variable your own resource addresses fill in.
That is a lint rule, not a convention: a literal ARN in authored content fails a hard rule, and in the studio a failing hard rule disables saving.
One flow, two environments
The promotion example is that argument as commands rather than prose: one FlowDoc reaching a dev and a prod environment, on both deploy paths. Two facts about it are worth stating because a test asserts each, on every push to main and every pull request.
-
Emitting the same document for dev and for prod produces two Terraform trees that differ
in exactly one file,
flow_refs.tf. The rendered flow template,flows.tfandvariables.tfare byte-identical, because the template names its variables after the tokens rather than after what they resolve to. No byte of either tree matchesarn:aws. -
The CDK path takes no map at all. Each stack's binder hands
FlowSeta value the app already holds: a construct attribute in the environment that creates its supporting resources, anFn.importValuein the environment where a platform team owns them. Neither synthesized template names a Connect ARN.
tests/promoteAcrossEnvironments.test.ts runs the example's own inputs and holds
both. The example's README also names what it does not show.
The map that is not that table
The Terraform path does take an --address-map, one small JSON file per
environment, and it looks like the table described above. It is a different object. Its
values are Terraform addresses of resources your own configuration manages,
aws_connect_queue.appointments.arn where the environment creates the queue and
data.terraform_remote_state.platform.outputs.appointments_queue_arn where a
platform team owns it. The emitter refuses any value matching /arn:aws/i
outright, telling you to map it to a terraform address instead, so the file cannot decay
into an ARN table.
The CDK path needs no such file because the flows and the resources are objects in one program, so the app already holds the reference and there is nothing to name. HCL has no equivalent, so something has to name the address in text. That, and only that, is why one path takes a map and the other takes none.
What it is
Open-source tooling for Amazon Connect flows: typed flow authoring, lint, simulate-based testing, export from a live instance, and a visual editor, all round-tripping through one interchange format, with CDK and Terraform/OpenTofu code as outputs. Everything but the live commands works offline.
FlowDoc is the pivot. It is a JSON document holding Connect Flow-language content with
${cdref:type:name} reference tokens, plus layout and metadata. Every tool in
the set reads and writes it, so there is no second format to keep in sync.
typed TS builder --synth--> FlowDoc --codegen--> typed TS builder studio canvas <--edit--> FlowDoc FlowDoc --cdk--> CDK stacks (CloudFormation tokens) FlowDoc --tf--> .tf + .tftpl files (resource references) live instance --export--> FlowDoc (+ codegen to TS)
Packages
- @flow-as-code/core Typed builder, synthesizer, codegen, lint engine, FlowDoc interchange, export, and the simulate client.
- @flow-as-code/cli lint, codegen, synth, render, emit, studio, plus the live export and simulate.
-
@flow-as-code/studio
Visual editor over FlowDoc, served locally by
flow-cli studio. - @flow-as-code/cdk CDK token binding and the FlowSet construct.
- @flow-as-code/tf Terraform/OpenTofu emitter: FlowDoc to .tf and .tftpl files.
Try it
Published on npm under the @flow-as-code scope. The five packages version together and are released as a set, so install them at matching versions. Node 22.12 or newer.
npm i -D @flow-as-code/cli npx flow-cli init flows/ # a first flow to open: FlowDoc + .flow.ts npx flow-cli studio flows/ # visual editor, live-synced npx flow-cli lint flows/ # the rule set over the document set npx flow-cli emit flows/ --target tf # or --target cdk
Every command after the install works on a directory of FlowDocs, so init comes
first: it writes a small demo flow and the typed .flow.ts that synthesizes to
it. If nothing above the directory is already a package it adds a package.json marking the
pair as modules; inside a project that has one it leaves yours alone, and the pair loads
either way. Edit either side and the studio keeps them in step.
@flow-as-code/cli depends on the other four, so installing it alone is enough
for the CLI. The studio demo above needs no install at all, and
the promotion example runs from a clone of
the repository.