Skip to content

Instantly share code, notes, and snippets.

@adbertram
Created July 18, 2024 16:52
Show Gist options
  • Save adbertram/b6817436a9689545f2ce7793373d436a to your computer and use it in GitHub Desktop.
Save adbertram/b6817436a9689545f2ce7793373d436a to your computer and use it in GitHub Desktop.
N2WS Azure Site Recovery Example
# Corrected Azure Site Recovery PowerShell Script with Azure CLI Commands
# Set variables (modify these as needed)
$ResourceGroup = "MyASRTestRG"
$Location = "eastus"
$RecoveryLocation = "westus"
$VaultName = "MyASRTestVault"
$PrimaryVNet = "PrimaryTestVNet"
$RecoveryVNet = "RecoveryTestVNet"
$StorageAccount = "myasrteststorage"
$RecoveryPlan = "EcommerceTestRecoveryPlan"
$TestVNet = "TestFailoverVNet"
$ContainerName = "scripts"
# VM Configuration
$VMConfig = @(
@{Name="VM-DB1"; Size="Standard_DS2_v2"; Image="Win2019Datacenter"},
@{Name="VM-APP1"; Size="Standard_DS2_v2"; Image="Win2019Datacenter"},
@{Name="VM-WEB1"; Size="Standard_DS2_v2"; Image="Win2019Datacenter"}
)
# Ensure you're logged in to Azure CLI
az login
# Create Resource Group
az group create --name $ResourceGroup --location $Location
# Create Recovery Services Vault
az backup vault create --name $VaultName --resource-group $ResourceGroup --location $Location
# Create Primary VNet
az network vnet create --resource-group $ResourceGroup --name $PrimaryVNet --address-prefixes 10.0.0.0/16 --subnet-name default --subnet-prefix 10.0.0.0/24 --location $Location
# Create Recovery VNet
az network vnet create --resource-group $ResourceGroup --name $RecoveryVNet --address-prefixes 10.1.0.0/16 --subnet-name default --subnet-prefix 10.1.0.0/24 --location $RecoveryLocation
# Create Test Failover VNet
az network vnet create --resource-group $ResourceGroup --name $TestVNet --address-prefixes 10.2.0.0/16 --subnet-name default --subnet-prefix 10.2.0.0/24 --location $RecoveryLocation
# Create subnets for ASR
az network vnet subnet create `
--name ASR_Subnet `
--resource-group $ResourceGroup `
--vnet-name $PrimaryVNet `
--address-prefix 10.0.1.0/24
az network vnet subnet create `
--name ASR_Subnet `
--resource-group $ResourceGroup `
--vnet-name $RecoveryVNet `
--address-prefix 10.1.1.0/24
# Set up VNet peering
az network vnet peering create `
--name PrimaryToRecovery `
--resource-group $ResourceGroup `
--vnet-name $PrimaryVNet `
--remote-vnet $RecoveryVNet `
--allow-vnet-access
az network vnet peering create `
--name RecoveryToPrimary `
--resource-group $ResourceGroup `
--vnet-name $RecoveryVNet `
--remote-vnet $PrimaryVNet `
--allow-vnet-access
# Create and configure Network Security Groups (NSGs)
az network nsg create --name ASR_NSG --resource-group $ResourceGroup --location $Location
az network nsg rule create `
--name AllowASRTraffic `
--nsg-name ASR_NSG `
--priority 100 `
--resource-group $ResourceGroup `
--access Allow `
--protocol Tcp `
--direction Inbound `
--source-address-prefixes AzureSiteRecovery `
--source-port-ranges "*" `
--destination-address-prefixes "*" `
--destination-port-ranges 443
# Configure storage
az storage account create `
--name $StorageAccount `
--resource-group $ResourceGroup `
--location $Location `
--sku Standard_RAGRS `
--kind StorageV2
# Create a container for scripts
az storage container create --name $ContainerName --account-name $StorageAccount
# Create check_db.ps1 script
$CheckDbScript = @"
# check_db.ps1
`$ServerInstance = "VM-DB1"
`$Database = "TestDB"
try {
`$query = "SELECT COUNT(*) FROM sys.databases WHERE name = '`$Database'"
`$result = Invoke-Sqlcmd -ServerInstance `$ServerInstance -Query `$query
if (`$result.Column1 -eq 1) {
Write-Output "Database '`$Database' exists and is accessible."
# Perform a simple query to check data
`$dataQuery = "SELECT TOP 1 * FROM TestTable"
`$dataResult = Invoke-Sqlcmd -ServerInstance `$ServerInstance -Database `$Database -Query `$dataQuery
if (`$dataResult) {
Write-Output "Successfully queried data from the database."
} else {
Write-Output "Database exists but no data retrieved. Further investigation may be needed."
}
} else {
Write-Error "Database '`$Database' does not exist or is not accessible."
}
} catch {
Write-Error "An error occurred: `$(`$_.Exception.Message)"
}
"@
# Save check_db.ps1 to a local file
$CheckDbScript | Out-File -FilePath "check_db.ps1" -Encoding utf8
# Upload check_db.ps1 to the storage account
az storage blob upload `
--account-name $StorageAccount `
--container-name $ContainerName `
--name check_db.ps1 `
--file check_db.ps1
# Create VMs
foreach ($vm in $VMConfig) {
az vm create `
--resource-group $ResourceGroup `
--name $vm.Name `
--image $vm.Image `
--size $vm.Size `
--vnet-name $PrimaryVNet `
--subnet default `
--admin-username azureuser `
--admin-password "ComplexPassword123!" `
--location $Location
}
# Enable backup for the VMs
foreach ($vm in $VMConfig) {
az backup protection enable-for-vm `
--resource-group $ResourceGroup `
--vault-name $VaultName `
--vm $vm.Name `
--policy-name DefaultPolicy
}
# Create a Recovery Services fabric
az site-recovery fabric create `
--resource-group $ResourceGroup `
--vault-name $VaultName `
--name "PrimaryFabric"
az site-recovery fabric create `
--resource-group $ResourceGroup `
--vault-name $VaultName `
--name "RecoveryFabric"
# Create a protection container in the primary fabric
az site-recovery protection-container create `
--fabric-name "PrimaryFabric" `
--resource-group $ResourceGroup `
--vault-name $VaultName `
--name "PrimaryProtectionContainer"
# Create a protection container in the recovery fabric
az site-recovery protection-container create `
--fabric-name "RecoveryFabric" `
--resource-group $ResourceGroup `
--vault-name $VaultName `
--name "RecoveryProtectionContainer"
# Create a replication policy
az site-recovery replication-policy create `
--name "ReplicationPolicy" `
--resource-group $ResourceGroup `
--vault-name $VaultName `
--recovery-point-retention-in-hours 24 `
--app-consistent-frequency-in-minutes 60
# Create protection container mapping between primary and recovery containers
az site-recovery protection-container-mapping create `
--name "ContainerMapping" `
--policy-name "ReplicationPolicy" `
--protection-container "PrimaryProtectionContainer" `
--resource-group $ResourceGroup `
--vault-name $VaultName `
--recovery-fabric "RecoveryFabric" `
--recovery-protection-container "RecoveryProtectionContainer"
# Enable replication for each VM
foreach ($vm in $VMConfig) {
az site-recovery replicated-item create `
--name $vm.Name `
--resource-group $ResourceGroup `
--vault-name $VaultName `
--source-vm $vm.Name `
--protection-container "PrimaryProtectionContainer" `
--recovery-fabric "RecoveryFabric" `
--recovery-protection-container "RecoveryProtectionContainer" `
--recovery-point-retention-in-hours 24
}
# Create a Recovery Plan
az site-recovery recovery-plan create `
--name $RecoveryPlan `
--resource-group $ResourceGroup `
--vault-name $VaultName `
--primary-fabric "PrimaryFabric" `
--recovery-fabric "RecoveryFabric"
# Add VMs to the Recovery Plan
$VMNames = $VMConfig.Name -join " "
az site-recovery recovery-plan update `
--name $RecoveryPlan `
--resource-group $ResourceGroup `
--vault-name $VaultName `
--add-protectable-items $VMNames
# Add custom actions to the Recovery Plan
$ScriptUri = "https://$StorageAccount.blob.core.windows.net/$ContainerName/check_db.ps1"
az site-recovery recovery-plan update `
--name $RecoveryPlan `
--resource-group $ResourceGroup `
--vault-name $VaultName `
--add-post-action `
"{`"resourceId`": `"`", `"actionType`": `"AutomationRunbookAction`", `"customDetails`": {`"runbookUri`": `"$ScriptUri`", `"name`": `"CheckDB`", `"description`": `"Check DB after failover`"}}"
az site-recovery recovery-plan update `
--name $RecoveryPlan `
--resource-group $ResourceGroup `
--vault-name $VaultName `
--add-pre-action `
"{`"resourceId`": `"`", `"actionType`": `"ManualAction`", `"customDetails`": {`"name`": `"Update DNS records`", `"description`": `"Update DNS A record for app.mycompany.com to point to new IP`"}}"
# Initiate a test failover
az site-recovery recovery-plan test-failover start `
--name $RecoveryPlan `
--resource-group $ResourceGroup `
--vault-name $VaultName `
--recovery-point Latest `
--test-network $TestVNet
# Clean up test failover
# Note: Run this after you've verified the test failover
az site-recovery recovery-plan test-failover cleanup `
--name $RecoveryPlan `
--resource-group $ResourceGroup `
--vault-name $VaultName
Write-Output "Azure Site Recovery setup and test completed."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment