Terraform中如何合并对象为正确类型适配CloudWatch告警
问题场景
尝试创建CloudWatch告警,需求是:
- 直接传入原有的
dimensions - 当告警包含
InstanceId维度时,通过查询替换为实际的EC2实例ID
当前编写的locals代码导致dimensions结构错误,无法被aws_cloudwatch_metric_alarm资源识别,报错提示InstanceId需要字符串类型。
错误代码示例
Locals定义
cloudwatch_metric_alarms = { for alarm_name, alarm in try(var.cloudwatch_metric_alarms, {}) : alarm_name => merge( alarm, # Add the EC2 instance ID by looking it up from instance name if needed: contains(keys(alarm), "dimensions") ? { dimensions = { for dimension_name, dimension in alarm.dimensions : dimension_name => merge( { dimension_name = dimension }, dimension_name == "InstanceId" ? { "InstanceId" = local.ec2_instance_lookups[dimension].instance_id } : {} ) } } : {} ) }
资源配置
resource "aws_cloudwatch_metric_alarm" "CW-ALARM" { for_each = local.cloudwatch_metric_alarms # Required for all alarms: alarm_name = each.value.alarm_name threshold = each.value.threshold comparison_operator = each.value.comparison_operator dimensions = each.value.dimensions [...] }
报错信息
Error: Incorrect attribute value type
on ../../terraform-cloudwatch-metric-alarms/cw-alarm-main.tf line 9, in resource "aws_cloudwatch_metric_alarm" "CW-ALARM":
9: dimensions = each.value.dimensions
|----------------
| each.value.dimensions is object with 1 attribute "InstanceId"Inappropriate value for attribute "dimensions": element "InstanceId": string required.
问题原因
错误出现在维度的循环处理中:使用merge({ dimension_name = dimension }, ...)会让每个维度的值变成嵌套对象,而不是直接的字符串。比如InstanceId最终变成了{ "InstanceId": "i-xxxxxx" },但资源要求的是"InstanceId": "i-xxxxxx"这种键值对(值为字符串)。
merge在这里确实用错了,因为你不需要合并对象,而是要直接返回字符串值。
正确代码实现
修改locals中的维度处理逻辑,直接返回字符串值,而非嵌套对象:
cloudwatch_metric_alarms = { for alarm_name, alarm in try(var.cloudwatch_metric_alarms, {}) : alarm_name => merge( alarm, contains(keys(alarm), "dimensions") ? { dimensions = { for dimension_name, dimension_val in alarm.dimensions : dimension_name => ( dimension_name == "InstanceId" ? local.ec2_instance_lookups[dimension_val].instance_id : dimension_val ) } } : {} ) }
代码解释
- 去掉了不必要的
merge操作,直接通过三元表达式判断:如果是InstanceId维度,就返回查询到的实例ID字符串;否则返回原维度值。 - 这样处理后,
dimensions会保持{ "维度名": "字符串值" }的正确结构,符合aws_cloudwatch_metric_alarm资源对dimensions参数的类型要求。
调试建议
如果需要验证数据结构,可以用Terraform的output输出中间结果:
output "debug_cloudwatch_alarms" { value = local.cloudwatch_metric_alarms sensitive = false }
执行terraform plan或terraform apply后就能看到dimensions的实际结构,方便排查问题。
内容的提问来源于stack exchange,提问作者nikobelia

