使用For_Each创建azurerm_servicebus_topic后,如何通过主题名称获取对应ID以创建Service Bus订阅?
问题根源
你碰到的This object does not have an attribute named "name"错误,核心原因是:用for_each创建的数据源是一个映射(Map),而非列表。你尝试用data.azurerm_servicebus_topic.topic.*.name这种列表式的引用方式完全不适用——Terraform不会为for_each生成的资源/数据源提供.*的列表属性,自然找不到name这个顶层属性。
另外,其实你完全没必要多此一举去查询数据源,毕竟你已经通过azurerm_servicebus_topic.topic资源创建了所有主题,直接引用这个资源的输出属性就够了,还能避免额外的依赖和查询开销。
方案1:直接引用已创建的主题资源(强烈推荐)
既然你的主题是用for_each = local.parsed_topics创建的,而local.parsed_topics的键就是主题名称,那你可以直接用订阅配置里的topic_name作为键,快速定位到对应的主题ID:
resource "azurerm_servicebus_subscription" "subscription" { for_each = local.parsed_subscriptions name = each.value.name # 关键:通过主题名称直接访问主题资源的ID topic_id = azurerm_servicebus_topic.topic[each.value.topic_name].id max_delivery_count = 1 default_message_ttl = each.value.default_message_ttl enable_batched_operations = each.value.enable_batched_operations # 这里的depends_on可以省略,Terraform能自动识别资源依赖关系 # depends_on = [ azurerm_servicebus_topic.topic ] }
这个方案的好处:
- 省去额外的数据源配置,简化代码
- Terraform自动管理资源依赖,不用手动维护
depends_on - 避免数据源和实际资源不同步的潜在问题
方案2:如果一定要用数据源(不推荐)
如果因为某些场景限制必须使用数据源,同样利用for_each生成的映射特性,直接通过主题名称作为键访问数据源对象:
resource "azurerm_servicebus_subscription" "subscription" { for_each = local.parsed_subscriptions name = each.value.name # 用主题名称作为键,直接获取数据源中的主题ID topic_id = data.azurerm_servicebus_topic.topic[each.value.topic_name].id max_delivery_count = 1 default_message_ttl = each.value.default_message_ttl enable_batched_operations = each.value.enable_batched_operations depends_on = [ azurerm_servicebus_topic.topic ] }
注意:必须保证local.parsed_subscriptions里的topic_name和local.parsed_topics的键完全匹配,否则会出现找不到对应数据源项的错误。
补充说明
当使用for_each时,Terraform返回的是键值对映射,键是你传入的for_each集合的元素(这里是主题名称),值是对应的资源/数据源实例。而.*语法只适用于count创建的列表型资源,所以你之前用index加.*的方法完全不适用,这就是报错的根本原因。
内容的提问来源于stack exchange,提问作者Joshua Duxbury

