Automating the creation of a test / lab environment hosted on Microsoft Azure with Azure PowerShell
A couple of weeks ago a client who is a Microsoft Gold ISV partner approached me about building out a test lab environment in Microsoft Azure. For those of you who may not know, Microsoft Azure is Microsoft’s public cloud offering (IaaS / PaaS / etc).
Ultimately, my client wanted an environment that they could prepare initially, then replicate. The clients goal is ultimately to be able to provide some of their potential clients with a live trial of the enterprise software they create. So, a big public cloud provider like Azure was a perfect fit. Obviously, you could also use AWS / Google Cloud or any one of a number of big IaaS providers for this purpose. However, since this client is a Microsoft Partner, and mostly a Microsoft shop overall, Azure was the direction they wanted to go.
Since the client ultimately wanted to automate the replication of these environments, I dove in to find out what the best way would be to do this. It did not take long to figure out that Microsoft Azure PowerShell would offer the exact functionality I needed. So, I decided to dive in and learn a bit while doing this.
I decided that if I was ultimately going to replicate these environments using PowerShell, that I should go ahead and build the initial “gold” environment using PowerShell as well. The result of that effort is the script below.
In the next few days (once I have the bugs worked out) I’ll be posting scripts that can be used to replicate a lab, as well as destroy a lab once you are finished. If I have time, I’m also going to create some simple scripts that can be used to power a lab down and power it back up as well.
If you ever wanted to build a lab setup quickly in PowerShell perhaps this will help you too. It’s unlikely my script is perfect for your project, but perhaps it will serve as a starting point for something you can customize to fit your needs. Here are a few things to mention before you get started.
- Azure costs money. So, if you are going to test this be sure you know what you are doing and you know how to turn things off / remove them when you are finished.
- You can sign up for a free $200 trial which is a great way to start.
- You will need Azure PowerShell which you can download here.
- Once you have Azure PowerShell downloaded and installed you will need to connect it to your Azure account. To do that simply run:
[code language=”powershell”]
Add-AzureAccount
[/code]This will pop up a box that will ask you to authenticate to your Azure account.
- If this is your first time using Azure PowerShell you may also need to run the command below to correctly target your Azure PowerShell session at the correct subscription.:
[code language=”powershell”]
Select-AzureSubscription -SubscriptionName "Pay-As-You-Go"
[/code] - My script relies on you building out a virtual network in Azure for these VMs to live in manually first. What is needed should be clear from the Pre-Deployment Notes section of my script. At this time, Microsoft does not seem to offer a great way to automate the creation of this virtual network (if they do I am missing it).
Once you have all that done, you should be ready to run the script. The sample script below will build two Azure VMs. You can choose the following variables in the Pre-Deployment Variables section of the script before running it. Those variables are:
- $labName – This is essentially the unique ID of this lab. That way when you use this tool over and over again to create hundreds of labs that are infinitely useful to you and your organization, you will be able to tell all of the components apart. Sorry – I got a bit carried away there. This $labName will either be the name that is used for components (Affinity Group / Storage Account etc) or be prepended to the name of components (reserved IPs, VMs etc).
- $azureLocation – This is the Azure region that everything this script creates will be deployed in. You can find a list of Azure Regions here. It is important that the location you choose match the location of your virtual network.
- $instanceSize – This is the instance size of the VMs that you will deploy. You can find a list of Azure Instance sizes here.
- $baseImage – This is the base operating system image your VMs will start with. To get a list of potential images to start with you can run this command from your Azure PowerShell window. Be sure and filter for what you are looking for by replacing the “Windows Server 2012 R2 Datacenter*” in the example below.
[code language=”powershell”]
Get-AzureVMImage | where-object { $_.Label -like "Windows Server 2012 R2 Datacenter*" }
[/code] - $adminUser – This is the guest VM administrator username for the VMs that you will deploy.
- $adminPassword – This is the password for the guest VM $adminUser username you chose above.
Once you have those chosen variables, you should be ready to roll. Oh, before you run the script… Please remember that I am in no way responsible for what this script might do. I believe it to be useful, and generally safe. You should make sure you are 100% comfortable with what this script is doing before you begin. Ok – now that the lawyers can sleep again at night, here we go. When you run the script you should see output something like this.
Here is what is going on in the background.
- An Azure Affinity Group named with your $labName variable is being created. Affinity groups are critically important. When I started this process, I did not understand that. I was getting terrible network performance between my VMs even though all my VMs were in the same region. Basically, the affinity group makes sure that all of the other resources are located as close together as possible from a storage / network standpoint. You can read more about affinity groups here.
- An Azure Storage Account named with your $labName variable is being created. This is where all of your VMs disks will be stored.
- Two Azure reserved IP addresses are reserved. Reserved IP addresses are IP addresses that will stay with a VM even if it is powered off / deprovisioned. You can read more about them here. There are costs associated with these which you can better understand by reading more about that topic here.
- Two Azure VMs are built out in your affinity group, based on the $instanceSize and $baseImage you selected in the script. These VMs have your $labName pre-pended to them. In addition, the admin username and password that you chose is configured on these VMs. Be sure and change that password after the VMs are deployed. Having your admin password laying around in a plain text PowerShell script is a terrible idea!
In summary, I am sure this script could be improved. However, I also hope it is in good enough shape to be helpful to some of you. If you see ways it could be made better, please let me know. Keep an eye on my blog for my lab replication / lab destruction scripts that I’ll be posting soon.
[code language=”powershell”]
#region Notes
# Lab Builder – V2 Built on 2/9/2015
# Built by David Winslow – wdw.org
#
# Azure PowerShell General Notes / Commands
# [Console]::CursorSize = 25 (Makes the cursor blink so you don’t go insane.)
# Add-AzureAccount (Connects Azure PowerShell up to your Azure account.)
#
# V2 improvements:
# -Proper affinity group provisioning
# -Added variable for regions
# -Added variable for base images
# -Added variable for instance sizes
#
# Pre-Deployment Notes:
# Run Add-AzureAccount (Connects Azure PowerShell up to your Azure account) before running this script.
# The virtual network must be built manually in the Azure portal before running this script. I hope to improve this in a future version.
# -The virtual network name must match the $labName variable below.
# -The virtual network location must match the $azureLocation variable below.
# -Address space: 10.20.0.0 /16
# -Subnet-1 10.20.15.0 /24
# -Subnet name must be Subnet-1
# You need to choose values for the variables below.
#endregion
#region Pre-Deployment Variables
$labName = "lab51"
# This name will be user entirely for, or prepended to most components.
# Must be all lower case letters and numbers only.
# Must be Azure globally unique.
# Must not exceed 11 characters in length.
$azureLocation = "East US 2"
# This is the location that resources will be created in.
$instanceSize = "Standard_D2"
# This determines what size instances you launch.
$baseImage = "a699494373c04fc0bc8f2bb1389d6106__Windows-Server-2012-R2-201412.01-en.us-127GB.vhd"
# This determines what image these VMs are built from.
# "a699494373c04fc0bc8f2bb1389d6106__Windows-Server-2012-R2-201412.01-en.us-127GB.vhd" – Windows 2012 R2 – Data Center – December 2015
# reference this to get image names if needed. (Get-AzureVMImage | where-object { $_.Label -like "Windows Server 2012 R2 Datacenter*" } )
$adminUser = "labadmin"
# This is the initial default admin username for each VM.
$adminPassword = "@@labAdminPassword2015!!"
# This is the initial $adminUser account password for each VM.
#endregion
#region Write Pre-Deployment Variables to screen
Write-Output " "
Write-Output "=================================="
Write-Output "Building Lab."
Write-Output "=================================="
Write-Output " "
Write-Output "Lab name set to:" $labName
Write-Output " "
Write-Output "Azure location set to:" $azureLocation
Write-Output " "
Write-Output "Instance size set to:" $instanceSize
Write-Output " "
Write-Output "Base Image set to:" $baseImage
Write-Output " "
Write-Output "Admin username set to:" $adminUser
Write-Output " "
Write-Output "Admin password set to:" $adminPassword
Write-Output " "
#endregion
#region Provisioning Affinity Group
Write-Output " "
Write-Output "=================================="
Write-Output "Provisioning Affinity Group"
Write-Output "=================================="
New-AzureAffinityGroup -Name $labName -Location $azureLocation
#endregion
#region Provisioning Storage Account
Write-Output " "
Write-Output "=================================="
Write-Output "Provisioning Storage Account"
Write-Output "=================================="
New-AzureStorageAccount $labName -AffinityGroup $labName
#endregion
#region Assigning Azure Subscription
Write-Output " "
Write-Output "=================================="
Write-Output "Assigning Azure Subscription"
Write-Output "=================================="
Set-AzureSubscription -SubscriptionName "Pay-As-You-Go" -CurrentStorageAccount $labName
#endregion
#region Provisioning Reserved IP Addresses
Write-Output " "
Write-Output "=================================="
Write-Output "Provisioning Reserved IP Addresses"
Write-Output "=================================="
$vm1IP = $labName + "vm1IP"
$vm2IP = $labName + "vm2IP"
New-AzureReservedIP -ReservedIPName $vm1IP -Label $vm1IP -Location $azureLocation
New-AzureReservedIP -ReservedIPName $vm2IP -Label $vm2IP -Location $azureLocation
#endregion
#region Building VMs
Write-Output " "
Write-Output "=================================="
Write-Output "Building VMs"
Write-Output "=================================="
$vm1vm = $labName + "vm1"
$vm2vm = $labName + "vm2"
New-AzureVMConfig -Name $vm1vm -InstanceSize $instanceSize -ImageName $baseImage | Add-AzureProvisioningConfig -Windows -AdminUsername $adminUser -Password $adminPassword | Set-AzureSubnet ‘Subnet-1’ | Set-AzureStaticVNetIP -IPAddress 10.20.15.5 | New-AzureVM -AffinityGroup $labName -ServiceName $vm1vm -ReservedIPName $vm1IP -VNetName $labName -Location $azureLocation
New-AzureVMConfig -Name $vm2vm -InstanceSize $instanceSize -ImageName $baseImage | Add-AzureProvisioningConfig -Windows -AdminUsername $adminUser -Password $adminPassword | Set-AzureSubnet ‘Subnet-1’ | Set-AzureStaticVNetIP -IPAddress 10.20.15.6 | New-AzureVM -AffinityGroup $labName -ServiceName $vm2vm -ReservedIPName $vm2IP -VNetName $labName -Location $azureLocation
#endregion
#region Building VMs
Write-Output " "
Write-Output "=================================="
Write-Output "Lab Builder Script Complete!"
Write-Output "=================================="
#endregion
[/code]