SharePoint on Windows Azure – 10 Tips

SharePoint

Tip #1: Growing Your SharePoint Farm

So you have a bunch of servers in your farm but you need one or two more. The steps I outlined before will work with some modification. Now that you’ve already created your Cloud Service, you have to do things a bit differently. If you try to do the New-AzureVM command like before, you’ll likely see "DNS name already taken" error that I’ve highlighted further down. To add additional servers to your Cloud Service, follow the guidance from the previous articles about connecting to Windows Azure, specifying your Cloud Service, storage account, and other pertinent information. Then define your new server or servers as we did before.

## Create SP App3 
$size = "Small"
$vhdname = "Arch-SPApp3.vhd" 
$vmStorageLocation = $mediaLocation + $vhdname
$spwebnew = New-AzureVMConfig -Name 'Arch-SPApp3' -AvailabilitySetName $avsetwfe -ImageName $spimage -InstanceSize $size -MediaLocation $vmStorageLocation | Add-AzureEndpoint -Name 'https' -LBSetName 'lbhttps' -LocalPort 443 -PublicPort 443 -Protocol tcp -ProbeProtocol http -ProbePort 80 -ProbePath '/healthcheck/iisstart.htm' | Add-AzureEndpoint -Name "RemoteDesktop" -Protocol TCP -PublicPort (get-random -max 65000 -min 20000) -LocalPort 3389 | Set-AzureSubnet $subnetName | Add-AzureProvisioningConfig -Password 'pass@word1' -Windows

The example I’m showing would create a new Windows server without SharePoint. Then here’s the part to really pay attention to, the New-AzureVM command:

New-AzureVM -DeploymentName "NewSharePointServer" -ServiceName $serviceName -VNetName $vnetname -VMs $spwebnew

Here, we only need to give the -ServiceName, -VNetName, and -VMs parameters. If you put in more information, especially the AffinityGroup, you’ll get the DNS already taken error.

Tip #2: DNS name already taken

image

If you get this, it usually means this is the second time you’ve run the New-AzureVM for the Cloud Service. If everything is OK and you just want to add a server, see tip #1. If something is wrong, remove the Cloud Service and try it (the New-AzureVM cmdlets) again:

Remove-AzureService -ServiceName "ArchSharePoint" -Force

Tip #3: Finish Up

Remember to create the healthcheck file. In our examples, we specified the -ProbePath ‘/healthcheck/iisstart.htm’. So, we need to go to each server that we want our Azure load balancer to use and add that file. To do so, open IIS Manager and expand the Default Web Site. Right-click on the Default Web Site and select Add Virtual Directory. Give it a name (Alias) of “healthcheck” and choose a path (I used C:\Inetpub\wwwroot). Finally copy the “iisstart.htm” from the root of Default Web Site to the healthcheck folder, or just create an empty file called “iisstart.htm” Here’s how it looks when its finished.

image

Tip #4: Automate

If you’re going to be doing this repeatedly (for example, to perform a bunch of tests), automate. Lucky for us, Ram Gopinathan has written a script to do just that: http://gallery.technet.microsoft.com/PowerShell-Script-for-f43bb414 Ram’s script using an input file so you can easily document what you’re doing, then it provisions all your VMs. After you’re done, it’ll tear everything down, nice and neatly.

Tip #5: CSUpload Error: Too many arguments for command Add-Disk

This error occurred when using Add-Disk:

image

The problem was the – (the dashes). I used notepad, PowerGUI and other tools, but the dash doesn’t translate to the command prompt. To resolve this, just re-type the dashes.

Tip #6: CSUpload Error: CSUpload is expecting a page blob

I had copied down a vhd to make some modifications and then tried to upload it later. Here’s what I kept getting:

image

In fact, CloudXplorer and other tools I tried reported the same thing. What’s the fix? Use AzCopy. See the link to read up on how to get it. AzCopy is great because it can multi-thread. My uploads using AzCopy were very fast. By specifying the /BlobType:page parameter, you can upload a block blob as a page blob. Here’s how my command looks: azcopy E:\ftproot\vhd http://storageacct.blob.core.windows.net/vhds/ /blobtype:page /destkey:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxxxxx/== /Z /V

Tip #7: No RemoteDesktop Endpoint

I also realized that none of my VMs had an endpoint for Remote Desktop. We can add that by using a command like this:

$vmname = "Arch-SQL1"
Get-AzureVM -ServiceName "ArchSharePoint" -Name $vmname | Add-AzureEndpoint -Name "RemoteDesktop" -Protocol "TCP" -PublicPort 53223 -LocalPort 3389 | Update-AzureVM

Remember to use a different PublicPort for each VM in a particular Cloud Service. I just incremented by one (53223, 53224, etc.). Or, this should work too:

Get-AzureVM -ServiceName $servicename | ForEach-Object {Add-AzureEndpoint -Name $endpointname -Protocol TCP -PublicPort (get-random -max 65000 -min 20000) -LocalPort 3389} | Update-AzureVM

I understand that it’s possible, but not likely, Get-Random could pick the same random number. I supposed you could script it further to check if a port is being used but that wasn’t worth the effort here.

Tip #8: Office Web Apps Tip

I published my SharePoint farm publicly, by creating an endpoint at 443. This allows me to browse SharePoint from anywhere using https://.cloudapp.net. However, I couldn’t use my Office Web App servers since they were only accessible from within Azure. The Office Web Apps were configured to use https and run on port 443. I could publish an endpoint, but I had already used 443 for SharePoint (and I don’t want to use an insecure port, like 80). So, I picked a random port, 9443. Now, how do we get SharePoint to know about this? SharePoint will PULL information from Office Web Apps, so first we need to configure Office Web Apps with this information (do this on any Office Web Apps server):

# Set the ExternalUrl on any Office Web Apps Server
New-OfficeWebAppsFarm -ExternalUrl https://archsharepoint.cloudapp.net:9443

Then, on the SharePoint server, New-SPWopiBinding will bind to the public address on the public port (https://archsharepoint.cloudapp.net:9443). Create an endpoint in the WAC server for a TCP connection on public port 9443 to private port 443.

Tip #9: Blocked RDP in Firewall

During testing of ports and protocols, I blocked all connections on one of my servers. Obviously, that included the RDP port. If you do that, the only thing you can really do is download the VHD for that server, mount it on-premises somewhere (which will give you console access) and change it. Then, upload it back to Windows Azure. It’s a tedious process, so if you really don’t need machine or data, re-create it.

Tip #10: Reduce costs

UPDATE: 6/10/2013

As of June 3rd, we are no longer billed for VMs in a stopped state, great news! See Scott Guthrie’s Blog for more info.

If you’re testing scenarios over a period of week or months and you don’t necessarily use your VM’s every day – you can cut some costs. For some of my work, I might go a week or two without ever logging in to my SharePoint farms in Windows Azure. All the while though, you’re being charged for compute hours. The only way to stop that is to delete your VMs. Powering down doesn’t cut your costs, you’re still charged for compute hours. You can use tip #4 to do a complete “tear down,” or use some PowerShell snippets to do a partial tear down. I’ll show you an example that should be pretty easy to follow. First, we’ll backup our VM configs:

#Region Func: BackupVMConfigs
function BackupVMConfigs () 
{
	param
    (
        [Parameter(Mandatory=$True)][ValidateNotNullOrEmpty()]
        [String]$CloudService,
        [Parameter(Mandatory=$True)][ValidateNotNullOrEmpty()]
        [String]$Folder
	)
If (Get-AzureVM -ServiceName $cloudservice) 
	{
		Write-Host "Backing up SharePoint VM Configurations..."
		Try {
			Get-AzureVM -ServiceName $cloudservice | foreach {
			$path = $folder + $_.Name + '.xml'
			Export-AzureVM -ServiceName $cloudservice -Name $_.Name -Path $path 
			}
			}
		Catch 
			{
			Write-Output $_
			Throw " - Error occurred. Not continuing since VM configuration may not have been saved and we don't want to delete them."
			}	
	}
Else { Write-Host " - Skipping $cloudservice because it doesn't seem to exist." -ForegroundColor Yellow}
Write-Host "- Done backing up. Removing VM's from Cloud Service..." -ForegroundColor Yellow
}
#EndRegion

To use that function, you’d type in BackupVMConfig -CloudService " -Folder "" I’ve used that function in the next snippet, where we actually remove the Cloud Service/Deployment:

#Region Func: Remove SP Deployment
function RemoveSP {
$CloudService = "ArchSharePoint"
BackupVMConfigs -CloudService $CloudService -Folder "C:\Scripts\Azure\SharePoint\"
#Removing all VMs while keeping the cloud service/DNS name
Remove-AzureDeployment -ServiceName $cloudservice -Slot Production -Force
Write-Host "Removed all SharePoint VMs"
}
#EndRegion

Once you run the “RemoveSP”; function, you’re no longer incurring compute charges. The actual VHD’s are still there in your Windows Azure storage account though. The charges for storage are very small, almost insignificant so we keep those there. Later, when you’re ready to fire up your farm you can do the inverse. First, I have a function to do the actual import of the VMs:

#Region Func: ImportVMs ($CloudService, $Folder)
function ImportVMs ()
{
	param
    (
        [Parameter(Mandatory=$True)][ValidateNotNullOrEmpty()]
        [String]$CloudService,
        [Parameter(Mandatory=$True)]
		[ValidatePattern({\\$})]
		[ValidateScript({Test-Path $_ -PathType 'Container'})] 
        [String]$Folder
	)
$cs = Get-AzureVM -ServiceName $cloudservice
If ($cs.Count -ge 1) 
	{
	Write-Host "It looks like (at least some) of the VMs are up. Skipping restore..." -ForegroundColor Green
	}
Try 
	{
	Write-Host " - Importing VMs..." -ForegroundColor Yellow
	$script:vms = @()
	Get-ChildItem $folder | foreach {
		$path = $folder + $_
		$script:vms += Import-AzureVM -Path $path
		}
	}
Catch
	{
	Write-Output $_
	Throw " - Error occurred."
	}	
}
#EndRegion

And here’s where I’m using it:

#Region Restore SharePoint Deployment
function RestoreSharePoint 
{
$cloudservice = "ArchSharePoint"
$folder = "C:\Scripts\Azure\SharePoint\" #must include trailing \
ImportVMs -CloudService $CloudService -Folder $Folder
Write-Host " - Restoring takes about 30 minutes..." -ForegroundColor Yellow
Measure-Command {New-AzureVM -ServiceName $cloudservice -VNetName "vNet-Arch" -VMs $vms}
}
<# 
#This is used if the Cloud Service was deleted
## Cloud Service Paramaters  
$serviceLabel = $cloudservice
$serviceDesc = "Architecture SharePoint Farm" 
$ag = 'AG-SharePoint-Arch'
Measure-Command {New-AzureVM -ServiceName $cloudservice -ServiceLabel $serviceLabel -ServiceDescription $serviceDesc -AffinityGroup $ag -VNetName $vNet -VMs $vms}
#>
#EndRegion

Running the “RestoreSharePoint” function will call the ImportVMs function with the right parameters and attach your VHDs to some shiny new VMs based on the backups you took before.

Conclusion

That’s the end of our series. I hope I’ve achieved my goal of consolidating all the information needed to understand and setup SharePoint farms on Windows Azure IaaS. If you haven’t started playing around yet, go back to the introduction to learn how to sign up for a free account. A big thanks to my colleagues who helped me throughout the series: Paul Stubbs, Len Cardinal, Kosma Zygouras, Shane Laney.

0 comments… add one

Leave a Reply