0.9.11
module "edge" {
source = "./modules/function"
# ...
}
variable "environment" {
type = "map"
default = {}
}
resource "aws_lambda_function" "main" {
function_name = "hello-world"
# ...
environment = {
variables = "${var.environment}"
}
}
It should allow an empty object for the function environment variables, specifically because Lambda@Edge doesn't allow environment variables. Breaks all the function modules that want to be repurposed for edge functions.
It doesn't allow the empty object. If I try to go one step up as seen below, it produces a different error:
resource "aws_lambda_function" "main" {
function_name = "hello-world"
# ...
environment = "${var.environment}"
}
Obviously environment
is supposed to be a map, but it seems to expect a list. I can't find a workaround to providing a default empty object for the environment, aside from creating a seperate module specifically for edge functions, which would omit environment
entirely.
Also, according to the docs, aws_lambda_function.environment.variables
is optional. But if you omit it, you get the At least one field is expected inside environment
error, making it required.
I can also confirm this issue on my end and know of no workarounds to the problem.
yes, this is problematic in my case as well
I have a lambda module and I pass environment variables to it only if I need to
Terraform v. 0.10.7
Is there any developments on this?
The configuration language of Terraform (HCL) currently has some limitations when it comes to typing and lacks the ability to pass an "empty"/null
value. The good news is that this is likely being addressed in the next major version of Terraform: https://www.hashicorp.com/blog/terraform-0-1-2-preview
In this situation, the following configuration will be available:
# Potential Terraform 0.12 configuration - implementation may change during development
resource "aws_lambda_function" "main" {
# ... other configuration ...
environment = var.environment
}
When var.environment
is assigned to a new, special null
value, this will trigger the provider to see the resource configuration as:
# Potential Terraform 0.12 equivalent after processing null value for environment argument
resource "aws_lambda_function" "main" {
# ... other configuration ...
}
There are some upstream issues in Terraform core for tracking this feature prior to its release:
For what it's worth we found a workaround for this.
We created a local variable that checks the length of our environment_variables map.
locals {
enable_environment_variables = "${length(var.environment_variables) > 0 ? "true" : "false"}"
}
We then created two resources that checked the enable_environment_variables
flag. This will either create the lambda with or without environment variables.
# -------------------------------------------------------------------------------------
# Create Lambda Function with Environment Variables
# -------------------------------------------------------------------------------------
resource "aws_lambda_function" "lambda_function" {
count = "${local.enable_environment_variables == "true" ? 1 : 0}"
function_name = "${var.name}"
...
environment {
variables = "${var.environment_variables}"
}
}
# -------------------------------------------------------------------------------------
# Create Lambda Function with NO Environment Variables
# -------------------------------------------------------------------------------------
resource "aws_lambda_function" "lambda_function_no_environment_variables" {
count = "${local.enable_environment_variables == "false" ? 1 : 0}"
function_name = "${var.name}"
...
}
I had the same problem and I solved it by passing a conditional:
count = "$ {var.has_environment ? 1: 0}"
and I add the variable:
variable "has_environment" {
type = "string"
description = "true or false"
default = "true"
}
In Terraform 12 you can use NULL as the value.
IE:
resource "aws_lambda_function" "lambda_handler" {
...
environment {
variables = var.environment
}
}
where the default value of var.environment is null
My solution actually doesn't work. It only works the first time. The 2nd time it is run, terraform will try and add
"+ environment {}"
Which will fail with the same error.
Hi folks 👋 It seems my note above was only partially correct, however I can confirm this is resolved in Terraform 0.12, which supports new functionality in the configuration language aimed at solving this issue. The new dynamic
block syntax can be used to dynamically generate configuration blocks and their arguments. These can be combined with the new null
value, which can be used to omit arguments as if they were not defined in the configuration at all.
Given this configuration:
terraform {
required_providers {
aws = "2.20.0"
}
required_version = "0.12.5"
}
provider "aws" {
region = "us-east-1"
}
variable "test1" {
type = list(object({
variables = map(string)
}))
default = []
}
variable "test2" {
type = list(object({
variables = map(string)
}))
default = [{
variables = {
key1 = "value1"
key2 = "value2"
}
}]
}
resource "aws_iam_role" "test" {
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Principal = {
Service = "lambda.amazonaws.com"
}
Effect = "Allow"
Sid = ""
}]
})
}
resource "aws_iam_role_policy_attachment" "test" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
role = aws_iam_role.test.name
}
resource "aws_lambda_function" "test1" {
depends_on = [aws_iam_role_policy_attachment.test]
filename = "test.zip"
function_name = "test1"
role = aws_iam_role.test.arn
handler = "exports.example"
runtime = "nodejs8.10"
dynamic "environment" {
for_each = var.test1
content {
variables = environment.value.variables
}
}
}
resource "aws_lambda_function" "test2" {
depends_on = [aws_iam_role_policy_attachment.test]
filename = "test.zip"
function_name = "test2"
role = aws_iam_role.test.arn
handler = "exports.example"
runtime = "nodejs8.10"
dynamic "environment" {
for_each = var.test2
content {
variables = environment.value.variables
}
}
}
Produces the following apply output:
$ terraform apply
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_iam_role.test will be created
+ resource "aws_iam_role" "test" {
+ arn = (known after apply)
+ assume_role_policy = jsonencode(
{
+ Statement = [
+ {
+ Action = "sts:AssumeRole"
+ Effect = "Allow"
+ Principal = {
+ Service = "lambda.amazonaws.com"
}
+ Sid = ""
},
]
+ Version = "2012-10-17"
}
)
+ create_date = (known after apply)
+ force_detach_policies = false
+ id = (known after apply)
+ max_session_duration = 3600
+ name = (known after apply)
+ path = "/"
+ unique_id = (known after apply)
}
# aws_iam_role_policy_attachment.test will be created
+ resource "aws_iam_role_policy_attachment" "test" {
+ id = (known after apply)
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
+ role = (known after apply)
}
# aws_lambda_function.test1 will be created
+ resource "aws_lambda_function" "test1" {
+ arn = (known after apply)
+ filename = "test.zip"
+ function_name = "test1"
+ handler = "exports.example"
+ id = (known after apply)
+ invoke_arn = (known after apply)
+ last_modified = (known after apply)
+ memory_size = 128
+ publish = false
+ qualified_arn = (known after apply)
+ reserved_concurrent_executions = -1
+ role = (known after apply)
+ runtime = "nodejs8.10"
+ source_code_hash = (known after apply)
+ source_code_size = (known after apply)
+ timeout = 3
+ version = (known after apply)
+ tracing_config {
+ mode = (known after apply)
}
}
# aws_lambda_function.test2 will be created
+ resource "aws_lambda_function" "test2" {
+ arn = (known after apply)
+ filename = "test.zip"
+ function_name = "test2"
+ handler = "exports.example"
+ id = (known after apply)
+ invoke_arn = (known after apply)
+ last_modified = (known after apply)
+ memory_size = 128
+ publish = false
+ qualified_arn = (known after apply)
+ reserved_concurrent_executions = -1
+ role = (known after apply)
+ runtime = "nodejs8.10"
+ source_code_hash = (known after apply)
+ source_code_size = (known after apply)
+ timeout = 3
+ version = (known after apply)
+ environment {
+ variables = {
+ "key1" = "value1"
+ "key2" = "value2"
}
}
+ tracing_config {
+ mode = (known after apply)
}
}
Plan: 4 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_iam_role.test: Creating...
aws_iam_role.test: Creation complete after 0s [id=terraform-20190724062422187100000001]
aws_iam_role_policy_attachment.test: Creating...
aws_iam_role_policy_attachment.test: Creation complete after 0s [id=terraform-20190724062422187100000001-20190724062422605200000002]
aws_lambda_function.test2: Creating...
aws_lambda_function.test1: Creating...
aws_lambda_function.test2: Still creating... [10s elapsed]
aws_lambda_function.test1: Still creating... [10s elapsed]
aws_lambda_function.test1: Creation complete after 16s [id=test1]
aws_lambda_function.test2: Creation complete after 17s [id=test2]
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
There may be ways to simplify this setup, but from a functionality standpoint, the relevant pieces are in place. Enjoy! 🚀
Building on @bflad's example I'm using the following in a module which avoids the weird array of maps of maps variable.
variable "environment_vars" {
type = map(string)
default = null
}
locals {
environment_map = var.environment_vars == null ? [] : [var.environment_vars]
}
resource "aws_lambda_function" "lambda" {
# ... omitted
dynamic "environment" {
for_each = local.environment_map
content {
variables = environment.value
}
}
}
Snippet from @blefevre didn't work for me - a slight modification was needed.
variable "environment_vars" {
type = map(string)
default = null
}
locals {
environment_map = var.environment_vars[*]
}
resource "aws_lambda_function" "lambda" {
# ... omitted
dynamic "environment" {
for_each = local.environment_map
content {
variables = environment.value
}
}
}
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 feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. Thanks!
Most helpful comment
yes, this is problematic in my case as well
I have a lambda module and I pass environment variables to it only if I need to
Terraform v. 0.10.7