In this article, I'm going to show you how you can build an entire Azure lab setup with a single line of PowerShell. This lab environment will allow you to be 100% sure your students can follow along with your training material exactly.

I write a lot of how-to posts like this. I also author a lot of online training courses. This kind of training content always requires me to define a Prerequisites section. The Prerequisites section has to go into detail on what it takes to get the reader's environment exactly like mine. I grew tired of that and decided to automate it.

When you build technical, how-to content like this, all trainers struggle with getting you (the learner) in sync with them (the trainers). We can provide scripts for you to run but they always fall short of getting everything.

I thought to myself, why can't I simply tell students to run a one-line PowerShell command and have the entire lab environment built for them exactly how mine is? It turns out, it's possible!

The only requirement is the student's own Azure subscription.

Note that this entire process is NOT 100% tested. Rather than let this information die, I decided to document it in a blog post and get it 95% of the way there. Please let me know if any code I provide does not work.

Student Instructions

Does this sound familiar when starting a new course?

  • Click this
  • Run this command
  • Now go over here and click that
  • Run this script
  • ....

So many trainers must run through an entire section to train you to build the demo environment just to get started! Now how about this? Regardless if you're building a lab environment with a single machine or an entire datacenter, the process is the same.

"We will be working with a remote Windows Server and a Windows 10 workstation. When the environment is built, your username will be foo and your password will be far. The IP address to connect to is X.X.X.X."

Notice that I provide a username, password and an IP address to RDP to, to get started. That's it!

To provision the entire lab comes down to opening up a PowerShell console and running one line; the labsetup.ps1 script.

iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/adbertram/devops-from-the-ground-up-resources/master/labsetup.ps1'))
Invoking lab setup

Project Overview

The lab environment automation project I'm about to share involves six rough steps.

  1. Create a GitHub repo to house the files/scripts
  2. Create the Azure ARM template to provision the lab resources
  3. Create the main PowerShell script the student will download and execute
  4. Sync all of the files to the GitHub repo
  5. Instruct the student how to invoke the main PowerShell script
  6. Prompt the student if they'd like to connect to the lab now

My goal was to remove as many steps a student would have to go through to get a lab environment up and running.

Create the GitHub Repo

There's no secret sauce in this step. Create any old GitHub repo and call it anything you wish. I've called mine devops-from-the-ground-up-resources since the course I was build this for was called DevOps from the Ground Up.

Create the Azure ARM Template

This is not going to be an article on how to create Azure ARM templates. There are plenty of resources already out there to do that. If you'd like to see the one I've created for this project, check it out here.

Creating the Lab Setup Script

The lab setup script is where the magic happens. This is the script the student downloads and executes automatically. If you'd like to download this script and see it in its entirety, feel free to do so here.

The lab setup scripts consists of a few different sections:

  1. Installing the Azure PowerShell module and authenticating (if necessary)
  2. Downloading the ARM template
  3. Create the Azure resource group
  4. Invoking the ARM deployment into the resource group
  5. Provide the public IP addresses of all VMs provisioned to the student
  6. Optionally connect to the starting VM for the student

When the student downloads from my public GitHub repo and executes the lab setup script like below, the magic starts to happen:

iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/adbertram/devops-from-the-ground-up-resources/master/labsetup.ps1'))
Invoking lab setup

Download the Azure Module and Authenticate

The script requires some PowerShell cmdlets to interact with Azure. The student will need the Azure PowerShell module. They will also need to be authenticated to their subscription.

## Download the Azure PowerShell module if the student doesn't have it
if (-not (Get-Module -Name Azure -ListAvailable -ErrorAction Ignore)) {
    Install-Module -Name Azure -Force
}

## If the student isn't already authenticated to Azure, ask them to
if (-not (Get-AzContext)) {
    Connect-AzAccount
}

Downloading the ARM Template

Once the script itself is downloaded, it then go back out to GitHub and downloads the ARM template. Below, the script hides the annoying progress bar that Invoke-WebRequest brings up and saves the ARM template locally.

## Hide the prog bar generated by Invoke-WebRequest
$progPrefBefore = $ProgressPreference
$ProgressPreference = 'SilentlyContinue'

## Download the ARM template
$templatePath = "$env:TEMP\lab.json"
$url = 'https://raw.githubusercontent.com/adbertram/devops-from-the-ground-up-resources/master/lab.json'
Invoke-WebRequest -Uri $url -OutFile $templatePath

Create the Azure Resource Group

The script then create's the course lab's resource group if it doesn't exist. This resource group is where all of the course resources will be created in.

## Azure resource group will be the course name
$rgName = "$($CourseName -replace ' ','-')"

## Create the lab's resource group
if (-not (Get-AzResourceGroup -Name $rgName -Location $AzureRegion -ErrorAction Ignore)) {
    $null = New-AzResourceGroup -Name $rgName -Location $AzureRegion
}

Invoke the Azure Deployment

The script then kicks off the Azure ARM deployment via the New-AzResourceGroupDeployment cmdlet.

## Deploy lab using the ARM template just downloaded
$deploymentName = "$rgName-Deployment"
$null = New-AzResourceGroupDeployment -Name $deploymentName -ResourceGroupName $rgName -TemplateFile $templatePath -Verbose

$deploymentResult = (Get-AzResourceGroupDeployment -ResourceGroupName $rgName -Name $deploymentName).Outputs

Give the Student IPs to Connect to

The student is then notified of the public IP addresses of the Azure virtual machines created for them.

Write-Host "Your lab VM IPs to RDP to are:"
$vmIps = @()
foreach ($val in $deploymentResult.Values.Value) {
    $pubIp = Get-AzResource -ResourceId $val
    $vmName = $pubIp.Id.split('/')[-1].Replace('-pubip', '')
    $ip = (Get-AzPublicIpAddress -Name $pubip.Name).IpAddress
    $vmIps += [pscustomobject]@{
        Name = $vmName
        IP   = $ip
    }
    Write-Host "VM: $vmName IP: $ip"
}

Connect to the VM Automatically

The final step the script runs through is automatically connecting to the VM via mstsc.exe. This eliminates the need for the user to do anything at all to get started.

## If the student is on Windows, prompt and connect for them. Otherwise, tell them the IPs to connect to
if ($env:OS -eq 'Windows_NT') {
    $rdpNow = Read-Host -Prompt "RDP to the required host ($RequiredRdpVM) now (Y,N)?"
    if ($rdpNow -eq 'Y') {
        $requiredVM = $vmIps.where({ $_.Name -eq $RequiredRdpVM })
        $ip = $requiredVM.IP
        mstsc /v:$ip
    } else {
        Write-Host "Please RDP to the VM [$($RequiredRdpVM) : $ip] now to begin course. The username and password are provided in the Lab Preparation section of the chapter."
    }
} else {
    Write-Host "Please RDP to the VM [$($RequiredRdpVM) : $ip] now to begin course. The username and password are provided in the Lab Preparation section of the chapter."
}

Wrapping Up

You have seen that you can completely automate a lab environment for any purposes (not just for training) by running a single PowerShell script!

I hope this project of mine gives you a foundation to create a more robust tool! If you do improve upon it, let me know. I'd be glad to update this post and the GitHub repo.

Further Reading