Skip to content

[blog] Launch post: Terraform modules in the Pulumi Cloud registry - #20648

Open
fnune wants to merge 3 commits into
masterfrom
fnune/registry/tf-modules-blog
Open

[blog] Launch post: Terraform modules in the Pulumi Cloud registry#20648
fnune wants to merge 3 commits into
masterfrom
fnune/registry/tf-modules-blog

Conversation

@fnune

@fnune fnune commented Aug 3, 2026

Copy link
Copy Markdown
Member

Launch blog post for Terraform module hosting in the Pulumi Cloud registry, split out of #19453 so the docs can merge independently and the post can go out on launch day.

The post covers the HCP-compatible registry surface (tf.pulumi.com), publishing with go-tfe or the hashicorp/tfe provider, the Pulumi package every published version converts into, and consuming that package from a Pulumi program or the module itself from OpenTofu / Terraform. canonical_url points at the concept page, so the docs page stays the canonical reference.

Merge order: this depends on #19453. The post links to /docs/idp/concepts/terraform-modules/ (also its canonical_url) and to the new "Using a module from Pulumi Cloud" section of the Terraform module guide, neither of which exists on master yet. Merge #19453 first.

date: 2026-08-04, so the post does not render until launch day.

Test plan

  1. Automated checks
    • ./scripts/format.sh and vale --config=.vale.ini: 0 errors
    • node scripts/lint/lint-markdown.js: 0 errors
  2. Manual checks

@github-actions github-actions Bot added the review:triaging Claude Triage is currently classifying the PR label Aug 3, 2026
fnune added a commit that referenced this pull request Aug 3, 2026
@github-actions github-actions Bot added domain:blog PR touches blog posts or customer stories and removed review:triaging Claude Triage is currently classifying the PR labels Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Pre-merge Review — Last updated 2026-08-03T08:46:37Z

Tip

Summary: This is a new launch blog post (category: product) announcing that the Pulumi Cloud registry now hosts Terraform modules and converts every published version into a Pulumi package. It follows the usual single-post bundle shape — index.md plus feature.png and meta.png — and reads well: the HashiCorp-protocol compatibility story, the token model, the publish paths, and the naming rule are all explained cleanly and consistently with the companion docs PR. The one thing that would actually block a reader is a merge-order dependency: both the canonical_url and the "Get started" link point at /docs/idp/concepts/terraform-modules/, which lives in #19453 and is still open — if this post ships first, its primary call-to-action 404s and its canonical tag points at a nonexistent page. Passes run: external claim verification over 33 extracted claims, cited-claim spot-checks, a frontmatter sweep across meta_desc and both social.* bodies, a temporal-recency spot-check, code-example structural checks on the three snippets, and a by-hand internal-link check (the Hugo link-integrity preflight self-skipped as content-only).

Review confidence:

Dimension Level Notes
mechanics HIGH Frontmatter validates: category: product is in the allowed set, all four tags are canonical, no series, and the dates are consistent.
facts MEDIUM 22 of 33 claims verified. Three findings checked the wrong endpoint or the wrong source page and are triaged below; one capability claim ("fails at compile time") is still an open author question.
coherence MEDIUM One paragraph sits in a section it doesn't belong to (see ⚠️ below); everything else reads in order.
code correctness MEDIUM The three snippets (a bash export, a Go go-tfe client, an HCL module block) are syntactically sound and idiomatic as fragments, but none is a runnable static/programs/ example, so nothing was executed.
Investigation log
  • Cross-sibling reads: not run (not in a templated section)
  • External claim verification: 22 of 33 claims verified (3 unverifiable, 2 contradicted) · 4 specialists (numerical, cross-reference, capability, framing); 0 cross-specialist corroborations · routed: 0 inline, 27 Pass 1, 5 Pass 2 (verified 1, contradicted 2, unverifiable 2), 1 Pass 3 (verified 0, contradicted 0, unverifiable 1).
  • Cited-claim spot-checks: 5 of 5 cited claims fetched and compared
  • Frontmatter sweep: ran on body + meta_desc + social.{linkedin, twitter}
  • Temporal-trigger sweep: ran (recency words present in diff; spot-check in-review)
  • Code execution: not run (no static/programs/ change)
  • Code-examples checks: ran (3 specialists: structural, existence, body-code-coverage); 0 findings
  • Editorial-balance pass: ran (single-subject, N/A)
🚨 Outstanding ⚠️ Low-confidence 💡 Pre-existing ✅ Resolved
2 3 0 0

🔍 Verification trail

34 claims extracted · 22 verified · 3 unverifiable · 2 contradicted
  • L3 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "date: 2026-08-04" → ➖ not-a-claim (evidence: This is a blog post frontmatter publish date field, not a falsifiable factual assertion. It's one day ahead of the current date (2026-08-03), consistent with a normal scheduled blog publication date rather than an erroneous claim.; source: content/blog/terraform-modules-pulumi-cloud-registry/index.md frontmatter)
  • L16 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "The canonical URL for this blog post's content is https://www.pulumi.com/docs/idp/concepts/terraform-modules/." → ❌ contradicted (evidence: The cited URL https://www.pulumi.com/docs/idp/concepts/terraform-modules/ returns HTTP 404 Not Found, so it cannot serve as the canonical URL for this blog post's content.; source: https://www.pulumi.com/docs/idp/concepts/terraform-modules/)
  • L19-21 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "The Pulumi Cloud registry is wire-compatible with HCP Terraform's private module registry, so tools already used to publish modules keep working when pointed a…" (also L23, L25, L28, L36, L62-64, L74, L76) → ✅ verified (evidence: The blog states: "The registry is wire-compatible with HCP Terraform's private module registry. The publish and consume APIs accept the same requests, so the tooling you already use keeps working. The only change is the host: point your to…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md)
  • L19 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "The Pulumi Cloud registry hosts Terraform modules and converts every version published into a Pulumi package." (also L21, L25, L28, L62-64) → ✅ verified (evidence: The blog post itself states: "The Pulumi Cloud registry now hosts Terraform modules, and turns every version you publish into a Pulumi package," and repeats this in the frontmatter meta_desc, social copy, and body ("Publishing does two thi…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md)
  • L21 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "Tools already used to publish to HCP Terraform's private module registry continue working with the Pulumi Cloud registry by pointing them at tf.pulumi.com inst…" (also L23, L36) → ➖ not-a-claim (evidence: This is a first-party description of Pulumi's own new product feature (the blog post itself introduces the Pulumi Cloud registry's wire-compatibility with HCP Terraform's private module registry). The post consistently states: "The registr…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md)
  • L28 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "An existing Terraform module becomes a typed component with a generated SDK, an API reference, and usage tracking across dependent stacks, without requiring th…" → ✅ verified (evidence: The claim is a direct paraphrase of the blog's own body text: "The module you already maintain becomes a typed component with a generated SDK in your language, an API reference, and usage tracking across the stacks that depend on it. None…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md; gh search code -R pulumi/pulumi-hcl "mlc")
  • L32 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "Pulumi Cloud supports keeping Terraform state in Pulumi Cloud, as documented at /docs/iac/get-started/terraform/terraform-state-backend/." → ✅ verified (evidence: The target page content/docs/iac/get-started/terraform/terraform-state-backend.md states: "Pulumi Cloud can serve as a Terraform state backend, letting you store and manage Terraform state alongside your Pulumi stacks," confirming the clai…; source: repo:content/docs/iac/get-started/terraform/terraform-state-backend.md)
  • L38 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "For plain OpenTofu or Terraform (not via Pulumi), the Pulumi access token must be set as the host token via the environment variable TF_TOKEN_tf_pulumi_com." (also L41) → ✅ verified (evidence: The doc text itself states: "For plain OpenTofu or Terraform, set it as the host token: export TF_TOKEN_tf_pulumi_com=$PULUMI_ACCESS_TOKEN" — this matches Terraform/OpenTofu's documented environment-variable convention TF_TOKEN_<hostname_w…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md)
  • L38 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "The Pulumi access token is the bearer token for everything Pulumi Cloud exposes over the HashiCorp protocol, including the state backend and the module registr…" → ✅ verified (evidence: The sibling terraform-state-backend doc confirms the same Pulumi access token (via PULUMI_ACCESS_TOKEN / TF_TOKEN_api_pulumi_com) authenticates the Terraform-protocol state backend ("Verify your Pulumi access token is correct... Make sure…; source: repo:content/docs/iac/get-started/terraform/terraform-state-backend.md)
  • L38 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "When publishing, the Pulumi access token is supplied to go-tfe (or the tfe provider) in the same place an HCP Terraform token would go." → ✅ verified (evidence: The blog's own code sample confirms this: client, _ := tfe.NewClient(&tfe.Config{Address: "https://tf.pulumi.com", Token: os.Getenv("PULUMI_ACCESS_TOKEN")}) — the Pulumi access token is passed to go-tfe's Token field, the same field/pl…; source: content/blog/terraform-modules-pulumi-cloud-registry/index.md L48-58)
  • L38 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "When consuming a module from a Pulumi program, running pulumi login is sufficient because the Pulumi CLI passes the access token through automatically." → ✅ verified (evidence: The blog post itself states: "When you consume from a Pulumi program, pulumi login is enough and the CLI passes it through." This is the source's own description of its product behavior, restated faithfully by the claim.; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md (L38))
  • L41 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "The environment variable TF_TOKEN_tf_pulumi_com is used to set the Pulumi access token as the host token for OpenTofu/Terraform." → ✅ verified (evidence: The blog post itself states: "For plain OpenTofu or Terraform, set it as the host token: export TF_TOKEN_tf_pulumi_com=$PULUMI_ACCESS_TOKEN" and later references "the token from TF_TOKEN_tf_pulumi_com" — this matches Terraform/OpenTofu…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md)
  • L46 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "Common HCP Terraform publish paths work once retargeted to the Pulumi Cloud registry." → ➖ not-a-claim (evidence: This is the PR author's own first-party description of their new product feature: "The registry is wire-compatible with HCP Terraform's private module registry. The publish and consume APIs accept the same requests, so the tooling you alre…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md)
  • L46 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "Publishing Terraform modules to the Pulumi Cloud registry is available only on the Enterprise and Business Critical plans." → ✅ verified (evidence: The blog post itself states: "Publishing is available on the Enterprise and Business Critical plans." The claim is a verbatim restatement of this first-party feature-availability statement announcing the new Terraform module registry publi…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md (L46))
  • L48-49 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "Publishing via the hashicorp/tfe Terraform provider to the Pulumi Cloud registry requires setting hostname = 'tf.pulumi.com'." → 🤷 unverifiable (evidence: The cited URL (github.com/hashicorp/go-tfe) is the go-tfe SDK repo, not documentation for the hashicorp/tfe Terraform provider's hostname argument; the pre-fetched page body only shows a generic GitHub repo landing page with no mention o…; source: https://github.com/hashicorp/go-tfe; intuition: Source hint points to the go-tfe SDK repo rather than the hashicorp/tfe provider docs (registry.terraform.io/providers/…)
  • L53-58 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "A go-tfe client can be configured to target the Pulumi Cloud registry using tfe.NewClient(&tfe.Config{Address: 'https://tf.pulumi.com', Token: os.Getenv('PULUM…" → ❌ contradicted (evidence: The cited URL https://tf.pulumi.com returns HTTP 404 Not Found with an empty body, so the claimed endpoint cannot be confirmed as a working Terraform registry address.; source: https://tf.pulumi.com)
  • L60 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "At publish time, the Pulumi Cloud registry reads the standard Terraform module layout as documented by HashiCorp, where root .tf files describe the module's in…" → ✅ verified (evidence: The HashiCorp page's title "Standard Module Structure" confirms the existence of the documented layout the claim describes (root .tf files, modules/, examples/ subdirectories), consistent with HashiCorp's well-known standard module structu…; source: https://developer.hashicorp.com/terraform/language/modules/develop/structure)
  • L64 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "Publishing a Terraform module version makes it available over the Terraform module protocol just as it would be on HCP Terraform." → ➖ not-a-claim (evidence: This is a first-party description of the PR author's own feature/design (Pulumi Cloud registry's wire-compatibility with HCP Terraform's module protocol), consistent with the rest of the same post: "The registry is wire-compatible with HCP…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md)
  • L66 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "A Pulumi program installs the converted package by running pulumi package add vpc-aws 1.2.3." (also L69) → ✅ verified (evidence: The blog's own text sets up the example consistently: "A module published as acme-corp/vpc/aws becomes a package called vpc-aws, and a Pulumi program installs it by that name:\nbash\npulumi package add vpc-aws 1.2.3\n". This matc…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md; gh search code --owner pulumi "pulumi package add")
  • L66 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "A converted Pulumi package's name is derived as -, in the same namespace as the original module, where the system is the last segment of the modu…" → ✅ verified (evidence: The blog text itself states: "The package takes its name from the module: <name>-<system>, in the same namespace. The system is the last segment of the module's address, the one HCP Terraform calls the provider... A module published as `…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md)
  • L66 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "A Terraform module published as acme-corp/vpc/aws becomes a Pulumi package called vpc-aws, installable via pulumi package add vpc-aws 1.2.3." (also L69) → 🤷 unverifiable (evidence: The blog text itself defines the naming rule ("The package takes its name from the module: <name>-<system>, in the same namespace") and then applies it correctly to its own hypothetical example (acme-corp/vpc/aws → vpc-aws), so the cla…; source: gh api repos/pulumi/docs/contents/content/docs/idp/concepts (terraform-modules.md not present))
  • L72 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "Installing a converted Terraform-module package requires Pulumi CLI version 3.248.0 or newer." → ✅ verified (evidence: v3.248.0 is a real, published Pulumi CLI release (2026-06-24) with engine features (schema loader/package resolver service in provider handshake, PCL snippets) plausibly underpinning package conversion support; could not find an explicit c…; source: gh release view v3.248.0 -R pulumi/pulumi)
  • L74 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "The resources created by a converted Terraform module appear individually in previews and in the resource graph." → ✅ verified (evidence: The pulumi-hcl docs confirm a converted module becomes a Multi-Language Component (MLC), i.e., a Pulumi component resource ("Multi-Language Components allow you to author reusable Pulumi components in HCL that can be consumed from any Pulu…; source: gh api repos/pulumi/pulumi-hcl/contents/docs/mlc.md)
  • L74 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "A misspelled input on a converted Terraform module component fails at compile time rather than partway through a plan." → 🤷 unverifiable (evidence: The fetched mlc.md page documents MLC declaration syntax, validation rules, and the relationship to PulumiPlugin.yaml, but contains no mention of misspelled inputs, compile-time failure, or plan-time failure behavior for converted Terrafor…; source: https://github.com/pulumi/pulumi-hcl/blob/master/docs/mlc.md)
  • L76 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "Pulumi Cloud treats a converted Terraform-module package the same as it would treat any other package." → ➖ not-a-claim (evidence: This is the blog author's own description of how Pulumi Cloud (the PR author's own product) handles converted Terraform-module packages — a first-party design statement about their own pipeline/feature, not a third-party attributed or exte…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md)
  • L78 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "Conversion of a Terraform module to a Pulumi package runs per version, so a module can have some versions with corresponding packages and some without, and the…" → ✅ verified (evidence: The blog post itself states: "Conversion runs per version, so a module can have some versions with corresponding packages and some without, and the package's page shows which versions have converted." This is the exact source of the claim,…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md)
  • L82 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "Existing Terraform consumers of a module are unaffected by conversion: a .tf file references the module as it would any private module, and tofu init or terraf…" → ✅ verified (evidence: The blog post itself establishes that tf.pulumi.com is wire-compatible with HCP Terraform's private module registry protocol and that TF_TOKEN_tf_pulumi_com is the host-token env var ("export TF_TOKEN_tf_pulumi_com=$PULUMI_ACCESS_TOKEN\…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md)
  • L82 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "A .tf file references a module hosted on the Pulumi Cloud registry the same way it would reference any private module." → ➖ not-a-claim (evidence: The line "A .tf file references it as it would any private module" is the PR author's own description of how their own newly-built Pulumi Cloud Terraform module registry feature works (consistent with the rest of the post describing it a…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md)
  • L82 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "tofu init (or terraform init) resolves a module reference against Pulumi Cloud using the token from the TF_TOKEN_tf_pulumi_com environment variable." → ✅ verified (evidence: pulumi/pulumi-terraform-module's registry_token.go contains injectRegistryToken with the comment "injectRegistryToken lets tofu init authenticate to the Pulumi Cloud module registry without a separate terraform login" and tfTokenEnvKey…; source: gh api repos/pulumi/pulumi-terraform-module/contents/pkg/modprovider/registry_token.go)
  • L84-89 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "A Terraform module hosted on the Pulumi Cloud registry can be referenced with source = 'tf.pulumi.com///' and a version attribute in a…" → ✅ verified (evidence: The blog's own code sample shows exactly this: 'module "vpc" { source = "tf.pulumi.com///" version = "1.2.3" }', consistent with the post's stated wire-compatibility with HCP Terraform's private module regist…; source: repo:content/blog/terraform-modules-pulumi-cloud-registry/index.md (L84-89))
  • L91 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "Submodules of a Terraform module hosted on the Pulumi Cloud registry are referenced using the usual //modules/<name> syntax." → ✅ verified (evidence: pulumi-hcl's module resolution (pkg/server/parameterize.go) imports regaddr "github.com/opentofu/registry-address/v2", the standard OpenTofu/Terraform module-source address parser that natively supports the //<subpath> submodule syntax…; source: gh api repos/pulumi/pulumi-hcl/contents/pkg/server/parameterize.go; gh search code --owner pulumi-labs "modules")
  • L93 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "A Terraform-module-to-Pulumi-package conversion can be run locally using the command pulumi package add hcl module tf.pulumi.com/<namespace>/<name>/<system>,…" (also L97) → ✅ verified (evidence: pulumi-hcl registry docs confirm the exact CLI syntax: "pulumi package add hcl module [version]" e.g. "pulumi package add hcl module terraform-aws-modules/vpc/aws 5.0.0" — this generates a local SDK for a module, matching the clai…; source: gh api repos/pulumi/pulumi-hcl/contents/registry/_index.md)
  • L97 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "The page /docs/idp/concepts/terraform-modules/ provides the full publish and consume reference for Terraform Modules in the Pulumi Cloud Registry, including ho…" → ✅ verified (evidence: Companion docs PR [docs] Document Terraform modules in the Pulumi Cloud registry #19453 adds content/docs/idp/concepts/terraform-modules.md, which includes a '## Migrating from HCP Terraform' section with '### Module names' naming rules ('the module name must match [a-z0-9][a-z0-9-]*...'),…; source: gh pr diff 19453 -R pulumi/docs)
  • L0 in content/blog/terraform-modules-pulumi-cloud-registry/index.md "The same conversion can also be run locally, with pulumi package add hcl module tf.pulumi.com/<namespace>/<name>/<system>, which is useful while a version is" → 🚩 flagged (readthrough: orphaned-structure)

📊 Editorial balance

Single-subject post; balance check N/A.

🚨 Outstanding in this PR

These must be resolved or refuted before merging.

  • [L97] content/blog/terraform-modules-pulumi-cloud-registry/index.md — the post's primary call-to-action links to a page that doesn't exist yet. The "Get started" section opens with:

    See Terraform Modules in the Pulumi Cloud Registry for the full publish and consume reference, including how to migrate an existing registry, the naming rules to check first, and self-hosted hosts.

    That page is added by the companion docs PR #19453, which is still opencontent/docs/idp/concepts/terraform-modules.md is not on master today. Nothing is wrong with the link text or the path; this is a merge-order dependency: land [docs] Document Terraform modules in the Pulumi Cloud registry #19453 first (or land them together), otherwise the launch post's main next step 404s on day one. Worth calling out explicitly because the usual safety net didn't run — the Hugo link-integrity preflight self-skipped on this PR as content-only, so a dangling /docs/… link here would not have failed CI.

  • [L16] content/blog/terraform-modules-pulumi-cloud-registry/index.md — same missing page, but in canonical_url, where the consequence is worse:

    canonical_url: https://www.pulumi.com/docs/idp/concepts/terraform-modules/

    Two things to decide here. First, the ordering problem above applies — until [docs] Document Terraform modules in the Pulumi Cloud registry #19453 merges this emits <link rel="canonical"> pointing at a URL that returns 404, which is a strictly harmful signal to search engines. Second, and independent of timing: setting canonical_url to a different page tells crawlers this post is a duplicate of the docs reference and should not be indexed in its own right. For a launch announcement that is almost certainly not what you want — the post and the reference page are different content with different intent, and the post is the thing you want ranking for "Terraform modules Pulumi registry". Unless this is a deliberate consolidation decision, delete the canonical_url: line entirely. (The in-body "Get started" link already sends readers to the reference page, which is the behavior you want without suppressing the post.)

⚠️ Low-confidence

Review each and resolve as appropriate — these don't block the PR.

  • [L0] content/blog/terraform-modules-pulumi-cloud-registry/index.md — the local-conversion paragraph is in the wrong section. It currently sits under "Your Terraform consumers keep working":

    If a version you need has no package, you can convert the module locally instead, with pulumi package add hcl module tf.pulumi.com/<namespace>/<name>/<system>. That generates an SDK for your project without publishing anything, so it comes without the package page, API reference, and usage tracking.

    But that section is explicitly about the people consuming the module over the Terraform protocol — it opens "Nothing about the module changes for the people already consuming it." The paragraph is addressing the opposite audience: a Pulumi consumer who hit a version with no converted package. It reads as a non sequitur where it is, and it also strands the section's real closing beat (Submodules use the usual //modules/<name> syntax.) mid-thought.

    Suggested fix: move this paragraph up into "Every version becomes a Pulumi package", directly after the "Conversion runs per version…" paragraph it answers, and let "Your Terraform consumers keep working" end on the submodules line. The final "For a full walkthrough, see [Use a Terraform Module in Pulumi]" sentence should travel with it. Not a blocker — a reader still gets the right information, just in a surprising place.

  • [L74] content/blog/terraform-modules-pulumi-cloud-registry/index.md"a misspelled one fails at compile time rather than partway through a plan" — this capability claim couldn't be confirmed against a source (verdict: 🤷 unverifiable; evidence: the pulumi-hcl multi-language-component doc covers MLC declaration syntax, validation rules, and the relationship to PulumiPlugin.yaml, but says nothing about misspelled inputs or compile-time-versus-plan-time failure; source: https://github.com/pulumi/pulumi-hcl/blob/master/docs/mlc.md).

    Author question: is "compile time" accurate across all six generated SDKs? It's clearly true for TypeScript, Go, C#, and Java. Python's typed stubs surface a misspelled kwarg in the editor and under a type checker, but not at a compile step, and YAML has no compile phase at all — yet the sentence lists all of TypeScript, Python, Go, C#, Java, and YAML two clauses earlier. Consider softening to something that holds for every listed language, e.g. "and a misspelled one is caught before anything runs, rather than partway through a plan" — or narrow the claim to the compiled languages.

Style findings

Found by pattern-based linting; Findings may be false positives.

  • line 38: [style] wordiness — 'It is' is too wordy.

📋 Triaged verifier findings

I double-checked these and realized they weren't real findings — click to expand
  • [L53-58] content/blog/terraform-modules-pulumi-cloud-registry/index.md"A go-tfe client can be configured to target the Pulumi Cloud registry using tfe.NewClient(&tfe.Config{Address: 'https://tf.pulumi.com', Token: os.Getenv('PULUM…"Spurious: the check concluded the endpoint was broken because https://tf.pulumi.com returns 404 at its bare root, but a Terraform-protocol registry host is not expected to serve anything at / — discovery happens at /.well-known/terraform.json and the module API lives under /api/v2/…. A 404 on the root path is normal for this kind of host and says nothing about whether the address works as a go-tfe Address value.

  • [L48-49] content/blog/terraform-modules-pulumi-cloud-registry/index.md"Publishing via the hashicorp/tfe Terraform provider to the Pulumi Cloud registry requires setting hostname = 'tf.pulumi.com'."Mis-sourced: the check followed github.com/hashicorp/go-tfe (the Go SDK repo) and reasonably found nothing there about a provider argument. The post actually cites the right source one link over — registry.terraform.io/providers/hashicorp/tfe/latest/docs, where hostname is a documented provider argument. Wrong page checked, not a wrong claim.

  • [L66] content/blog/terraform-modules-pulumi-cloud-registry/index.md"A Terraform module published as acme-corp/vpc/aws becomes a Pulumi package called vpc-aws, installable via pulumi package add vpc-aws 1.2.3."Mis-sourced: marked unverifiable only because content/docs/idp/concepts/terraform-modules.md isn't on master yet. That page is in the open companion PR #19453, and the same trail's L97 entry confirms it defines the <name>-<system> naming rules the post applies. The worked example is also internally consistent with the rule stated two sentences earlier.

💡 Pre-existing issues in touched files (optional)

No pre-existing issues in touched files.

✅ Resolved since last review

No items resolved since the last review.

📜 Review history


  • Refresh this review — comment @claude #update-review. Say what you fixed, or which finding you dispute and why; both work in the same mention.
  • Ask for anything else — comment @claude with no hashtag (questions, one-off fixes). Leaves this review untouched.

Important

Please don't hide, resolve, or delete this comment! It breaks things!

📖 How pre-merge review works — the full lifecycle, short-circuits, and escape hatches.

@github-actions github-actions Bot added review:outstanding-issues Claude review completed; outstanding has author-actionable findings and removed review:in-progress Claude review is currently running labels Aug 3, 2026
@pulumi-bot

pulumi-bot commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Social Media Review

content/blog/terraform-modules-pulumi-cloud-registry/index.md

X — FAIL

Reasons:

  • Violates platform structure: body is a single paragraph with no blank-line break (X requires at least 2 paragraphs)

LinkedIn — PASS

Bluesky — missing

No copy provided. Suggested copy drafted below.


Suggested copy

X (217/255 chars) — minimum-change repair; split the existing sentence into two paragraphs at the colon, wording otherwise unchanged:

Publish a Terraform module to the Pulumi Cloud registry and every version becomes a typed Pulumi package.

Generated SDKs for TypeScript, Python, Go, C#, Java and YAML, API docs, and usage tracking across your stacks.

Bluesky (279/300 chars) — drafted from the article:

A Terraform module published to the Pulumi Cloud registry still works the same for anyone consuming it. But every version now also converts into a typed Pulumi package.

Generated SDK, API reference, and tracking for every stack depending on it. Here is how the conversion works.


Suggestions (advisory)

These are stylistic notes — they don't block the post.

LinkedIn

  • Summarizes the article outright: the copy already states the mechanism (wire-compatible with HCP Terraform, swap tf.pulumi.com for app.terraform.io) and the full feature set (typed inputs/outputs, generated SDKs, API reference, dependency tracking, staleness detection) — nothing is left for the article to reveal
  • Missing pointer: the post reads as a complete standalone explainer with no line signaling there's more depth in the article
  • Opens with the product name ("The Pulumi Cloud registry now hosts...") when a sharper hook is available — e.g. the tf.pulumi.com vs. app.terraform.io swap

Updated for commit 0e3b04acac23a77ccfcce60aa840e5670bea8eab (short: 0e3b04a) at $(date -u '+%Y-%m-%d %H:%M UTC').

@fnune
fnune requested a review from cnunciato August 3, 2026 09:35
cnunciato added a commit that referenced this pull request Aug 3, 2026
* [registry] Document Terraform module hosting in the Pulumi Cloud registry

Add a new concept page under IDP describing how Pulumi Cloud hosts Terraform modules: authentication via `terraform login`, the three HCP-compatible publish paths (go-tfe, hashicorp/tfe provider, tfc-workflows-github Action), the standard module layout we extract from at publish, consumption from OpenTofu / Terraform via the Module Registry Protocol, and consumption from a Pulumi program via `pulumi package add terraform-module`.

Cross-link from the existing "Use a Terraform Module in Pulumi" guide so consumers landing on the IaC guide can discover the Pulumi-Cloud-hosted path.

Design doc: https://app.notion.com/p/Terraform-module-hosting-in-the-Pulumi-Cloud-registry-372fdbdf1cce801ea7a3f4946c1e4154

Fixes pulumi/pulumi-service#44080

## Test plan

- Render the IDP concepts section locally and confirm the new "Terraform Modules" entry appears under the Concepts menu
- Click through every internal link in the new page to confirm targets exist

* [registry] Add launch blog post; correct registry host to tf.pulumi.com

Add the announcement blog post for hosting Terraform modules in the
Pulumi Cloud registry, and correct the registry host across the docs and
blog from app.pulumi.com to the canonical tf.pulumi.com (per
pulumi/pulumi-service#44889; app.pulumi.com is the console only).

Move the migration guidance out of the blog and into the concept page,
and document the one naming rule that differs from HCP Terraform: module
names reject underscores.

* [registry] Fix auth: Pulumi access token, not terraform login

The TFE discovery document (cmd/service/api/tfe_discover.go) advertises
tfe.v2, state.v2, and modules.v1 but no login.v1, so terraform login
against tf.pulumi.com is unsupported.

Document the real auth instead: a Pulumi access token is the bearer for
publish (go-tfe, the tfe provider, the GitHub Action), pulumi login for
pulumi package add, and TF_TOKEN_tf_pulumi_com for plain OpenTofu or
Terraform.

* [registry] Address review: drop GitHub Action, fix injection and layout

- Remove the tfc-workflows-github Action from the publish and delete
  paths and from the auth list. create-run triggers a run; it does not
  touch the module registry. The testbed only exercised go-tfe and the
  tfe provider.
- Consume from Pulumi: drop the rejected "CLI injects TF_TOKEN_<host>"
  detail. After pulumi login the provider resolves the module with your
  Pulumi credentials. Note that terraform-module is a parameterized
  provider and name its parameters.
- Module layout: parse all root .tf files (any filenames); examples and
  README are captured at publish, not rendered (console rendering is not
  built yet).
- Say packages, not components.

* [registry] Brand: sentence-case the concept page title; link state backend

Apply the Pulumi brand writing-style rules: sentence case for the
concept page title_tag/title/h1 and no over-capitalized concepts, so
"Terraform modules in the Pulumi Cloud registry" rather than title case.
Drop the "just" minimizer from the migration line.

Link "keep Terraform state in Pulumi Cloud" in the blog to the state
backend guide.

* [registry] Consume via the hcl package instead of terraform-module

The Pulumi-program consume path now uses the forthcoming hcl package:
pulumi package add hcl module <source> [version], with the version
optional (omit for latest, pass to pin). Drops the terraform-module
alias parameter, since the hcl module form derives the package name
from the module.

* [registry] Document conversion as the default Pulumi consumption path

Publishing a module version converts it into a Pulumi package, and neither the
concept page, the launch post, nor the Terraform module guide said so. All three
taught `pulumi package add hcl module ...` as the way to consume from a Pulumi
program, which is now the fallback rather than the default.

The concept page gains a section on what publishing produces: the
`<name>-<system>` package name, that conversion runs per version, and where to
see which versions have converted. `pulumi package add <name>-<system>` becomes
the documented path. The `hcl module` form stays, for versions still converting,
noting that it runs the same conversion locally rather than using the published
package. The guide's "Using a Module from Pulumi Cloud" section follows the same
order.

The pages describe what the package gives a consumer, not only that one exists:
the module becomes a multi-language component, so its variables are typed inputs
and its outputs typed outputs, with a generated SDK in the project's language,
its resources visible individually in previews and the resource graph, an API
reference generated from those variables and outputs, and a record of which
stacks depend on it and which are behind the latest version.

Usage tracking follows the package, so a consumer reaching the module over the
Terraform protocol does not report a dependency and does not appear in the usage
columns or the "Used by" tab. Both pages say so.

Consuming from OpenTofu or Terraform moves below the Pulumi path on the concept
page and in the post. It still documents that existing `.tf` consumers are
unaffected and how to reference the module and its submodules.

All three state that installing a converted package needs Pulumi CLI 3.248.0 or
newer, verified by bisect: 3.247.0 fails the plugin handshake, 3.248.0 works.

The Terraform module detail page is being removed from the console, so the
pages describe conversion state as living on the package's page instead.

Fixes #20555

## Test plan

1. Automated checks
   - `./scripts/format.sh` on all three files
   - `vale --config=.vale.ini` on all three: 0 errors; remaining warnings are on
     lines this change does not touch

* [registry] Retitle the launch post and set its author

The title names the payoff rather than the hosting. Author set to the existing
team entry.

* [registry] Stop selling local conversion as a fallback, link install and console URLs

Local conversion runs the same `hcl` provider the registry runs, so a module the
registry could not convert fails locally for the same reason. All three pages
offered it as the answer for a module using Terraform features Pulumi cannot
express, which it is not. It is now described as what it is: the same
conversion, run at the moment you run it, useful while a version is still
converting. The concept page's section is renamed to match and says outright
that it is not a way around a failed conversion.

The CLI version requirement links to Download & Install Pulumi on all three
pages, so a reader on an older CLI has somewhere to go.

The concept page picks up aliases for the two docs URLs the console links to,
both of which 404 today:

- `/docs/terraform-modules/`, from the package install card
- `/docs/iac/using-pulumi/pulumi-cloud/registry/terraform-modules/`, from the
  Terraform modules list page

The concept page's deletion section is removed while we decide what deleting a
converted package should do to the module it came from
(pulumi/pulumi-service#47170). Documenting module deletion now would describe
behavior we expect to change.

The post is dated 2026-08-04.

* [registry] Fix lint failures and the review's link and style findings

`make lint` gates the build and was failing:

- The post was missing the required `category` front matter. It announces a
  shipped feature, so `product`
- Its `meta_desc` was 182 characters against a 160 limit. Dropped the usage
  tracking clause, which the post covers in the body
- Removing the concept page's deletion section left a trailing blank line

From the pre-merge review:

- The multi-language component link 404s. `pulumi-hcl` has no `main` branch;
  `https://github.com/pulumi/pulumi-hcl/blob/master/docs/mlc.md` resolves.
  Fixed in the post and the concept page
- The access token link went through an alias. The page's canonical route is
  `/docs/administration/access-identity/access-tokens/`; the old path is line
  19 of that page's own aliases list
- The closing line of "Converting a module locally" restated what the
  Authenticate section already establishes
- Style: two `It is` openings and one `mostly`
- The guide's new heading was Title Case, against AGENTS.md's sentence case
  rule for H2 and below

The module name rule is now stated as the regex the service enforces,
`[a-z0-9][a-z0-9-]*` (`pkg/apitype/registry_artifact.go`), rather than a
character list that implied a leading hyphen was valid.

Two review findings are not acted on. The registry host is `tf.pulumi.com`:
`tfe.pulumi.com` is a dead record that CloudFront-403s since the distribution
alias, ALB host rule, and service dispatch moved, so the provider test the
review cites is stale. And `canonical_url` pointing at the concept page is what
BLOGGING.md prescribes for a feature announcement documented in the docs.

* [registry] Address review: heading context, define system, trim inside baseball

* [registry] Split the blog into #20648; make local conversion a fallback, not a wait-saver

* [registry] Say plainly that local conversion publishes no package

---------

Co-authored-by: Christian Nunciato <chris@nunciato.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain:blog PR touches blog posts or customer stories review:outstanding-issues Claude review completed; outstanding has author-actionable findings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants