Hi
Creating module for elb and face issue with data provider
I have module elb:
resource "aws_elb" "this" {
name = "${var.service_name}"
listener = ["${var.listeners}"]
health_check = "${var.health_check}"
security_groups = ["${var.security_groups}"]
subnets = ["${var.subnet}"]
cross_zone_load_balancing = true
idle_timeout = "${var.idle_timeout}"
connection_draining = "${var.connection_draining}"
connection_draining_timeout = "${var.connection_draining_timeout}"
tags = "${var.tags}"
}
and have data like :
data "aws_acm_certificate" "internal" {
domain = "${var.certificates["internal"]}"
statuses = ["ISSUED"]
}
module "elb" {
source = "./modules/elb"
service_name = "${var.service_name}"
listeners = [
{
instance_port = "443"
instance_protocol = "HTTP"
lb_port = "433"
lb_protocol = "HTTPS"
ssl_certificate_id = "${data.aws_acm_certificate.internal.arn}"
},
]
security_groups = [
"${data.state.vpc.security}"]
subnet = [
"${split(",",data.state.vpc.subnet)}"]
}
terraform -v
Terraform v0.10.8
Error: module.elb.aws_elb.this: "listener.0.instance_port": required field is not set
Error: module.elb.aws_elb.this: "listener.0.instance_protocol": required field is not set
Error: module.elb.aws_elb.this: "listener.0.lb_port": required field is not set
Error: module.elb.aws_elb.this: "listener.0.lb_protocol": required field is not set
But when i remove
ssl_certificate_id = "${data.aws_acm_certificate.internal.arn}"
It's work fine
I tried data null_resource, use data "aws_acm_certificate" as module and but there output but still have same error
Can someone tell me if it's possible to implement or not ?
Best regard
Anton
Hi @ruanton29! Sorry for this confusing behavior.
Assigning values to nested blocks is not supported, but appears to work in certain cases due to a number of coincidences in how Terraform represents values internally. To be more specific: it uses the Go map[string]interface{} values for a number of different purposes, and so in some cases it's possible to confuse Terraform into misinterpreting a map created for one purpose as one for another purpose, as you did here, but this will not work robustly due to different expectations about the contents of those maps in different scenarios.
So in fact, unfortunately, the bug here is that what you did ever worked at all, rather than that it failed once you added a data interpolation into the mix :confounded:. I expect it failed because by doing so this caused Terraform to see the whole list as <computed> during validation, but since it's not supposed to be possible to assign values to nested blocks there is no handling of <computed> there and so it fails in this strange way.
(Assigning to nested blocks is not supported because Terraform, as you can see here, wants to be able to validate the attribute names in the block and so it requires them to be enumerated physically in the configuration file. This is part of Terraform's goal of being predictable and explicit at plan time, rather than failing dynamically at some point during apply, wherever possible. This is somewhat analogous to the distinction between a struct/class and a map/dictionary in a statically-typed programming language.)
We are planning to properly support this use-case as part of our current series of configuration language improvements. This particular aspect is being discussed in #7034. Once that is implemented, it will be possible to express what you wanted to express here using the following syntax, which will then be robustly supported and tested:
# DESIGN SKETCH: not yet implemented and may change before release
resource "aws_elb" {
# (other attributes and blocks)
dynamic "listener" {
foreach = "${var.listeners}"
content {
instance_port = "${dynamic.foreach.instance_port}"
instance_protocol = "${dynamic.foreach.instance_protocol}"
lb_port = "${dynamic.foreach.lb_port}"
lb_protocol = "${dynamic.foreach.lb_protocol}"
ssl_certificate_id = "${dynamic.foreach.ssl_certificate_id}"
}
}
}
The key difference with this new approach is that you still enumerate the contents of the block so that the attributes and nested block structure can be validated, and so it's only the _values_ that can potentially be <computed>, which is then consistent with how Terraform behaves in the non-dynamic case.
Since this bug (at least, it's root cause) is already being discussed in #7034, I'm going to close this just to consolidate discussion in that issue.
I'm afraid at this time I don't have a great workaround for what you are trying to do here. Generally-speaking we don't recommend making modules that are just wrappers around existing resources that literally just pass through arguments like this, but I assume there's some other content in this module that wasn't relevant to the issue at hand that warrants this not just being a standalone aws_elb resource.
@apparentlymart i understand, thx
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.
Most helpful comment
Hi @ruanton29! Sorry for this confusing behavior.
Assigning values to nested blocks is not supported, but appears to work in certain cases due to a number of coincidences in how Terraform represents values internally. To be more specific: it uses the Go
map[string]interface{}values for a number of different purposes, and so in some cases it's possible to confuse Terraform into misinterpreting a map created for one purpose as one for another purpose, as you did here, but this will not work robustly due to different expectations about the contents of those maps in different scenarios.So in fact, unfortunately, the bug here is that what you did ever worked at all, rather than that it failed once you added a data interpolation into the mix :confounded:. I expect it failed because by doing so this caused Terraform to see the whole list as
<computed>during validation, but since it's not supposed to be possible to assign values to nested blocks there is no handling of<computed>there and so it fails in this strange way.(Assigning to nested blocks is not supported because Terraform, as you can see here, wants to be able to validate the attribute names in the block and so it requires them to be enumerated physically in the configuration file. This is part of Terraform's goal of being predictable and explicit at plan time, rather than failing dynamically at some point during apply, wherever possible. This is somewhat analogous to the distinction between a struct/class and a map/dictionary in a statically-typed programming language.)
We are planning to properly support this use-case as part of our current series of configuration language improvements. This particular aspect is being discussed in #7034. Once that is implemented, it will be possible to express what you wanted to express here using the following syntax, which will then be robustly supported and tested:
The key difference with this new approach is that you still enumerate the contents of the block so that the attributes and nested block structure can be validated, and so it's only the _values_ that can potentially be
<computed>, which is then consistent with how Terraform behaves in the non-dynamic case.Since this bug (at least, it's root cause) is already being discussed in #7034, I'm going to close this just to consolidate discussion in that issue.
I'm afraid at this time I don't have a great workaround for what you are trying to do here. Generally-speaking we don't recommend making modules that are just wrappers around existing resources that literally just pass through arguments like this, but I assume there's some other content in this module that wasn't relevant to the issue at hand that warrants this not just being a standalone
aws_elbresource.