You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Terraform配置Azure Site Recovery遇Azure Firewall致复制验证失败求助

问题:Azure Site Recovery Terraform轮询错误(代码28020)与Azure Firewall网络配置调整

使用Terraform管理Azure Site Recovery时,更新复制虚拟机后轮询出现错误:代码"28020",消息"请求的操作失败"。

环境信息:

  • Terraform版本:1.5.0
  • Azurerm版本:3.62.0

现象:虚拟网络未部署Azure Firewall时,Terraform配置可正常运行;部署Firewall后,复制已成功创建,但Terraform无法验证复制状态。

相关Terraform配置代码:

resource "azurerm_site_recovery_fabric" "primary" {
  name                = "asr-fabric-primary-${var.postfix}"
  location            = var.location
  recovery_vault_name = azurerm_recovery_services_vault.vault[0].name
  resource_group_name = module.resource_group_dr.resource_group.utilities.name
}

resource "azurerm_site_recovery_fabric" "secondary" {
  name                = "asr-fabric-secondary-${var.postfix}"
  location            = var.location_secondary
  recovery_vault_name = azurerm_recovery_services_vault.vault[0].name
  resource_group_name = module.resource_group_dr.resource_group.utilities.name
}

resource "azurerm_site_recovery_protection_container" "primary" {
  name                 = "asr-protection-container-primary-${var.postfix}"
  recovery_fabric_name = azurerm_site_recovery_fabric.primary.name
  recovery_vault_name  = azurerm_recovery_services_vault.vault[0].name
  resource_group_name  = module.resource_group_dr.resource_group.utilities.name
}

resource "azurerm_site_recovery_protection_container" "secondary" {
  name                 = "asr-protection-container-secondary-${var.postfix}"
  recovery_fabric_name = azurerm_site_recovery_fabric.secondary.name
  recovery_vault_name  = azurerm_recovery_services_vault.vault[0].name
  resource_group_name  = module.resource_group_dr.resource_group.utilities.name
}

resource "azurerm_site_recovery_network_mapping" "network_mapping_primary_to_secondary" {
  name                        = "asr-protection-container-mapping-primary-${var.postfix}"
  recovery_vault_name         = azurerm_recovery_services_vault.vault[0].name
  resource_group_name         = module.resource_group_dr.resource_group.utilities.name
  source_recovery_fabric_name = azurerm_site_recovery_fabric.primary.name
  target_recovery_fabric_name = azurerm_site_recovery_fabric.secondary.name
  source_network_id           = module.vnet_spoke.vnet.spoke1.id
  target_network_id           = module.vnet_spoke_dr.vnet.spoke1.id
}

resource "azurerm_site_recovery_network_mapping" "network_mapping_secondary_to_primary" {
  name                        = "asr-protection-container-mapping-secondary-${var.postfix}"
  recovery_vault_name         = azurerm_recovery_services_vault.vault[0].name
  resource_group_name         = module.resource_group_dr.resource_group.utilities.name
  source_recovery_fabric_name = azurerm_site_recovery_fabric.secondary.name
  target_recovery_fabric_name = azurerm_site_recovery_fabric.primary.name
  source_network_id           = module.vnet_spoke_dr.vnet.spoke1.id
  target_network_id           = module.vnet_spoke.vnet.spoke1.id
}

resource "azurerm_site_recovery_protection_container_mapping" "container_mapping" {
  name                                      = "container-mapping"
  resource_group_name                       = module.resource_group_dr.resource_group.utilities.name
  recovery_vault_name                       = azurerm_recovery_services_vault.vault[0].name
  recovery_fabric_name                      = azurerm_site_recovery_fabric.primary.name
  recovery_source_protection_container_name = azurerm_site_recovery_protection_container.primary.name
  recovery_target_protection_container_id   = azurerm_site_recovery_protection_container.secondary.id
  recovery_replication_policy_id            = azurerm_site_recovery_replication_policy.policy.id
}

resource "azurerm_site_recovery_replication_policy" "policy" {
  name                                                 = "${var.replication_policy.name}-${var.postfix}"
  resource_group_name                                  = module.resource_group_dr.resource_group.utilities.name
  recovery_vault_name                                  = azurerm_recovery_services_vault.vault[0].name
  recovery_point_retention_in_minutes                  = var.replication_policy.recovery_point_retention_in_minutes
  application_consistent_snapshot_frequency_in_minutes = var.replication_policy.application_consistent_snapshot_frequency_in_minutes
}

// Azure Site Recovery: Windows Bastion VM
data "azurerm_managed_disk" "os_disk_vm_win_bastion" {
  depends_on = [
    module.vm_win_bastion,
  ]
  name                = module.vm_win_bastion[0].windows_vm.os_disk[0].name
  resource_group_name = module.vm_win_bastion[0].windows_vm.resource_group_name
}

resource "azurerm_site_recovery_replicated_vm" "windows_bastion" {
  name                           = "asr-vm-windows-bastion"
  recovery_replication_policy_id = azurerm_site_recovery_replication_policy.policy.id
  recovery_vault_name            = azurerm_recovery_services_vault.vault[0].name
  resource_group_name            = module.resource_group_dr.resource_group.utilities.name

  source_recovery_fabric_name               = azurerm_site_recovery_fabric.primary.name
  source_recovery_protection_container_name = azurerm_site_recovery_protection_container.primary.name
  source_vm_id                              = module.vm_win_bastion[0].windows_vm.id

  target_recovery_fabric_id               = azurerm_site_recovery_fabric.secondary.id
  target_recovery_protection_container_id = azurerm_site_recovery_protection_container.secondary.id
  target_resource_group_id                = module.resource_group_dr.resource_group.treasury.id
  target_availability_set_id              = null
  target_zone                             = null
  target_network_id                       = module.vnet_spoke_dr.vnet.spoke1.id

  managed_disk {
    disk_id                    = data.azurerm_managed_disk.os_disk_vm_win_bastion.id
    staging_storage_account_id = module.storage_account_asr_cache[0].storage_account_id
    target_resource_group_id   = module.resource_group_dr.resource_group.treasury.id
    target_disk_type           = data.azurerm_managed_disk.os_disk_vm_win_bastion.storage_account_type
    target_replica_disk_type   = data.azurerm_managed_disk.os_disk_vm_win_bastion.storage_account_type
  }

  network_interface {
    source_network_interface_id   = module.vm_win_bastion[0].vm_nic_id
    target_subnet_name            = module.subnet_dr.subnet.vm-subnet.id
    target_static_ip              = null
    recovery_public_ip_address_id = null
  }

  depends_on = [
    azurerm_site_recovery_network_mapping.network_mapping_primary_to_secondary,
    azurerm_site_recovery_network_mapping.network_mapping_secondary_to_primary,
    azurerm_site_recovery_protection_container_mapping.container_mapping,
    azurerm_site_recovery_replication_policy.policy,
    azurerm_role_assignment.recovery_vaut_storage,
  ]

  lifecycle {
    ignore_changes = [
      target_network_id,
      source_vm_id,
    ]
  }
}

网络配置调整方案

1. 放行ASR核心流量的Firewall规则

在Azure Firewall的规则集中添加以下条目,确保源虚拟机所在子网能正常通信:

  • 应用规则:
    • 允许目标域:*.hypervrecoverymanager.windowsazure.com(ASR管理平面端点)、*.blob.core.windows.net(缓存存储账户Blob端点)、*.azure.com(Azure核心服务域)
    • 协议:HTTPS(TCP 443)
  • 网络规则:
    • 直接引用Azure服务标签AzureSiteRecovery作为目标,无需手动指定IP范围
    • 允许端口:TCP 443、TCP 9443(ASR代理通信端口)

2. 配置缓存存储账户的访问权限

针对module.storage_account_asr_cache对应的存储账户:

  • 在存储账户的防火墙和虚拟网络设置中,添加源虚拟机所在的子网到允许列表
  • 勾选"允许受信任的Microsoft服务访问此存储账户",确保ASR服务能正常访问缓存存储

3. 延长Terraform轮询超时(可选)

Firewall可能增加请求延迟,导致默认超时触发错误:
在azurerm_site_recovery_replicated_vm资源中添加超时配置:

resource "azurerm_site_recovery_replicated_vm" "windows_bastion" {
  // 其他配置保持不变
  timeouts {
    create = "60m"
    update = "60m"
  }
}

4. 验证ASR代理连通性

在源虚拟机上执行以下命令,确认代理能正常连接ASR服务:

Test-NetConnection hypervrecoverymanager.windowsazure.com -Port 443

同时在Azure门户的Recovery Services Vault中,检查源虚拟机的ASR代理注册状态是否正常。


内容的提问来源于stack exchange,提问作者Prashant Shetage

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 04:02:05