Hi all,
I’m fairly new to Terraform, and been having some issues with the diff I get when I run terraform plan
.
Project I’m working with uses Terraform for AWS. I’ve recently created some CloudWatch alerts for various queues from SQS.
This is how my tfvars file looks like:
stacks = ["stack1", "stack2", "stack3", "stack4"]
queue_identifiers = ["queue1", "queue2"]
thresholds = {
"stack1" = {
"queue1" = 1
"queue2" = 1
}
"stack2" = {
"queue1" = 1
"queue2" = 1
}
"stack3" = {
"queue1" = 1
"queue2" = 1
}
"stack4" = {
"queue1" = 1
"queue2" = 1
}
}
Then I use these variables in conjunction to create multiple resources. Here’s my .tf
file:
variable "queue_identifiers" {}
variable "stacks" {}
variable "thresholds" {}
locals {
message_too_old_thresholds = {
for data in setproduct(var.stacks, var.queue_identifiers) : "message_${data[0]}_${data[1]}" => {
stack = data[0]
name = data[1]
threshold = min(
max(
lookup(
lookup(
var.thresholds,
"${data[0]}"
),
"${data[1]}"
),
0
),
100
)
}
}
}
resource "aws_cloudwatch_metric_alarm" "message_too_old" {
for_each = local.message_too_old_thresholds
alarm_name = "${each.value.name}_message_too_old"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "2"
metric_name = "ApproximateAgeOfOldestMessage"
namespace = "AWS/SQS"
period = "120"
statistic = "Average"
threshold = each.value.threshold
alarm_description = "Description"
dimensions = {
QueueName = "${each.value.stack}_${each.value.name}"
}
}
When I run terraform plan -out tfplan
this is how part of my diff looks like:
# aws_cloudwatch_metric_alarm.message_too_old["message_stack1_queue1"] will be updated in-place
~ resource "aws_cloudwatch_metric_alarm" "message_too_old" {
actions_enabled = true
~ alarm_description = "Description"
alarm_name = "queue1_message_too_old"
comparison_operator = "GreaterThanOrEqualToThreshold"
datapoints_to_alarm = 0
~ dimensions = {
~ "QueueName" = "stack3_queue1" -> "stack1_queue1"
}
evaluation_periods = 2
id = "queue1_message_too_old"
insufficient_data_actions = []
metric_name = "ApproximateAgeOfOldestMessage"
namespace = "AWS/SQS"
ok_actions = []
period = 120
statistic = "Average"
tags = {}
threshold = 1
treat_missing_data = "missing"
}
# aws_cloudwatch_metric_alarm.message_too_old["message_stack1_queue2"] will be updated in-place
~ resource "aws_cloudwatch_metric_alarm" "message_too_old" {
actions_enabled = true
~ alarm_description = "Description"
alarm_name = "queue2_message_too_old"
comparison_operator = "GreaterThanOrEqualToThreshold"
datapoints_to_alarm = 0
~ dimensions = {
~ "QueueName" = "stack4_queue2" -> "stack1_queue2"
}
evaluation_periods = 2
id = "queue2_message_too_old"
insufficient_data_actions = []
metric_name = "ApproximateAgeOfOldestMessage"
namespace = "AWS/SQS"
ok_actions = []
period = 120
statistic = "Average"
tags = {}
threshold = 1
treat_missing_data = "missing"
}
# aws_cloudwatch_metric_alarm.message_too_old["message_stack2_queue1"] will be updated in-place
~ resource "aws_cloudwatch_metric_alarm" "message_too_old" {
actions_enabled = true
~ alarm_description = "Description"
alarm_name = "queue1_message_too_old"
comparison_operator = "GreaterThanOrEqualToThreshold"
datapoints_to_alarm = 0
~ dimensions = {
~ "QueueName" = "stack3_queue1" -> "stack2_queue1"
}
evaluation_periods = 2
id = "queue1_message_too_old"
insufficient_data_actions = []
metric_name = "ApproximateAgeOfOldestMessage"
namespace = "AWS/SQS"
ok_actions = []
period = 120
statistic = "Average"
tags = {}
threshold = 1
treat_missing_data = "missing"
}
# aws_cloudwatch_metric_alarm.message_too_old["message_stack2_queue2"] will be updated in-place
~ resource "aws_cloudwatch_metric_alarm" "message_too_old" {
actions_enabled = true
~ alarm_description = "Description"
alarm_name = "queue2_message_too_old"
comparison_operator = "GreaterThanOrEqualToThreshold"
datapoints_to_alarm = 0
~ dimensions = {
~ "QueueName" = "stack4_queue2" -> "stack2_queue2"
}
evaluation_periods = 2
id = "queue2_message_too_old"
insufficient_data_actions = []
metric_name = "ApproximateAgeOfOldestMessage"
namespace = "AWS/SQS"
ok_actions = []
period = 120
statistic = "Average"
tags = {}
threshold = 1
treat_missing_data = "missing"
}
# aws_cloudwatch_metric_alarm.message_too_old["message_stack3_queue2"] will be updated in-place
~ resource "aws_cloudwatch_metric_alarm" "message_too_old" {
actions_enabled = true
~ alarm_description = "Description"
alarm_name = "queue2_message_too_old"
comparison_operator = "GreaterThanOrEqualToThreshold"
datapoints_to_alarm = 0
~ dimensions = {
~ "QueueName" = "stack4_queue2" -> "stack3_queue2"
}
evaluation_periods = 2
id = "queue2_message_too_old"
insufficient_data_actions = []
metric_name = "ApproximateAgeOfOldestMessage"
namespace = "AWS/SQS"
ok_actions = []
period = 120
statistic = "Average"
tags = {}
threshold = 1
treat_missing_data = "missing"
}
If I terraform apply
the changes and run terraform plan
again, same thing happens. It almost looks like setproduct
is creating the result in different order every time, resulting in a weird diff.
To be clear, this doesn’t remove or add any resources, just shuffles it around, but creates a “n number of resources will be updated” warning anyhow.
Note: I’ve changed the names of my stacks and queues, and also removed some of as there are quite a lot of number of combinations. If you see anything that doesn’t make sense, please let me know.