Terraform创建Azure主机名绑定失败:依赖配置无误却报错
问题:Azure App Service自定义域名绑定首次部署失败,第二次成功
问题场景
我有两个Terraform模块,一个用于创建Azure App Service,另一个为该App Service创建主机名绑定。
主模块main.tf代码
# Inside main.tf # App Service module "app_service" { source = "./AppService" resource_group_name = azurerm_resource_group.rg_0.name resource_group_location = azurerm_resource_group.rg_0.location } # Custom Domain module "custom_domain" { source = "./CustomDomain" base_domain = var.domain_name sub_domain = azurerm_resource_group.rg_0.name web_app = module.app_service.web_app }
CustomDomain模块代码
variable "base_domain" { } variable "sub_domain" { } variable "web_app" { } data "azurerm_dns_zone" "dns-zone" { name = var.base_domain } resource "azurerm_dns_txt_record" "domain-verification" { name = "asuid.${var.sub_domain}" zone_name = data.azurerm_dns_zone.dns-zone.name resource_group_name = data.azurerm_dns_zone.dns-zone.resource_group_name ttl = 3600 record { value = var.web_app.custom_domain_verification_id } } resource "azurerm_dns_cname_record" "cname-record" { name = var.sub_domain zone_name = data.azurerm_dns_zone.dns-zone.name resource_group_name = data.azurerm_dns_zone.dns-zone.resource_group_name ttl = 3600 record = var.web_app.default_hostname depends_on = [azurerm_dns_txt_record.domain-verification] } resource "azurerm_app_service_custom_hostname_binding" "hostname-binding" { hostname = "${var.sub_domain}.${var.base_domain}" app_service_name = var.web_app.name resource_group_name = var.web_app.resource_group_name depends_on = [azurerm_dns_cname_record.cname-record] }
部署错误信息
module.custom_domain.azurerm_dns_txt_record.domain-verification: Creating... module.custom_domain.azurerm_dns_txt_record.domain-verification: Creation complete after 1s [id=/subscriptions/7c34cc50-2353-4be0-bd25-d43ce1e7856e/resourceGroups/simsemsdevopsresources/providers/Microsoft.Network/dnsZones/dummy.net/TXT/asuid.tftest4-env8] module.custom_domain.azurerm_dns_cname_record.cname-record: Creating... module.custom_domain.azurerm_dns_cname_record.cname-record: Creation complete after 1s [id=/subscriptions/7c34cc50-2353-4be0-bd25-d43ce1e7856e/resourceGroups/simsemsdevopsresources/providers/Microsoft.Network/dnsZones/dummy.net/CNAME/tftest4-env8] module.custom_domain.azurerm_app_service_custom_hostname_binding.hostname-binding: Creating... ╷ │ Error: creating/updating Custom Hostname Binding "tftest4-env8.dummy.net" (App Service "tftest4-env8" / Resource Group "tftest4-env8"): web.AppsClient#CreateOrUpdateHostNameBinding: Failure responding to request: StatusCode=400 -- Original Error: autorest/azure: Service returned an error. Status=400 Code="BadRequest" Message="A TXT record pointing from asuid.tftest4-env8.dummy.net to 05334922dcaf46e4cd40398f25d5c6e96a9c1e8833b716b140eaeb06b9f838aa was not found."
我设置的依赖关系看似正确:azurerm_app_service_custom_hostname_binding显式依赖azurerm_dns_cname_record,后者又依赖azurerm_dns_txt_record。azurerm_linux_web_app从AppService模块输出后传入CustomDomain模块,供TXT记录使用custom_domain_verification_id。Terraform输出和Azure门户都显示TXT记录已创建成功,但首次部署失败,第二次执行部署计划则成功。
原因分析
这是DNS记录的全球传播延迟导致的问题:
- Terraform仅确认Azure DNS服务端已创建TXT记录,但该记录还未完成全球DNS节点的同步传播。
- Azure验证自定义域名绑定时,会从全球DNS节点查询TXT记录,此时新创建的记录可能还未同步到所有节点,导致验证失败。
- 第二次部署时,间隔了足够的时间,DNS记录已完成全球传播,Azure能查询到TXT记录,验证通过。
另外,当前的depends_on仅保证Terraform资源创建的顺序,无法等待DNS记录的全球传播完成。
解决方案
可以通过以下两种方式解决:
- 添加显式延迟:在
azurerm_app_service_custom_hostname_binding资源前添加time_sleep资源,等待DNS记录传播完成:
resource "time_sleep" "wait_for_dns_propagation" { create_duration = "2m" # 根据实际情况调整,一般1-5分钟足够 depends_on = [azurerm_dns_txt_record.domain-verification] } resource "azurerm_app_service_custom_hostname_binding" "hostname-binding" { hostname = "${var.sub_domain}.${var.base_domain}" app_service_name = var.web_app.name resource_group_name = var.web_app.resource_group_name depends_on = [time_sleep.wait_for_dns_propagation] }
- 使用DNS验证数据源:利用Terraform的
dns_txt_record_set数据源,主动查询TXT记录直到存在,替代固定延迟:
data "dns_txt_record_set" "verify_asuid" { host = "asuid.${var.sub_domain}.${var.base_domain}" depends_on = [azurerm_dns_txt_record.domain-verification] # 轮询直到记录存在,最多等待10分钟,每次间隔30秒 lifecycle { postcondition { condition = length(self.records) > 0 && contains(self.records, var.web_app.custom_domain_verification_id) error_message = "ASUID TXT record not found or does not match verification ID." } } } resource "azurerm_app_service_custom_hostname_binding" "hostname-binding" { hostname = "${var.sub_domain}.${var.base_domain}" app_service_name = var.web_app.name resource_group_name = var.web_app.resource_group_name depends_on = [data.dns_txt_record_set.verify_asuid] }
第二种方式更可靠,因为它会主动验证记录是否可查询,而不是依赖固定时间等待。
内容的提问来源于stack exchange,提问作者Neutrino
相关产品推荐
相关产品推荐

