hashicorp / terraform

Terraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.
https://www.terraform.io/
Other
42.31k stars 9.49k forks source link

Accept strings as 'to' argument in import block #35614

Open stuartpurgavie opened 3 weeks ago

stuartpurgavie commented 3 weeks ago

Terraform Version

Terraform v1.9.4
on linux_amd64

Use Cases

I want to migrate many state files from one platform to another platform. I don't want to spend a lot of time refactoring Terraform code. For organizational reasons, I do not have direct access to the original state files, but I can get all of the resource keys and provider IDs easily enough. I would like to approach in the following way:

locals {
  # TODO: replace with a jsondecode(file("imports.json")) call
  imports = {
    "module.repospace.module.repository.github_repository.this" = "tfw-sample-repository-name"
  }
}

import {
  for_each = local.imports
  to = each.key
  id = each.value
}

Obviously, this is not supported, because the only acceptable input to the to field is a raw terraform resource reference, not strings. As it is trivial to get these pairs from a state file (if the version is known) and would satisfy my organizational policy constraint around seeing potential secrets that may be in state, this seems like a decent path forward.

Attempted Solutions

locals {
  # TODO: replace with a jsondecode(file("imports.json")) call
  imports = {
    "module.repospace.module.repository.github_repository.this" = "tfw-sample-repository-name"
  }
}

import {
  for_each = local.imports
  to = each.key
  id = each.value
}

Attempting the above returns an error that says a provider "hashicorp/each" could not be found, which means that in this scenario, the "to" field expecting an addrs.AbsResourceInstance forces it to treat "each" as a provider and attempt to import that provider in order to evaluate the expression.

Proposal

I would propose something similar to #33618 where the accepted inputs for the to block is updated to allow a string, and if a string is provided, attempt to do a type conversion to an addrs.AbsResourceInstance and then attempt to match, throwing an error if the resource reference does not exist in the Terraform config.

References

33618

jbardin commented 3 weeks ago

Hi @stuartpurgavie,

Thanks for filing the issue, but there are a couple problems with the proposal we would need to figure out first.

The import block's to address is not a value which can be assigned, it is a reference. This means in the Terraform language it needs to be a static address corresponding to another object. This stems from the underlying HCL language, where Terraform relies on these HCL static references to construct the relationships between objects and and determine the order with which to evaluate the configuration as a whole, and how to lookup reference values. We can bend this a little by evaluating index expressions within the configuration address, but the overall configuration address remains static.

Somehow interpreting strings as a reference is not something done in the HCL language, so Terraform would have to invent some lower level handling of the value here, which would make both its implementation and its syntax inconsistent with the rest of the language.

The linked issue regarding the id attribute was relatively straightforward. id always was and still is a string value, so it was just a matter of refining the correct scope and handling of static references to allow for evaluation. This is contrary to determining references at runtime, which means that Terraform would not be able to resolve the topological order for evaluation before evaluation begins.