Terraform reusing config blocks - terraform

Terraform noob here. I have a datadog config along the line of (shamelessly copied from terraform website):
resource "datadog_dashboard" "ordered_dashboard" {
title = "Ordered Layout Dashboard"
description = "Created using the Datadog provider in Terraform"
layout_type = "ordered"
is_read_only = true
widget { # SIMILAR GRAPH
alert_graph_definition {
alert_id = "895605"
viz_type = "timeseries"
title = "Widget Title"
live_span = "1h"
}
}
widget { # SIMILAR GRAPH
alert_graph_definition {
alert_id = "895605"
viz_type = "timeseries"
title = "Widget Title"
live_span = "2h"
}
}
widget {
alert_value_definition {
alert_id = "895605"
precision = 3
unit = "b"
text_align = "center"
title = "Widget Title"
}
}
widget { # SIMILAR GRAPH
alert_graph_definition {
alert_id = "895605"
viz_type = "timeseries"
title = "Widget Title"
live_span = "1w"
}
}
widget { # SIMILAR GRAPH
alert_graph_definition {
alert_id = "895605"
viz_type = "timeseries"
title = "Widget Title"
live_span = "2w"
}
}
}
As seen, we have 5 graphs: graph 1,2,4 and 5 are very similar, and only differ at 1 attribute, whereas graph 3 is very different. Is there a strategy in TF to reuse all the configs for graph 1,2 4 and 5?
I have attempted at using dynamic blocks, but while dynamic blocks can generate graphs 1 and 2 well, graph 3 is so different that I cannot fit it in the dynamic block.
In practice, the graphs are much more complex, and there are more of them, hence I find the config to be too hard to manage.
This is what I tried:
resource "datadog_dashboard" "ordered_dashboard" {
title = "Ordered Layout Dashboard"
description = "Created using the Datadog provider in Terraform"
layout_type = "ordered"
is_read_only = true
dynamic widget {
for_each = ["1h", "2h"]
iterator = live_span_iterator
content {
alert_graph_definition {
alert_id = "895605"
viz_type = "timeseries"
title = "Widget Title"
live_span = live_span_iterator.value
}
}
}
widget {
alert_value_definition {
alert_id = "895605"
precision = 3
unit = "b"
text_align = "center"
title = "Widget Title"
}
}
dynamic widget {
for_each = ["1w", "2w"]
iterator = live_span_iterator
content {
alert_graph_definition {
alert_id = "895605"
viz_type = "timeseries"
title = "Widget Title"
live_span = live_span_iterator.value
}
}
}
}
Are there any other strategies that might help me reduce the code duplication? I hope to retain the order of the graphs if possible.

Related

Looping over tags/aliases instead of whole resource

I'm trying to figure out a way to loop over a list of aliases for Datadog dashboards.
In 1 single dashboard I want to have multiple servers involved that will be fetched from variables.
For example:
variable "africa" {
type = map(any)
default = {
"server1" = {
alias = "server1"
hostname = "hostname1"
name = "query1"
formula_expression = "query1 * 8"
}
"server2" = {
name = "server2"
hostname = "hostname2"
name = "query2"
formula_expression = "query2 * 8"
}
"server3" = {
name = "server3
hostname = "hostname3"
name = "query3"
formula_expression = "query3 * 8"
}
}
}
resource "datadog_dashboard" "global_dashboards_africa" {
title = "Terraform - Africa"
description = "Dashboards for Global"
layout_type = "ordered"
widget {
group_definition {
title = "Quick Overview"
show_title = "true"
layout_type = "ordered"
widget {
timeseries_definition {
title = "Traffic"
title_size = "16"
title_align = "left"
show_legend = false
legend_layout = "auto"
legend_columns = ["avg", "min", "max", "value", "sum"]
request {
for server in var.africa:
formula {
alias = ${alias}
formula_expression = ${formula_expression}
}
query {
metric_query {
name = ${name}
query = "max:system.net.bytes_sent{device:bond0,host:${hostname}}"
}
}
style {
palette = "warm"
line_type = "solid"
line_width = "normal"
}
display_type = "area"
}
yaxis {
include_zero = "true"
scale = "linear"
min = "auto"
max = "auto"
}
}
widget_layout {
x = "0"
y = "5"
width = "12"
height = "5"
}
}
}
}
}
I've added for_each at the top, right after resource but it creates only 3 resources. I want to create 1 resource and in aliases - add like 50 servers with hostnames and names in specific locations.
I've tried multiple solutions but Terraform cannot help me with this.

How to create a map by switching keys and values from another map in Terraform?

Given the map of objects that contains a list of repeating strings:
variable "input_var" = {
type = object(map(
name = string
tags = optional(list(string))
)),
default = {
"one" = {
name = "nameone"
tags = ["tag1", "tag2"]
}
"two" = {
name = "nametwo"
tags = ["tag1", "tag3"]
}
"three" = {
name = "namethree"
tags = ["tag2"]
}
"four" = {
name = "namefour"
}
}
}
I want to create a map with keys being the tags from all elements of var.input_var.
Taking the above defaults, a sample of output is:
{
"tag1": [
{
name = "nameone"
key = "one"
},
{
name = "nametwo"
key = "two"
},
],
"tag2": [
{
name = "namethree"
key = "three"
}
],
"tag3": [
{
name = "nametwo"
key = "two"
}
]
}
I was able to achieve this in a two step steps.
First is to create a unique set of tags:
locals{
tags_set = distinct(flatten([
for k, v in var.input_var: coalesce(v.tags, [])
]))
}
followed by creating the (local) map out of local.tags_set:
tags_map = { for key_tag in local.tags_set:
key_tag => flatten([for k, v in var.input_var: [
for tag in coalesce(v.tags, []) : {
key = k
name = v.name
} if tag == key_tag
]]) }
Is there a better implementation for this, in a single step and/or more efficient?

Terraform dynamic block module

I am trying to create dynamic block for the below datadog custom pipeline however I can see only verbose category but not Debug category, is there any way to get both debug & Verbose.
Resource:
dynamic "processor" {
for_each = var.processor
content {
dynamic "category_processor" {
for_each = length(keys(lookup(processor.value, "category_processor", {}))) == 0 ? [] : [lookup(processor.value, "category_processor", {})]
content {
target = category_processor.value["target"]
name = lookup(category_processor.value, "name", null)
is_enabled = lookup(category_processor.value, "is_enabled", null)
dynamic "category" {
for_each = length(keys(lookup(category_processor.value, "category", {}))) == 0 ? [] : [lookup( category_processor.value, "category", {})]
content {
name = category.value.name
dynamic "filter" {
for_each = length(keys(lookup(category.value, "filter", {}))) == 0 ? [] : [lookup( category.value, "filter", {})]
content{
query=filter.value.query
}
}
}
}
}
}
}
}
Variable:
variable "processor" {
description = "One or more processors (multiples allowed)."
type = any
default = {
processor {
category_processor = {
target = "foo.severity"
category = {
name = "debug"
filter {
query = "#severity: \".\""
}
}
category = {
name = "verbose"
filter {
query = "#severity: \"-\""
}
}
name = "sample category processor"
is_enabled = true
}
}
}

Jetpack Compose - How Do I program this composable to access the "url" in the "list"

//I'm having problems trying to get the title text in the KbfPlayScreen.kt to access the "url" in the list that is in the Data.kt on button click.
**KbfPlayScreen.kt**
#Composable
fun KbfSermonsView(sermons: List<Sermon>) {
Column() {
sermons.forEach {
SermonRow(it.title, it.timestamp)
Divider(thickness = 1.dp, modifier = Modifier.padding(top = 5.dp, bottom = 5.dp))
}
}
}
**Data.kt**
val kbfList = listOf<Kbf>(
Kbf(
title = "The Purpose Of KBF", description = "", imageId = R.drawable.kbf_tony, sermons =
listOf<Sermon>(
Sermon(title= "The Purpose Of KBF", image= "kbf_tony", url=
"https://www.youtube.com/embed/O5xZCZlCqBw", timestamp= "1612674000"),
)),
Kbf(
title = "Goal Setting", description = "", imageId = R.drawable.kbf_mike, sermons =
listOf<Sermon>(
Sermon(title = "Goal Setting", image = "kbf_mike", url =
"https://www.youtube.com/embed/HRDPWpUagxY", timestamp = "1612674000")
))
)

How can I do dimensions block "dynamic"?

Would like to know how can I make dimensions block in aws_cloudwatch_metric_alarm resource "dynamic".
So far have code which I'm sure won't work... but wouldl like to ask how it should be written to achive the goal.
locals {
backend_tg_name = data.terraform_remote_state.network.outputs.backend_tg_name
frontend_tg_name = data.terraform_remote_state.network.outputs.frontend_tg_name
webadmin_tg_name = data.terraform_remote_state.network.outputs.webadmin_tg_name
dimensions = [
{
LoadBalancer = data.terraform_remote_state.network.outputs.alb_suffix
TargetGroup = data.terraform_remote_state.network.outputs.backend_tg_suffix
},
{
LoadBalancer = data.terraform_remote_state.network.outputs.alb_suffix
TargetGroup = data.terraform_remote_state.network.outputs.frontend_tg_name
},
{
LoadBalancer = data.terraform_remote_state.network.outputs.alb_suffix
TargetGroup = data.terraform_remote_state.network.outputs.webadmin_tg_suffix
}
]
}
resource "aws_cloudwatch_metric_alarm" "httpcode_target_5xx_count" {
for_each = {
backend_tg_name = local.backend_tg_name
frontend_tg_name = local.frontend_tg_name
webadmin_tg_name = local.webadmin_tg_name
}
alarm_name = format("ALB: High amount of 5XX errors on target group %s", each.value)
comparison_operator = "GreaterThanThreshold"
evaluation_periods = var.tg_evaluation_periods
metric_name = "HTTP_Code_Target_5XX_Count"
namespace = "AWS/ApplicationELB"
period = var.tg_period
statistic = "Sum"
threshold = var.tg_5xx_threshhold
alarm_description = "Average API 5XX target group error code count is too high"
alarm_actions = aws_sns_topic.infra_monitoring.arn
ok_actions = aws_sns_topic.infra_monitoring.arn
treat_missing_data = "notBreaching"
dimensions = {
"LoadBalancer" = ???
"TargetGroup" = ???
}
}
How I have to change dimensions and/or locals block? I would like to iterate three times and create three same alarms for three different target groups behind one and the same ALB.
Help please.
Seems this is working, hope it will be helpfull:
locals {
alb_suffix = data.terraform_remote_state.network.outputs.alb_suffix
tg_alarms = {
"backend_tg" = {
tg_name = data.terraform_remote_state.network.outputs.backend_tg_name
tg_suffix = data.terraform_remote_state.network.outputs.backend_tg_suffix
},
"frontend_tg" = {
tg_name = data.terraform_remote_state.network.outputs.frontend_tg_name
tg_suffix = data.terraform_remote_state.network.outputs.frontend_tg_name
},
"webadmin_tg" = {
tg_name = data.terraform_remote_state.network.outputs.webadmin_tg_name
tg_suffix = data.terraform_remote_state.network.outputs.webadmin_tg_suffix
}
}
}
resource "aws_cloudwatch_metric_alarm" "httpcode_target_5xx_count" {
for_each = local.tg_alarms
alarm_name = format("ALB: High amount of 5XX errors on target group %s", each.value["tg_name"])
comparison_operator = "GreaterThanThreshold"
evaluation_periods = var.tg_evaluation_periods
datapoints_to_alarm = var.tg_datapoints_to_alarm
metric_name = "HTTPCode_Target_5XX_Count"
namespace = "AWS/ApplicationELB"
period = var.tg_period
statistic = "Sum"
threshold = var.tg_5xx_threshold
alarm_description = "Average API 5XX target group error code count is too high"
alarm_actions = [aws_sns_topic.infra_monitoring.arn]
ok_actions = [aws_sns_topic.infra_monitoring.arn]
treat_missing_data = "notBreaching"
dimensions = {
"LoadBalancer" = local.alb_suffix
"TargetGroup" = each.value["tg_suffix"]
}
}

Resources