Azure Site Recovery para VMs Multi-Região

Azure Site Recovery para VMs Multi-Região

Neste artigo você vai configurar o Azure Site Recovery para replicar VMs de Brazil South para East US com RPO de 5 minutos, usando o Recovery Services Vault criado com Terraform. A infraestrutura define fabrics, protection containers, replication policy e network mapping — tudo versionado em código. Na sequência, você usa scripts PowerShell para habilitar a replicação em nível de VM, verificar a saúde do RPO e executar um Test Failover sem impacto em produção.

Série: DR Azure e Landing Zone
  • 📖 Art. 01: Planejamento de DR Azure e Landing Zone
  • 📖 Art. 02: Hub-Spoke Landing Zone com Terraform
  • 📖 Art. 03: Azure Firewall e NSGs na Landing Zone
  • 📖 Art. 04: Azure Bastion na Landing Zone sem IP Público
  • 📖 Art. 05: Azure Front Door e Traffic Manager para Failover
  • 📖 Art. 06: DNS Privado Multi-Região no Azure
  • ⚙️ Art. 07 (este): Azure Site Recovery para VMs Multi-Região
  • 🔒 Art. 08: Azure Storage com Geo-Replicação para DR
  • 🔒 Art. 09: AKS Multi-Região com Failover no Azure
  • 🔒 Art. 10: Velero no AKS para Backup e Restore Cross-Region
  • 🔒 Art. 11: Runbooks de Failover no Azure Automation
  • 🔒 Art. 12: Simular um DR no Azure sem Impacto em Produção
  • 🔒 Art. 13: Monitoramento do DR no Azure com Azure Monitor

Sumário

Por que usar Azure Site Recovery para DR de VMs

O Azure Backup protege contra corrupção de dados e deleção acidental, mas não protege contra falha de região. Se a região Brazil South ficar indisponível, os backups armazenados na mesma região ficam inacessíveis junto com as VMs. O Azure Site Recovery resolve esse problema replicando continuamente os discos das VMs para uma região secundária, mantendo snapshots consistentes que permitem um failover rápido para East US.

CaracterísticaAzure BackupAzure Site Recovery
Proteção contra falha de regiãoNão (sem GRS para VM backups)Sim — réplica contínua em outra região
RPO típico24h (1x ao dia)5 minutos (crash-consistent)
RTO típicoHoras (restore completo)Minutos (failover de réplica existente)
ConsistênciaCrash-consistent ou app-consistentCrash-consistent a cada 5min + app-consistent a cada 4h
Custo adicionalStorage de vaultReplication + cache storage + compute na região alvo (apenas durante failover)

O ASR mantém a VM replicada desligada em East US — você só paga pelo armazenamento dos discos replicados. O compute em East US é ativado apenas durante um failover real ou um Test Failover.

Arquitetura do lab

O Recovery Services Vault fica em East US, na região de destino do failover. Isso é uma boa prática: se Brazil South falhar completamente, o vault precisa estar acessível na região que ficará ativa. O fluxo de replicação passa por uma Storage Account de cache (também em East US) que armazena temporariamente os dados antes de gravá-los nos discos da réplica:

VM em Brazil South (origem)
    │
    ▼ replicação contínua (porta 443)
stblogcastilhocache (East US)  ← cache temporário de dados de replicação
    │
    ▼ gravação nos discos
Discos replicados em East US (desligados)
    │
    ▼ em caso de failover
rsv-blog-castilho-eastus → ativa a VM em vnet-spoke-blog-castilho-eastus
RecursoTipoRegiãoResource Group
rsv-blog-castilho-eastusRecovery Services VaultEast USrg-blog-castilho-network-eastus
fabric-brazilsouthASR Fabric (origem)Brazil Southno vault acima
fabric-eastusASR Fabric (destino)East USno vault acima
policy-blog-castilho-5minReplication Policyno vault acima
stblogcastilhocacheStorage Account (cache)East USrg-blog-castilho-network-eastus

O network mapping garante que, após um failover, as NICs das VMs que estavam em vnet-spoke-blog-castilho-brazilsouth sejam criadas em vnet-spoke-blog-castilho-eastus automaticamente — sem precisar reconfigurar endereçamento manualmente.

Pré-requisitos

  • Hub-Spoke Landing Zone implantada (Arts. 02-06) — as quatro VNets e DNS privado existentes
  • Terraform ≥ 1.5 e Azure CLI autenticado (az login)
  • Storage Account de remote state configurado (bootstrap do Art. 02)
  • Módulo Az do PowerShell instalado: Install-Module -Name Az -AllowClobber -Scope CurrentUser
  • Storage Account de cache (stblogcastilhocache) — deployado com o Art. 08

Dependência: Os Passos 1 e 2 (Terraform) podem ser executados agora. Os Passos 3-6 (PowerShell para replicar VMs individuais) requerem que a Storage Account de cache stblogcastilhocache já exista — ela é criada junto com o Art. 08. Execute o deploy do Art. 08 antes de habilitar a replicação de VMs.

Passo 1 — Código Terraform do Recovery Services Vault

O Terraform do Art. 07 cria toda a infraestrutura do vault em um único apply: o RSV, dois fabrics (um por região), dois protection containers, a política de replicação, o mapeamento de containers e o network mapping. Veja o código completo do main.tf:

# Data sources — referências às VNets existentes
data "azurerm_resource_group" "this" {
  name = "rg-blog-castilho-network-eastus"
}

data "azurerm_virtual_network" "spoke_brazilsouth" {
  name                = "vnet-spoke-blog-castilho-brazilsouth"
  resource_group_name = "rg-blog-castilho-network-brazilsouth"
}

data "azurerm_virtual_network" "spoke_eastus" {
  name                = "vnet-spoke-blog-castilho-eastus"
  resource_group_name = "rg-blog-castilho-network-eastus"
}

# Recovery Services Vault na região de destino (East US)
resource "azurerm_recovery_services_vault" "this" {
  name                = "rsv-blog-castilho-eastus"
  resource_group_name = data.azurerm_resource_group.this.name
  location            = data.azurerm_resource_group.this.location
  sku                 = "Standard"
  tags                = var.tags
}

# Fabrics — representam cada região no contexto do ASR
resource "azurerm_site_recovery_fabric" "brazilsouth" {
  name                = "fabric-brazilsouth"
  resource_group_name = data.azurerm_resource_group.this.name
  recovery_vault_name = azurerm_recovery_services_vault.this.name
  location            = "brazilsouth"
}

resource "azurerm_site_recovery_fabric" "eastus" {
  name                = "fabric-eastus"
  resource_group_name = data.azurerm_resource_group.this.name
  recovery_vault_name = azurerm_recovery_services_vault.this.name
  location            = data.azurerm_resource_group.this.location
}

# Protection Containers — agrupam os itens protegidos por fabric
resource "azurerm_site_recovery_protection_container" "brazilsouth" {
  name                 = "container-brazilsouth"
  resource_group_name  = data.azurerm_resource_group.this.name
  recovery_vault_name  = azurerm_recovery_services_vault.this.name
  recovery_fabric_name = azurerm_site_recovery_fabric.brazilsouth.name
}

resource "azurerm_site_recovery_protection_container" "eastus" {
  name                 = "container-eastus"
  resource_group_name  = data.azurerm_resource_group.this.name
  recovery_vault_name  = azurerm_recovery_services_vault.this.name
  recovery_fabric_name = azurerm_site_recovery_fabric.eastus.name
}

# Replication Policy — define RPO e frequência de snapshots
resource "azurerm_site_recovery_replication_policy" "this" {
  name                                                 = "policy-blog-castilho-5min"
  resource_group_name                                  = data.azurerm_resource_group.this.name
  recovery_vault_name                                  = azurerm_recovery_services_vault.this.name
  recovery_point_retention_in_minutes                  = 1440   # 24h de histórico de recovery points
  application_consistent_snapshot_frequency_in_minutes = 240    # snapshot app-consistent a cada 4h
}

# Container Mapping — vincula origem e destino com a policy
resource "azurerm_site_recovery_protection_container_mapping" "this" {
  name                                      = "mapping-brazilsouth-to-eastus"
  resource_group_name                       = data.azurerm_resource_group.this.name
  recovery_vault_name                       = azurerm_recovery_services_vault.this.name
  recovery_fabric_name                      = azurerm_site_recovery_fabric.brazilsouth.name
  recovery_source_protection_container_name = azurerm_site_recovery_protection_container.brazilsouth.name
  recovery_target_protection_container_id   = azurerm_site_recovery_protection_container.eastus.id
  recovery_replication_policy_id            = azurerm_site_recovery_replication_policy.this.id
}

# Network Mapping — define VNet destino para as NICs após failover
resource "azurerm_site_recovery_network_mapping" "this" {
  name                        = "nm-brazilsouth-to-eastus"
  resource_group_name         = data.azurerm_resource_group.this.name
  recovery_vault_name         = azurerm_recovery_services_vault.this.name
  source_recovery_fabric_name = azurerm_site_recovery_fabric.brazilsouth.name
  target_recovery_fabric_name = azurerm_site_recovery_fabric.eastus.name
  source_network_id           = data.azurerm_virtual_network.spoke_brazilsouth.id
  target_network_id           = data.azurerm_virtual_network.spoke_eastus.id
}

Pontos importantes do código:

  • RSV em East US: o vault fica na região de destino, não na origem. Se Brazil South falhar, o vault ainda estará disponível em East US.
  • Fabrics representam regiões: um fabric por região define o contexto geográfico do ASR. Cada fabric contém um protection container que agrupa as VMs a replicar.
  • recovery_point_retention_in_minutes = 1440: mantém 24 horas de recovery points — o suficiente para escolher um ponto anterior em caso de corrupção de dados.
  • application_consistent_snapshot_frequency_in_minutes = 240: a cada 4 horas o ASR tira um snapshot com consistência de aplicação (VSS no Windows / freeze no Linux). Entre eles, snapshots crash-consistent a cada 5 minutos.
  • Network Mapping: garante que após o failover as NICs das VMs sejam conectadas à VNet Spoke de East US automaticamente.

Passo 2 — Deploy com Terraform

Use o script deploy-infra.sh da série para criar o RSV com todos os seus recursos de uma vez. O deploy leva aproximadamente 10-15 minutos porque o provisionamento dos fabrics exige que o Azure registre os endpoints de replicação em ambas as regiões:

# Deploy somente do Art. 07 (RSV + fabrics + policy + mappings)
cd scripts/
bash deploy-infra.sh art-07

# Output esperado:
# ═══ ONDA: Art-07 — Recovery Services Vault ════════════════
# [HH:MM:SS] START  rsv-blog-castilho-eastus
# [HH:MM:SS] OK     rsv-blog-castilho-eastus

# Verificar o vault criado
az resource show \
  --resource-group rg-blog-castilho-network-eastus \
  --resource-type "Microsoft.RecoveryServices/vaults" \
  --name rsv-blog-castilho-eastus \
  --query "{name:name, location:location, sku:sku.name, state:properties.provisioningState}" \
  --output table

# Verificar a replication policy criada (RPO 5 min, 24h de retention)
az rest \
  --method get \
  --url "https://management.azure.com/subscriptions/\$AZURE_SUBSCRIPTION_ID/resourceGroups/rg-blog-castilho-network-eastus/providers/Microsoft.RecoveryServices/vaults/rsv-blog-castilho-eastus/replicationPolicies?api-version=2023-06-01" \
  --query "value[].{name:name, rpo:properties.providerSpecificDetails.appConsistentFrequencyInMinutes, retention:properties.providerSpecificDetails.recoveryPointHistory}" \
  --output table

# Exemplo de output:
# Name                                          Rpo    Retention
# --------------------------------------------  -----  ---------
# policy-rsv-blog-castilho-eastus-1440min       5      1440

Após o deploy, o vault existe mas ainda não há VMs replicadas. Os próximos passos usam PowerShell para habilitar a replicação VM a VM.

Passo 3 — Habilitar replicação de uma VM

O script 01-configure-replication.ps1 recebe o nome de uma VM e configura sua replicação para East US. Ele lê os discos da VM e cria um AzureToAzureDiskReplicationConfig para cada disco, apontando para a Storage Account de cache e para o Resource Group de destino:

# Habilitar replicação de uma VM específica
# Requer: stblogcastilhocache já deployada (Art. 08)
./01-configure-replication.ps1 -VmName "vm-blog-castilho-01"

# O script executa os seguintes passos:
# 1. Obtém o contexto do vault rsv-blog-castilho-eastus
# 2. Busca fabrics, containers, policy e mapping já criados pelo Terraform
# 3. Para cada disco da VM, cria um DiskReplicationConfig com:
#    - LogStorageAccountId: stblogcastilhocache (cache temporário)
#    - RecoveryResourceGroupId: rg-blog-castilho-workload-eastus
#    - RecoveryReplicaDiskAccountType: Premium_LRS
# 4. Inicia o job de replicação assíncrono

# Output esperado:
# Replicação iniciada. Job ID: a1b2c3d4-...
# Acompanhe com: Get-AzRecoveryServicesAsrJob -Name a1b2c3d4-...

O script completo com os parâmetros aceitos:

param(
    [Parameter(Mandatory = $true)]
    [string]$VmName,

    [string]$SourceResourceGroup = "rg-blog-castilho-workload-brazilsouth",
    [string]$VaultResourceGroup  = "rg-blog-castilho-network-eastus",
    [string]$VaultName           = "rsv-blog-castilho-eastus",
    [string]$TargetResourceGroup = "rg-blog-castilho-workload-eastus",
    [string]$CacheStorageAccount = "stblogcastilhocache"
)

A sincronização inicial (initial replication) pode levar de minutos a horas dependendo do tamanho dos discos e da bandwidth disponível. Durante esse período, o status da VM será InitialReplicationInProgress. Somente após concluída, o RPO começa a ser calculado.

Passo 4 — Verificar saúde da replicação e RPO

Use o script 02-check-replication-health.ps1 para verificar o status de todas as VMs replicadas no container de Brazil South, incluindo o RPO atual em minutos:

./02-check-replication-health.ps1

# Exemplo de output (após sincronização inicial concluída):
# FriendlyName          ReplicationHealth  RPO (min)  Último sync
# --------------------  -----------------  ---------  ---------------------
# vm-blog-castilho-01   Normal             2.3        2026-06-03 15:42:18
# vm-blog-castilho-02   Normal             1.8        2026-06-03 15:42:05

O campo ReplicationHealth pode ter três valores:

StatusSignificadoAção
NormalReplicação saudável, RPO dentro do objetivoNenhuma — monitoramento de rotina
WarningRPO acima do alvo mas replicação ativaVerificar conectividade e largura de banda entre as regiões
CriticalReplicação parada ou RPO muito altoVerificar logs no portal e reiniciar o agente de mobilidade se necessário

Para acompanhar o progresso da sincronização inicial de uma VM específica:

# Verificar status do job de replicação (substitua pelo Job ID retornado no Passo 3)
Get-AzRecoveryServicesAsrJob -Name "" | Select-Object State, StateDescription, StartTime, EndTime

# Ou verificar diretamente o item protegido
$vault = Get-AzRecoveryServicesVault -ResourceGroupName "rg-blog-castilho-network-eastus" -Name "rsv-blog-castilho-eastus"
Set-AzRecoveryServicesAsrVaultContext -Vault $vault
$fabric = Get-AzRecoveryServicesAsrFabric -Name "fabric-brazilsouth"
$container = Get-AzRecoveryServicesAsrProtectionContainer -Fabric $fabric -Name "container-brazilsouth"

Get-AzRecoveryServicesAsrReplicationProtectedItem -ProtectionContainer $container |
  Where-Object { $_.FriendlyName -eq "vm-blog-castilho-01" } |
  Select-Object FriendlyName, ProtectionState, ProtectionStateDescription

Passo 5 — Executar Test Failover

O Test Failover cria uma cópia temporária da VM em East US em uma VNet de isolamento — sem impactar a VM de produção em Brazil South nem redirecionar tráfego real. É a forma recomendada de validar periodicamente que o DR funciona antes de precisar dele de verdade.

# Criar uma VNet de isolamento para o Test Failover (boa prática — não usar a VNet de produção)
az network vnet create \
  --resource-group rg-blog-castilho-workload-eastus \
  --name vnet-test-failover \
  --address-prefix 192.168.0.0/24 \
  --subnet-name snet-test \
  --subnet-prefix 192.168.0.0/24

# Obter o ID da VNet de teste
TEST_VNET_ID=$(az network vnet show \
  --resource-group rg-blog-castilho-workload-eastus \
  --name vnet-test-failover \
  --query id --output tsv)

# Executar o Test Failover
./03-test-failover.ps1 -VmName "vm-blog-castilho-01" -TestNetworkId $TEST_VNET_ID

# Output esperado:
# Iniciando Test Failover para vm-blog-castilho-01...
# Recovery Point: 2026-06-03 15:40:00
# Test Failover iniciado. Job: b2c3d4e5-...
# Após validar a VM em East US, execute: ./04-cleanup-test-failover.ps1 -VmName vm-blog-castilho-01

O script usa o recovery point mais recente disponível e inicia o job de Test Failover. Quando concluído (5 a 15 minutos), a VM de teste fica visível no portal com o sufixo -test no Resource Group rg-blog-castilho-workload-eastus. Valide que ela inicializou corretamente, que os serviços estão respondendo e que os dados estão íntegros antes de prosseguir para o cleanup.

# Acompanhar o job de Test Failover
$vault = Get-AzRecoveryServicesVault -ResourceGroupName "rg-blog-castilho-network-eastus" -Name "rsv-blog-castilho-eastus"
Set-AzRecoveryServicesAsrVaultContext -Vault $vault

Get-AzRecoveryServicesAsrJob -Name "" |
  Select-Object DisplayName, State, StartTime, EndTime

# Listar a VM criada pelo Test Failover
az vm list --resource-group rg-blog-castilho-workload-eastus \
  --query "[?contains(name,'-test')].{name:name, powerState:powerState}" \
  --show-details --output table

Passo 6 — Cleanup do Test Failover

Após validar a VM de teste, execute o cleanup para remover os recursos temporários criados pelo Test Failover. Esse passo é obrigatório — enquanto o Test Failover não for encerrado, não é possível executar um failover real ou um novo Test Failover para a mesma VM:

# Encerrar o Test Failover e remover recursos temporários
./04-cleanup-test-failover.ps1 -VmName "vm-blog-castilho-01"

# Output esperado:
# Cleanup do Test Failover iniciado. Job: c3d4e5f6-...

# Após o cleanup, verificar que não há Test Failover pendente
$fabric = Get-AzRecoveryServicesAsrFabric -Name "fabric-brazilsouth"
$container = Get-AzRecoveryServicesAsrProtectionContainer -Fabric $fabric -Name "container-brazilsouth"
Get-AzRecoveryServicesAsrReplicationProtectedItem -ProtectionContainer $container |
  Where-Object { $_.FriendlyName -eq "vm-blog-castilho-01" } |
  Select-Object FriendlyName, ProtectionState, TestFailoverState

# ProtectionState deve ser "Protected" e TestFailoverState deve ser "None"

Lembre de remover também a VNet de isolamento criada para o teste, caso não vá reutilizá-la:

az network vnet delete \
  --resource-group rg-blog-castilho-workload-eastus \
  --name vnet-test-failover

Troubleshooting

ProblemaCausaSolução
Fabric demora mais de 30min para criarProvisionamento do endpoint de replicação na regiãoNormal em Brazil South — aguardar. Verificar no portal: Recovery Services → Site Recovery Infrastructure → Fabrics
soft_delete_enabled = false falha no TerraformProvider azurerm v4+ não permite desabilitar soft deleteRemover o argumento — soft delete é obrigatório no Azure por política de segurança
Erro ao habilitar replicação da VM: cache storage não encontradostblogcastilhocache não foi deployado aindaExecutar o deploy do Art. 08 primeiro: bash deploy-infra.sh art-08
RPO em Warning ou CriticalLargura de banda insuficiente ou conectividade interrompidaVerificar NSGs (porta 443 outbound liberada), Azure Monitor → Replication Health
Test Failover falha com erro de VNetVNet de isolamento não fornecida ou incorretaCriar VNet de isolamento específica (não usar VNet de produção) e passar o ID correto
Não é possível iniciar novo Test FailoverTest Failover anterior não foi encerrado com cleanupExecutar 04-cleanup-test-failover.ps1 para a VM em questão

Limpeza dos recursos

Antes de destruir o vault via Terraform, remova todos os itens de replicação protegidos. O Azure não permite deletar um vault com itens ativos:

# 1. Desabilitar replicação de cada VM (PowerShell)
$vault = Get-AzRecoveryServicesVault -ResourceGroupName "rg-blog-castilho-network-eastus" -Name "rsv-blog-castilho-eastus"
Set-AzRecoveryServicesAsrVaultContext -Vault $vault
$fabric = Get-AzRecoveryServicesAsrFabric -Name "fabric-brazilsouth"
$container = Get-AzRecoveryServicesAsrProtectionContainer -Fabric $fabric -Name "container-brazilsouth"

# Para cada item protegido
$items = Get-AzRecoveryServicesAsrReplicationProtectedItem -ProtectionContainer $container
foreach ($item in $items) {
    Remove-AzRecoveryServicesAsrReplicationProtectedItem -ReplicationProtectedItem $item -Force
}

# 2. Após remover todos os itens, destruir via Terraform
cd scripts/art-07-site-recovery/rsv-blog-castilho-eastus
terraform destroy -var subscription_id="$AZURE_SUBSCRIPTION_ID"
# Remove: vault, 2 fabrics, 2 containers, policy, container mapping, network mapping

Próximos passos

Com o Azure Site Recovery configurado e os fabrics provisionados, a infraestrutura de replicação de VMs está pronta. O próximo artigo da série cobre o Azure Storage com geo-replicação para DR: criação da Storage Account primária com RAGZRS em Brazil South, Private Endpoints em ambas as regiões, e a Storage Account de cache (stblogcastilhocache) em East US — que também é o pré-requisito para habilitar a replicação de VMs deste artigo.

Série: DR Azure e Landing Zone
  • 📖 Art. 01: Planejamento de DR Azure e Landing Zone
  • 📖 Art. 02: Hub-Spoke Landing Zone com Terraform
  • 📖 Art. 03: Azure Firewall e NSGs na Landing Zone
  • 📖 Art. 04: Azure Bastion na Landing Zone sem IP Público
  • 📖 Art. 05: Azure Front Door e Traffic Manager para Failover
  • 📖 Art. 06: DNS Privado Multi-Região no Azure
  • ⚙️ Art. 07 (este): Azure Site Recovery para VMs Multi-Região
  • 🔒 Art. 08: Azure Storage com Geo-Replicação para DR
  • 🔒 Art. 09: AKS Multi-Região com Failover no Azure
  • 🔒 Art. 10: Velero no AKS para Backup e Restore Cross-Region
  • 🔒 Art. 11: Runbooks de Failover no Azure Automation
  • 🔒 Art. 12: Simular um DR no Azure sem Impacto em Produção
  • 🔒 Art. 13: Monitoramento do DR no Azure com Azure Monitor

Interessado em saber mais sobre artigos relacionados ao Microsoft Azure CLIQUE AQUI

🚀 Vamos nos conectar?

Não perca nenhuma oportunidade! Cadastre-se nas minhas redes e no canal do YouTube para receber conteúdos de TI, Cloud, Azure, Kubernetes e DevOps em primeira mão.

Dica: No Facebook, todos os artigos do blog são publicados automaticamente. Vale a pena curtir!


💬 Dúvidas ou Problemas?

Com o intuito de ajudar a comunidade, caso você tenha dúvidas ou encontre problemas na execução dos comandos deste artigo, deixe um comentário abaixo. Responderei o mais breve possível!

Muito obrigado pela visita e até o próximo post!

Jefferson Castilho Especialista em Cloud & DevOps.

Este guia técnico é exclusivo do Blog do Castilho. Explore nossa para mais conteúdos sobre IA e Cloud.

 

Deixe uma resposta

Descubra mais sobre Blog do Castilho - Tecnologia | FinOps | DevOps | Cloud

Assine agora mesmo para continuar lendo e ter acesso ao arquivo completo.

Continuar lendo