Azure Site Recovery (ASR): a great way to create a SharePoint test environment

Overview

For all the IT Pro’s who have managed SharePoint farms, you know that it is difficult to set up a SharePoint test environment on premises.  It takes money, time and effort to replicate a production SharePoint farm.  Azure Site Recovery (ASR) is a cloud replication service that provides a great way to quickly create a SharePoint test environment.

  1. The Problem we are trying to solve
  2. The Solution: Azure Site Recovery to quickly and reliably create a SharePoint est environment
  3. Steps to Create a SharePoint test environment with ASR
    • 0: (optional) Create a local SP environment if you do not have one yet
    • 1: Check the requirements for ASR replication and prepare your servers
    • 2: Replicate your SP farm servers to Azure using ASR
    • 3: Using the ASR replica, create the SP Test Environment in Azure

The Problem: creating a SharePoint test environment – the traditional way – takes MONTHS

In my last IT Manager post, I was responsible for a SharePoint farm.  For a long time, we did not have the luxury of a test environment for SharePoint.  I was finally able to procure an HP Z220 workstation and used it for creating a SharePoint test environment.

I increased the memory and hard disk space on the HPZ220 and then used it to build a SharePoint test farm.  However, setting up this SharePoint test environment took 2 months time.  I spent time ordering and receiving the server and parts (upgrades).  And I spent time setting up the server and copying the production virtual machines – DC, Sql Server and SharePoint server – to the sandbox test environment.

The Solution: Azure Site Recovery – a quick and reliable mechanism to create a SharePoint test environment

What is Azure Site Recovery (ASR)? 

Azure Site Recovery is a business continuity and disaster recovery solution in the cloud.  You can protect on-premises servers or virtual machines by replicating them either to Azure or to a secondary site (data center).  If the primary data center location is down, you can fail over to the secondary site (Azure or the secondary data center).  Then when the primary site is back up and running, you can fail back to it.

Can Azure Site Recovery be used to create a SharePoint test environment?

Yes, since ASR can protect whole servers or virtual machines running different workloads, it can certainly replicate those SharePoint servers.  ASR can replicate all the components of a SharePoint farm:

  • Active Directory Domain Services (ADDS) Domain Controller (DC)
  • Sql Server
  • SharePoint server

ASR can take images of your Production servers.  From those images, you can then create application consistent replica servers – in the cloud or on the secondary site.  The replica servers may then be used as test servers in a SharePoint test environment.

The whole process is fast.  At home, I have a fiber optics connection.  I was able to replicate a 3-server SharePoint farm to Azure within a few days.  However, the speed of replication depends on your network’s speed and your network’s load.

Steps to create a SharePoint Test Environment with ASR

We will cover the steps – to create the SharePoint test environment – in three major steps.

O) Optional: if, like me, you want to create a local SharePoint environment – on your laptop at home – so that you can try out Azure Site Recovery, find out how to create such a SharePoint environment using Windows 10 and Windows Server 2016 Nested Virtualization step-by-step.

The following steps will be covered in separate posts.  Stay tuned:

1) Check the requirements for Azure Site Recovery (ASR) replication and prepare the SharePoint Farm’s servers
2) I will explain how to Set up a Recovery Services Vault in Azure and how to go through the Getting Started wizard.  Subsequently, we will start Replication of the SharePoint servers
3) Once we have replicated all the SharePoint servers, we can go ahead and Create a SharePoint test farm environment in the Cloud

Create a Load Balanced Azure IAAS Virtual Machine (VM) with an RDP connection

Overview

A very common scenario in Azure Infrastructure As A Service (IAAS) is to have two or more VM’s behind an Azure external Internet-facing load balancer.  The load balancer provides high availability and network performance by distributing traffic according to a predetermined algorithm.

In this exercise we will create a load-balanced VM in Azure – using Powershell.  And we will enable RDP access to the VM by creating a NAT rule.

Details

In our scenario, we are creating one VM and the load balancer is forwarding Internet traffic to the VM by using an RDP NAT rule that maps an Azure public IP address to a private IP address.  The private IP address is on the Network Interface (NIC) card attached to the VM.

Consequently, no Load Balancing rules are created or used – only a NAT rule – to allow RDP into the VM.

Please notice that the script below creates a second data disk on the VM (because the VM is to be a Domain Controller).  You can easily remove the code for the second disk if it is not needed.

break

Login-AzureRmAccount
Get-AzureRmSubscription
Select-AzureRmSubscription -SubscriptionId <your subscription ID>

#################################################
# Create Resource Group - or use an existing one
#################################################
$loc = 'westeurope'
$rgName = 'RG-ASRConfigServer'
New-AzureRmResourceGroup -Name $rgname -Location $loc -Force

# Create a storage account
$stoName = 'storasrconsrvr4'
$stoType = 'Standard_LRS'
$naResult = Get-AzureRmStorageAccountNameAvailability $stoName

if ($naResult.NameAvailable) {
New-AzureRmStorageAccount -ResourceGroupName $rgName -Name $stoName `
                          -Location $loc -SkuName $stotype -Kind Storage
}
else { # The LoadBalancer is not used to create Load Balancing rules
# But to create NAT rules (to allow RDP from the Internet)
 Write-Host ''
 Write-Host -ForegroundColor Yellow "This storage account name: $stoName is not available"
 break
}
$storageAccount = Get-AzureRmStorageAccount -ResourceGroupName $rgName -Name $stoName

###############################
# Set up networking for the VM
###############################
# Create VNet and Subnet(s) - if needed
$beSubnetName = 'LB-SUBNET-BE'
$backendSubnet = New-AzureRmVirtualNetworkSubnetConfig -Name $beSubnetName `
                                                        -AddressPrefix '10.0.0.0/24'
$vnetName = 'vnetConfserv'
$vnet = New-AzureRmVirtualNetwork -Name $vnetName -ResourceGroupName $rgname `
                                  -Location $loc -AddressPrefix '10.0.0.0/16' `
                                  -Subnet $backendSubnet 

#Create Public IP address 
#########################
$ipName = 'confservPublicIP'
$locName = 'West Europe'
$domainName = 'asrconfig'
$pip = New-AzureRmPublicIpAddress -Name $ipName -ResourceGroupName $rgName `
                                  -Location $locName -AllocationMethod Dynamic `
                                  -DomainNameLabel $domainName

#Create a front-end IP address pool tied to the Public IP address
#################################################################
$frontendIP = New-AzureRmLoadBalancerFrontendIpConfig -Name 'LB-FrontendIP' `
                                                      -PublicIpAddress $pip

#Create a back-end IP address pool that will be tied to the NIC
###############################################################
$backendAP = New-AzureRmLoadBalancerBackendAddressPoolConfig -Name 'LB-BackendAP'

#Create an Inbound NAT rule for Remote Desktop
##############################################
$natRuleRDP = New-AzureRmLoadBalancerInboundNatRuleConfig -Name 'natRuleRDP' `
                            -FrontendIpConfiguration $frontendIP -Protocol TCP `
                            -FrontendPort 3442 -BackendPort 3389 

#Create the external Load Balancer
##################################
$lbName = 'vmLoadBalancer'
$loadBal = New-AzureRmLoadBalancer -ResourceGroupName $rgName -Name $lbName `
                               -Location 'West Europe' -FrontendIpConfiguration $frontendIP `
                               -InboundNatRule $natRuleRDP -BackendAddressPool $backendAP

# Attach backend address pool to load balancer                                 
Add-AzureRmLoadBalancerBackendAddressPoolConfig -Name $backendAP -LoadBalancer $loadBal | `
                                                Set-AzureRmLoadBalancer
                                                     
#Create the Network Interface
#############################

$vnet1 = Get-AzureRmVirtualNetwork -Name $vnetName -ResourceGroupName $rgName
$backendSubnet1 = Get-AzureRmVirtualNetworkSubnetConfig -Name $beSubnetName -VirtualNetwork $vnet1
$nicName = 'vmNIC'
$vmNIC = New-AzureRmNetworkInterface -ResourceGroupName $rgName -Name $nicName `
                             -Location $locName -PrivateIpAddress '10.0.0.4' `
                             -Subnet $backendSubnet1 `
                             -LoadBalancerBackendAddressPool $loadBal.BackendAddressPools[0] `
                             -LoadBalancerInboundNatRule $loadBal.InboundNatRules[0]

##############################################
#Create the VM
##############################################
$vmName = 'vm-asrcsrvr'
$AvID = (New-AzureRmAvailabilitySet -ResourceGroupName $rgName `
                                    -Name 'adAvailabiltySet' -Location 'West Europe').id

#create a VM configuration
##########################
$vmObj = New-AzureRmVMConfig -VMName $vmName -VMSize 'Standard_A1' -AvailabilitySetId $AvID

$compName = 'vm-asrcsrvr'
$cred = Get-Credential -Message 'Enter name / password of VM administrator account.'
$vmObj = Set-AzureRmVMOperatingSystem -VM $vmObj -Windows -ComputerName $compName `
                                     -Credential $cred -ProvisionVMAgent `
                                     -EnableAutoUpdate

$vmObj = Set-AzureRmVMSourceImage -VM $vmObj -PublisherName MicrosoftWindowsServer `
                                 -Offer WindowsServer -Skus 2012-R2-Datacenter `
                                 -Version 'latest'

# Add the network interface (NIC) to the VM
###########################################
$vmObj = Add-AzureRmVMNetworkInterface -VM $vmObj -Id $vmNIC.Id

# ADD OS Disk and Data Disk
###########################
$blobPath = 'vhds/spfarm-ad-osdisk.vhd'
$osDiskUri = $storageAccount.PrimaryEndpoints.Blob.ToString() + $blobPath

$diskName = 'adVmOSDisk'
$vmObj = Set-AzureRmVMOSDisk -VM $vmObj -Name $diskName -VhdUri $osDiskUri `
                            -CreateOption fromImage -DiskSizeInGB 1000

# Add data disks by using the URLs of the copied data VHDs at the appropriate 
#  Logical Unit Number (Lun).
$dataDiskBlobPath = 'vhds/ADDataDisk-1.vhd'
$dataDiskUri = $storageAccount.PrimaryEndpoints.Blob.ToString() + $dataDiskBlobPath

# AD DC in Azure need the data disk storing sysvol to have NO caching
$vmObj = Add-AzureRmVMDataDisk -VM $vmObj -Name 'ADDataDisk-1' -Caching None `
                            -VhdUri $dataDiskUri -Lun 0 -DiskSizeInGB 1000 `
                            -CreateOption empty

# Create the actual VM
New-AzureRmVM -ResourceGroupName $rgName -Location $locName -VM $vmObj

######################
####Post VM Creation:
######################

# Add the network interface (NIC) to the load balancer
######################################################

$lb = Get-AzureRmLoadBalancer -Name $lbName -ResourceGroupName $rgName
$backend = Get-AzureRmLoadBalancerBackendAddressPoolConfig -Name $backendAP `
                                                           -LoadBalancer $lb
$nic = Get-AzureRmNetworkInterface -Name $nicName -ResourceGroupName $rgName
$nic.IpConfigurations[0].LoadBalancerBackendAddressPools = $backend
Set-AzureRmNetworkInterface -NetworkInterface $nic



Lessons Learned

1. If $backendSubnet is used instead of $backendSubnet1 and Error is produced: “Parameter set cannot be resolved using the specified named parameters” OR “Cannot parse the request”.

2. The Backend Address pool will contain the objects (IPs) targeted by the Load Balancer. If you want to redirect via NAT, The VM’s NIC should be part of the backend pool.  Therefore, you need to associate the Backend AddressPool with the IP address of the VM (held in the NIC).  This needs to be done after the VM is created.

Build an on-premises Domain Controller (DC) using Desired State Configuration

In my last post I described how to create a DC, in Azure, using DSC and ARM Templates.  In this post, we will discuss how to automate the creation of a local – on premises – Acitve Directory Domain Services (ADDS) Domain Controller (DC) using DSC.

Overview

DSC has two modes: push mode and pull mode.

In push mode, you will author the configuration.  You will then stage the configuration by creating MOF files.  And finally, you will manually push the desired configuration onto the target server or node.  The target server can be the local server or a remote server.

On the other hand, in DSC PULL mode, you author the configuration and stage it onto a designated Pull server.  The target nodes contact the central pull server at regular intervals to obtain their desired configuration.

In our scenario, we will be using DSC in push mode.  We will author the configuration and push it onto the local Windows server (not remotely).

Details

Prerequisites

On the target Windows Server (Windows Server 2008 R2 SP1 and Windows Server 2012 or 2012 R2):

  1. Download and install Windows Management Framework (WMF).  This WMF 5.0 is currently available and is the recommended version.
  2. Copy the script below to the target server
  3. Open the script below in Powershell ISE as administrator
  4. Install the required Powershell modules using install-module: xActiveDirectory, xComputerManagement, xNetworking and xStorage.

Run the script

Run the script in Powershell ISE.  The first command creates the .mof files which contain the desired configuration.  The second command actually applies the configuration to the local server.  After about half an hour and one reboot, you will have a fully functional Domain Controller with a new user (domain admin).

# Configure all of the settings we want to apply for this configuration
$ConfigData = @{
    AllNodes = @(
        @{
            NodeName = 'localhost'
            MachineName = 'spfarm-ad'
            IPAddress = '10.0.0.4'
            InterfaceAlias = 'Ethernet'
            DefaultGateway = '10.0.0.1'
            PrefixLength = '24'
            AddressFamily = 'IPv4'
            DNSAddress = '127.0.0.1', '10.0.0.4'
            PSDscAllowPlainTextPassword = $true
            PSDscAllowDomainUser = $true
        }
    )
}

Configuration BuildADDC {

    param (
        [Parameter(Mandatory)]
        [String]$FQDomainName,

        [Parameter(Mandatory)]
        [PSCredential]$DomainAdminstratorCreds,

        [Parameter(Mandatory)]
        [PSCredential]$AdmintratorUserCreds,

        [Int]$RetryCount=5,
        [Int]$RetryIntervalSec=30
    )

    Import-DscResource -ModuleName PSDesiredStateConfiguration
    Import-DscResource -ModuleName xActiveDirectory, `
                                    xComputerManagement, `
                                    xNetworking,
									xStorage
 
    Node $AllNodes.NodeName 
    {
        LocalConfigurationManager 
        {
            ActionAfterReboot = 'ContinueConfiguration'            
            ConfigurationMode = 'ApplyOnly'            
            RebootNodeIfNeeded = $true  
        }

        # Change Server Name
        xComputer SetName { 
          Name = $Node.MachineName 
        }

        # Networking
        xDhcpClient DisabledDhcpClient
        {
            State          = 'Disabled'
            InterfaceAlias = $Node.InterfaceAlias
            AddressFamily  = $Node.AddressFamily
        }

        xIPAddress NewIPAddress
        {
            IPAddress      = $Node.IPAddress
            InterfaceAlias = $Node.InterfaceAlias
            PrefixLength   = $Node.PrefixLength
            AddressFamily  = $Node.AddressFamily
        }

        xDefaultGatewayAddress SetDefaultGateway
        {
            Address        = $Node.DefaultGateway
            InterfaceAlias = $Node.InterfaceAlias
            AddressFamily  = $Node.AddressFamily
            DependsOn = '[xIPAddress]NewIPAddress'
        }
       
        xDNSServerAddress SetDNS {
            Address = $Node.DNSAddress
            InterfaceAlias = $Node.InterfaceAlias
            AddressFamily = $Node.AddressFamily
        }

        # Install the Windows Feature for AD DS
        WindowsFeature ADDSInstall {
            Ensure = 'Present'
            Name = 'AD-Domain-Services'
        }

        # Make sure the Active Directory GUI Management tools are installed
        WindowsFeature ADDSTools            
        {             
            Ensure = 'Present'             
            Name = 'RSAT-ADDS'             
        }           

        # Create the ADDS DC
        xADDomain FirstDC {
            DomainName = $FQDomainName
            DomainAdministratorCredential = $DomainAdminstratorCreds
            SafemodeAdministratorPassword = $DomainAdminstratorCreds
            DependsOn = '[xComputer]SetName','[xDefaultGatewayAddress]SetDefaultGateway','[WindowsFeature]ADDSInstall'
        }   
        
        $domain = $FQDomainName.split('.')[0] 
        xWaitForADDomain DscForestWait
        {
            DomainName = $domain
            DomainUserCredential = $DomainAdminstratorCreds
            RetryCount = $RetryCount
            RetryIntervalSec = $RetryIntervalSec
            DependsOn = '[xADDomain]FirstDC'
        } 

        #
        xADRecycleBin RecycleBin
        {
           EnterpriseAdministratorCredential = $DomainAdminstratorCreds
           ForestFQDN = $domain
           DependsOn = '[xADDomain]FirstDC'
        }
        
        # Create an admin user so that the default Administrator account is not used
        xADUser FirstUser
        {
            DomainAdministratorCredential = $DomainAdminstratorCreds
            DomainName = $domain
            UserName = $AdmintratorUserCreds.UserName
            Password = $AdmintratorUserCreds
            Ensure = 'Present'
            DependsOn = '[xADDomain]FirstDC'
        }
        
        xADGroup AddToDomainAdmins
        {
            GroupName = 'Domain Admins'
            MembersToInclude = $AdmintratorUserCreds.UserName
            Ensure = 'Present'
            DependsOn = '[xADUser]FirstUser'
        }
        
    }
}

# Build MOF (Managed Object Format) files based on the configuration defined above 
# (in folder under current dir) 
# Local Admin is assigned 
BuildADDC -ConfigurationData $ConfigData `
          -FQDomainName 'spdomain.local' `
          -DomainAdminstratorCreds (get-credential -Message "Enter Admin Credentials" -UserName "Administrator" ) `
          -AdmintratorUserCreds (get-credential -Message "Enter New Admin User Credentials" -UserName "admin1" ) 

# We now enforce the configuration using the command syntax below
Start-DscConfiguration -Wait -Force -Path .\BuildADDC -Verbose -Debug

Lessons Learned

Since the Powershell xActiveDirectory module is being updated all the time, a DSC script that worked a year ago needs to be updated to work with WMF 5.0 (in the last quarter of 2016).

WMF 5.0 is included in the latest version of Windows 10 and included on Windows Server 2016.

Issues and Solutions

With some of the xActiveDirectory resources, the use of the fully qualified domain name (FQDN) produced an error: “Could not find mandatory property DomainName. Add this property and try again.”

Solution: use the first part of the domain name