terraform icon indicating copy to clipboard operation
terraform copied to clipboard

Allow import blocks within child modules

Open kmoe opened this issue 1 year ago • 31 comments

Currently, import blocks may only appear in the root module. Should we allow them to appear in child modules?

Related: https://github.com/hashicorp/terraform/issues/33376

kmoe avatar Jul 04 '23 12:07 kmoe

Appearing within child modules should be fine as it would only really apply to local modules and it would make passing providers to the import block easier or not needed

Shocktrooper avatar Aug 04 '23 18:08 Shocktrooper

i would appreciate the option to specify either a fully qualified namespace, or a local/relative namespace for import block within modules.

dylan-shipwell avatar Aug 09 '23 00:08 dylan-shipwell

This is a huge blocker for moving to providers like the AWS Provider v6.x which has announced breaking changed to aws_ssm_parameter which requires import of changed variables.

 Warning: Argument is deprecated
with module.this.aws_ssm_parameter.tfe_api_url
on .terraform/modules/this/ssm.tf line 17, in resource "aws_ssm_parameter" "tfe_api_url":

  overwrite = true

this attribute has been deprecated

arnvid avatar Sep 13 '23 23:09 arnvid

Adding my 2 cents, this is also a major issue for us. We've been writing a lot of modules that deploy cloud infrastructure and modules being unable to import within a child module is incredibly burdensome.

AMEvers avatar Dec 27 '23 16:12 AMEvers

Hey @AMEvers @arnvid @dylan-shipwell, I'd love to learn more here about your workflows and use cases. Could you email me at [email protected] and we can schedule time to chat?

omarismail avatar Dec 27 '23 21:12 omarismail

Hey I'd love to learn more here about your workflows and use cases. Could you email me at [email protected] and we can schedule time to chat?

You can reach me on slack if you ping Robert G.

arnvid avatar Dec 28 '23 02:12 arnvid

i also have a use case.

i use the tailscale terraform provider, and it requires the tailscale_acl resource is imported before it can be used - https://registry.terraform.io/providers/tailscale/tailscale/latest/docs/resources/acl

I have a module that wraps that resource, and i need to import the tailscale_acl in that module.

markwellis avatar Jan 25 '24 07:01 markwellis

Is there any planning to implement this feature?

GeorgeGkinis avatar Mar 05 '24 13:03 GeorgeGkinis

I would also like to be able to use it within child modules, I hadn't realised it was only for the root module and have spent the last few weeks implementing imports into a number of modules so we can update them and move away from legacy code since some of our modules date back to terraform 0.12 before we had an internal standard set and such.

We also have other scenarios that would be solved with the ability to use them specifically for the following two scenarios

Changing Security Groups/Rules without it deleting and re-creating, this has been an annoying blocker for a lot of people within the Company.

Datadog Monitors for example we not too long ago had the Data Dog monitors all moved into a single company wide environment isntead of a different one for each aws environment

A lot of builds stopped working and had to have monitors manually deleted so they could be recreated by the module.

TrueCyberAxe avatar Mar 07 '24 15:03 TrueCyberAxe

off topic, work around in case it helps anyone

i don't really believe hcl import will be a successor to proper state migrations. (even if they reverse their opinion on child import statements being illegal)

there aren't any great terraform migration frameworks out there that i know of. there is https://github.com/minamijoyo/tfmigrate but it struggles to accomplish obvious things in a lot of ways similar to terraform native import does.

for now, i'm sticking to manual migration script files these generally read like this

#!/bin/sh -eux
# terraform/myproject/migrations/0001-some-state-migration.sh
: "${TERRAFORM_BIN:=terraform}" # use "terraform" from $PATH or $TERRAFORM_BIN if specified

do_forward=1
while [[ $# -gt 0 ]]; do
  case $1 in
    --reverse) do_forward=0;;
    *) echo "Unknown option $1"; exit 1;;
  esac
  shift
done

fn_tf_forward() {
    $TERRAFORM_BIN import foo.bar.baz my_uid
}

fn_tf_backward() {
    $TERRAFORM_BIN state rm foo.bar.baz my_uid
}

if [ $do_forward -eq 1 ]; then
    fn_tf_forward; 
else
    fn_tf_backward;
fi

the most annoying thing about this low tech solution is that there is no persistent database of which migration files have been completed. i just remember with my mind right now.

hcl import is nice because it shows a plan of what should happen if both the import step and apply step are happy, this is a great feature, and it remembers if an import is complete.

tfmigrate is nice because it remembers which migrations have been applied.

the nice thing about manual migration scripts is that you can write them in any language you like, meaning i have access to for loops, and if this then that logic control, and string format/templates, not to mention service provider apis like cloud vendors. very helpful when i have 1000 users of a module that aren't all homogeneous but are patterned. given that its also software, i could use these to generate correct import hcl blocks, and i have considered doing this, the most annoying part of this to me, is that the import hcl files must be in the same folder as the top level terraform.tf/main.tf, so i usually move them to a mgirations/ subfolder after they have been applied to not clutter the project. scripts that I run can live anywhere so i prefer that slightly.

dylan-shipwell avatar Mar 07 '24 19:03 dylan-shipwell

I also have a scenario where this feature would be very useful. We have some child modules that dynamically crates (or not) and manages (or not) several resources depending on the environment they are deployed. This is controlled in the root module by passing some flags. Some of these resources also need to be imported, and at the moment the only way to make this work is to have several complicated import blocks in the root module, where we use the for_each construct to replicate the flag-controlled configuration accordingly. If we could push the import in the child module, our configuration would be way simpler, easier to maintain and less prone to errors.

Of course, the ID of the imported resource would be an input variable for the child module, while at the moment import blocks only accept literal strings. I can imagine this being a challenge, but it should be doable to relax such constraint.

carloprone avatar Mar 20 '24 18:03 carloprone

Another use case I'd love to be covered. We have a module managing Azure network resources, due to how the azurerm provider implements some of its resources, and how we want to use Azure governance tooling, we cannot use the azurerm provider for a subset of these resources. To bypass this limitation I want to use the Microsoft provided azapi provider. Since our module is widely used internally I'm interested in doing the state operations within the module itself to avoid the operational overhead of having to do it manually for each root module.

If I could use both the removed and imported block within the module itself, I can use the resource ID from the already deployed Terraform object to import the new one.

Can you give any sort of time frame for when such a feature will be available, or if it will be?

nikolaifa avatar Apr 03 '24 05:04 nikolaifa

Any news on this?

oliverwiegers avatar Apr 08 '24 14:04 oliverwiegers

I have just hit this roadblock, too :-/. Come on, @teamterraform team, make it happen!

h2oearth avatar Apr 23 '24 11:04 h2oearth

Another use case I'd love to be covered. We have a module managing Azure network resources, due to how the azurerm provider implements some of its resources, and how we want to use Azure governance tooling, we cannot use the azurerm provider for a subset of these resources. To bypass this limitation I want to use the Microsoft provided azapi provider. Since our module is widely used internally I'm interested in doing the state operations within the module itself to avoid the operational overhead of having to do it manually for each root module.

If I could use both the removed and imported block within the module itself, I can use the resource ID from the already deployed Terraform object to import the new one.

Can you give any sort of time frame for when such a feature will be available, or if it will be?

Hey @nikolaifa , does the moved{} block help you here? Why is it not sufficient?

omarismail avatar May 06 '24 19:05 omarismail

@h2oearth @oliverwiegers we are actively exploring this problem, can you email me? [email protected]

omarismail avatar May 06 '24 19:05 omarismail

Using a module for AWS EC2 instances, wanted to refactor SG attachment from static to dynamic like following:

diff --git a/main.tf b/main.tf
index b5ee768..692436c 100644
--- a/main.tf
+++ b/main.tf
@@ -29,7 +29,6 @@ resource "aws_volume_attachment" "this" {
 
 resource "aws_network_interface" "this" {
   subnet_id         = var.subnet_id
-  security_groups   = var.security_group_ids
   private_ip        = var.private_ip
   source_dest_check = var.source_dest_check
 
@@ -38,6 +37,17 @@ resource "aws_network_interface" "this" {
       Name = "${local.hostname}-if"
     }
   )
+
+  lifecycle {
+    ignore_changes = [security_groups]
+  }
+}
+
+resource "aws_network_interface_sg_attachment" "this" {
+  count = length(var.security_group_ids)
+
+  security_group_id    = var.security_group_ids[count.index]
+  network_interface_id = aws_network_interface.this.id
 }

Something that could be as simple as

import {
  count = length(var.security_group_ids)
  to    = aws_network_interface_sg_attachment.this[count.index]
  id    = "${aws_network_interface.this.id}_${var.security_group_ids[count.index]}"
}

is currently impossible and I'd have to add this in every state that uses the module, changing references everywhere. PS! Not sure if count is allowed, but according to documentation, for_each is, so it would be possible to use that instead.

Blefish avatar May 10 '24 09:05 Blefish

Second. Here's where I'm struggling;

locals {
  service_linked_roles_file               = "${path.module}/service-linked-roles.json"
  service_linked_roles                    = jsondecode(file(local.service_linked_roles_file))
  service_linked_roles_service_principals = keys(local.service_linked_roles)

  service_linked_roles_to_provision = var.service_linked_roles_to_provision == ["*"] ? local.service_linked_roles_service_principals : var.service_linked_roles_to_provision
}

data "aws_caller_identity" "this" {}

resource "aws_iam_service_linked_role" "this" {
  for_each = toset(local.service_linked_roles_to_provision)

  aws_service_name = each.key
  tags             = var.tags

  lifecycle {
    ignore_changes = [
      # Description is auto-generated by AWS.
      description
    ]
  }
}

# Currently importing inside child modules is not supported: https://github.com/hashicorp/terraform/issues/33474
# import {
#   # Some of the service linked roles get automatically created by AWS when an account is created.
#   for_each = toset(local.service_linked_roles_to_provision)
#
#   to = aws_iam_service_linked_role.this[each.key]
#   id = "arn:aws:iam::${data.aws_caller_identity.this.account_id}:role/aws-service-role/${each.key}/${local.service_linked_roles[each.key]}"
# }

lijok avatar May 29 '24 16:05 lijok

We would really appreciate this feature !

We are using the Keycloak Terraform Provider and every Instance has its own Import. That would make them 100% seperated. Right now we need to build a workaround with templatefile-Function.

robson90 avatar Jun 06 '24 07:06 robson90

I had a simple case: I wanted to import aws_cloudwatch_log_group resource in my lambda module, but:

Import blocks are only allowed in the root module.

I call this module XXX times and i wanted to import all at once. I hope it would be implemented someday and it would not be like all other issues: https://github.com/hashicorp/terraform/issues/25534

YevhenLodovyi avatar Jun 07 '24 12:06 YevhenLodovyi

I also wanted to import a resource from security child module but its not possible.

findajay avatar Jul 11 '24 06:07 findajay

Also waiting for this feature as we have tens of thousands of resources to import using child modules.

cah-louis-verzi avatar Jul 18 '24 15:07 cah-louis-verzi

another use case: we want to refactor module A, and move some of it's resources into module B without having to add import blocks to the parent terraform code

markwellis avatar Jul 29 '24 14:07 markwellis

We're also facing an issue with this, especially in monorepo structures. If it doesn't work for "public" usages of Terraform, maybe it can be a configuration option?

Could be cool to be able to combine perhaps the import and moved blocks at the same time:

# root.tf
import {
  id = "MyTopic"
  to = google_pubsub_topic.my_topic
}

resource "google_pubsub_topic" "my_topic" {
  name = "MyTopic"
}

moved {
  from = google_pubsub_topic.my_topic
  to = module.xyz.google_pubsub_topic.my_topic
}
# xyz.tf
resource "google_pubsub_topic" "my_topic" {
  name = "MyTopic"
}

Alas, I think I'll have no way but to do 3 successive deployments to achieve this structure.

zackarydev avatar Sep 06 '24 17:09 zackarydev

Throwing my vote in here, this is blocking some refactoring we're trying to do.

kuzmik avatar Oct 02 '24 20:10 kuzmik