Extending AlwaysOn Availability Group to Remote Azure Datacenter (PowerShell) If you are looking for multi region continuous availability, by extending your current availability group to a remote Azure datacenter, then this blog is for you. We start with an existing availability group already setup within Azure, walk you through setting up VPN connectivity by taking advantage of the Azure-to-Azure VPN functionality, extend your infrastructure and setup your asynchronous replica. At the end of this blog, your SQL Server AlwaysOn Availability Group in Azure will consist of the following elements: 2 virtual networks with VPN connectivity between West US and East US, as an example. A domain controller on each site with Active Directory (AD) and Domain Name Services (DNS). 2 synchronous replicas in WestUS and an asynchronous replica in EastUS. Synchronous was chosen for the WestUS replicas since they are within the same region and asynchronous for the EastUS replica since it is cross region. An extended availability group with an asynchronous commit replica of an availability database. Below are a couple diagrams showing you what your environment should look like before you start (Figure 1) and what it will look like when you are finished (Figure 2). Figure 1: Initial State Figure 2: Final State Prerequisites: An Azure subscription (http://azure.microsoft.com/en-us/pricing/free-trial/) Azure PowerShell configured (http://azure.microsoft.com/en-us/documentation/articles/install-configurepowershell/) A SQL Server AlwaysOn Availability Group setup in Azure on your primary site (Pictured in Figure 1) (http://msdn.microsoft.com/en-us/library/jj870963.aspx) The following steps will be covered to extend your existing SQL Server AlwaysOn Availability Group: 1. Connect to your Azure subscription, create a virtual network in your EastUS site and establish a VPN connection between the two sites. 2. Create a storage account and create your secondary domain controller that you will use to extend your WestUS cloud IT infrastructure to EastUS. 3. Create your SQL Server on the EastUS site. 4. Add the probe port and SQL endpoints to your new SQL VM. 5. Add EastSQL-01 to the availability group. Connect to your Azure subscription, create a virtual network in your EastUS site and establish a VPN connection between the two sites. Azure-to-Azure VPN tutorial: http://msdn.microsoft.com/en-us/library/azure/dn690122.aspx Note: When setting up your EastUS network, be sure to specify the WestUS DNS first under the EastUS configuration. If you don’t, you may have troubles promoting your EastUS DC. For our scenario, we used the following IP range and endpoint configuration: West Coast (10.3.0.0/16) DNS: 10.3.0.12, 172.16.0.12 Name IP Endpoints WestDC-01 10.3.0.12 WestSQL-01 10.3.0.13 Probe Port (DSR): 59999 SQL Server (DSR): 1433 WestSQL-02 10.3.0.14 Probe Port (DSR): 59999 SQL Server (DSR): 1433 Name EastDC-01 EastSQL-01 East Coast (172.16.0.0/16) DNS: 10.3.0.12, 172.16.0.12 IP Endpoints 172.16.0.12 172.16.0.13 Probe Port (DSR): 59999 SQL Server (DSR): 1433 Create a storage account and create your secondary domain controller that you will use to extend your WestUS cloud IT infrastructure to EastUS. 1. From an elevated PowerShell window on your local machine, define the following global variables. $affinityGroupName = "EastAG" $affinityGroupDescription = "East SQL HADR Affinity Group" $affinityGroupLabel = "IaaS Affinity Group" $virtualNetworkName = "CloudContosoEastNet" $storageAccountName = "<uniquestorageaccountname>" #The name of a new storage account that will get created in EastUS for step 2. $storageAccountLabel = "Contoso East HADR Storage Account" $storageAccountContainer = "https://" + $storageAccountName + ".blob.core.windows.net/vhds/" $availabilitySetName = "SQLHADR" $vmAdminUser = “azureuser” $vmAdminPassword = "Contoso!000" $domainName = "cloudcontoso" $FQDN = "cloudcontoso.local" $subscriptionName = "<your azure subscription name>" 2. Create a new storage account in EastUS and set your default subscription and storage account. New-AzureStorageAccount ` -StorageAccountName $storageAccountName ` -Label $storageAccountLabel ` -AffinityGroup $affinityGroupName #select Azure subscription select-azuresubscription $subscriptionName #default storage account Set-AzureSubscription $subscriptionName -CurrentStorageAccountName $storageAccountName 3. Provisioning the DC (Domain Controller) VM (EastDC-01). #EastDC-01 variables $dcServerName = "EastDC-01" #The name of your EastUS domain controller $dcServiceName = "EastDC" $winImageName = (Get-AzureVMImage | where {$_.Label -like "Windows Server 2012 R2 Datacenter*"} ` | sort PublishedDate -Descending)[0].ImageName #This is the image that will get used for your domain controller VM #EastDC-01 creating the VM New-AzureVMConfig ` -Name $dcServerName ` -InstanceSize Medium ` -ImageName $winImageName ` -MediaLocation "$storageAccountContainer$dcServerName.vhd" ` -DiskLabel "OS" -Verbose | Add-AzureProvisioningConfig ` -Windows ` -DisableAutomaticUpdates ` -AdminUserName $vmAdminUser ` -Password $vmAdminPassword -Verbose | New-AzureVM ` -ServiceName $dcServiceName ` -AffinityGroup $affinityGroupName ` -VNetName $virtualNetworkName –Verbose 4. Setup your EastUS site in Active Directory Sites and Services by logging on to your DC in the WestUS site (WestDC-01) and running the following script from an elevated PowerShell window. $secondarySiteName = "EastCoast" #This is the name of your secondary site used in AD sites and services $secondarySiteSubnet = "172.16.0.0/16" #Secondary site subnet #Create site “EastCoast” in AD sites and services New-ADReplicationSite -Name $secondarySiteName New-ADReplicationSubnet -Name $secondarySiteSubnet -Site $secondarySiteName -Location $secondarySiteName 5. Logoff WestDC-01. 6. Now we will be configuring your EastUS DC. Login to EastDC-01 as the local user account, once it is done being provisioned and from an elevated PowerShell window, run the following script to install Active Domain Services and tools and add a tree to your current forest. #EastDC-01 DC variables $secondarySiteName = "EastCoast" #This is the name of your secondary site in AD Sites and Services $domainAdminUserName = "cloudcontoso\domainuser" #A domain account that has permissions to promote a VM to a DC $domainAdminPassword = "Contoso!000" $domainSafeModePassword = "Contoso!000" #Safe mode / recovery password $domainName = “cloudcontoso.local” #Name of the existing domain on the WestUS site #Install Active Directory Domain Services and Tools Add-WindowsFeature AD-Domain-Services, ` RSAT-AD-Tools, ` RSAT-ADDS, ` RSAT-ADDS-Tools, ` RSAT-AD-AdminCenter, ` RSAT-AD-PowerShell #Convert password string to secure password $secDomainSafeModePassword = ConvertTo-SecureString $domainSafeModePassword AsPlainText -Force $secDomainAdminPassword = ConvertTo-SecureString $domainAdminPassword -AsPlainText -Force $secDomainCreds = New-Object System.Management.Automation.PSCredential ($domainAdminUserName, $secDomainAdminPassword) #Promotes the VM to a DC and adds EastDC-01 to the EastCoast site in active directory sites and services Install-ADDSDomainController -DomainName $domainName -Credential $secDomainCreds – InstallDns ` -SiteName $secondarySiteName -SafeModeAdministratorPassword $secDomainSafeModePassword 7. Let EastDC-01 reboot. Create your SQL Server on the EastUS site. 1. Provision the SQL Server VM (EastSQL-01). Note: The VHD and disks for this SQL server will reside in EastUS #EastSQL-01 variables $sqlServerName = "EastSQL-01" $sqlImageName = (Get-AzureVMImage | where {$_.Label -like "SQL Server 2014 RTM Enterprise*"} ` | sort PublishedDate -Descending)[0].ImageName $sqlServiceName = "EastSQL" $domainUser = "domainuser" #User with permissions to join a machine to a domain (do not include <domain>\<username, just use <domain username> $domainPassword = "Contoso!000" #EastSQL-01 creating the VM New-AzureVMConfig ` -Name $sqlServerName ` -InstanceSize Large ` -ImageName $sqlImageName ` -MediaLocation "$storageAccountContainer$sql1ServerName.vhd" ` -AvailabilitySetName $availabilitySetName ` -HostCaching "ReadOnly" ` -DiskLabel "OS" | Add-AzureProvisioningConfig ` -WindowsDomain ` -AdminUserName $vmAdminUser ` -Password $vmAdminPassword ` -DisableAutomaticUpdates ` -Domain $domainName ` -JoinDomain $FQDN ` -DomainUserName $domainUser ` -DomainPassword $domainPassword | New-AzureVM ` -ServiceName $sqlServiceName ` -AffinityGroup $affinityGroupName ` -VNetName $virtualNetworkName –Verbose 2. Once EastSQL-01 is finished provisioning, login with the local administrator account. 3. Setup your directory structure to match that of your SQL servers on the primary site. Note: When configuring your SQL Server, be sure you mirror the same data structure of that on your primary site. Make sure you provision the appropriate disks. i.e. If you use e:\SQLData and f:\SQLLog, make sure these drives and directories exist on the new VM 4. Install Windows Failover Clustering. Import-Module ServerManager Add-WindowsFeature Failover-Clustering, RSAT-Clustering-Mgmt, RSAT-ClusteringPowerShell, RSAT-Clustering-CmdInterface 5. Open SQL Server Management Studio and add any necessary accounts needed to perform SQL administrative tasks. 6. Open firewall ports for SQL, AlwaysOn listener and probe port. #Ports and port name Variables $sqlServerPort = "1433" #SQL Server port $sqlServerPortName = "SQL Server" #SQL Server port name/description $alwaysOnListenerPort = "5022" #AlwaysOn listener port $alwaysOnListenerPortName = "AlwaysOn Listener" #AlwaysOn listener port name/description $csProbePort = "59999" #Cloud Service probe port $csProbePortName = "Cloud Service Probe Port" #Cloud Service probe port name /description #SQL Server $port = New-Object -ComObject HNetCfg.FWOpenPort $port.Port = $sqlServerPort $port.Name = $sqlServerPortName $port.Enabled = $true $fwMgr = New-Object -ComObject HNetCfg.FwMgr $profile = $fwMgr.LocalPolicy.CurrentProfile $profile.GloballyOpenPorts.Add($port) #Cloud Service Probe Port $port = New-Object -ComObject HNetCfg.FWOpenPort $port.Port = $csProbePort $port.Name = $csProbePortName $port.Enabled = $true $fwMgr = New-Object -ComObject HNetCfg.FwMgr $profile = $fwMgr.LocalPolicy.CurrentProfile $profile.GloballyOpenPorts.Add($port) #AlwaysOn Listener $port = New-Object -ComObject HNetCfg.FWOpenPort $port.Port = $alwaysOnListenerPort $port.Name = $alwaysOnListenerPortName $port.Enabled = $true $fwMgr = New-Object -ComObject HNetCfg.FwMgr $profile = $fwMgr.LocalPolicy.CurrentProfile $profile.GloballyOpenPorts.Add($port) 7. Logoff. Add the probe port and SQL endpoints to your new SQL VM 1. From your local machine, run the following PowerShell scripts to create your probe port endpoint and SQL Server endpoint. Note: Make sure your probe port and SQL ports match that of your primary site ports. i.e. if you use 1433 for SQL on WestUS, make sure you use 1433 on your EastUS site. # Define variables $agNode = "EastSQL-01" # all availability group nodes should be included, separated by commas $serviceName = "EastSQL" # the name of the cloud service that contains the availability group nodes $probeEndpointName = "probePortEP" # name of the endpoint $probeEndpointPort = "59999" # public port to use for the endpoint $sqlEndpointName = "SQLServerEP" # name of the endpoint $sqlEndpointPort = "1433" # public port to use for the endpoint # Configure a load balanced endpoint for each node in $AGNodes, with direct server return enabled Get-AzureVM -ServiceName $ServiceName -Name $AGNode | Add-AzureEndpoint -Name $probeEndpointName -Protocol "TCP" -PublicPort $probeEndpointPort -LocalPort $probeEndpointPort -LBSetName "$probeEndpointName-LB" -ProbePort 59999 ProbeProtocol "TCP" -DirectServerReturn $true | Update-AzureVM Get-AzureVM -ServiceName $ServiceName -Name $AGNode | Add-AzureEndpoint -Name $sqlEndpointName -Protocol "TCP" -PublicPort $sqlEndpointPort -LocalPort $sqlEndpointPort -LBSetName "$sqlEndpointName-LB" -ProbePort 59999 ProbeProtocol "TCP" -DirectServerReturn $true | Update-AzureVM Add EastSQL-01 to the availability group Note: You will be running the following PowerShell scripts in this section from your primary node (WestSQL01) on the WestUS site. 1. Login to your WestSQL-01 on your WestUS site. 2. Define the following variables from an elevated PowerShell window. $server1 = "EastSQL-01" $server2 = "WestBackup-01" $acct1 = "cloudcontoso\sqlservice" $password = "Contoso!000" $primaryNodeName = "WestSQL-01" $timeout = New-Object System.TimeSpan -ArgumentList 0, 0, 30 $db = "AdventureWorks" $backupShare = "\\$server2\backup" $ag = "AWAG" $FQDN = "cloudcontoso.local" $agPath = "SQLSERVER:\Sql\$primaryNodeName\InstanceName\AvailabilityGroups\$ag" $endpointURL = "TCP://$primaryNodeName.$FQDN:5022" $failoverMode = "Manual" $availabilityMode = "AsynchronousCommit" $secondaryReadMode = "AllowAllConnections" 3. Add EastSQL-01 to the cluster. get-cluster $primaryNodeName | Add-ClusterNode $server1 4. From an elevated PowerShell window, add the new cloud service IP to your current client access point and configure the dependencies. This step needs to be done before you are able to add EastSQL-01 to your availability group. Note: For the next step, please see Appendices to find out the following: Appendix A: Determining the name of your second network ($clusterNetworkName) Appendix B: Determining the name of an IP Resource ($ipResourceName1) Appendix C: Determining the name of your client access point ($capName) # Define variables $clusterNetworkName = "NetworkName" #This is the network name of your second network $cloudServiceIP = "<CloudServiceIP>" #IP address of your east SQL cloud service $ipResourceName1 = "<IpRresourceName >" #the IP Address resource name of your current cloud service $ipResourceName2 = "IP Address $CloudServiceIP" #No need to edit $ag = "<NameOfAg>" $capName = "<NameOfClientAccessPoint>" #Client access point name Import-Module FailoverClusters Add-ClusterResource -Name $IPResourceName2 -ResourceType "IP Address" -Group $ag Get-ClusterResource $IPResourceName2 | Set-ClusterParameter -Multiple @{"Address"="$CloudServiceIP";"ProbePort"="59999";SubnetMask="255.255.255.255";" Network"="$ClusterNetworkName";"OverrideAddressMatch"=1;"EnableDhcp"=0} Set-ClusterResourceDependency -Resource $capName -Dependency "[$IPResourceName1] or [$IPResourceName2]" 5. Import the SQL Server PowerShell Provider. Set-ExecutionPolicy RemoteSigned -Force Import-Module "sqlps" -DisableNameChecking 6. Change the SQL Server service account on EastSQL-01. $wmi1 = new-object ("Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer") $server1 $wmi1.services | where {$_.Type -eq 'SqlServer'} | foreach{$_.SetServiceAccount($acct1,$password)} $svc1 = Get-Service -ComputerName $server1 -Name 'MSSQLSERVER' $svc1.Stop() $svc1.WaitForStatus([System.ServiceProcess.ServiceControllerStatus]::Stopped,$ti meout) $svc1.Start(); $svc1.WaitForStatus([System.ServiceProcess.ServiceControllerStatus]::Running,$ti meout) 7. Enable AlwaysOn on EastSQL-01. $svc2 = Get-Service -ComputerName $server1 -Name 'MSSQLSERVER' $timeout = New-Object System.TimeSpan -ArgumentList 0, 0, 30 Enable-SqlAlwaysOn ` -Path SQLSERVER:\SQL\$server1\Default ` -Force $svc2.Stop() $svc2.WaitForStatus([System.ServiceProcess.ServiceControllerStatus]::Stopped,$ti meout) $svc2.Start(); $svc2.WaitForStatus([System.ServiceProcess.ServiceControllerStatus]::Running,$ti meout) 8. Take a full database and a log backup and restore to EastSQL-01 WITH NO RECOVERY option. Backup-SqlDatabase -Database $db -BackupFile "$backupShare\db.bak" ServerInstance $primaryNodeName Backup-SqlDatabase -Database $db -BackupFile "$backupShare\db.log" ServerInstance $primaryNodeName -BackupAction Log Restore-SqlDatabase -Database $db -BackupFile "$backupShare\db.bak" ServerInstance $server1 -NoRecovery Restore-SqlDatabase -Database $db -BackupFile "$backupShare\db.log" ServerInstance $server1 -RestoreAction Log -NoRecovery 9. Create an endpoint for EastSQL-01. $endpoint = New-SqlHadrEndpoint MyMirroringEndpoint ` -Port 5022 ` -Path "SQLSERVER:\SQL\$server1\Default" Set-SqlHadrEndpoint ` -InputObject $endpoint ` -State "Started" 10. Add EastSQL-01 to the availability group and join the secondary database to the availability group. New-SqlAvailabilityReplica ` -Name $server1 ` -EndpointURL "TCP://$server1.cloudcontoso.local:5022" ` -AvailabilityMode "AsynchronousCommit" ` -FailoverMode "Manual" ` -ConnectionModeInSecondaryRole AllowAllConnections ` -Path "SQLSERVER:\Sql\$primaryNodeName\Default\AvailabilityGroups\$ag" Join-SqlAvailabilityGroup -Path SQLSERVER:\SQL\$server1\DEFAULT -Name $ag Add-SqlAvailabilityDatabase ` -Path "SQLSERVER:\Sql\$server1\Default\AvailabilityGroups\$ag" ` -Database $db 11. Verify that EastSQL-01 is in the “Synchronizing” state. $SqlServer = New-Object Microsoft.SqlServer.Management.Smo.Server($primaryNodeName) Do { $syncState = ( $SqlServer.AvailabilityGroups[$ag].DatabaseReplicaStates | where {$_.AvailabilityReplicaServerName -EQ $server1 } | select {$_.SynchronizationState} ) $SqlServer.AvailabilityGroups["partestag"].DatabaseReplicaStates | Select-Object AvailabilityReplicaServerName,AvailabilityDatabaseName,SynchronizationState Start-Sleep -Seconds 5 } While ( $syncState.'$_.SynchronizationState' -ne "Synchronizing" ) Appendices: Appendix A: Determining the name of your second network - Open Windows Server Failover Clustering. - Connect to your cluster. - Expand Networks and the one that matches your second subnet will be the name you are looking for i.e. “Cluster Network 2”. Appendix B: Determining the name of an IP Resource - Open Windows Server Failover Clustering. - Connect to your cluster. - Expand Roles, click resources at the bottom of the page and double click “IP Address: *.*.*.*” and the name will be the first field in the properties window. Be sure to verify that the network matches the network of your primary site. Appendix C: Determining the name of your client access point - Open Windows Server Failover Clustering. - Connect to your cluster. - Expand Roles and click resources and your client access point will be the name following “Name:”.