Making create idempotent (for “already exists”) given module‑level import limitations

Some of our new APIs require one‑to‑many migrations (from older deprecated APIs) that Terraform’s built‑in moved block can’t automate (as it only handles 1:1).
Because moved only supports a single target resource, we must use terraform import for the extra assignments. Unfortunately, import blocks only work at the root module level (see Allow import blocks within child modules · Issue #33474 · hashicorp/terraform · GitHub), so platform teams can’t embed them in reusable modules—every downstream user must write their own import steps, resulting in a poor UX at scale.

Proposed solution

Instead of relying on root‑level import, we’d like to make the create operation idempotent when a resource already exists. Concretely:

  1. Introduce an opt‑in flag (e.g. imports_if_exists ) on our new resources.
  2. If set, a CREATE of an already‑existing resource would not error, but instead fetch its state via a GET and adopt it into state.
    This lets modules ship self‑contained migrations without requiring root imports.

This lets modules ship self‑contained migrations without requiring root imports.

Questions:

  1. Is adding this opt‑in idempotent‑create behavior compatible with Terraform’s design principles & best practices?
  2. Are there any core implications or edge cases we haven’t considered?
  3. Do any existing providers implement a similar “create if exists” pattern for resources?
  4. Would you recommend another supported mechanism to solve multi‑resource migrations without root‑level imports?

Hi @maastha,

That should work exactly as you expect. Terraform cannot tell if you “create” the resource by making a new one through the API, or just reading an existing one. The real hazard here is making it too easy for a user to adopt a resource into multiple managed configurations, each of which would then be fighting to manage that resource. That of course is possible with import already, so it just needs to be clear to the user how to use the resource correctly.

I would probably keep the identifying information separate from the resource’s computed data in the schema, in which case you may not need a separate flag to trigger the behavior, only the existence of whatever fields you need to trigger the “import” behavior (IMHO I might use the work “adopt” rather than “import”, since the latter has a specific meaning in Terraform already, but we’ve never written official docs on this that I’m aware of)

For example if you use the traditional computed id field to identify the resource, you might then have an existing_id field to take in a string to locate and adopt an existing instance.