Import Terraform Registry modules as blueprints
Platform staff import verified Terraform Registry modules as platform-global blueprints — Cushy VENDORS the module source, parses its variables.tf into form fields, and materializes the source into each org's workspace at apply.
Cushy imports public Terraform Registry modules as platform-global blueprints. Import vendors the module: it downloads the module's .tf source, parses its variables.tf into form fields, and stores the source WITH the blueprint. The blueprint then shows up in every organization's Deploy a blueprint form; each org picks its own infra repo at workspace-create, and the source is materialized into that repo at apply.
Imports are performed by the Cushy platform team. Your organization consumes the imported blueprints from the catalog; you do not run the importer.
Two ways to import
- Browse & vendor — search the registry (by text, provider, verified-only), preview a module's parsed fields + its registry link + vendored file list, then Vendor & import.
- Seed top verified — bulk-vendor the top-N verified modules by downloads (optionally filtered to one provider). Non-mappable providers are skipped and reported.
How a module becomes a blueprint
Import maps the registry provider to a cloud (aws→AWS, google→GCP, azurerm→AZR, alicloud→ALI — anything else is skipped), resolves the module's GitHub source, downloads the archive (bounded: ≤60 .tf files, ≤1 MB total), and parses its `variables.tf` with a hand-written HCL type parser — NOT the registry's input metadata. The parsed fields, the module template (a LOCAL-path module block) and the vendored source files are stored on the platform custom-blueprints row (id custom/platform/<namespace>-<name>-<provider>). No Git repo is written at import.
string→ text field ·bool→ toggle ·number→ numeric field.object({…})→ a nested field group: each attribute is its own input (anoptional()attribute is not required, and its default applies when left blank).list(…)/set(…)/tuple([…])→ a repeatable list: use + Add to add an entry and × to remove one; each entry renders the element's schema (alist(object({…}))is a list of nested groups).map(…)→ repeatable key/value rows.- A
tagsmap becomes the standard tags field so the mandatory-tags policy is satisfiable. - A variable with no default is required; a variable with a default is optional (its module default applies when left blank).
- Only genuinely-exotic HCL types (user-defined type aliases,
${}interpolation in a type, deeply heterogeneous tuples) fall back to a passthrough raw-HCL/JSON text field.
Planning + applying a vendored blueprint
Composing the plan wraps the module in a single Terraform module block with a LOCAL source path: module "<slug>" { source = "./blueprints/<slug>" …your inputs }, plus the platform-stamped terraform/provider/backend blocks (no version — it is a local path). At apply, Cushy materializes the vendored .tf under terraform/workspace-<id>/blueprints/<slug>/ next to main.tf, so ./blueprints/<slug> resolves in-dir. Deleting the workspace prunes that directory.
A module is opaque at compose time — Cushy cannot see the resources it will create until tofu plan expands it. So the plan shows ONE placeholder resource, and the policy (no-public-bucket / mandatory-tags / region-allowlist) and cost estimate run on that placeholder. The real, per-resource policy and cost only materialize when the real runner runs tofu plan on the expanded module.
Limitations
- Apply is recorded-only unless real provisioning is enabled for the target account — same as every blueprint.
- Nested/repeatable inputs are live (object → nested group, list → repeatable entries).
optional()and other complex defaults are best-effort — a field left blank uses the module's own default. Binding a NESTED input to another workspace's output is deferred (top-level inputs can still be bound). - Vendoring supports public GitHub module sources (and direct
.tar.gzarchives); private registries / private repos and non-GitHub git hosts are a follow-up. Remote submodule sources inside a module stay remote (fetched atterraform init). - Bounded by hard caps: ≤60
.tffiles, ≤1 MB total source per blueprint, ≤8 MB archive. Over-cap or an unreachable/unsupported source is an honest error — never a half-stored blueprint. - Because the source is materialized into every workspace dir, N workspaces on one blueprint keep N copies in the repo (the cost of a self-contained, standalone-applyable hydrate dir).
- Removing an imported blueprint disables it (platform rows are never hard-deleted).
The importer reads the public Terraform Registry and GitHub. Requests are bounded (caps + per-call timeouts) and degrade to an honest error if a source is unreachable — never a fabricated blueprint.