Hi @srinath,
It seems like the requirement is to declare one datadog_synthetics_test.url_monitoring
instance for each distinct tuple of appid
, env
, and region
, with the special case that a null value for regions
is treated as a special “non-region” entry.
The typical first step for a requirement like this is to transform the given data structure into a different shape where there’s one element per instance to declare, which we can do with local values:
variable "app_details" {
type = map(object({
app_id = string
app_name = string
environments = set(string)
regions = set(string)
channel = string
}))
}
locals {
# First we'll normalize the input so that
# a null "regions" becomes a set with one
# element, since otherwise having no regions
# would lead to us generating no tuples at
# all. An empty string region name signifies
# a region-agnostic endpoint here.
app_details = [
for k, d in var.app_details : {
key = k
app_id = d.app_id
app_name = d.app_name
environments = d.environments
regions = coalesce(d.regions, toset([""]))
channel = d.channel
}
]
app_monitors = flatten([
for d in local.app_details : [
for env in d.environments : [
for reg in d.regions : {
key = "${d.app_id}:${env}%{ if reg != "" }:${reg}%{ endif }"
app_id = d.app_id
app_name = d.app_name
environment = env
region = reg != "" ? reg : null
channel = d.channel
url = "https://${d.app_name}%{ if reg != "" }-${reg}%{ endif }-${env}.<url>"
is_prod = env == "prod"
}
]
]
])
}
resource "datadog_synthetics_test" "url_monitoring" {
for_each = { for m in local.app_monitors : m.key => m }
request_definition {
method = "GET"
url = each.value.url
}
# ...
message = <<-EOT
The ${ each.value.is_prod ? "Prod" : "Non-Prod" } Endpoint for the ${title(each.value.app_name)} (${each.value.app_id}) is not returning 200.
Endpoint: [${each.value.url}]
{{#is_alert}}
${each.value.channel}
{{/is_alert}}
EOT
}
This is largely the same as your original approach of flattening the two-level hierarchy of apps and environments. I added in a third level of hierarchy for the regions in a similar way, but also factored out some of the rules for constructing strings that may or may not contain regions just to keep that extra complexity as isolated as possible.
The main “special” thing here compared to the normal flatten
pattern is the fact that I treated a null regions
as a single region with an empty name, which then allows a region-agnostic application to work even though by normal flatten
rules you’d otherwise end up with no elements at all for that application in the case where there are no regions.
Here’s an example of how to set the variable with some of the regions set to null
to activate that special behavior:
app_details = {
sampleapp_regional = {
app_id = "123456"
app_name = "sampleapp"
environments = ["staging", "prod"]
channel = "alert_channel"
regions = ["us-east-1", "us-east-2"]
}
sampleapp_nonregional = {
app_id = "123456"
app_name = "sampleapp"
environments = ["qa"]
channel = "alert_channel"
regions = null
}
}
Notice that the “sampleapp” is split into two entries here so that the qa
incarnation of it can have regions = null
and thus activate the no-regions behavior for it.
This is quite a long example and I’ve not tested it, so I expect I’ve probably made at least one typo in there somewhere. If you get an error when you try this and you’re not sure how to resolve it, let me know and I’ll see if I can improve the example.