Skip to content

Test framework: add ephemeral resource support to mock_provider #38608

@aivinog1

Description

@aivinog1

Terraform Version

Terraform v1.15.2
on linux_arm64
+ provider registry.terraform.io/hashicorp/random v3.7.2


Reproducible identically on `darwin_arm64` and `linux_amd64` — the code path that emits the diagnostic lives in core, not in any platform-specific shim.

Use Cases

The canonical pattern for managing write-only secret values in Terraform — straight from the docs on ephemeral values — combines:

ephemeral "random_password" "this" {
  length = 32
}

resource "aws_secretsmanager_secret" "this" {
  name       = var.name
  kms_key_id = var.kms_key_id
}

resource "aws_secretsmanager_secret_version" "this" {
  secret_id        = aws_secretsmanager_secret.this.id
  secret_string_wo = ephemeral.random_password.this.result
}

This shape gives the user three desirable properties at once: the random value never enters state, the secret is encrypted with a caller-supplied KMS key, and rotation is handled by bumping a *_wo_version integer. Wrapping it into a small helper module so multiple consumer modules can reuse it is the obvious next step.

Once that helper exists, consumers want to test that they wire it correctly. The most common security-relevant invariant is "the helper is invoked with the right kms_key_id" — easy to assert structurally if the test framework can plan / apply the consumer under mock_provider. Today it cannot. The first parse of the helper trips the diagnostic below, regardless of which assertion the test was going to make. Every downstream module that depends on the helper is uniformly untestable.

The pattern isn't niche: any module that surfaces a random-generated secret value (RDS master password, OAuth client credentials, OIDC cookie key, …) follows the same shape; the testing gap blocks all of them.

Attempted Solutions

We tried, in order:

  1. Module-local tests for the helper itself — command = apply with mock_provider. The error fires at module-load time, before any run block executes:
    Error: No ephemeral resource types in mock providers
      with ephemeral.random_password.this,
    The provider mocking mechanism does not yet support ephemeral resource
    types.
    
  2. Plan-only testscommand = plan. Same diagnostic; the rejection isn't apply-specific.
  3. override_module on the helper from the consumer module's tests:
    override_module {
      target = module.helper
      outputs = { secret_arn = "arn:aws:secretsmanager:..." }
    }
    The module is still loaded for type-checking before the override applies, so the ephemeral block is still parsed and the diagnostic still fires. The override has no effect on the rejection.
  4. Variable-level validation testsvalidation {} blocks inside variable "X" are testable via expect_failures at command = plan because the variable is evaluated before the resource graph is walked. We use this for "is kms_key_id non-null" type invariants. But it cannot cover invariants that live on resource attributes (the actual kms_key_id value flowing into aws_secretsmanager_secret, downstream wiring of secret_arn into IAM grants, etc.) — those require an actual plan, which is what's blocked.
  5. Falling back to make env.<env>.check against the real AWS provider on a host with credentials. Works, but moves the assertion from CI-friendly terraform test into an SSO-driven manual workflow, with the security-relevant invariants only checked by humans at PR-review time.

None of these close the gap for consuming modules that need to assert on resource-level attributes inside an ephemeral-using helper.

Proposal

Extend mock_provider so ephemeral resource types follow the same model as managed resources:

  • mock_resource blocks accept defaults for ephemeral types, returning the configured values from OpenEphemeralResource and the appropriate empty / no-op responses from RenewEphemeralResource and CloseEphemeralResource. Example:
    mock_provider "random" {
      mock_resource "random_password" {
        defaults = {
          result = "mock-password-value"
        }
      }
    }
  • override_resource blocks accept ephemeral resource addresses, overriding the mock defaults for a specific instance, same syntax as managed resources today.
  • ValidateEphemeralResourceConfig returns no diagnostics under the mock provider, leaving validation to the consumer's own logic.

Concretely, in internal/providers/mock.go, the four ephemeral protocol methods today return the same hard-coded diagnostic; three carry a FIXME: Design some means to mock an ephemeral resource type. comment (introduced 2024-09-13 in 3b77e57ce4854820411134caf44361d48e75ce83). Replacing those stubs with the same Mock / Resources / Overrides plumbing the managed-resource methods already use would close the gap symmetrically.

We do not have a preference between "default to a generated mock value of the correct type" vs. "require the user to set mock_resource defaults explicitly". The first is more ergonomic; the second is more predictable. Either is dramatically better than the current state, where no path through the test framework reaches the consuming module's resources.

References

This request was drafted with the help of Claude (Anthropic) in a Claude Code session; the reproducer, code references, and search-for-prior-art steps were verified manually against the linked commits and source.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions