Skip to content

docs(concepts): clarify the difference between direct-form and output-form provider functions#18931

Open
joeduffy wants to merge 2 commits into
masterfrom
fix_issue_12373_provider_functions_clarify
Open

docs(concepts): clarify the difference between direct-form and output-form provider functions#18931
joeduffy wants to merge 2 commits into
masterfrom
fix_issue_12373_provider_functions_clarify

Conversation

@joeduffy
Copy link
Copy Markdown
Member

What changed

This PR makes two changes to the Provider functions concepts page to address the gaps described in issue #12373.

Change 1 — clarify that the two forms are distinct functions, not overloads

A new paragraph has been added immediately before the "Direct form" subsection to state plainly that in most languages the two variations are two separately named functions rather than overloads of a single function. It includes the concrete naming examples (aws.ec2.getAmi / aws.ec2.getAmiOutput), calls out Java's inverted convention (getAmi() is the output form, getAmiPlain() is the direct form), and notes that YAML uses fn::invoke for both.

Change 2 — concrete code examples showing when to choose each form

Two new choosable code blocks have been added at the end of the "Choosing between direct form and output form" section, one per form:

  • The direct-form example uses getAmiIds to look up AMIs and gates instance creation on whether any matching AMIs were found — illustrating that the direct form returns plain values suitable for conditional logic.
  • The output-form example uses getAmiOutput (and its per-language equivalents) with a config secret (requireSecret) as a filter value — illustrating that the output form accepts Pulumi Input values directly, avoiding an apply() wrapper.

Both examples use AWS EC2 AMI lookups, consistent with the existing examples on the page, and are provided for TypeScript, Python, Go, C#, Java, and YAML.

Verification

  • Front-matter unchanged; exactly two --- delimiters confirmed.
  • All {{< chooser >}} and {{% choosable %}} tags balanced (5 chooser blocks, 30 choosable blocks).
  • No trailing whitespace.
  • File ends with a newline.
  • provider-functions.md is hand-authored content, not auto-generated.

Fixes #12373


🧠 This PR was created by workprentice on behalf of @joeduffy.

Two changes to content/docs/iac/concepts/functions/provider-functions.md:

1. Add an explicit statement (just before the "Direct form" subsection) that
   the two variations are two distinct, separately named functions in most
   languages — not overloads of a single function.  Includes the concrete
   naming examples (aws.ec2.getAmi / aws.ec2.getAmiOutput, Java's inverted
   convention, and YAML's single fn::invoke form).

2. Add a pair of choosable code examples at the end of the "Choosing between
   direct form and output form" section illustrating the two common scenarios:
   - Direct form used to gate whether a resource is created at all (the lookup
     result must be a plain value for the Pulumi engine to resolve the
     dependency graph).
   - Output form used when an existing Output value (e.g. a config secret)
     feeds directly into the lookup's arguments, avoiding an apply() wrapper.

Fixes #12373
@joeduffy joeduffy added the impact/no-changelog-required This issue doesn't require a CHANGELOG update label May 13, 2026
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 13, 2026

Docs review

Solid addition that addresses the gaps in #12373. The cross-language coverage is thorough and the examples are realistic and consistent with the page's existing style. A few small things to consider before merging:

Awkward colon at the section lead-in (line 237)

The original sentence ends with a colon:

Provider functions are exposed in each language as regular functions, in two variations:

That colon was previously introducing the ### Direct form and ### Output form subheadings. With the new paragraph slotted in between, the colon now points at the explanatory paragraph instead, which reads oddly. Consider changing it to a period, or rewording to flow into the new content.

Provider functions are exposed in each language as regular functions, in two variations.

Minor redundancy (line 239)

...the two variations take the form of two distinct, separately named functions rather than overloads...

"distinct" and "separately named" overlap. Tightening this would help:

In most languages, the two variations take the form of **two separately named functions** rather than overloads of a single function. For example, the AWS function `aws.ec2.getAmi` has a corresponding output form named `aws.ec2.getAmiOutput`. Java's naming convention is inverted: `Ec2Functions.getAmi()` is the _output_ form, while `Ec2Functions.getAmiPlain()` is the direct form. In YAML, both forms are invoked using `fn::invoke`, and the runtime handles the distinction transparently.

Inconsistent Python filter style between the two examples

The direct-form Python example (around line 396) uses a plain dict for the filter:

filters=[{\"name\": \"name\", \"values\": [\"amzn2-ami-hvm-*-x86_64-gp3\"]}],

The output-form Python example (around line 610) uses the typed argument class:

filters=[aws.ec2.GetAmiFilterArgs(name=\"name\", values=[ami_name_filter])],

Both work, but mixing styles in adjacent examples is the kind of thing a careful reader will trip on. Recommend picking one (typed args are the more discoverable, IDE-friendly choice) and using it in both.

YAML "direct form" example doesn't actually demonstrate the direct form

The YAML direct-form example (around line 533) carries a comment explaining that YAML can't do conditional resource creation, and then proceeds to write an fn::invoke block that is structurally identical to the output-form example below it. Readers comparing the two YAML blocks will see no meaningful difference and may wonder why both are shown.

Two options:

  • Drop the YAML choosable from the direct-form chooser entirely and add a one-line note above it like "YAML does not distinguish between direct and output forms; see the output-form example below."
  • Keep both but make the contrast clearer in the comment (e.g., "YAML always evaluates fn::invoke after variable resolution, equivalent to the output form in other languages").

Nits

  • Line 359: "no apply wrapper required" — fine as prose, but worth knowing the actual API is .Apply() in C#/Go and .applyValue() in Java. Could read "no apply call required" without the backticks, which would sidestep the case mismatch with most SDKs.
  • Java import com.pulumi.*; (lines 495, 708) is a wildcard import. The page's existing Java snippets use explicit imports (e.g., the existing Output-form example near the top of the file). For consistency, list Context and Pulumi explicitly.

Mention @claude if you'd like another pass after revisions.

- Change 'in two variations:' to a period (colon was left dangling by new para)
- Remove redundant 'distinct' from 'two distinct, separately named functions'
- Replace plain dict filter with GetAmiIdsFilterArgs in Python direct-form example,
  matching the typed-args style used in the output-form Python example
- Drop YAML from the direct-form chooser; add an info note explaining YAML
  does not distinguish between direct and output forms at the language level
- Replace wildcard 'import com.pulumi.*' with explicit Context/Pulumi imports
  in both new Java examples for consistency with the page's existing style
- Change 'no apply wrapper required' to 'no apply call required' (no backticks)
@joeduffy
Copy link
Copy Markdown
Member Author

Thanks for the detailed review! Addressed all feedback in the follow-up commit:

  • Colon → period: Fixed the dangling colon at the section lead-in.
  • Redundancy: Removed "distinct" from "two distinct, separately named functions".
  • Python filter style: Updated the direct-form Python example to use GetAmiIdsFilterArgs consistently with the output-form example.
  • YAML direct-form: Dropped YAML from the direct-form chooser entirely and replaced it with an info note explaining that YAML does not distinguish between direct and output forms at the language level.
  • Java wildcard imports: Replaced import com.pulumi.*; with explicit import com.pulumi.Context; import com.pulumi.Pulumi; in both new Java examples.
  • apply wrapper: Changed to "no apply call required" without backticks.

@claude

@pulumi-bot
Copy link
Copy Markdown
Collaborator

pulumi-bot commented May 13, 2026

Claude finished @joeduffy's task in 1m 39s —— View job


Follow-up commit review — all feedback addressed ✅

Reviewed 16aca83 against the six items from the previous round:

  • Colon → period (line 237) — in two variations. now flows correctly into the new paragraph.
  • Redundancy tightened (line 239) — **two separately named functions**; "distinct" removed.
  • Python filter style consistent (line 400) — direct-form example now uses aws.ec2.GetAmiIdsFilterArgs(...), matching the output-form example.
  • YAML direct-form handled — choosable removed from the direct-form chooser (language "typescript,python,go,csharp,java"), with an info note above the chooser explaining YAML uses fn::invoke for both forms.
  • Java wildcard imports replaced (lines 499–500, 683–684) — both new examples now use explicit import com.pulumi.Context; / import com.pulumi.Pulumi;.
  • no apply call required (line 359) — no backticks, matches the wording suggestion.

Structural sanity checks

  • {{< chooser >}} / {{< /chooser >}}: 5 / 5
  • {{% choosable %}} / {{% /choosable %}}: 29 / 29 ✅ (one fewer pair than before, reflecting the dropped YAML block)
  • {{% notes %}} / {{% /notes %}}: 3 / 3
  • Frontmatter intact, no trailing whitespace introduced.

LGTM — ready to merge from a docs-review standpoint. Nothing else jumped out on this pass.
· branch fix_issue_12373_provider_functions_clarify

@pulumi-bot
Copy link
Copy Markdown
Collaborator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

impact/no-changelog-required This issue doesn't require a CHANGELOG update

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Clarify the difference between getFoo and getFooOutput

3 participants