Setting up a Test Lab in Azure

I’ve been on a bit of a Web Applications kick for the last few weeks – I thought it was about time to take a step back and get some infrastructure work done as well. One of the tasks on my plate was the configuration of a lab environment within Microsoft Azure. I have a requirement to be running a number of Windows Server 2012 R2 machines that are linked to an Active Directory domain. I can then quickly install whatever software I want on them and start using them. I don’t care where the servers are – they could be on a Hyper-V box (which is what I would normally do). Today I’m going to do them in Azure. This article will kick things off by creating my first machine in Windows Azure plus all the other bits that I need as well.

To start with you need to download and install the Azure PowerShell SDK. This is your basic Web Platform Installer, so read the license, agree to the terms and install as normal. You will also need an Azure Subscription – if you haven’t signed up for a free trial yet, why not?

One thing I did find was that I needed to totally log out and back in again in order for the Azure PowerShell cmdlets to be recognized. I’m not sure if that is normal or not but it’s a reasonable thing to expect.

To create a connection to the Azure environment, use:


The command will prompt you via a GUI pop-up to log in – use the same credentials that you use to access Azure. Everything else can be done in the context of this authenticated session. I don’t want to embed credentials into my setup script and it’s not something I do on a regular basis, so this is perfectly fine with me.

I also want my machines to communicate over a virtual network on the back-end. Since Virtual Networks are per-subscription, not per-service, it makes sense to set these up ahead of time. This can be done in the portal (and I recommend that way). However if you insist on doing it on the command line then you need an XML document describing your subscriptions network topology. You can then apply this configuration with PowerShell:

Set-AzureVNetConfig -ConfigurationFile $tempXmlFile

A good plan is to set up your network in the portal and then export it. You can do the export with the following PowerShell:

Get-AzureVNetConfig -ExportToFile "C:\temp\MyLab-Network.xml"

Creating a Virtual Machine

In order to create a virtual machine with PowerShell I need to go through the following steps:

  1. Create an Affinity Group (if it doesn’t exist)
  2. Create a Storage Account (if it doesn’t exist)
  3. Create a Cloud Service (if it doesn’t exist)
  4. Re-connect to the subscription with the storage account
  5. Create the Virtual Machine

Before I get started I need the following information:

  1. Location: Where am I going to run my environment? You can see the selections when you drop down the Location field creating a VM in the Portal.
  2. Affinity Group: I get to choose this but it has to be unique within my subscription.
  3. Virtual Network: This comes from my network setup described above.
  4. Storage Account Name: I generate this but it has to be unique within Azure.
  5. Cloud Service Name: I generate this but it has to be unique within Azure.

Since I don’t want to re-create everything every time I’m going to store the storage account name and cloud service name for later. I’m creating a Create-Lab.ps1 script to hold all this. This is how it starts:

$VerbosePreference = "Continue"
$Location = "West US"
$AffinityGroup = "Lab"
$VNet = "Lab-Network"
$Subnet = "Subnet-1"
$IPNetwork = "10.1.1"

$StorageAccountFile = "$(get-location)StorageAccount.txt"
$StorageAccount = "lab" + ([guid]::NewGuid()).ToString().Substring(24)
if (Test-Path $StorageAccountFile) {
    $StorageAccount = Get-Content $StorageAccountFile

$CloudServiceFile = "$(get-location)CloudService.txt"
$CloudService= "lab" + ([guid]::NewGuid()).ToString().Substring(24)
if (Test-Path $CloudServiceFile) {
    $CloudService= Get-Content $CloudServiceFile

An Affinity Group is a collection of resources (like storage, cloud services, virtual machines in our case) that you want to be close to one another. It allows the Azure Fabric Controller (which is the thing you interact with to create your environment) to more intelligently locate the resources you want. The Affinity Group Name is your choice, but you will get an error if you try to add it twice so I’ve done a little test:

### Create the Affinity Group
$AffinityGroupExists = Get-AzureAffinityGroup -Name $AffinityGroup -ErrorAction SilentlyContinue
if (!$AffinityGroupExists) {
    Write-Verbose "[Create-Lab]:: Affinity Group $AffinityGroup does not exist... Creating."
    New-AzureAffinityGroup -Name $AffinityGroup -Location $Location
} else {
    Write-Verbose "[Create-Lab]:: Affinity Group $AffinityGroup already exists"

Once I have the affinity group created I can move on to the storage account and cloud service. These need to be created once as well. Since they have DNS names within the Azure namespace they need to be unique. What I’ve done above is create a name based on a globally unique ID. There is still a chance of a collision, so I preprended a specific string – in this case “lab”. Choose your own string to make it more unique to you if you bump into problems. (Side note: I wish Azure would allow GUIDs here – it would make it much easier to create unique names).

### Create the Storage Account
$StorageAccountExists = Get-AzureStorageAccount -StorageAccountName $StorageAccount -ErrorAction SilentlyContinue
if (!$StorageAccountExists) {
    Write-Verbose "[Create-Lab]:: Storage Account $StorageAccount does not exist... Creating."
    New-AzureStorageAccount -StorageAccountName $StorageAccount -AffinityGroup $AffinityGroup
    $StorageAccount | Out-File "$CWDStorageAccount.txt"
} else {
    Write-Verbose "[Create-Lab]:: Storage Account $StorageAccount already exists"

### Create a Cloud Service
$CloudServiceExists = Get-AzureService -ServiceName $CloudService -ErrorAction SilentlyContinue
if (!$CloudServiceExists) {
    Write-Verbose "[Create-Lab]:: Cloud Service $CloudService does not exist... Creating."
    New-AzureService -ServiceName $CloudService -AffinityGroup $AffinityGroup
    $CloudService | Out-File "$CWDCloudService.txt"
} else {
    Write-Verbose "[Create-Lab]:: Cloud Service $CloudService already exists"

Note I am storing the names I have chosen into text files. This allows me to re-use the names later on but it also allows me to do other things in automation with other scripts.

Now that I have created all my base services it’s time to create a virtual machine. My first machine is going to be special – it will be my domain controller. I need some more information here:

  1. The Image Name that I’m going to base my VM on
  2. The Instance Size or how much resources I want to give it
  3. The Admin Username and password for accessing it afterwards

The Image Name is probably the most complex here. You can use Get-AzureVMImage to get a full list of the images available to you – both on the marketplace and the ones you have uploaded. I’m going to use the latest Windows Server 2012 R2 Datacenter build that they have provided. To do that I need to do some filtering, like this:

$Image = Get-AzureVMImage |
    ? ImageFamily -eq "Windows Server 2012 R2 Datacenter" |
    Sort PublishedDate | Select -Last 1

You can get a list of the instance sizes using the following:

Get-AzureRoleSize | 
    ? SupportedByVirtualMachines -eq $true | 
    Select InstanceSize,Cores,MemoryInMb

For my purposes (a domain controller), a Medium or Basic_A3 instance size seems perfect. I want this to have a static IP address so here is my eventual configuration:

### Create the Domain Controller VM
$Image = Get-AzureVMImage | ? ImageFamily -eq "Windows Server 2012 R2 Datacenter" | Sort PublishedDate | Select -Last 1
$AdminUsername = "itadmin"
$AdminPassword = "P@ssw0rd"

# Set the VM Size and Image
$DC = New-AzureVMConfig -Name LAB-DC `
    -ImageName $Image.ImageName `
    -InstanceSize Medium -HostCaching ReadWrite
# Add credentials for logging in
Add-AzureProvisioningConfig -VM $DC -Windows `
    -AdminUsername $AdminUsername `
    -Password $Adminpassword
# Set Networking Information
Set-AzureSubnet -VM $DC -SubnetNames $Subnet
Set-AzureStaticVNetIP -VM $DC -IPAddress "$IPNetwork.2"

# Create the VM
New-AzureVM -ServiceName $CloudService -VNetName $VNet -VM $DC

The first few IP addresses in the C-class range I constructed are needed for the platform. I started my addressing at to be sure to give Azure enough room. Since a domain controller has to have a static IP address, this is really needed.

By default you’ll get a PowerShell endpoint and an RDP endpoint created. You can find out where these endpoints are using PowerShell too:

Get-AzureEndpoint -VM (Get-AzureVM | ? Name -eq "LAB-DC")

So now you have the cloud service name (which you created and it’s in CloudService.txt) – add to that to get the computername, and the port number (from the endpoint command above). You need to go and import the certificate before using Enter-PSSession to connect using the AdminUsername and password you created. You can find out all about this process from this blog post.

Note that you can also create additional machines on your lab network in Azure in the same way – they just need a different IP address and name.