Skip to content

DDE Azure Site Recovery — Configuration Planning

Environment: Azure DDE (tenant ff1c5d68, ciriusdde.com) Scope: Disaster recovery for DDE VMs per Defender for Cloud finding Status: Planning doc — Terraform implementation is a separate PR in azure-dde-infra, requires Rory approval Related story: DDEENV-032 (DDE-020)


Why ASR for DDE

DDE is customer-facing Medicare billing. Loss of the AVD environment or the identity plane means customers cannot submit claims. HIPAA and HITRUST expect a documented DR strategy with tested RTO/RPO targets. Defender for Cloud flags DDE VMs as "unprotected" when they have no DR configuration, and this is a HIPAA-relevant finding.


DR Strategy Decision Matrix

Before provisioning ASR for a VM, classify it:

VM RoleDR StrategyReason
AVD session host (bc-host-*)Cross-region image pool, NOT ASR per instanceSession hosts are stateless; rebuild from image in DR region is faster and cheaper than per-VM replication
Domain controllers (ACTDIRAZC01, ACTDIRAZC02)Cross-region backup + rebuild or ASRRebuild from a good backup is often cleaner than replicating AD state — replicating a compromised DC replicates the compromise
Entra Connect (ENTRACAZC01)ASRStateful config, painful to rebuild; ASR gets it back quickly
Application/file serversASRStateful, bespoke configuration
Jump / bastion hostsRebuild from imageStateless
Test / sandbox VMsNo DRNot in HIPAA scope

Default when unsure: crash-consistent backup to Recovery Services Vault with cross-region restore enabled is the minimum. ASR is layered on top for the VMs that need lower RTO.


Target Region

SourceTarget
DDE primary region (Central US)East US 2 as Azure DR region (paired region, Azure-managed)

Cross-region pairing is the default — avoids manual capacity management in the secondary. East US 2 is the Microsoft-paired region for Central US.


VMs in Scope (April 2026 Inventory)

The Defender for Cloud finding lists 14 DDE VMs as unprotected. Classification below reflects current understanding; confirm against the live inventory before the Terraform PR lands.

VM Name PatternCount (approx)DR Strategy
bc-host-* (AVD session hosts)Variable (pool-driven)Cross-region image pool — no per-VM ASR
ACTDIRAZC01, ACTDIRAZC02 (DDE DCs)2ASR or cross-region backup + documented rebuild
ENTRACAZC01 (Entra Connect)1ASR
Other stateful DDE VMsTBD during inventoryCase-by-case

Inventory source: run az vm list --subscription <dde-sub-id> plus a Defender for Cloud export filtered to the "Virtual machines should have disaster recovery configured" finding before finalizing the PR.


Terraform Resources Required

The azure-dde-infra PR creates:

Recovery Services Vault (prerequisite — may already exist)

hcl
resource "azurerm_recovery_services_vault" "dde_asr" {
  name                = "rsv-dde-asr"
  location            = "eastus2"             # target region hosts the vault
  resource_group_name = azurerm_resource_group.dde_asr.name
  sku                 = "Standard"
  soft_delete_enabled = true
  storage_mode_type   = "GeoRedundant"
  cross_region_restore_enabled = true
  tags                = local.tags            # required tag set per TF standards
}

Note: reuse module recovery-vault if already established in azure-dde-infra to get diagnostic settings, immutability (Unlocked), and tagging for free.

ASR Fabric, Container, Network Mapping

hcl
resource "azurerm_site_recovery_fabric" "primary" {
  name                = "fabric-centralus"
  resource_group_name = azurerm_resource_group.dde_asr.name
  recovery_vault_name = azurerm_recovery_services_vault.dde_asr.name
  location            = "centralus"
}

resource "azurerm_site_recovery_fabric" "secondary" {
  name                = "fabric-eastus2"
  resource_group_name = azurerm_resource_group.dde_asr.name
  recovery_vault_name = azurerm_recovery_services_vault.dde_asr.name
  location            = "eastus2"
}

resource "azurerm_site_recovery_protection_container" "primary" {
  name                 = "container-centralus"
  resource_group_name  = azurerm_resource_group.dde_asr.name
  recovery_vault_name  = azurerm_recovery_services_vault.dde_asr.name
  recovery_fabric_name = azurerm_site_recovery_fabric.primary.name
}

resource "azurerm_site_recovery_protection_container" "secondary" {
  name                 = "container-eastus2"
  resource_group_name  = azurerm_resource_group.dde_asr.name
  recovery_vault_name  = azurerm_recovery_services_vault.dde_asr.name
  recovery_fabric_name = azurerm_site_recovery_fabric.secondary.name
}

resource "azurerm_site_recovery_network_mapping" "main" {
  name                        = "netmap-centralus-to-eastus2"
  resource_group_name         = azurerm_resource_group.dde_asr.name
  recovery_vault_name         = azurerm_recovery_services_vault.dde_asr.name
  source_recovery_fabric_name = azurerm_site_recovery_fabric.primary.name
  target_recovery_fabric_name = azurerm_site_recovery_fabric.secondary.name
  source_network_id           = data.azurerm_virtual_network.dde_primary.id
  target_network_id           = data.azurerm_virtual_network.dde_secondary.id
}

Replication Policy

hcl
resource "azurerm_site_recovery_replication_policy" "main" {
  name                                                 = "policy-dde-asr"
  resource_group_name                                  = azurerm_resource_group.dde_asr.name
  recovery_vault_name                                  = azurerm_recovery_services_vault.dde_asr.name
  recovery_point_retention_in_minutes                  = 24 * 60      # 24h
  application_consistent_snapshot_frequency_in_minutes = 4 * 60       # 4h
}

resource "azurerm_site_recovery_protection_container_mapping" "main" {
  name                                      = "mapping-centralus-to-eastus2"
  resource_group_name                       = azurerm_resource_group.dde_asr.name
  recovery_vault_name                       = azurerm_recovery_services_vault.dde_asr.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_name = azurerm_site_recovery_protection_container.secondary.name
  recovery_replication_policy_id            = azurerm_site_recovery_replication_policy.main.id
}

Per-VM Replicated Item

Create one azurerm_site_recovery_replicated_vm per in-scope VM. Start with the highest-value stateful VMs (Entra Connect, any app servers) and measure cost before expanding.


Prerequisites (Must Exist Before PR Merges)

  1. Recovery Services Vault in the target region (East US 2), with geo-redundant storage and soft-delete enabled.
  2. Target VNet and subnet in East US 2 provisioned and peered (or routable) to the DDE primary VNet — the network mapping references this.
  3. Target resource group in East US 2 for the replicated VM resources.
  4. Target storage account (cache storage) for replication logs — ASR provisions this automatically on first replication, but the RG must exist.
  5. Diagnostic settings on the RSV forwarding to cirius-logging-law-central.
  6. IAM: github-deploy-main (DDE) needs Contributor on the ASR resource group and User Access Administrator if it assigns replication identity roles.
  7. Inventory confirmed — the Defender for Cloud VM list matches the expected scope; any VM classified as "no DR" has an explicit justification.

RTO / RPO Targets (Draft — Rory Approves)

TierVM ClassRTORPO
Tier 1Entra Connect, app servers1 hour15 minutes
Tier 2Domain controllers (via rebuild)4 hours24 hours
Tier 3AVD session hosts (rebuild from image)4 hoursN/A (stateless)

Testing and Validation

Ongoing:

  1. Test failover per Tier 1 VM quarterly — uses the replication without interrupting prod. Validate that the replicated VM boots, joins the network, and the application responds.
  2. Unplanned failover drill annually — simulated region outage, full DR execution against the runbook. Document RTO/RPO actuals vs. targets.
  3. Replication health alerts — enable ReplicationHealth alerts on the RSV, route to SecOps action group.

Out of Scope

  • AVD session host image management — handled by separate image build pipeline; ASR is not the right answer for ephemeral hosts.
  • AWS DR — separate workstream in aws-infra, uses cross-region AMI replication and RDS snapshots, not ASR.
  • Database replication — psql/SQL replication handled at the data tier, not ASR.

Implementation Process

  1. Confirm DDE VM inventory against the Defender for Cloud finding.
  2. Build the Terraform in azure-dde-infra on a branch feature/dde-asr.
  3. Ensure all prerequisites above are met (vault exists, target VNet exists, etc.); if any prerequisite is missing, land that as a smaller PR first.
  4. Open PR — CI runs terraform fmt, Checkov, terraform plan.
  5. Rory reviews the plan — confirms VM classification, target region choice, and cost impact.
  6. Merge triggers apply via OIDC.
  7. After apply, wait for initial replication to reach "Protected" state on each VM (can take hours).
  8. Run a test failover per Tier 1 VM; document results.
  9. Close DDEENV-032 with evidence: Defender for Cloud finding clear, test failover artifacts attached.

  • runbooks/backup-architecture.md — backup strategy that ASR complements
  • security/dde-subscription-security-controls.md — broader DDE baseline
  • security/terraform-standards.md — module usage, tagging, compliance defaults

Internal use only — Cirius Group