It might be great if modules could accept blocks. For example the AWS ELB
resource accepts a listener
block e.g.:
resource "aws_elb" "service" {
listener = {
lb_protocol = "HTTP"
lb_port = 80
instance_protocol = "HTTP"
instance_port = 8000
}
listener = {
lb_protocol = "HTTPS"
lb_port = 443
ssl_certificate_id = "${var.domain_certificate}"
instance_protocol = "HTTP"
instance_port = 8000
}
...
}
Sometimes I want to e.g. wrap an AWS ELB with some default subnets
/ tags
/ security_groups
just leaving the user to be required to define some listener
blocks. This is currently impossible, e.g.:
module "load_balancer" {
listener = {
lb_protocol = "HTTP"
lb_port = 80
instance_protocol = "HTTP"
instance_port = 8000
}
listener = {
lb_protocol = "HTTPS"
lb_port = 443
ssl_certificate_id = "${var.domain_certificate}"
instance_protocol = "HTTP"
instance_port = 8000
}
}
I realize that module/resource is not a 1:1
mapping (modules can encapsulate whole graphs) which means how to actually accomplish feature in a way that is flexible would need some consideration indeed. I don't have any special thoughts about that yet.
Another use case - say you have a module for setting up rds with an associated parameter group, one has no way to customize the parameters in that group from outside the module
resource "aws_db_parameter_group" "app" {
name = "${var.name}"
family = "${var.family}"
// parameter {
// name = "log_statement"
// value = "none"
// apply_method = "immediate"
// }
}
resource "aws_db_subnet_group" "app" {
name = "${var.name}"
subnet_ids = ["${split(",", var.subnet_ids_csv)}"]
}
resource "aws_db_instance" "app" {
identifier = "${var.name}"
engine = "${var.engine}"
engine_version = "${var.engine}"
instance_class = "${var.db_instance_type}"
allocated_storage = "${var.db_instance_storage}"
storage_type = "${var.db_instance_storage_type}"
username = "${var.db_username}"
password = "${var.db_password}"
name = "${var.db_name}"
db_subnet_group_name = "${aws_db_subnet_group.app.name}"
parameter_group_name = "${aws_db_parameter_group.app.name}"
vpc_security_group_ids = ["${compact(split(",", var.security_groups_csv))}"]
}
Something like this is now possible with the built-in map support!
@mitchellh can you give an example? My use case is passing parameters blocks to the aws_db_parameter_group resource inside a module.
@phoolish Oooooh I misread this then, actually. That isn't possible. I'll reopen.
However, you can get a lot more effecient with this by passing through maps and using the map elements.
@mitchellh Yeah, that feature has been extremely helpful.
Would blocks accepting the count argument be a solution?
+1 on this feature. Are there any work-arounds?
Found myself needing this feature as well +1
it can be very useful!
Do you need help? Because I really want it!
+1 on this feature. It'd be nice to be able to pass a block of dimension
to an aws_cloudwatch_metric_alarm
metric. its impossible to parameterize all possible dimensions using map keys
Another use case is placement strategies for ecs tasks
Yeah looking at this as a way to pass metadata to a wrapper module for handling ECS task_definition with LoadBalancer and not wanting to just create a myriad of parameters to pull it off
Looking for this same functionality +1
Going to have to nudge this. +1
+1
+1
Posting +1 comments does nothing but annoy the people who have subscribed to this issue for updates. Please instead vote for the issue by leaving a :+1: reaction on the original opening comment, which we can actually aggregate as an input to prioritization.
With that said, the next major release is already planned to have two features that I think together meet the use-case described here:
Since module inputs are still defined as single arguments rather than blocks, a different syntax is required:
# Feature planned for Terraform 0.12; details may change before release
module "load_balancer" {
listeners = [
{
lb_protocol = "HTTP"
lb_port = 80
instance_protocol = "HTTP"
instance_port = 8000
},
{
lb_protocol = "HTTPS"
lb_port = 443
ssl_certificate_id = "${var.domain_certificate}"
instance_protocol = "HTTP"
instance_port = 8000
},
]
}
...but some difference in punctuation aside, this sort of complex structure could then be passed into a module and used to dynamically-generate nested blocks inside resource configurations.
I have this method
resource "aws_api_gateway_method" "method_request" {
rest_api_id = "${var.rest_api_id}"
resource_id = "${var.resource_id}"
http_method = "${var.http_method}"
authorization = "${var.authorization}"
authorizer_id = "${var.authorizer_id}"
}
Has any way to send request_parameters
as a var too?
Hi @guidiego,
request_methods
is a map attribute rather than a child block, so it can already be passed through the module boundary and assigned as a variable:
variable "request_parameters" {
type = "map"
}
resource "aws_api_gateway_method" "method_request" {
rest_api_id = "${var.rest_api_id}"
resource_id = "${var.resource_id}"
http_method = "${var.http_method}"
authorization = "${var.authorization}"
authorizer_id = "${var.authorizer_id}"
request_parameters = "${var.request_parameters}"
}
In the new release the above will still work but some new syntax will allow removing the interpolation markers and specifying explicitly that the map keys are boolean, so Terraform can type-check that at the module interface and return better error messages if the module is not used correctly.
# In forthcoming 0.12 release (some details may change before release)
variable "request_parameters" {
type = map(bool)
}
resource "aws_api_gateway_method" "method_request" {
rest_api_id = var.rest_api_id
resource_id = var.resource_id
http_method = var.http_method
authorization = var.authorization
authorizer_id = var.authorizer_id
request_parameters = var.request_parameters
}
On Terraform v0.12-alpha1 I just tried the following example load balancer module based on the example given in the original comment on this issue:
variable "listeners" {
type = list(object({
lb_protocol = string
lb_port = number
instance_protocol = string
instance_port = number
ssl_certificate_id = string
}))
}
resource "aws_elb" "example" {
dynamic "listener" {
for_each = var.listeners
content {
lb_protocol = listener.value.lb_protocol
lb_port = listener.value.lb_port
instance_protocol = listener.value.instance_protocol
instance_port = listener.value.instance_port
ssl_certificate_id = listener.value.ssl_certificate_id
}
}
}
I called this module with the following root module:
provider "aws" {
region = "us-west-2"
}
variable "domain_certificate" {
type = string
default = null
}
module "load_balancer" {
source = "./load-balancer"
listeners = [
{
lb_protocol = "HTTP"
lb_port = 80
instance_protocol = "HTTP"
instance_port = 8000
ssl_certificate_id = null
},
{
lb_protocol = "HTTPS"
lb_port = 443
instance_protocol = "HTTP"
instance_port = 8000
ssl_certificate_id = var.domain_certificate
},
]
}
Applying this runs into #19152 for the moment, so I can't actually illustrate applying this, but I can see it generating the expected plan.
I don't have a certificate ARN to pass in there but I tried it with a bogus one and verified that I got a validation error about it, showing that it was populated into the resource as expected.
This is also subject to known issue #19142 which leads to a confusing error message if the caller doesn't provide all of the required attributes inside the listener blocks. That'll be addressed separately.
But with all of that said, I think the two features I indicated previously do address the use-case that this feature request was about, albeit in a different way than originally suggested, so I'm going to close this. We'll get those other issues addressed before v0.12.0 final so that a configuration like my example above is fully applyable.
Thanks for your patience on this, everyone!
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
On Terraform v0.12-alpha1 I just tried the following example load balancer module based on the example given in the original comment on this issue:
I called this module with the following root module:
Applying this runs into #19152 for the moment, so I can't actually illustrate applying this, but I can see it generating the expected plan.
I don't have a certificate ARN to pass in there but I tried it with a bogus one and verified that I got a validation error about it, showing that it was populated into the resource as expected.
This is also subject to known issue #19142 which leads to a confusing error message if the caller doesn't provide all of the required attributes inside the listener blocks. That'll be addressed separately.
But with all of that said, I think the two features I indicated previously do address the use-case that this feature request was about, albeit in a different way than originally suggested, so I'm going to close this. We'll get those other issues addressed before v0.12.0 final so that a configuration like my example above is fully applyable.
Thanks for your patience on this, everyone!