Skip to content
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

Passing a variable of type "list" compose with the output from a module to another module #13103

Closed
mario21ic opened this issue Mar 27, 2017 · 20 comments
Milestone

Comments

@mario21ic
Copy link

mario21ic commented Mar 27, 2017

Hi there, I have a problem passing variable type list from a module to another module, it is compose with the output from another module.
Example:
./tfmodules/vpc/main.tf

...
output "public_subnet_1_id" {
  value = "${aws_subnet.sn_public_1.id}"
}
output "private_subnet_2_id" {
  value = "${aws_subnet.sn_private_2.id}"
}
...

./tfmodules/elb/main.tf

...
resource "aws_elb" "my_elb" {
  name               = "myelb"
  # Public subnets
  subnets         = "${var.public_subnets}"
}
variable "public_subnets" {
  type        = "list"
  description = "Subnets available"
}
...

./main.tf

module "elb" {
  source = "./tfmodules/elb/"
  public_subnets = ["${module.vpc.public_subnet_1_id}", "${module.vpc.public_subnet_2_id}"]
}

I have got the follow error:

$ terraform plan
1 error(s) occurred:
* module.elb.aws_elb.my_elb: subnets: should be a list

But is worked if I put with this way:
./main.tf

module "elb" {
  source = "./tfmodules/elb/"
  public_subnets = ["subnet-ab978fcf", "subnet-ecabb39a"]
}

Terraform Version

v0.9.1

Affected Resource(s)

  • aws_elb
  • aws_vpc

Terraform Configuration Files

See above

Debug Output

See above

Expected Behavior

Catch the two values to compose the variable of type list

Actual Behavior

module.elb.aws_elb.my_elb: subnets: should be a list

Steps to Reproduce

See above

@mario21ic mario21ic changed the title Passing a type list compose with the output from a module to another module Passing a variable of type "list" compose with the output from a module to another module Mar 27, 2017
@grubernaut
Copy link
Contributor

Hi @mario21ic, thanks for the issue!

If you use the interpolation function list() do you still get the parse error?

module "elb" {
  source = "./tfmodules/elb/"
  public_subnets = "${list("${module.vpc.public_subnet_1_id}", "${module.vpc.public_subnet_2_id}")}"
}

@mario21ic
Copy link
Author

mario21ic commented Mar 28, 2017

Hello @grubernaut, thanks for the answer, but, yes I still giving the same error, I followed the guide https://www.terraform.io/docs/configuration/interpolation.html

@grubernaut
Copy link
Contributor

Using list() allows TF to parse for me locally with TF 0.9.2.

$ cat main.tf
variable "foo" { default = "bar" }
variable "bar" { default = "foo" }

module "mod" {
  source = "./mod"
  lists = "${list("${var.foo}", "${var.bar}")}"
}
output "foofoo" { value = "${module.mod.foo}"}
$ cat mod/main.tf
variable "lists" {
  type = "list"
  description = "list var"
}

output "foo" { value = "${var.lists}" }
$ terraform apply 

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

foofoo = [
    bar,
    foo
]

I'll try setting the input variables to the list() interpolation function next and see if I can reproduce.

@mario21ic
Copy link
Author

Hello @grubernaut , thanks for the attention, the case is uploaded in https://github.com/mario21ic/terraform-aws-vpc-elb/blob/master/main.tf#L32

@phinze
Copy link
Contributor

phinze commented May 8, 2017

Hey folks - we ran into this same issue in one of our configs.

I cooked up a minimal repro case here before I found this issue - it looks very similar to @mario21ic's description:

https://github.com/phinze/terraform-issues/tree/master/should-be-a-list

In my experimentation it seems that the key bit is that the output of the module generating the list needs to be computed. See the README in the link for detailed repro steps.

@grubernaut grubernaut added the bug label May 8, 2017
ralavay added a commit to moneysmartco/tf-aws-elasticache that referenced this issue Sep 26, 2017
@k1rk
Copy link

k1rk commented Oct 27, 2017

@mario21ic try to add [] in resource
./tfmodules/elb/main.tf

resource "aws_elb" "my_elb" {
  name               = "myelb"
  # Public subnets
  subnets         = ["${var.public_subnets}"]
}

@renaudhager
Copy link

renaudhager commented Oct 29, 2017

Hi :-)

Any updates about this issue ?
I have the following code :

data "terraform_remote_state" "vpc_rs" {
    backend = "s3"
    config {
        bucket = "${var.vpc_rs_bucket}"
        key    = "${var.vpc_rs_key}"
        region = "${var.region}"
    }
}

module "asg" {
  source = "../modules/asg/"

  owner = "${var.owner}"
  lc_name = "test"
  lc_ssh_key_name = "${var.ssh_key_name}"
  asg_name = "test asg"
  asg_max_size = "1"
  asg_min_size = "1"
  asg_desired_capacity = "1"
  asg_vpc_zone_identifier = ["${split( ",", data.terraform_remote_state.vpc_rs.private_subnet)}"]
}

output "test_list" {
  value = "${split( ",", data.terraform_remote_state.vpc_rs.private_subnet)}"
}

During a plan or a validate I got the following error :

terraform  validate
There are warnings and/or errors related to your configuration. Please
fix these before continuing.

Errors:

  * module.asg.aws_autoscaling_group.mod_asg: vpc_zone_identifier: should be a list

If I comment the module bit I got following in the output :

terraform apply
data.terraform_remote_state.vpc_rs: Refreshing state...

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

region = eu-west-1
test_list = [
    subnet-f1c38ab8,
    subnet-ba69eae1,
    subnet-741f5913
]

Did I do something wrong ?

Edit : Actually I did, it's now working. Sorry for the noise.

@samof76
Copy link

samof76 commented Nov 9, 2017

@renaudhager what did you do wrong that code? I am getting a similar issue!!!

@ayarmoluk17
Copy link

ayarmoluk17 commented Nov 14, 2017

@samof76 I've fixed similar issue. When you pass this list to resource inside module you have to wrap it in square brackets again, eg:

# module/main.tf
variable "list_sg" {
  type = "list"
}
varaible "list_subnets" {
  type = "list"
}
resource "aws_smthing" "example" {
  ...
  security_group_ids = ["${var.list_sg}"] # won't work without brackets
  subnet_ids = ["${var.list_subnets}"]
  ...
}

# example.tf
module "example" {
  source "./module"
  list_sg = [
    "${data.data.terraform_remote_state.security_group_1}",
    "${data.data.terraform_remote_state.security_group_2}"
  ]
  list_subnets = ["${split(",",data.terraform_remote_state.subnets)}"]
}

@fiducioso-elias
Copy link

I can confirm that the workaround suggested by @ayarmoluk17 works as expected (with terraform 0.11.0). Is this the idiomatic solution, or a workaround based on another bug, or something in between? Will the code like that will continue to work after this bug (or some other bug) is fixed?

@EAnushan
Copy link

Having the same issue. Seems to stem from constructing a list from the outputs of another module.

@domdere
Copy link

domdere commented Jan 10, 2018

I have encountered this issue (unsurprisingly when trying to pass around lists of subnets also)

I was using version 0.11.2.

While the workaround put forward by @ayarmoluk17 seems to "work" its got a bit of an annoying caveat.

For example, when I use it in an ASG:

resource "aws_autoscaling_group" "foo_asg" {
  ...
  
  vpc_zone_identifier = ["${var.subnets}"]

  ...
}

I get something like this in the plan output:

+ module.ec2.aws_autoscaling_group.foo_asg
      ...
      vpc_zone_identifier.#:               "1"
      vpc_zone_identifier.620245603:       "subnet1,subnet2,subnet3"
      ...

As you can see Terraform has produced a list with one element, that element being the comma separated list as a string.

This ends up working well and good, it looks like somewhere (I'm guessing on the AWS end) it gets "corrected".

The problem is when you then run the next plan/apply:

Terraform will perform the following actions:

  ~ module.ec2.aws_autoscaling_group.foo_asg
      ...
      vpc_zone_identifier.#:          "3" => "1"
      vpc_zone_identifier.2085961902: "subnet1" => ""
      vpc_zone_identifier.2697889770: "subnet2" => ""
      vpc_zone_identifier.620245603:  "" => "subnet1,subnet2,subnet3"
      vpc_zone_identifier.649183968:  "subnet3" => ""
      ...

This will happen every time plan/apply is called even though there is really no change.

Other than the niggle that there are changes and (identity) actions that are taken each time
when there are no changes, this causes an actual blocker when this list of subnets is used in
something like an ALB:

resource "aws_alb" "foo-alb" {
  ...
  subnets = ["${var.subnets}"]
  ...
}

Again the plan for this looks like the following:

+ module.ec2.aws_alb.foo-alb
  ...
      subnets.#:                           "1"
      subnets.620245603:                   "subnet1,subnet2,subnet3"
  ...

Unfortunately while it was workable in the ASG case, attempting to apply this plan results in the following error:

* aws_alb.foo-alb: Error creating Application Load Balancer: ValidationError: At least two subnets in two different Availability Zones must be specified
	status code: 400, request id: ****

I might also just point out that the 3 subnets were indeed in different availability zones, so it should have worked.
I believe its complaining because the list submitted in the request only had 1 element.

To work around this issue, I thought "oh, its interpreting the list as a comma separated string, so hopefully I just need to replace var.subnets with
split(",", var.subnets). Unfortunately this did not work, it had a runtime type error because the second input to split is required to be a string
while var.subnets is of type list. At this point I'm actually getting confused which it is myself.

Anyway what did eventually work for me was ["${split(",", join(",", var.subnets))}"]. So I had to turn the list into a comma separated string,
then I had to split it back out again.

So:

ASG:

resource "aws_autoscaling_group" "foo_asg" {
  ...
  vpc_zone_identifier = ["${split(",", join(",", var.subnets))}"]
  ...
}

ALB:

resource "aws_alb" "foo-alb" {
  ...
  subnets = ["${split(",", join(",", var.subnets))}"]
  ...
}

Both have the desired plans:

+ module.ec2.aws_autoscaling_group.foo_asg
      ...
      vpc_zone_identifier.#:               "3"
      vpc_zone_identifier.2085961902:      "subnet1"
      vpc_zone_identifier.2697889770:      "subnet2"
      vpc_zone_identifier.649183968:       "subnet3"
      ...

+ module.ec2.aws_alb.foo-alb
      ...
      subnets.#:                           "3"
      subnets.2085961902:                  "subnet1"
      subnets.2697889770:                  "subnet2"
      subnets.649183968:                   "subnet3"
      ...

And applying the plan works, the ALB is successfully created.

The join and then split is obviously not ideal. I spose I could join the list before i pass it into
the module, and split it inside the module to make it look (only slightly) prettier, but then again I'd rather have more ugliness localised together
to make the refactoring easier when the problem is eventually fixed.

@clangaxon
Copy link

We have a similar issue with a slight variation (?) from above:

  • Output variable is a string.
  • Module requires a list.
  • Passing in [ "${module.name.outputvar}" ] throws "should be a list"
  • Passing in a normal string in brackets [ "mystring" ] works just fine.
  • Changing the output variable to a list doesn't work either - which is surprising.
  • Adding brackets to the reference inside the destination module appears to work, but that's nasty and we're concerned about side effects as mentioned above.

@appelgriebsch
Copy link

Faced the same issue when trying to pass around list of objects (maps) such as those for the connection_string property on the Azure App Service:

locals {
  db_connectionstring = {
    name  = "DB"
    type  = "SQLAzure"
    value = "${format("%s;Initial Catalog=%s", module.data-common.sqlserver-connectionstring, module.db.instance["name"])}"
  }
}

and later:

module "appservice" {
  source = "../modules/appservices"
  ...
  connections = "${list(local.db_connectionstring)}"
}

always gives the error: connection_string should be a list

When trying the workaround with the brackets in the referenced App Service module it will show an error that the properties are not properly set for the connection_string input object (connection_string.0.name, connection_string.0.type, connection_string.0.value).

So looks like the workaround is not valid for list of objects...

@apparentlymart
Copy link
Contributor

apparentlymart commented Sep 13, 2018

Hi all,

Although we've not directly verified this one yet, I strongly suspect it will be fixed by the improved language interpreter coming in the next major release. Although it's not directly one of the line items in that article, the changes made to support things like rich types in module inputs and outputs have improved the robustness of how Terraform thinks about complex types (types constructed from other types, like lists), including allowing it to represent "unknown-ness" without losing track of the fact that the value is a list, which is the root cause of the bug here.

I've updated the labels here to remind us to look at this issue again when we're doing pre-release testing.

@whytoe
Copy link

whytoe commented Sep 17, 2018

@appelgriebsch I have the Connection string values working in Azure using Variables

connection_string       = [{
  name  = "ConnectionString"
  type  = "SQLAzure"
  value = "Data Source=tcp:${var.sql_server_name}.database.windows.net,1433;Initial Catalog=${var.mssql_database_name};User ID=${var.sql_server_login_name};Password=${var.sql_server_admin_password}"
}]

@appelgriebsch
Copy link

@whytoe yes, that will still work and I use this way for scenarios I have the required information for the connection string in my var-file anyway... but for connection strings to e.g. CosmosDB, ServiceBus, Storage Account, etcpp. I will need access keys that are not available upfront and so I have to refer to output from another tf module...

@whytoe
Copy link

whytoe commented Sep 18, 2018

@appelgriebsch ya for sure, I just saw the app_service example and thought I would try and help you out in the interim

@apparentlymart
Copy link
Contributor

Hi all! Sorry for the long silence here.

This issue is covering the same root cause as #12263, which I've closed today since it appears to be fixed in v0.12.0-alpha1. We do still have issue #19140 open related to the problem of updating the common workaround of wrapping an extra level of list brackets around the expression, which we'll aim to address separately before v0.12.0 final.

@apparentlymart apparentlymart added this to the v0.12.0 milestone Oct 29, 2018
@ghost
Copy link

ghost commented Apr 1, 2020

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@ghost ghost locked and limited conversation to collaborators Apr 1, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests