hashicorp/terraform

aws_lambda_function cannot pass list or map variable as argument

dayglojesus opened this issue ยท 9 comments

Terraform Version

0.7.13

Affected Resource(s)

aws_lambda_function and resources that demand list of maps

Terraform Configuration Files

variable "my_vpc_config" {
    type = "list"
    default = [{
      subnet_ids          = ["subnet-d34db337", "subnet-d34db338"],
      security_group_ids  = ["sg-d34db337"]
    }]
    description = "Lambda VPC config settings"
}

resource "aws_lambda_function" "lambda_function" {
    filename            = "foo.zip"
    function_name       = "foo"
    description         = "FOO Lambda"
    role                = "arn:aws:iam::123456789012:role/SomeRole"
    handler             = "foo.handler"
    vpc_config          = "${var.my_vpc_config}"
}

Debug Output

N/A

Expected Behavior

  • Terraform should configure this Lambda scoping access to the VPC and SG.
  • Terraform expects a list to be passed to vpc_config.

Actual Behavior

terraform plan produces the following error:

  lambda_function.lambda_function: vpc_config.0: expected object, got invalid
    * aws_lambda_function.lambda_function: vpc_config.0: expected object, got invalid

The maps inside the enforced list type cannot be validated.

More information

There appear to be two issues here:

1. Terraform is expecting vpc_config to be type 'List'

I am not certain this is correct behaviour.

There is no need for this var type to be list -- it should be map afaict.

For example, the following works:

resource "aws_lambda_function" "lambda_function" {
    filename            = "foo.zip"
    function_name       = "foo"
    description         = "FOO Lambda"
    role                = "arn:aws:iam::123456789012:role/SomeRole"
    handler             = "foo.handler"
    vpc_config          = {
      subnet_ids          = ["subnet-d34db337", "subnet-d34db338"],
      security_group_ids  = ["sg-d34db337"]
    }
}

No list, just a map and terraform is a happy camper.

Also, placing said map inside a list works as well...

resource "aws_lambda_function" "lambda_function" {
    filename            = "foo.zip"
    function_name       = "foo"
    description         = "FOO Lambda"
    role                = "arn:aws:iam::123456789012:role/SomeRole"
    handler             = "foo.handler"
    vpc_config          = [{
      subnet_ids          = ["subnet-d34db337", "subnet-d34db338"],
      security_group_ids  = ["sg-d34db337"]
    }]
}

Though (again) I am not sure this is correct behaviour because (I think) the AWS API expects a map.

2. Terraform does not parse static lists of maps

This appears to be a fairly serious defect -- there are already several reports of this behaviour.

Related Issues:

Dup of #7705 most likely.

Terraform simply doesn't support any sort of nested lists/maps at the moment. We should just make better error messages for this...

@mitchellh Probable duplicate on the "list of maps" front, but this doesn't address type checking on vcp_config argument to the aws_lambda_function resource. As reported, attaching a simple map object to this argument works. Is this not evidence of a deeper issue?

Ill take a look, but foo = [{}] and foo = {} is identical in Terraform. The latter is always turned into the former and is just syntactic sugar, so not too concerning.

I also ran into this issue when I try to pass a map variable to aws_lambda_function's environment parameter.

Tested Env: Terraform 0.8.2

Example 1:

# file: lambda.tf
resource "aws_lambda_function" "test" {
  ...
  environment = "${var.lambda_env}"
}
# file: variables.tf
...
variable "lambda_env" {
  type = "map"
  default = {
    variables = {
      foo = "bar"
    }
  }
}

The terraform plan command will output environment: should be a list.
Then, I changed the assignment syntax as example 2.

Example 2:

# file: lambda.tf
resource "aws_lambda_function" "test" {
  ...
  environment = ["${var.lambda_env}"]
}

Thus, it will saying environment.0: expected object, got string.
For me, this special case only works on assigned a map value directly.
which is quite inconvenient to modularize.
hope this issue can be solved. thanks.

@kevintsai any workaround?

@Puneeth-n
The way I use and it works to me, please ref as following:

# file: lambda.tf
resource "aws_lambda_function" "test" {
  ...
  environment = {
    variables = "${var.lambda_env}"
  }
}
# file: variables.tf
...
variable "lambda_env" {
  type = "map"
  default = {
      foo = "bar"
      user = "Alice"
  }
}

I think we may don't need to use this non-intuitive solution if Terraform can resolve the root issue (#7705).

b-luu commented

this helps!!
Thx @aurynn !!

I also had success with @kevintsai 's approach. One obvious thing worth mentioning if you're using modules is the passing syntax. You need to pass the variable all the way through the chain as type map even if you don't specify a default in the module.

File: /lambda/main.tf

resource "aws_lambda_function" "test" {
  ...
  environment = {
    variables = "${var.lambda_env}"
  }
}

File: /lambda/variables.tf

variable "lambda_env" {
  # type is crucial here, otherwise defaults to 'string' and throws "type string, got map" error
  type = "map"
}

File: /main.tf

module "lambda" {
  source = "./lambda"
  ...
  lambda_env = "${var.lambda_env_vars}"
}

File: /variables.tf

variable "lambda_env_vars" {
  type = "map"
  default = {
    foo = "bar"
  }
}

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.