I posted this issuse yesterday in here: Cannot use `count` with a default value of variables have type `object` or `list of objects` · Issue #32689 · hashicorp/terraform · GitHub .
And I knew how to do with list of object
but with object
I’m trying like below:
resource "aws_lambda_permission" "lambda_permission_cloudwatch_log" {
for_each = var.lambda_function_cloudwatch_log
# I use count also but it doesn't work:
# count = length(var.lambda_function_cloudwatch_log) > 0 ? 1 : 0
statement_id = "AllowExecutionFromCloudWatchLogs"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.lambda_function.arn
principal = "logs.${var.region}.amazonaws.com"
}
variable "lambda_function_cloudwatch_log" {
description = "Provide the Name and ARN of the CloudWatch log group you want to trigger to the Lambda Function"
default = {}
type = object(
{
log_subscription_filter_name = optional(string, null)
log_group_name = optional(string, null)
log_subscription_filter_pattern = optional(string, null)
}
)
}
module "lambda_codepipeline_notification" {
source = "xxx"
#basic
project = var.project
env = var.env
region = var.region
service = "codepipeline"
#lambda-zip
lambda_zip_python = {
code_path = "../../../../terraform-dependencies/lambda-function/codepipeline-notification"
code_zip_name = "codepipeline_notification"
code_zip_path = "../../../../terraform-dependencies/lambda-function/codepipeline-notification"
}
#lambda
lambda_function = {
name = "codepipeline"
role = module.iam_role_lambda_codepipeline_notification.iam_role_arn
runtime = "python3.8"
environment_variables = {
ENV = var.env
}
}
#sns
lambda_function_sns = [
{
topic_name = module.sns_codepipeline_notification.sns_topic_name
topic_arn = module.sns_codepipeline_notification.sns_topic_arn
}
]
}
Expected Behavior
Don’t create resource"aws_lambda_permission" “lambda_permission_cloudwatch_log” {}
Actual Behavior
# module.lambda_codepipeline_notification.aws_lambda_permission.lambda_permission_cloudwatch_log["log_group_name"] will be created
+ resource "aws_lambda_permission" "lambda_permission_cloudwatch_log" {
+ action = "lambda:InvokeFunction"
+ function_name = "arn:aws:lambda:ap-northeast-1:xxx:function:xxx-dev-codepipeline-lambda"
+ id = (known after apply)
+ principal = "logs.ap-northeast-1.amazonaws.com"
+ statement_id = "AllowExecutionFromCloudWatchLogs"
+ statement_id_prefix = (known after apply)
}
# module.lambda_codepipeline_notification.aws_lambda_permission.lambda_permission_cloudwatch_log["log_subscription_filter_name"] will be created
+ resource "aws_lambda_permission" "lambda_permission_cloudwatch_log" {
+ action = "lambda:InvokeFunction"
+ function_name = "arn:aws:lambda:ap-northeast-1:xxx:function:xxx-dev-codepipeline-lambda"
+ id = (known after apply)
+ principal = "logs.ap-northeast-1.amazonaws.com"
+ statement_id = "AllowExecutionFromCloudWatchLogs"
+ statement_id_prefix = (known after apply)
}
# module.lambda_codepipeline_notification.aws_lambda_permission.lambda_permission_cloudwatch_log["log_subscription_filter_pattern"] will be created
+ resource "aws_lambda_permission" "lambda_permission_cloudwatch_log" {
+ action = "lambda:InvokeFunction"
+ function_name = "arn:aws:lambda:ap-northeast-1:xxx:function:xxx-dev-codepipeline-lambda"
+ id = (known after apply)
+ principal = "logs.ap-northeast-1.amazonaws.com"
+ statement_id = "AllowExecutionFromCloudWatchLogs"
+ statement_id_prefix = (known after apply)
}
Plan: 3 to add, 0 to change, 0 to destroy.
Hi @quansang,
Using length
with a single object value is not the right approach here because for an object that function returns the number of attributes defined in the object type.
Since a single object represents only a single item rather than a collection of items, the typical way to represent the absence of it would be null
, which is how Terraform represents a single item being absent.
variable "lambda_function_cloudwatch_log" {
description = "Provide the Name and ARN of the CloudWatch log group you want to trigger to the Lambda Function"
default = null
type = object({
log_subscription_filter_name = optional(string, null)
log_group_name = optional(string, null)
log_subscription_filter_pattern = optional(string, null)
})
}
With the default set to null
you can use var.lambda_function_cloudwatch_log != null
to test if this variable was set.
An empty collection can be a good default for a variable of a collection type (list, set, or map), representing “zero of these”, but an object isn’t a collection so there isn’t an equivalent sense of “object with zero elements”. We use null
to represent the case where there can be either zero or one of something and there are currently zero of it.
Thanks for replying back to me @apparentlymart. I see and in this case, I used null
with no problem. But in another case, can you help me define why this error occurs?
resource "aws_s3_bucket_server_side_encryption_configuration" "s3_bucket_sse" {
count = var.s3_bucket.sse != null ? 1 : 0
bucket = aws_s3_bucket.s3_bucket.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = var.s3_bucket.sse.sse_algorithm
kms_master_key_id = var.s3_bucket.sse.kms_master_key_id
}
}
}
variable "s3_bucket" {
description = "All configurations to Provides a S3 bucket resource."
type = object({
name = string
sse = optional(object({
kms_master_key_id = optional(string, null)
sse_algorithm = string #Valid values: AES256 & aws:kms
}), null)
acl = optional(string, "private")
versioning = optional(string, "Suspended")
logging = optional(object({
target_bucket_id = string
}), null)
})
}
module "s3_standard" {
source = "../../modules/s3"
#s3-bucket
s3_bucket = {
name = "${var.project}-${var.env}-standard"
}
}
Expected Behavior
Don’t create resource “aws_s3_bucket_server_side_encryption_configuration” “s3_bucket_sse” {}
Actual Behavior
│ The given value is not suitable for module.s3_standard.var.s3_bucket declared at ../../modules/s3/_variables.tf:3,1-21: attribute "sse": attribute "sse_algorithm" is required.
In my opinion, the “sse_algorithm” attribute should not be required in this case. The count
won’t create this resource because the variable var.s3_bucket.sse
has received a null
value from optional
. This logic has been defined with the variable var.lambda_function_cloudwatch_log
Hi @quansang,
I agree with you that I’d expect what you showed to work. I tried to recreate your error but I wasn’t able to: for me, it worked as we both expected.
Here’s what I tried:
-
I created a module with just your variable "s3_bucket"
block, because that’s what your error message was talking about and the resource
block isn’t relevant to the problem. Here’s what’s in my .tf
file:
variable "s3_bucket" {
description = "All configurations to Provides a S3 bucket resource."
type = object({
name = string
sse = optional(object({
kms_master_key_id = optional(string, null)
sse_algorithm = string #Valid values: AES256 & aws:kms
}), null)
acl = optional(string, "private")
versioning = optional(string, "Suspended")
logging = optional(object({
target_bucket_id = string
}), null)
})
}
-
Because my module is just a root module, unlike yours, I created a terraform.tfvars
file instead of a module
block, with the following contents that should be equivalent to your module block:
s3_bucket = {
name = "example"
}
When I ran terraform plan
with this minimal configuration I saw no errors:
$ terraform plan
No changes. Your infrastructure matches the configuration.
Terraform has compared your real infrastructure against
your configuration and found no differences, so no changes
are needed.
To recreate your error I added a sse
attribute with an empty object to my terraform.tfvars
file, thereby making it invalid because sse_algorithm
is required for this object:
s3_bucket = {
name = "example"
sse = {}
}
$ terraform plan
Planning failed. Terraform encountered an error while generating
this plan.
╷
│ Error: Invalid value for input variable
│
│ on terraform.tfvars line 1:
│ 1: s3_bucket = {
│ 2: name = "example"
│ 3: sse = {}
│ 4: }
│
│ The given value is not suitable for var.s3_bucket declared at
│ var-optional-attr-nested.tf:1,1-21: attribute "sse": attribute
│ "sse_algorithm" is required.
╵
I would expect to see the error you showed only if you have an sse
attribute defined in your s3_bucket
object. Are you sure that the configuration you shared here is what you used when you saw that error?
Hi @apparentlymart ,
I just found out the exact reason. That’s because I’m using Terraform version 1.3.3 and when I try the new version (1.3.9), it works perfectly (I don’t know new version fixed this bug)… Are you using the latest version too?