In my main.tf im using a module, in the module there’s this snippet:
resource "aws_lb_listener" "ip_https" {
count = length(var.ip_https_listener) > 0 ? 1 : 0
load_balancer_arn = aws_lb.default.arn
port = var.ip_https_listener.https_port
protocol = "HTTPS"
ssl_policy = var.https_ssl_policy
certificate_arn = var.certificate_arn
default_action {
target_group_arn = aws_lb_target_group.ip[0].arn
type = "forward"
}
depends_on = [aws_lb_target_group.ip]
}
My problem with this that the listener will always have the same default action.
on my main.tf id like to create a boolean variable for example fixed
in case fixed == true id like to be able to use the module the same only change the default action:
default_action {
{
type = "fixed-response"
fixed_response = {
content_type = "text/plain"
message_body = "FORBIDDEN"
status_code = "403"
}
what the easiet way to do that?
2
Answers
This can be done with
for_each
meta-argument [1] anddynamic
[2]:[1] https://developer.hashicorp.com/terraform/language/meta-arguments/for_each
[2] https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks
Unfortunately this isn’t as easy as it might first appear because the "fixed-response" example in your question isn’t valid. According to the provider documentation, a "fixed-response" action should look like this:
Note that
fixed_response
is a nested block rather than an argument, which means that dynamically choosing the number offixed_response
blocks (either zero or one) will require using adynamic
block to generate a dynamic number of these blocks.Since there are only two possible cases for
default_action
I would implement this as a lookup table in a local value which shows each of the possible cases as a clear literal data structure, separate from the complexity of generating different nested blocks usingdynamic
blocks.For example:
There are three key parts to the above:
local.lb_listener_default_actions
describes the two possible "default actions" that any LB listener can have. I arbitrarily named themforward_to_ip
andfixed_forbidden
here, but you can choose any name that you find descriptive as long as thelocal.ip_https_listeners
condition results match.local.ip_https_listeners
is an extension ofvar.ip_https_listener
which adds the new attributedefault_action
to each of the objects in the list.This works by looking up one of the two members of
local.lb_listener_default_actions
based on whether thefixed
attribute is true or false.The
resource "aws_lb_listener" "ip_https"
block now useslocal.ip_https_listeners
instead ofvar.ip_https_listener
, and itsdefault_action
block is now dynamic based on thedynamic_action
attribute of each listener object.I used
try
to concisely tolerate certain attributes being unset in the default action object, usingnull
to represent absense instead. These expressions then each conditionally include thetarget_group_arn
argument and thefixed_response
nested block based on whether their corresponding attributes are set in the sourcedefault_action
object.There’s a subjective design tradeoff here which I want to be explicit about. I chose to factor out the two possible sets of values for
default_action
into a separate local value because I think that’ll make it easier to read and update them in future, but that does come at the expense of some extra indirection: it’s no longer clear just from reading the resource block exactly how thedefault_action
will be populated, and instead requires working backwards through all of these expressions to find the local value to update.I added a comment above the
default_action
block in the resource in an attempt to mitigate that by directing the future maintainer to the appropriate local value, but it would also be possible to write all of the values inline as part of all of these dynamic expressions and thus remove the indirection at the expense of making it (subjectively) harder to find and update a specific value.The repeated references to
local.ip_https_listeners[count.index]
are also unfortunate but come as a consequence of using a list of listeners and thecount
argument for repetition. If possible I would recommend changing the input variable to be a map of objects instead of a list of objects, and then usingfor_each
to describe the repetition so that you can useeach.value
as a more concise way to refer to the current element. That is far beyond the scope of this question though, so I won’t go into the details about it here.