Creating Multiple Azure Virtual Machines

So recently I did a rather large PowerShell project that involved Microsoft Windows Azure and microsoft-azure-logo-370x275[1]deploying our product to a number of machines in the cloud. I literally came up with hundreds of lines of code in order to accomplish this task. It occurred to me after talking to a co-worker that there is a real need to share this knowledge so that others with the same need will be able to benefit from it.

This will be the first in a series of posts in which I hope to illuminate you on taking your product to the cloud either for customer deployment, testing, etc. If you have questions along the way, please ask and I will try to answer them as best as I can. Ready? “Allons-y!

If you don’t have a Microsoft Azure account through your employer, then you will need to create one for yourself. The first step was to download and install the Microsoft Azure PowerShell module. You can do this by using the using the Microsoft Web Platform Installer. Please read “How to install and configure Azure PowerShell” to get some introduction on what the PowerShell module can do and how to install the necessary tools on your computer.

After you install the PowerShell module on your machine, you will need to connect your machine to your companies Azure portal account. You can do this by getting your Azure Publisher Settings file.

Get-AzurePublishSettingsFile

This will open your default web browser and download your settings file to your download folder. Next you will need to import the settings file with a certificate that will enable you to connect your PowerShell module to account in the cloud.

Import-AzurePublishSettingsFile -PublishSettingsFile 'C:\Users\\Downloads\Visual Studio Premium with MSDN-08-26-2014-credentials.publishsettings'

With that done you are ready to start working with the Azure cloud via your newly installed PowerShell module.

We will need a few helper functions to help us out and make sure that we are ready to start issuing commands to the Azure services. I’ve placed these types of methods in a PowerShell script file named “CommonFunctions.ps1”. This will enable me to reuse them in various other scripts.

function IsAdmin {
    $IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") 
    
    return $IsAdmin
}

function IsAzurePowerShellInstalled {
    $azurePSInstalled = ((Get-Module -ListAvailable Azure) -ne $null)
    return $azurePSInstalled
}
 

Now I’ve created a second script file named “CreateVirtualMachines.ps1” that will hold the code that creates the Azure Virtual Machines in the cloud. The first part makes sure that you are running this from an Administrative PowerShell window. The second, ensures that you have the Azure PowerShell module installed and visible to the system. Next we look for a cloud service name called “samplecloud”. If you don’t have this cloud service name, we can continue, otherwise it will ask you if you want to delete the existing cloud service and all of it’s deployments. Run this script as is just to ensure that you have everything working. If this is your first time running the script, you should have the word “done” written on the screen after the code executes.

# Import Functions From Other Scripts Here
. .\CommonFunctions.ps1

if ((IsAdmin) -eq $false) {
    throw "You need to be an Administrator or run in Elevated mode for the script to work properly."
}

if ((IsAzurePowerShellInstalled) -eq $false) {
    throw "Windows Azure PowerShell not found! Please install from http://www.windowsazure.com/en-us/downloads/#cmd-line-tools"
}
    
[string] $cloudServiceName = 'samplecloud'
if ((Test-AzureName -Service $cloudServiceName) -eq $true) {
    Write-Host ('Cloud Service "' + $cloudServiceName + '" already exists, please remove it and its deployments prior to running this script')
    Remove-AzureService -ServiceName $cloudServiceName -DeleteAll
    if ((Test-AzureName -Service $cloudServiceName) -eq $true) {
        throw 'Unable to remove the cloud service, aborting...'
    }
}

Write-Host 'done'
 

Now we are going to extend this file a little bit to create an affinity group if we don’t have one defined already. We are going to create a function in our script that will do this for us.

function GetAffinityGroup {
    Param([Parameter(Mandatory=$true, HelpMessage="The name of the location to create or get the affinity group for.  ie: West US`n")]
          [string] $locationName)

    [string] $affinityLabel = ('Sample ' + $locationName)
    [string] $affinityName = ($affinityLabel.Replace(' ', [System.String]::Empty) + 'Affinity')
    $group = Get-AzureAffinityGroup -Name $affinityName -ErrorAction SilentlyContinue
    if ($group -eq $null) {
        Write-Host 'Unable to find the Affinity Group.  Creating a new one.'
        New-AzureAffinityGroup -Name $affinityName -Label $affinityLabel -Description 'Affinity Group to be used for the samples' -Location $locationName -Verbose -ErrorAction Stop
    }
    else { Write-Host ('Affinity Group "' + $affinityName + '" Already Created') }

    return (Get-AzureAffinityGroup -Name $affinityName | Select-Object -First 1)
}
</pre><p>There is a lot going on here, but basically it looks to see if you have an affinity group created for the specified location name and if you do not it creates one for you. Let's add this to the script above.</p><pre>...
    Remove-AzureService -ServiceName $cloudServiceName -DeleteAll
    if ((Test-AzureName -Service $cloudServiceName) -eq $true) {
        throw 'Unable to remove the cloud service, aborting...'
    }
}
[string] $locationName = 'West US'
$affinityGroup = GetAffinityGroup -locationName $locationName

Write-Host 'done'

Now we have to have a storage account for the given location so that our Virtual Machines have a location to store their information. I have created another function to either get the storage location if it has already been created, if it has not to create one for me and then set it as my default subscription location.

function GetStorageAccountForLocation {
    Param([Parameter(Mandatory=$true, HelpMessage="The name of the location to get the storage account for.  ie: West US`n")]
          [string] $locationName,
          [Parameter(Mandatory=$true, HelpMessage="The name of the affinity group that you want to assign the storage account to.`n")]
          [string] $affinityGroupName)

    [string] $storageAccountName = ('Sample' + $locationName.Replace(' ', [System.String]::Empty) + 'Store').ToLowerInvariant()
    $storageAccountExists = Test-AzureName -Storage $storageAccountName
    if ($storageAccountExists -eq $false) {        
        Write-Host ('Unable to find a storage account in location ' + $locationName + ', Creating a new one with name: ' + $storageAccountName)
        New-AzureStorageAccount -StorageAccountName $storageAccountName -Label ('Sample ' + $locationName + ' Storage Account') -Description 'Sample Storage Account' -AffinityGroup $affinityGroupName -Verbose -ErrorAction Stop
    }
    else { Write-Host ('Storage Account "' + $storageAccountName + '" Already Created') }

    $defaultSubscription = Get-AzureSubscription -Default
    if ($defaultSubscription.CurrentStorageAccountName -ne $storageAccountName) {
        Write-Host ('Setting the Current Storage Account Name for the default subscription to "' + $storageAccountName + '"')
        Set-AzureSubscription -SubscriptionName $defaultSubscription.SubscriptionName -CurrentStorageAccountName $storageAccountName -ErrorAction Stop
    }

    return (Get-AzureStorageAccount -StorageAccountName $storageAccountName | Select-Object -First 1)
}
 

Now let’s add this to our script

...
}
[string] $locationName = 'West US'
$affinityGroup = GetAffinityGroup -locationName $locationName
$storageAccount = GetStorageAccountForLocation -locationName $locationName -affinityGroupName $affinityGroup.Name

Write-Host 'done'
 

Next we need to decide on the image that we want to use to create the virtual machines. Use this command to get a list of the images that you have available to you in your subscription.

Get-AzureVMImage | Where-Object { $_.OS -eq 'Windows' -and $_.IsPremium -eq $false } | Select Label

Once you have found an image that you want to use, then we can select it by label name to get the image name property from it.

Get-AzureVMImage | Where-Object { $_.OS -eq 'Windows' -and $_.IsPremium -eq $false -and $_.Label -eq 'Windows Server 2008 R2 SP1, June 2014' } | Select ImageName
 

This will produce the following output:

PS Z:\> Get-AzureVMImage | Where-Object { $_.OS -eq 'Windows' -and $_.IsPremium -eq $false -and $_.Label -eq 'Windows Se
rver 2008 R2 SP1, June 2014' } | Select ImageName

ImageName
---------
a699494373c04fc0bc8f2bb1389d6106__Win2K8R2SP1-Datacenter-201406.01-en.us-127GB.vhd

PS Z:\>

Finally we are ready to create a number of virtual machines. First we need to create the configuration files that we need in order to create them. Then we can pass that array of configurations to the New-AzureVm command so that they can be created. This PowerShell code below will create 3 machines in the Azure cloud that will have to additional end points each, one for HTTP traffic and one for the Remote Desktop traffic. It will create, boot, and provision these machines for you. Wait until they are finished and then exit the function.

[array] $vmConfigs = @()
[string] $imageName = 'a699494373c04fc0bc8f2bb1389d6106__Win2K8R2SP1-Datacenter-201406.01-en.us-127GB.vhd'
[string] $adminName = 'TestAdmin'
[string] $adminPassword = 'SuperSecretP@ssw0rd'
[int] $publicRdpPort = 3389

$numberOfMachines = 3    
for ($id = 0; $id -lt $numberOfMachines; $id++) {        
    $virtualMachineName = ('sample-vm-' + ($id + 1).ToString().PadLeft(2, '0'))
    [int] $httpEndPointPort = (80 + $id)
    $virtualMachineConfig = New-AzureVMConfig -Name $virtualMachineName -InstanceSize 'Small' -ImageName $imageName |         
                                Add-AzureProvisioningConfig -Windows -AdminUsername $adminName -Password $adminPassword -DisableAutomaticUpdates |                                
                                Add-AzureEndpoint -Name "HTTP" -Protocol tcp -LocalPort $httpEndPointPort -PublicPort $httpEndPointPort |
                                Set-AzureEndpoint -Name 'RDP' -LocalPort 3389 -PublicPort ($publicRdpPort + $id) -Protocol tcp
    $vmConfigs += $virtualMachineConfig
}

Write-Host ('Creating ' + $numberOfMachines + ' new Virtual Machines')
New-AzureVM -ServiceName $cloudServiceName -AffinityGroup $affinityGroup.Name -VMs $vmConfigs -WaitForBoot -Verbose -ErrorAction Stop
    
Write-Host "Virtual Machine(s) Created..."
 

Now we are ready to put it all together and run the script. This should be the output that you see on your screen.

PS C:\PowerShell\Scripts> .\CreateVirtualMachines.ps1
Affinity Group "SampleWestUSAffinity" Already Created
Storage Account "samplewestusstore" Already Created
Creating 2 new Virtual Machines
VERBOSE: 12:43:32 PM - Begin Operation: New-AzureVM - Create Cloud Service
VERBOSE: 12:43:33 PM - Completed Operation: New-AzureVM - Create Cloud Service

OperationDescription                    OperationId                             OperationStatus
--------------------                    -----------                             ---------------
New-AzureVM                             4d5ad75f-254c-0414-bbb3-42160f848bba    Succeeded
VERBOSE: 12:43:33 PM - Begin Operation: New-AzureVM - Create Deployment with VM sample-vm-01
VERBOSE: 12:44:21 PM - Completed Operation: New-AzureVM - Create Deployment with VM sample-vm-01
New-AzureVM                             3078530a-fb0c-04c5-97df-d7b07c22d09b    Succeeded
VERBOSE: 12:43:33 PM - Begin Operation: New-AzureVM - Create Deployment with VM sample-vm-02
VERBOSE: 12:44:21 PM - Completed Operation: New-AzureVM - Create Deployment with VM sample-vm-02
New-AzureVM                             3078530a-fb0c-04c5-97df-d7b07c22d09b    Succeeded
VERBOSE: 12:43:33 PM - Begin Operation: New-AzureVM - Create Deployment with VM sample-vm-03
VERBOSE: 12:44:21 PM - Completed Operation: New-AzureVM - Create Deployment with VM sample-vm-03
New-AzureVM                             3078530a-fb0c-04c5-97df-d7b07c22d09b    Succeeded

Virtual Machine(s) Created...
done

Here is a Microsoft Word document that contains the complete script files so you can see the entire PowerShell scripts for yourself. I hope you enjoyed this post and found it informative. Next time we will install some services on the remote boxes. Until then, Don’t Blink…

Advertisements

4 thoughts on “Creating Multiple Azure Virtual Machines

  1. Pingback: Creating Multiple Azure Virtual Machines Part Deux | OutOfMemoryException

  2. Pingback: Creating Multiple Azure Virtual Machines Part Tre | OutOfMemoryException

  3. Pingback: Creating Multiple Azure Virtual Machines – Part Vier | OutOfMemoryException

  4. Pingback: Creating Multiple Azure Virtual Machines – Part пять | OutOfMemoryException

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s