-
Notifications
You must be signed in to change notification settings - Fork 9.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Child-module-specific Provider Configuration #15762
Comments
We are running into this exact issue, using scenario 1 (passing a region variable into the module). While digging into it, I noticed that the terraform.tfstate contains the provider as a variable, not a static value: "aws_eip.nat_eip.1": {
"type": "aws_eip",
"depends_on": [],
"primary": {
"id": "eipalloc-98765432",
"attributes": {
"association_id": "",
"domain": "vpc",
"id": "eipalloc-98765432",
"instance": "",
"network_interface": "",
"private_ip": "",
"public_ip": "34.45.56.67",
"vpc": "true"
},
"meta": {},
"tainted": false
},
"deposed": [],
"provider": "aws.${var.target_region}"
},
If these variables were resolved and saved in the state file (or if there was a region section in the provider definition in state), the removal of the module doesn't require the user to enter a region (and with more complex configurations, if multiple modules across regions are removed simultaneously, it avoids the corrupted state file that mismatches the AWS configuration). The only caveat to this is that it requires a hybrid of scenario 1 AND scenario 2, above... # These providers are used when the module is destroyed by removing it.
# Since the state file would have a static value (aws.us-east-1,
# not aws.${var.target_region}), there is no need to specify a region,
# since the state KNOWS where the resources are deployed (which it
# SHOULD know anyway, right?)
provider "aws" {
region = "us-west-2"
alias = "us-west-2"
}
provider "aws" {
region = "us-east-1"
alias = "us-east-1"
}
module "child-usw2" {
source = "./child"
target_region = "us-east-2"
}
module "child-use1" {
source = "./child"
target_region = "us-east-1"
} ...and in the ./child module: variable "target_region" {
}
# This provider is configured to allow for re-usability of the module across multiple regions
provider "aws" {
region = "${var.target_region}"
}
resource "aws_vpc" "default" {
provider = "aws.${var.target_region}" <--- Resolve this before inserting into terraform.tfstate!
cidr = "10.0.0.0/16"
...
}
To test this, you can setup the config above with a simple resource, create it, edit the terraform.tfstate and replace the aws.${var.target_region} with aws.us-east-1 for child-use1 and aws.us-west-2 for child-usw2. Then, delete/comment out the child-use1 or usw2 module (or both) and everything cleans up nicely without a prompt. It seems to me that the state file should have a static knowledge of where resources exist in the infrastructure - there are no other places in the terraform.tfstate that I've seen or been able to create that store an embedded variable, which seems like the heart of this issue. |
Beating my head against this issue as well while trying to create AWS Config recorders in every region using v0.9.11. I'm defining alias and provider in the module definition:
Then passing the region variable when instantiating teh module:
Creation works, but removing module instantiation code wouldn't destroy the resources. I played around with manual state changes, like @badmojo76 suggested, and I agree that interpolating the variables used in specifying resource provider in the state seem to solve the problem. |
+1 for having static-via-interpolation provider regions in the state file |
Hi all! Thanks for sharing your configs and use-cases. The Terraform team is currently planning a collection of changes to the handling of modules and, amongst other things, their interactions with providers. Since there are many somewhat-related issues here that all affect similar portions of the code, we're approaching it holistically to try to address multiple issues together in a cohesive way. This planning is still in the early stages but we'll share more details when we have it. These real-world examples are very useful to inform this process! |
@apparentlymart Great to hear, looking forward to updates here. Here's our very simple example, where the
|
I think this issue is somewhat related to hashicorp/terraform-provider-aws#712, since we need to create some resources in a specific region no matter what is the region of the provider. |
Hi All, Not sure if this would help and probably over simplifying it but here's an idea I had: Inside the module have some defined variable say module.provider that can per interpolated in the provider string. If no provider string is provided module.provider would be equal to the default provider. Thanks, |
Hi all! It's been a while since I posted here so I just wanted to share an update. Over the last couple months we've prototyped a few different approaches to this problem and we think we've settled on a good approach to move forward with. Providers are an unusual object in Terraform in two ways:
With the above two constraints in mind, we decided to optimize for the approach of having all of the In the simple case with only default (unaliased) providers, the behavior will be the same as before: child modules can just implicitly use the provider configuration from their parent without any explicit declaration. For more complex scenarios involving multiple instances of the same provider, a new ### DESIGN SKETCH: may change before release ###
# Default (unaliased) provider is used for most purposes
provider "aws" {
region = "us-west-1"
}
# Alternative (aliased) aws provider instance is used for our child module
provider "aws" {
region = "us-east-1"
alias = "use1"
}
module "example" {
source = "./example"
providers = {
"aws" = "aws.use1"
}
} With the above configuration, any This design also allows passing in aliased providers to the child module, for situations where a module needs to work with more than one instance: ### DESIGN SKETCH: may change before release ###
provider "aws" {
region = "us-west-1"
alias = "usw1"
}
provider "aws" {
region = "us-east-1"
alias = "use1"
}
module "network_tunnel" {
source = "./network-tunnel"
providers = {
"aws.src" = "aws.usw1"
"aws.dst" = "aws.use1"
}
} With the We're still working through some of the finer details of this, since it requires some re-organization of how Terraform deals with provider configurations internally, but we're hoping to be able to ship this new approach soon. |
Great news, thank you for the update! |
Hi, A minor clarification from the last update: In order for modules to use a specific provider, they will need to have an unconfigured provider block to be named in the For example, the "network_tunnel" module above would have two blocks like so:
|
The new configuration feature from my earlier comment -- with the later modification noted by @jbardin -- is now merged into master and coming as part of Terraform 0.11.0. This functionality is available in 0.11.0-beta1 for testing in experimental configurations. There is a bugfix around module destruction coming in 0.11.0-rc1 but otherwise we believe that the functionality in the beta should be complete. Please see the upgrade guide linked from the changelog for more information, along with the current documentation (which will appear on the website once we reach 0.11.0 final). I'm going to close this issue now. If anyone tries the new functionality and find bugs or difficulties with it, please open a new top-level issue so that we can track each problem separately. This more-general issue will no longer be monitored. Thanks for your patience here, everyone! Hopefully this new approach makes the interactions between providers and modules more intuitive and convenient. |
Consider the following root module:
...and the following
./child
:When this configuration is applied, it will (as expected) create one EC2 instance in the us-west-2 region and another in the us-east-1 region, and record both of these in state.
If the user then removes the
module.child-use1
block from the root module and re-plans, we run into a problem: Terraform no longer has theprovider "aws"
block from that module, so there's not enough information to destroymodule.child-use1.aws_instance.example
. In this case, Terraform will fail because theregion
argument is required for theaws
provider.Working around this currently requires some awkward steps:
terraform destroy -target=module.child-use1
terraform plan
should produce an empty plan, since the resources were already destroyedConfronted with this issue, some users then try to hoist the provider declarations up to the root:
and then in the child module:
This fails, because Terraform does not permit dynamically-populating the provider pseudo-argument in this way. (It can't, because that value is needed for graph construction.)
Being able to instantiate the same module multiple times with different provider settings is useful in a number of situations, including the above example of AWS regions. It'd be good to find a different config formulation that avoids the usability problems in the first example without creating the chicken-and-egg problems that'd result from supporting the latter.
Some related issues:
The text was updated successfully, but these errors were encountered: