Terraform execution engine
Cloud Control's single mutation path: compose a blueprint into HCL-JSON, plan, pass the policy and cost gates, approve, apply the saved plan, and track drift and state versions.
Terraform is the platform's one mutation path. Every change to your cloud estate — from a form or from the resource drawer — becomes a workspace run that goes plan → policy/cost gates → approval → apply the saved plan → inventory. The console never hand-writes HCL strings; it composes versioned blueprints into machine-generated HCL-JSON.
Workspaces
A workspace is one (organization, cloud account, region, blueprint) instance. Create one from the blueprint picker on the Terraform screen once you have a healthy cloud connection. Runs are strictly serialized per workspace — a second plan while one is open returns a conflict.
When you create a workspace you pick its criticality, which sets its approval policy: Production / critical (the default) requires a *second operator* to approve every run — the plan author can't approve their own work (separation of duties). Standard / non-production lets the operator who planned also approve and apply, so a solo change to a dev or staging estate isn't blocked waiting for someone else. Both policies still require the plan's policy checks to pass before approval. Existing workspaces (and any request that omits the field) default to Production.

Plan → policy & cost → approve → apply
- Configure & plan
Fill the blueprint's typed form (name, region, tags, and type-specific fields).
POST /api/terraform/workspaces/{id}/plancomposes the module, runs the plan, evaluates policy, and estimates the monthly cost delta. The plan is saved on a run. - Read the gates
The run shows the human diff (adds / changes / destroys), the policy result (e.g. no-public-bucket, mandatory-tags, region-allowlist), and the Δ$/month. A policy violation blocks approval and names the failing rule.
- Approve
POST .../approverequires the policy to pass, and — on a Production / critical workspace — a second operator: the plan's author can't approve their own run (separation of duties, enforced with a 403). On a Standard / non-production workspace the plan author may approve their own run. - Apply the saved plan
POST .../applyapplies the exact approved plan, never re-planned. The created resources land in your Inventory, a new encrypted state version is written, and outputs are returned.
Deploy a new resource from Inventory
You don't have to start on the Terraform screen. The + New resource button on the Inventory page opens a guided Deploy wizard in place — no page jump. It walks you through the same plan → review → approve → apply pipeline in one overlay.
- Pick a cloud account
Choose one of your healthy connected accounts. The wizard only offers accounts that are connected and validated.
- Choose what to deploy
Pick a blueprint for that cloud — EC2 / S3 / VPC and the other T1 blueprints. The wizard shows the Terraform type it maps to and the policies it will be checked against.
- Choose a Terraform folder
Deploy into a New folder (a fresh workspace named by you, with its approval policy) OR an Existing folder (any of your current workspaces on that cloud). An existing folder recompiles to include the new resource alongside everything it already manages.
- Configure & deploy
Fill the blueprint's typed variables, then Deploy →. The wizard plans, shows the diff + policy result + estimated Δ$/month, and — once policy passes — lets you approve and apply, ending on a success (or an honest failure you can adjust and retry) without ever leaving the page.
The wizard is orchestrated entirely over the existing Terraform routes. A NEW folder is a blueprint workspace — the workspace IS the folder. For an EXISTING folder, the blueprint is deployed through a short-lived standard workspace, then the applied resource is reassigned into your chosen folder (which recompiles its module to include it) and the emptied short-lived workspace is torn down. The wizard's workspaces are Standard criticality so you can approve your own plan in the flow; a new-folder deploy can opt into Production, in which case a second operator approves the saved run on the Terraform screen.
The generated Terraform (hydrated repo)
Every plan stores the composed main.tf.json and resolved variables. The View generated Terraform button shows it. This artifact is the hydrated source of truth for your estate: on apply, the console commits it to your connected repository (GitOps-style) so the repo — not just platform state — is the record.
Cloud Control writes each workspace to terraform/workspace-<id>/ (and each import to terraform/imported/) as a COMPLETE Terraform config: alongside the module it emits a versions.tf.json (required providers) and a provider.tf.json (region; credentials from your environment — no secrets are committed). There is no backend block, so it uses the LOCAL backend. Run terraform -chdir=terraform/<dir> init && terraform -chdir=terraform/<dir> apply from a clone to recreate the resources outside Cloud Control — e.g. to recover the estate if the account is wiped. Cloud Control's own runs use its encrypted internal/HTTP state backend; the repo copy is a portable local-backend copy.

Drift, state versions and rollback
- Drift — a refresh run (
POST .../drift) compares your applied state to live inventory and reports any out-of-band change as a reconcile plan. - State versions — every apply writes a new encrypted version. The raw state is never exposed in the UI.
- Rollback —
POST .../state { version }re-applies a prior version's config as a new version (never raw state surgery).
Real vs recorded deploys
An apply is either REAL (it runs OpenTofu against your live cloud and creates/modifies/destroys billable resources) or recorded-only (it is modeled in Cloud Control — inventory and state — but nothing is touched in the cloud). The wizard and the Terraform apply button tell you which one you're about to do, right at apply time.
A deploy is REAL only when BOTH are true: the platform is running the real runner (the compose or full edition with CC_TF_RUNNER=real, where the OpenTofu binary is resident — the serverless micro edition never runs it) AND the target cloud account has Terraform provisioning enabled. Otherwise the apply is recorded-only and byte-identical to the mock pipeline.
- Enable provisioning on the account
On the Cloud accounts screen, a manager (Org Admin / SRE · Cloud Admin) opens the account's Terraform provisioning control and confirms the acknowledgement — this authorizes real, billable resource creation with short-lived write credentials.
POST /api/cloud/accounts/{id}/provisioning { enabled: true, ack }. It is OFF by default: a connected account is read-only until you opt in. - Check the runner mode
The account screen shows whether the platform's apply runner is real or recorded (from
/api/health). On micro/Vercel it is always recorded-only (no resident binary); enable it on a compose or full deployment. - Deploy
Plan → approve → apply as usual. When both conditions hold, the apply banner reads REAL and OpenTofu runs
init+applyagainst your account with freshly-minted, short-lived STS credentials. When they don't, the banner reads recorded-only and explains what to enable.
Cloud Control holds NO long-lived cloud keys. For each real apply it mints SHORT-LIVED credentials via the same keyless role-assumption used for read-only discovery (AWS STS AssumeRole; a separate write-scoped apply role is supported if you configure one), injects them into the one OpenTofu child process for that apply only, and tears them down with it. The credentials are never stored, never placed in the platform environment, and are redacted from the run logs. A real apply against a non-opted-in account is refused with a clear 409. AWS is live today; GCP, Azure and Alibaba real applies are a documented follow-on (they still sync and produce recorded plans).
Destroy
Destroying a workspace requires typing its exact name to confirm (DELETE /api/terraform/workspaces/{id} with { confirm }). Without a matching confirm you get a preview of what would be removed. On confirm, the managed resources are marked deleted and a final empty state version is written.
The resource drawer on real orgs doesn't run a separate apply — it routes you to the workspace that manages the resource. Plan → approve → apply is the only way the platform changes your cloud.