We recently deployed a five-node CRM 2011 topology using Windows Azure IaaS with the following objectives:

  • Understand how a multiple node CRM setup can be provisioned using Windows Azure IaaS. Specifically, how the networking capabilities offered by the Windows Azure platform (i.e. stateless load balancing) map to the CRM requirements.
  • Develop an automated way to provision and de-provision a CRM setup. This is not only useful for dev and test scenarios, but also for production scenarios where it is notoriously difficult to conduct capacity planning before acquiring the necessary hardware. For example, it is hard to know upfront what CRM functional building blocks (aka CRM roles) the business stakeholders will want to focus on, such as async processes, sandbox, reports, etc. By dynamically scaling out the “needed” features on demand, we can enhance the business agility of the CRM.
  • Offer our customers an educated choice between CRM Online (no setup costs but less control) and CRM On-Premises (extensive setup costs but complete control).
  • Take advantage of hybrid apps that combine CRM capabilities with Windows Azure services, such as Windows Azure Active Directory, mobile services, etc.

Our configuration consists of the following machines, and each machine is based on Windows Server 2008 R2 OS:

  • Server 1: hosts IIS 7.0 with a Full Server installation of Microsoft Dynamics CRM.
  • Server 2: hosts IIS 7.0 with a Full Server installation of Microsoft Dynamics CRM.

Note: We recommend consulting the guidance on Microsoft Dynamics CRM multi-server topologies for appropriately placing the CRM server roles based on your workload.

  • Server 3: Serves as the resource domain controller.
  • Server 4: Hosts Microsoft SQL Server 2008, as well as Microsoft Dynamics CRM Reporting Extensions.
  • Server 5: Hosts the ADFS role used to process claims for internal and external ADFS traffic. (This server is part of the resource domain controller hosted on Server 3. A nice optimization would be to combine the roles on Server 3 and Server 5.)
  • Server 6: Hosts AD and ADFS roles. This separate set of AD and ADFS instances are needed for an IFD setup. Since this is an internet-facing CRM configuration, IFD access allows users to access CRM over the Internet without the need for a VPN. Server 6 has been placed in a separate cloud service (ais-crmusers-cloudservice) and the communication between the two cloud services happens via the VIPs.

Note: Please consult the best practices for running SQL Server 2012 on Windows Azure IaaS, including disabling caching, avoiding the use OS drive for large databases (use multiple data drives instead for best performance), use the transient D: drive for TEMPDB, and make databases highly available by adding them to an an AlwaysOn Availability Group.

In addition, you may also want review the overall SQL Server best practice guidance, found here and here.

Finally, since SQL Server is hosted within a virtualized OS, you may also want to review these database virtualization tips and tricks.

Server 1 and  Server 2 are load balanced using a single public IP address, known as a virtual IP address (VIP). Windows Azure provides round-robin load-balancing of incoming network traffic to Server 1 and Server 2.

An important aspect of this setup is that all servers (1-4) are part of a single cloud service (as shown in diagram below). This is what makes it possible to load balance Servers 1 and 2. Additionally, Server 1 and Server 2 are part of an availability set. This means that Windows Azure will ensure that these machines are placed in two distinct fault domains.

While we did not implement a scheme to check the health of the load-balanced nodes, it is possible to add a load-balancing probe that will be invoked by Windows Azure every 15 seconds. If no response is received after two probes, Windows Azure takes the machine out of the rotation.

Windows Azure VNET (named “CRM” ) is used to connect all four machines. VNET can be partitioned into one or more subnets. We choose to make all machines part of one subnet. Clearly, you would want to segregate machines across more than one subnets in a production setting.

The best part of having this configuration in Windows Azure is that we can provision and de-provision the resources using PowerShell and a service management API. Provisioning and de-provisioning scripts are pasted below for your reference.

One important note about the scripts: Since we are deleting and recreating the machines, we need to ensure that the DNS address (known to Servers 1, 2 and 4) remains the same. Unfortunately, Windows Azure does not have the ability to assign fixed IP addresses to machines yet. So to ensure that the DNS server (also the domain controller) gets the same IP address each time, we rely on an undocumented behavior.  (Translation: “subject to change.”) We have consistently observed that the first three IP addresses in a subnet are reserved. It is the fourth IP address in the subnet that gets assigned to an IaaS VM instance. So as long as we provision the domain controller machine first (as is the case with the script below), we are assured that the DNS server address remains the same.

Note: We needed to rely on this undocumented capability (i.e. the fact that first three IP addresses are reserved) because we wanted to place all the machines within a single cloud service. An alternative approach (something we recommend) would be to place the domain controller in a cloud service by itself. The remaining computers can then be in a different cloud service. The benefit of this approach (even though it comes with some additional complexity in automation) is that the DNS server address is available upon the provisioning of the first cloud service (with the attached DC machine). As a result, subsequent cloud service provisioning can take advantage of the known DNS server by supplying it as part of the cloud service configuration. For more information about this approach please refer to this link.

# Export the VM configuration. SubscriptionInfo.ps1 contains subscription details.
$ScriptDirectory = Split-Path $MyInvocation.MyCommand.Path
. (Join-Path $ScriptDirectory 'Include\SubscriptionInfo.ps1')
. (Join-Path $ScriptDirectory 'Validation.ps1')

# Specify the storage account location
Set-AzureSubscription -SubscriptionName $subscriptionName -CurrentStorageAccount $storageAccountName -SubscriptionID $subscriptionId -Certificate $myCert

# Select the correct subscription (allows multiple subscription support)
Select-AzureSubscription -SubscriptionName $subscriptionName

$myCert = Get-Item cert:\\CurrentUser\My\$thumbprint

#ais-crm-ad
$cloudService = "ais-crm-cloudservice"
$vm = "ais-crm-ad"
Stop-AzureVM -ServiceName $cloudService -Name $vm -Verbose
$ExportPath = (Join-Path $ScriptDirectory “config\$vm-config.xml”)
Export-AzureVM -ServiceName $cloudService -name $vm -Path $ExportPath -Verbose
Remove-AzureVM -ServiceName $cloudService -name $vm -Verbose

#ais-crm-sql
$vm = "ais-crm-sql"
Stop-AzureVM -ServiceName $cloudService -Name $vm -Verbose
$ExportPath = (Join-Path $ScriptDirectory “config\$vm-config.xml”)
Export-AzureVM -ServiceName $cloudService -name $vm -Path $ExportPath -Verbose
Remove-AzureVM -ServiceName $cloudService -name $vm -Verbose

# Note VMs ais-crm-web1 and ais-crm-web3 are part of the same cloud service
#ais-crm-web1
$vm = "ais-crm-web1"
Stop-AzureVM -ServiceName $cloudService -Name $vm -Verbose
$ExportPath = (Join-Path $ScriptDirectory “config\$vm-config.xml”)
Export-AzureVM -ServiceName $cloudService -name $vm -Path $ExportPath -Verbose
Remove-AzureVM -ServiceName $cloudService -name $vm -Verbose

#ais-crm-web3
$vm = "ais-crm-web3"
Stop-AzureVM -ServiceName $cloudService -Name $vm -Verbose
$ExportPath = (Join-Path $ScriptDirectory “config\$vm-config.xm”)
Export-AzureVM -ServiceName $cloudService -name $vm -Path $ExportPath -Verbose
Remove-AzureVM -ServiceName $cloudService -name $vm -Verbose
• # Import the VM configuration.
# -------------------------------------------------------------------------------------------------
# ImportCRMSetup - This script exports the existing VM configuration

#
# To do -
# 1) Create a file within a subfolder called "include" and add a script
# file called SubscriptionInfo.ps1 with the following variables
#
# $subscriptionName
# $storageAccountName
# $subscriptionId
# $storageAccountName
# $thumbPrint
#
# 2) Specify appropriate values for variables $myCert, $location, $instanceSize, $serviceName
#
# -------------------------------------------------------------------------------------------------

# include the subscription info
. C:\Users\vishwas.lele\Documents\MyScripts\include\SubscriptionInfo.ps1

# Specify the storage account location
Set-AzureSubscription -SubscriptionName $subscriptionName -CurrentStorageAccount $storageAccountName -SubscriptionID $subscriptionId -Certificate $myCert

# Select the correct subscription (allows multiple subscription support)
Select-AzureSubscription -SubscriptionName $subscriptionName

$myCert = Get-Item cert:\\CurrentUser\My\$thumbprint
$vnetName = "CRM"

#ais-crm-ad
$cloudService = "ais-crm-cloudservice"
$vm = "ais-crm-ad"
$ImportPath = (Join-Path $ScriptDirectory “config\$vm-config.xml”)
Import-AzureVM -Path $ImportPath | New-AzureVM -ServiceName $cloudService -VNetName $vnetName
Start-AzureVM -ServiceName $cloudService -name $vm

#ais-crm-sql
$vm = "ais-crm-sql"
$ImportPath = (Join-Path $ScriptDirectory “config\$vm-config.xml”)
Import-AzureVM -Path $ImportPath | New-AzureVM -ServiceName $cloudService
Start-AzureVM -ServiceName $cloudService -name $vm

# Note VMs ais-crm-web1 and ais-crm-web3 are part of the same cloud service
#ais-crm-web1
$vm = "ais-crm-web1"
$ImportPath = (Join-Path $ScriptDirectory “config\$vm-config.xm”)
Import-AzureVM -Path $ImportPath | New-AzureVM -ServiceName $cloudService
Start-AzureVM -ServiceName $cloudService -name $vm

#ais-crm-web3
$vm = "ais-crm-web3"
$ImportPath = (Join-Path $ScriptDirectory “config\$vm-config.xm”)
Import-AzureVM -Path $ImportPath | New-AzureVM -ServiceName $cloudService
Start-AzureVM -ServiceName $cloudService -name $vm

I would like to thank Bob Nelson (Infrastructure Architect at AIS) for his help with this task.

Upcoming Azure Events with AIS: 

AIS is the guest “chef” at the next Azure ‘n’ Action Café session, coming up on April 10th. We’ll be discussing Windows Azure Virtual Machines: IaaS “On Your Terms.” Please click here for more information or to register for this free online session.

Next month we’re teaming up again with Microsoft for a Windows Azure Developer Camp on May 9th. Click here for more information about that event.