terraform: passing list through modules, getting "Error:... should be a list"

Scott picture Scott · Aug 31, 2018 · Viewed 7.2k times · Source

ETA: Found discussion in terraform github issues, https://github.com/hashicorp/terraform/issues/13103. Known and very... "idiosyncratic"... issue.


After passing a list through two other modules (from top.tf through autoscaling_group to launch_configuration), both of which define the variable as type = "list", I'm getting the following complaint from terraform plan:

"Error: module.autoscaling_group.aws_launch_configuration.this: 
security_groups: should be a list"

It seems to recognize the parameter as a list type (if I remove the type specification from the variable declaration, it complains). But the only way I find to silence the error is to wrap the final usage in list brackets. This seems counterintuitive (and I haven't seen what happens with apply when I do so) - what's going on?


The sequence:

  • top.tf calls security_group.
  • top.tf also calls autoscaling, passing a list with the security group id output.
  • autoscaling calls launch_configuration, passing the parameter through.

Here's the layout, contents of .tf files is below.

.
├── autoscaling_group
│   └── main.tf
├── launch_configuration
│   └── main.tf
├── security_group
│   └── main.tf
└── top.tf

The initiating call in top.tf is:

module "autoscaling_group" {
  source = "launch_configuration"
  security_groups = ["${module.security_group.id}"]
  }

The offending usage is in launch_configuration/main.tf:

resource "aws_launch_configuration" "this" {
  name_prefix = "foobar"
  image_id = "this_is_fake"
  instance_type = "ts.small"
  security_groups = "${var.security_groups}"
  }
  • If I add brackets: ["${var.security_groups}"] I don't get the error.
  • If I remove the type = "list" from the variable definition block in launch_configuration/main.tf, terraform plan complains: module.autoscaling_group.var.security_groups: variable security_groups in module autoscaling_group should be type string, got list

The content of top.tf:

provider "aws" {
  region = "us-east-1"
  }

module "security_group" {
  source = "security_group"
  }

module "autoscaling_group" {
  source = "launch_configuration"
  security_groups = ["${module.security_group.id}"]
  }

The content of autoscaling_group/main.tf:

variable "security_groups" {
  type = "list"
  description = "The security groups to attach to launched instances."
  }

module "launch_configuration" {
  source = "launch_configuration"
  security_groups = "${var.security_groups}"
  }

resource "aws_autoscaling_group" "service_autoscaling_group" {

  name_prefix = "foobar"

  min_size = 1
  max_size = 1
  health_check_type = "EC2"
  launch_configuration = "${module.launch_configuration.name}"
  vpc_zone_identifier = ["this_is_fake"]
  }

The content of launch_configuration/main.tf:

variable "security_groups" {
  type = "list"
  description = "The security groups to attach to launched instances."
  }

output "name" {
  value = "${aws_launch_configuration.this.name}"
  }

resource "aws_launch_configuration" "this" {
  name_prefix = "foobar"
  image_id = "this_is_fake"
  instance_type = "ts.small"
  security_groups = "${var.security_groups}"
  }

Again, if I wrap brackets around the RHS of the last assignment ["${var.security_groups}"], I don't see the error from terraform plan. But terraform recognizes the variable as a list (since it complains about it being a list type if I remove the type = "list" from the declaration). So why does it complain that it expects a list here?

The content of security_group/main.tf:

output "id" {
  value = "${aws_security_group.this.id}"
  description = "The security group ID."
  }

resource "aws_security_group" "this" {

  name_prefix = "foobar"
  vpc_id      = "this_is_fake"
  description = "foobar"

  egress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks     = ["0.0.0.0/0"]
    }
  }

Answer

BaptisteM picture BaptisteM · Apr 23, 2019

I had the same issue, it should be resolved in the v0.12 : https://github.com/hashicorp/terraform/issues/18923#issuecomment-434901762

If you want to pass a list as module parameter here is a small hack to do it. I just send a string and then parse it to a list (Example with AWS Backup module) :

terraform.tf

module "your_module" {
  source = "./module_path"

  list_as_string = "${var.element_1};${var.element_1}"
}

./module_path/variables.tf

variable "list_as_string" {
  description = "List in string"
}

./module_path/main.tf

resource "resource" "exemple" {
  real_list = ["${split(";", var.list_as_string)}"]
}