An Action is the only way infrastructure changes in Terrantula. It pairs a graph operation (create/update/transition an entity or relationship) with an external trigger that does the real work — opening a pull request, firing a Terraform Cloud run, dispatching an Atmos workflow, or calling Atlantis.
Terrantula never runs terraform apply itself. Actions open pull requests
against your repo or dispatch runs to your runner; your CI applies. The graph
is a read-only projection of the resulting TF-derived state.
Phase: schema. This is the largest kind — operations, triggers, recommendations, cascade rules, and per-env overrides are all defined here.
| Field | Type | Required | Description |
|---|---|---|---|
kind | "Action" | yes | Discriminator. Always Action. |
name | string | yes | Unique action name within the project. Referenced by applier, dependsOn, and triggers. |
displayName | string | no | Human label for the UI. |
description | string | no | Help text shown in the Action picker. |
associatedWith | AssociatedWith | yes | Which EntityType the Action attaches to, and whether it appears at the collection or instance level. |
conditions | Condition[] | no | All must pass for the Action to be available on an entity. Default []. |
parameters | Property[] | no | Inputs collected when the Action is triggered. Default []. Same shape as EntityType properties. |
recommendations | Recommendation[] | no | Cell-ranked placement suggestions surfaced at trigger time. Default []. |
operation | Operation | yes | The graph mutation. One of eight operation types. |
trigger | Trigger | yes | The external dispatch. One of six trigger types. |
envOverrides | object (env→TriggerOverride) | no | Per-env partial overrides shallow-merged onto the base trigger. |
dependsOn | DependsOn[] | no | Other Apply Actions that must have succeeded for related entities first. |
timeout | positive integer | no | Minutes an ActionRun may be in-flight before the reaper marks it failed. Default 60. |
mutatesProperty | string | no | Names the entity property this Action changes. Drives the graph Action picker. Must reference a declared property of associatedWith.entityType. |
| Field | Type | Required | Description |
|---|---|---|---|
entityType | string | yes | Name of the EntityType the Action attaches to. |
scope | "collection" | "instance" | yes | collection: appears at the type level (create new). instance: appears on each existing entity (modify it). |
All conditions must pass for the Action to be offered on an entity (evaluated against the entity for instance-scoped actions).
| Field | Type | Required | Description |
|---|---|---|---|
field | string | yes | Dot-path to the value being tested. Valid prefixes: entity.state, entity.properties.*, entity.metrics.*, entity.labels.*. |
operator | eq | neq | in | gt | lt | gte | lte | yes | Comparison. |
value | string | number | boolean | string[] | yes | Right-hand operand (use a string array with in). |
A cell-ranked placement suggestion. At trigger time Terrantula ranks the cell's
members and proposes the best target; the selection is then available to the
operation and trigger via {{ recommendations.<name>.* }}.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Recommendation key (used in interpolation). |
title | string | yes | Human label. |
cell | string | yes | Name of the Cell whose members are ranked. Must reference an existing Cell. |
sortBy | string | yes | Metric name to rank members by. |
order | "asc" | "desc" | no | Sort direction. Default asc. |
required | boolean | no | Whether a selection is mandatory. Default true. |
operation is discriminated on type. Most carry onTrigger / onSuccess /
onFailure states that drive the entity (or relationship) through its lifecycle
as the ActionRun progresses.
type | What it does | Key fields |
|---|---|---|
create-entity | Creates a new entity (collection scope). | entityType, optional name, optional addToCell, onTrigger/onSuccess/onFailure, optional properties, optional createRelationship, optional cascadeRules. onFailure may be "delete". |
delete-entity | Deletes the entity and its relationships on success. | onTrigger, onSuccess: "delete", onFailure. |
update-entity | Updates properties and/or state. | onTrigger/onSuccess/onFailure, optional properties, optional state. |
transition-entity | Transitions entity state with optional cascade. | onTrigger/onSuccess/onFailure, optional cascadeRules. |
create-relationship | Creates an edge between two entities. | relationshipType, from, to, onTrigger/onSuccess/onFailure, optional properties. onFailure may be "delete". |
delete-relationship | Deletes an edge on success. | relationshipType, onTrigger, onSuccess: "delete", onFailure. |
update-relationship | Updates edge properties. | relationshipType, onTrigger/onSuccess/onFailure, optional properties. |
migrate-relationship | Re-points an edge to a new to-entity (rebalance). Bypasses property-sum constraints. | relationshipType, to, onTrigger/onSuccess/onFailure. |
Inside a create-entity operation, optionally create a relationship from the
just-created entity on success:
| Field | Type | Required | Description |
|---|---|---|---|
relationshipType | string | yes | RelationshipType name. |
to | string | yes | Interpolation expression for the to-entity ID (typically {{ recommendations.<name>.id }}). |
onSuccess | string | yes | State for the new relationship on success. |
onFailure | string | yes | State for the new relationship on failure. |
properties | object (string→string) | no | Property values; supports interpolation. |
trigger is discriminated on type. Six types, covering the substrates
Terrantula sits on top of — never replacing your runners.
type | Integration | Notes |
|---|---|---|
pull-request | GitHub PR | Opens a PR, optionally fires repository_dispatch on merge. Auto-completes on merge via webhook. |
terraform-cloud | TFC / HCP Terraform | Fires a run against a pre-existing workspace. Terrantula never creates workspaces. |
atlantis | Atlantis | PR mode (default) or direct api-dispatch. Atlantis is always customer-hosted. |
atmos-workflow | Atmos | Invokes a named workflow on a customer-deployed reference runner. |
webhook | Generic HTTP | Calls an arbitrary URL with an interpolated payload and retry policy. |
noop | none | Transitions the run to succeeded immediately; for Actions whose whole effect is in-Terrantula (e.g. cascade-only). |
| Field | Type | Required | Description |
|---|---|---|---|
type | "pull-request" | yes | — |
repo | string | yes | owner/repo. Supports interpolation. |
auth | { type: "token", token?: string } | yes | token is optional: when omitted, the runtime resolves an installation token via the linked GitHub App. |
title | string | yes | PR title. Supports interpolation. |
body | string | no | PR body. {{ run.id }} is safe to embed as a reference. |
head | string | yes | Branch to create. Supports interpolation. |
base | string | no | Base branch. Default main. |
files | FileEntry[] (min 1) | yes | Files to commit. |
labels | string[] | no | PR labels. |
reviewers | string[] | no | GitHub usernames to request review from. |
teamReviewers | string[] | no | GitHub team slugs to request review from. |
webhookSecret | string | no | Secret holding the repo's webhook HMAC secret. Required for auto-completion on merge; omit if you complete runs manually. |
postMergeDispatch | PostMergeDispatch | no | After merge, fire a repository_dispatch and wait for the workflow callback. |
| Field | Type | Required | Description |
|---|---|---|---|
path | string | yes | Path relative to repo root. Supports interpolation. |
operation | "replace" | "patch" | no | replace (default) commits content. patch fetches the file and applies patch. |
content | string | conditional | Required when operation: replace. Supports interpolation. |
patch | FilePatch | conditional | Required when operation: patch. |
Patch mode lets a cattle workflow extend a monolithic tfvars/yaml file without
refactoring to one-file-per-tenant. Discriminated on type:
type | Fields | Behavior |
|---|---|---|
json-merge | pointer (RFC 6901, default ""), value | RFC 7396 JSON Merge Patch at the pointer; null fields delete keys. |
json-array-append | pointer, value | Append value to the JSON array at the pointer. |
yaml-key | path (dot-separated; integer segments are array indices), value | Set a key in a YAML document, preserving comments and formatting. |
| Field | Type | Required | Description |
|---|---|---|---|
workflow | string | yes | Workflow filename (informational). |
eventType | string | yes | repository_dispatch event_type. |
repo | string | no | Override the target repo for the dispatch. Defaults to the trigger's repo. |
payload | object (string→string) | yes | client_payload. Supports interpolation; the runtime auto-injects callback_url and callback_token. |
Fires a run against a pre-existing workspace. Terrantula tracks the outcome; TFC runs the plan/apply.
| Field | Type | Required | Description |
|---|---|---|---|
type | "terraform-cloud" | yes | — |
organization | string | yes | TFC organization. Supports interpolation. |
workspaceName | string | conditional | Workspace name. Exactly one of workspaceName / workspaceId. |
workspaceId | string | conditional | Workspace ID (ws-xxxxxxxx). Exactly one of the two. |
apiToken | string | yes | Secret holding the TFC API token, e.g. {{ secrets.tfc-api-token }}. |
apiBaseUrl | string (https) | no | TFC API base URL. Default https://app.terraform.io. Must be https. |
configSource | { type: "vcs", repo?, branch? } or { type: "current" } | no | Where TFC pulls config from. Defaults to the workspace's current config. |
variables | object (string→string) | no | Run-scoped TF variables. Interpolated, then JSON-stringified as HCL literals. |
autoApply | boolean | no | Auto-apply after a successful plan. Default false (plan-only). |
waitForCompletion | boolean | no | Poll until terminal. Default true. |
pollIntervalSeconds | int 2–60 | no | Poll interval. Default 10. |
message | string | no | Message attached to the TFC run. Supports interpolation. |
Atlantis is always customer-hosted. Two modes:
| Field | Type | Required | Description |
|---|---|---|---|
type | "atlantis" | yes | — |
endpoint | string (https) | yes | Customer-hosted Atlantis base URL. Must be https. |
auth | { type: "token", token } | yes | X-Atlantis-Token value, e.g. {{ secrets.atlantis-token }}. |
mode | "pull-request" | "api-dispatch" | no | Default pull-request. |
pullRequest | PR config | conditional | Required when mode: pull-request. Same fields as the pull-request trigger. |
repo / ref / vcsType | string | conditional | Required when mode: api-dispatch. |
project | string | no | Atlantis project name. Supports interpolation. |
workflow | string | no | Atlantis workflow name (informational). |
command | "plan" | "apply" | no | api-dispatch only. Default plan. |
waitForCompletion | boolean | no | Default true. |
pollIntervalSeconds | int 2–60 | no | Default 10. |
Invokes a named Atmos workflow on a customer-deployed reference runner.
Terrantula never executes atmos directly.
| Field | Type | Required | Description |
|---|---|---|---|
type | "atmos-workflow" | yes | — |
workflow | string | yes | Atmos workflow name. Supports interpolation. |
stack | string | yes | Atmos stack identifier, e.g. tenant-{{ parameters.customer_id }}. |
runner.endpoint | string (https) | yes | Runner HTTP endpoint. Must be https. |
runner.auth | bearer or header auth | no | Only bearer/header are accepted (the reference runner's tested auth types). |
variables | object (string→string) | no | Variables passed to the workflow. Supports interpolation. |
waitForCompletion | boolean | no | Default true. |
cascadeRules (on create-entity and transition-entity operations) propagate
changes along the graph when the parent entity enters a phase. Phases:
on-trigger, on-success, on-failure. Two flavors, discriminated by which
payload is present:
State-transition rule — move existing relationships into a new state:
| Field | Type | Required | Description |
|---|---|---|---|
phase | on-trigger | on-success | on-failure | yes | When the rule fires. |
relationshipType | string | yes | RelationshipType to transition. |
fromState | string | yes | Only edges in this state are eligible. |
toState | string | yes | Target state. |
direction | from | to | both | no | Which end the transitioning entity occupies. Default both. |
createEntity rule — seed a sibling entity + a linking relationship:
| Field | Type | Required | Description |
|---|---|---|---|
phase | on-trigger | on-success | on-failure | yes | When the rule fires. |
createEntity.type | string | yes | EntityType of the created sibling. |
createEntity.name | string | yes | Name template. Supports interpolation. |
createEntity.properties | object | no | Property values; supports interpolation. Default {}. |
createEntity.relationship | { type, direction: "from" | "to" } | yes | Relationship linking parent to the new entity. |
Sequences Apply Actions across the entity relationship graph — the high-level golden path. Both fields are literal name references (no interpolation), validated at apply.
| Field | Type | Required | Description |
|---|---|---|---|
relationship | string | yes | RelationshipType name linking this entity to its upstream. |
appliedAction | string | yes | Action name that must have succeeded for the linked upstream entity first. |
Apply-time validation: DEPENDS_ON_RELATIONSHIP_NOT_FOUND,
DEPENDS_ON_ACTION_NOT_FOUND, and DEPENDS_ON_CYCLE (the dependency graph,
including edges from prior applies, must be acyclic).
envOverrides is a map of env name → a partial of the base trigger (same type,
validated at apply). At dispatch, override fields are shallow-merged over the base
trigger. Use it to point dev/staging/prod at different repos, workspaces, or
runner endpoints from one Action definition.
OnboardTenant from the SaaS-tenants demo — creates a Tenant,
links it to the least-loaded cluster, and fires a TFC run:
ApplyTenant from the multi-stack greenfield demo — opens a PR once
all upstream applies have succeeded, then dispatches the apply workflow on merge:
DeprovisionTenant from the SaaS-tenants demo — transitions the
tenant and cascades its runs_on edge to removing then removed:
associatedWith.entityType, recommendations[].cell, every operation's
relationshipType / entityType, and cascadeRulesreferences are all validated
at apply against the schema kinds — declare them earlier in the same blueprint or
in a prior apply.
Auto-completion of a pull-request run on merge requires a webhook configured on
the repo (webhookSecret) and GITHUB_WEBHOOK_SECRET set on the server. Without
it, the run stays running until an external caller posts to the callback URL —
{{ run.id }}is safe to embed in the PR body as a reference identifier.
migrate-relationship bypasses the property-sumconstraint on the
RelationshipType — intentionally, so a rebalance is not blocked by the ceiling it
relieves.
noop triggers transition the run to succeeded as soon as the operation
commits, with no external HTTP call — use them for Actions whose entire effect is
in-Terrantula (e.g. a create-entity that only seeds a graph via cascadeRules).
associatedWith.entityType, applier, mutatesProperty.recommendations[].cell ranks placement targets.{{ parameters.* }}, {{ entity.* }}, {{ recommendations.* }}, {{ secrets.* }}, {{ run.* }} surface.