-
Notifications
You must be signed in to change notification settings - Fork 9.6k
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
Objects with optional(list(string))
attributes cannot have useful defaults applied with defaults
function
#28406
Comments
Hi @ibacalu, thanks for reporting this. This is a duplicate of #28372. It looks like you are trying to assign a list value as a default for a list attribute, which is the cause of this error. While I understand that is an intuitive thing to want to do, this is not how the
That means that there is no way (that I can think of) to use While I don't think this is a bug in Terraform, the |
Sorry @alisdair, do we take it as a "wait for a change in the design" or "use coalescelist as there won't be any change"? |
The former! We're still gathering feedback on both optional object attributes and the associated Providing detailed feedback and use cases at this stage is extremely helpful, and will help us to improve the design of these features for future versions. We appreciate your input! |
so, @alisdair you are suggesting the following configuration would work? Because it doesn't Configurationvariable "config" {
description = "(Required) Module configuration"
type = object({
cluster_id = string,
replication_group_id = optional(string),
engine = optional(string),
engine_version = optional(string),
maintenance_window = optional(string),
node_type = optional(string),
num_cache_nodes = optional(number),
parameter_group_name = optional(string),
port = optional(number),
subnet_group_name = optional(string),
security_group_names = optional(list(string)),
security_group_ids = optional(list(string)),
apply_immediately = optional(bool),
snapshot_arns = optional(list(string)),
snapshot_name = optional(string),
snapshot_window = optional(string),
snapshot_retention_limit = optional(string),
notification_topic_arn = optional(string),
az_mode = optional(string),
availability_zone = optional(string),
preferred_availability_zones = optional(list(string)),
final_snapshot_identifier = optional(string),
tags = optional(map(string)),
})
}
locals {
config = defaults(var.config, {
maintenance_window = "sun:05:00-sun:09:00"
engine = "redis"
engine_version = "3.2.10"
node_type = "cache.t2.medium"
num_cache_nodes = 1
port = 6379
apply_immediately = false
az_mode = "single-az"
security_group_ids = "sg-000001"
})
}
output "security_group_ids" {
value = local.config.security_group_ids
} Output+ security_group_ids = [] |
@alisdair we have the same problems with maps. Some input objects should be able to supply tags, but optional. So the default would be either variable "child_accounts" {
description = "Map of child accounts to create. The map key is the name of the account and the value is an object containing account configuration variables. See the comments below for what keys and values this object should contain."
# The key for the account are used to reference them in outputs.
type = map(object({
# Human readable name for the account.
name = string
# Email address for the AWS account.
email = string
# Organization Unit for the account, defined as key in `organizational_units`.
ou_key = string
//noinspection TFIncorrectVariableType
tags = optional(map(string))
}))
}
locals {
child_accounts = defaults(var.child_accounts, {
tags = {}
})
} Error:
Or similar to it: locals {
child_accounts = defaults(var.child_accounts, {
tags = var.default_tags
})
}
|
👀 I got it running for setting another map as default value with locals {
child_accounts = {
for key, child in var.child_accounts : key => merge(
child,
{
tags = merge(child.tags, var.default_tags)
}
)
}
} |
Thanks for the follow-up, and I agree. While this statement isn't completely correct (see below), I agree that it's worth retitling and reopening this ticket, which I'm doing now.
terraform {
experiments = [module_variable_optional_attrs]
}
variable "website" {
type = object({
base_url = string
ttl = optional(number)
pages = list(
object({
path = string
data = string
draft = optional(bool)
})
)
})
}
locals {
website = defaults(var.website, {
ttl = 86400
pages = {
draft = false
}
})
}
output "website" {
value = local.website
} website = {
base_url = "https://example.com"
pages = [
{
path = "index.txt"
data = "Hello, world"
},
{
path = "release.txt"
data = "Our app shipped today!"
draft = true
},
]
}
As you have noticed however, optional lists with primitive element types (string, number, bool) have no useful way to be used with the @QuingKhaos Thanks for sharing that workaround! Until we find a way to redesign |
optional
not working as expected with defaults
optional(list(string))
attributes cannot have useful defaults applied with defaults
function
Can anyone comment on if the information described in the additional context above is a bug or as designed? |
This information is not correct. Values returned from If you find this not to be the case, please open a separate issue with reproduction steps. |
Pretty much same issue. Downgraded back to v0.14.7 Error: Invalid function argument
│
│ on locals.tf line 14, in locals:
│ 14: network = defaults(var.network, {
│ 15: id = random_uuid.instance.id
│ 16: })
│ ├────────────────
│ │ random_uuid.instance.id will be known only after apply
│
│ Invalid value for "defaults" parameter: .id: invalid default value for string: string required. |
Below my code works even though Configurationterraform {
experiments = [module_variable_optional_attrs]
}
variable "security_groups" {
description = "Security gorups"
type = list(object({
description = string
name_tag_postfix = string
additional_tag = optional(map(string))
ingresses = list(object({
description = string
from_port = string
to_port = string
protocol = string
cidr_blocks = optional(list(string))
source_security_group_identifier = optional(string)
source_security_group_id = optional(string)
self = optional(bool)
}))
}))
}
locals {
security_groups = defaults(var.security_groups, {
# additional_tag = {} ---> It applies without definition!
ingresses = {
# cidr_blocks = [] ---> It applies without definition!
source_security_group_identifier = ""
source_security_group_id = ""
self = false
}
})
}
output "out" {
value = local.security_groups
} Input valuessecurity_groups = [
{
identifier = "ec2-bastion"
name_tag_postfix = "ec2-bastion"
description = "the security group for bastion host"
ingresses = [{
description = "SSH connection"
from_port = "22"
to_port = "22"
protocol = "tcp"
cidr_blocks = ["1.2.3.4/32"]
}
]
},
{
identifier = "elb-web"
description = "the security group for prod elb"
name_tag_postfix = "elb-web"
ingresses = [{
description = "Web service"
from_port = "80"
to_port = "80"
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
]
},
{
identifier = "ec2-web"
name_tag_postfix = "ec2-web"
description = "the security group for prod elb"
ingresses = [{
description = "Web service"
from_port = "80"
to_port = "80"
protocol = "tcp"
source_security_group_identifier = "elb-web"
}, {
description = "Connect from bastion host"
from_port = "22"
to_port = "22"
protocol = "tcp"
source_security_group_identifier = "ec2-bastion"
}
]
}
] Outputs[
{
additional_tag = {}
description = "the security group for bastion host"
ingresses = [
{
cidr_blocks = [
"1.2.3.4/32",
]
description = "SSH connection"
from_port = "22"
protocol = "tcp"
self = false
source_security_group_id = ""
source_security_group_identifier = ""
to_port = "22"
},
]
name_tag_postfix = "ec2-bastion"
},
{
additional_tag = {}
description = "the security group for prod elb"
ingresses = [
{
cidr_blocks = [
"0.0.0.0/0",
]
description = "Web service"
from_port = "80"
protocol = "tcp"
self = false
source_security_group_id = ""
source_security_group_identifier = ""
to_port = "80"
},
]
name_tag_postfix = "elb-web"
},
{
additional_tag = {}
description = "the security group for prod elb"
ingresses = [
{
cidr_blocks = []
description = "Web service"
from_port = "80"
protocol = "tcp"
self = false
source_security_group_id = ""
source_security_group_identifier = "elb-web"
to_port = "80"
},
{
cidr_blocks = []
description = "Connect from bastion host"
from_port = "22"
protocol = "tcp"
self = false
source_security_group_id = ""
source_security_group_identifier = "ec2-bastion"
to_port = "22"
},
]
name_tag_postfix = "ec2-web"
},
] |
Came across the same issue: Configuration of the Variable with
|
@kurianoff has summarized my exact issue where if an entire object is optional it simply returns null and doesn't populate the minimal values. Using a merge solves the issue, but using both a merge AND the optional defaults function leads to a very difficult to read solution. I feel it's a behavioral issue when you set a default for an attribute and get a null back null instead of the object defined. |
Hi all, we recently released the Terraform v1.3 alpha, which includes the ability to mark object type attributes as optional and set default values. I'm following up with issues that have provided feedback on the previous experiment, and wanted to invite you all to try the alpha and provide any feedback on this updated design. You can learn more/add comments here. Thank you so much in advance, and hope you like the feature! |
Hello again! As a result of this and other feedback that the The example in the original issue comment could be adapted to the final design of the feature as follows: variable "config" {
description = "(Required) Module configuration"
type = object({
cluster_id = string,
replication_group_id = optional(string),
engine = optional(string, "redis"),
engine_version = optional(string, "3.2.10"),
maintenance_window = optional(string, "sun:05:00-sun:09:00"),
node_type = optional(string, "cache.t2.medium"),
num_cache_nodes = optional(number, 1),
parameter_group_name = optional(string),
port = optional(number, 6379),
subnet_group_name = optional(string),
security_group_names = optional(list(string)),
security_group_ids = optional(list(string), ["sg-000000"]),
apply_immediately = optional(bool, false),
snapshot_arns = optional(list(string)),
snapshot_name = optional(string),
snapshot_window = optional(string),
snapshot_retention_limit = optional(string),
notification_topic_arn = optional(string),
az_mode = optional(string, "single-az"),
availability_zone = optional(string),
preferred_availability_zones = optional(list(string)),
final_snapshot_identifier = optional(string),
tags = optional(map(string)),
})
} This new syntax, unlike the experimental This functionality is already available in the release candidate of Terraform v1.3.0 and will be included in the forthcoming final release. Thanks! |
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. |
As you will see in the example below,
defaults
doesn't work as expected.I've been struggling with this for too much time already.
Terraform Version
Terraform Configuration Files
Debug Output
Crash Output
Expected Behavior
Set default value of
local.config.security_group_ids
to["sg-000000"]
Actual Behavior
Error
Steps to Reproduce
terraform init
terraform plan
Additional Context
Worthwhile mentioning also that
for_each
loops based on values fromdefaults
do not work and complain that its value cannot be determined until apply.References
The text was updated successfully, but these errors were encountered: