The Power of JSON Templates

Creating templates is a powerful way to quickly deploy environments in Azure.  There are many use cases including student labs, environments that need to be duplicated across testing, QA, and production, and in the example outlined below, an environment to support a specific solution or application.  Benefits of deploying templates include the speed of deployment, the accuracy of deployment, and the ability to adjust, change, or test different configurations in an iterative way.  The video and materials below demonstrate a few of the capabilities of templates, but these templates can do much more, including joining domains, adding secondary disks, looping through code to create many instances of a particular configuration, and even using predefined VHD templates that deploy a fully configured environment.  In addition to all of these benefits, Azure provides you with the ability to also delete the environments quickly so that you can deploy, change, delete, rinse and repeat until you have what you want.

In the video that follows, we will talk through the deployment of the environment depicted in the image below using a JSON template. You will see that this deployment unfolds at jaw dropping speed, and then we can choose to delete the environment or shutdown the associated VMs at any time as we test.  This is a glance of the real power of cloud computing.

 

ADFS in Azure Image

https://www.youtube.com/watch?v=vSoXY-FYvm4

 

Deploy with Hybrid Use Benefit (HUB) - Bring your own licenses and save.

 

The template in this section will deploy the ADFS environment in the diagram below, using the unique names, IP ranges, etc., that you'll assign as default values in the JSON template.  The only omissions are the load balancers, which are left out to prevent any public IP exposure without your explicit creation of those objects.  You can learn how to deploy the load balancers in the ADFS deployment series video #4 located at https://aka.ms/edu/azureadfs.  Note, if you have VNets that already exist, the template can be modified to place VMs into those networks.

Deployment Time: ~10-15 minutes.

 

Prerequisites:

  1. Obtain JSON Template on GitHub and place into a directory where you will execute your PowerShell script. Note, ensure that the current path when running the script is the path where the JSON file is located.
  1. Create Azure resource group to deploy into.
  2. Create two storage accounts in region of choice.
  3. Upload custom images to storage accounts per the instructions in the HUB benefit video. https://aka.ms/edu/hub
  4. Gather subscription details, names, and address spaces for your unique deployment and document them using the planning worksheet below.

To Build:

  1. Update parameter defaults as desired to provide your own unique names and addresses.
  2. Update the URI parameters listed below in JSON template to point to your VHD as source.
    • storageAccountOne_osDiskVhdUri
    • storageAccountTwo_osDiskVhdUri
  3. Run DeployADFSHUB.ps1 (provided below)

The JSON Template:  https://github.com/edebord/EDUJSON/blob/master/DeployADFSHUB.json

The PowerShell Script:

param(

 [Parameter(Mandatory=$True)]

 [string]

 $subscriptionId,

 [Parameter(Mandatory=$True)]

 [string]

 $resourceGroupName,

 [string]

 $resourceGroupLocation,

  [Parameter(Mandatory=$True)]

 [string]

 $deploymentName,

 [string]

 $templateFilePath ="DeployADFSHUB.json",

 [string]

 $parametersFilePath ="DeployADFSParameters.json"

)

<#

.SYNOPSIS

    Registers RPs

#>

Function RegisterRP {

    Param(

        [string]$ResourceProviderNamespace

    )

     Write-Host "Registering resource provider '$ResourceProviderNamespace'";

    Register-AzureRmResourceProvider -ProviderNamespace $ResourceProviderNamespace;

}

#******************************************************************************

# Script body

# Execution begins here

#******************************************************************************

$ErrorActionPreference
= "Stop"

# sign in

Write-Host "Logging
in...";

Login-AzureRmAccount;

# select subscription

Write-Host "Selecting
subscription '$subscriptionId'";

Select-AzureRmSubscription
-SubscriptionID $subscriptionId;

# Register RPs

$resourceProviders
= @("microsoft.compute","microsoft.network","microsoft.storage");

if($resourceProviders.length) {

    Write-Host "Registering resource providers"

    foreach($resourceProvider in $resourceProviders) {

        RegisterRP($resourceProvider);

    }

}

#Create or check for existing resource group

$resourceGroup
= Get-AzureRmResourceGroup -Name
$resourceGroupName
-ErrorAction SilentlyContinue

if(!$resourceGroup)

{

    Write-Host "Resource group '$resourceGroupName' does
not exist. To create a new resource group, please enter a location.";

    if(!$resourceGroupLocation)
{

        $resourceGroupLocation =
Read-Host "resourceGroupLocation";

    }

    Write-Host "Creating resource group '$resourceGroupName' in location '$resourceGroupLocation'";

    New-AzureRmResourceGroup -Name
$resourceGroupName
-Location $resourceGroupLocation

}

else{

    Write-Host "Using existing resource group '$resourceGroupName'";

}

# Start the deployment

Write-Host "Starting
deployment...";

if(Test-Path $parametersFilePath) {

    New-AzureRmResourceGroupDeployment -ResourceGroupName $resourceGroupName -TemplateFile $templateFilePath -TemplateParameterFile $parametersFilePath;

}
else {

    New-AzureRmResourceGroupDeployment -ResourceGroupName $resourceGroupName -TemplateFile $templateFilePath;

}

 

Planning Worksheet

 

Gather the Following Information in Advance of Using Template

Subscription ID – Used as the deployment target subscription.

Resource Group Name – The Resource Group where objects will be deployed.

Deployment Region – Choose same region as where storage accounts will be located.

Administrator Username and Password – This will become your local administrator on each VM deployed.

Availability Set Names

  1. Domain Controllers
  2. ADFS Servers
  3. Web Application Proxy Servers

VM Names

  1. Domain Controller #1
  2. Domain Controller #2
  3. ADFS Server #1
  4. ADFS Server #2
  5. WAP Server #1
  6. WAP Server #2

DC Static IP Addresses – These will be assigned for Domain Controller requirements

Network Security Group Names

Network Details  - Choose a subnet space not currently used on premise

  1. IP Address Space
  2. Subnet Names
  3. Subnet Address Space

Storage Account Names – Must be globally unique

  1. VM Storage Account #1 (Created in advance for HUB benefit deployment)
  2. VM Storage Account #2 (Created in advance for HUB benefit deployment)
  3. VM Backups Storage Account
  4. VM Diagnostics Storage Account (Required for Premium Storage)

 

Deploy without HUB Benefit

 

This template will deploy the environment in the diagram below, using the same object names and addresses by default.    If you wish to deploy with unique names, IP ranges, etc., then you'll need to provide parameters to adjust as desired.  The only omissions are the load balancers, so if you or customers deploy this template, there is not public IP exposure.

 

Deployment Time: ~10-15 minutes.

 

Prerequisites:

  1. Obtain JSON Template on GitHub and place into a directory where you will execute your PowerShell script. Note, ensure that the current path when running the script is the path where the JSON file is located.
  1. Create Azure resource group to deploy into.
  2. Gather subscription details, names, and address spaces for your unique deployment and document them using the planning worksheet below.

 

To Build:

  1. Update parameter defaults as desired to provide your own unique names and addresses.
  2. Run DeployADFS.ps1 (provided below)

The JSON Template: https://github.com/edebord/EDUJSON/blob/master/DeployADFS.json

The PowerShell Script:

param(

 [Parameter(Mandatory=$True)]

 [string]

 $subscriptionId,

 [Parameter(Mandatory=$True)]

 [string]

 $resourceGroupName,

 [string]

 $resourceGroupLocation,

 [Parameter(Mandatory=$True)]

 [string]

 $deploymentName,

 [string]

 $templateFilePath ="DeployADFS.json",

 [string]

 $parametersFilePath ="parameters.json"

)

<#

.SYNOPSIS

    Registers RPs

#>

Function RegisterRP {

    Param(

        [string]$ResourceProviderNamespace

    )

    Write-Host "Registering resource provider '$ResourceProviderNamespace'";

    Register-AzureRmResourceProvider -ProviderNamespace $ResourceProviderNamespace -Force;

}

#******************************************************************************

# Script body

# Execution begins here

#******************************************************************************

$ErrorActionPreference
= "Stop"

# sign in

Write-Host "Logging
in...";

Login-AzureRmAccount;

# select subscription

Write-Host "Selecting
subscription '$subscriptionId'";

Select-AzureRmSubscription
-SubscriptionID $subscriptionId;

# Register RPs

$resourceProviders
= @("microsoft.compute","microsoft.network","microsoft.storage");

if($resourceProviders.length) {

    Write-Host "Registering resource providers"

    foreach($resourceProvider in $resourceProviders) {

        RegisterRP($resourceProvider);

    }

}

#Create or check for existing resource group

$resourceGroup
= Get-AzureRmResourceGroup -Name
$resourceGroupName
-ErrorAction SilentlyContinue

if(!$resourceGroup)

{

    Write-Host "Resource group '$resourceGroupName' does
not exist. To create a new resource group, please enter a location.";

    if(!$resourceGroupLocation)
{

        $resourceGroupLocation =
Read-Host "resourceGroupLocation";

    }

    Write-Host "Creating resource group '$resourceGroupName' in location '$resourceGroupLocation'";

    New-AzureRmResourceGroup -Name
$resourceGroupName
-Location $resourceGroupLocation

}

else{

    Write-Host "Using existing resource group '$resourceGroupName'";

}

# Start the deployment

Write-Host "Starting
deployment...";

if(Test-Path $parametersFilePath) {

    New-AzureRmResourceGroupDeployment -ResourceGroupName $resourceGroupName -TemplateFile $templateFilePath -TemplateParameterFile $parametersFilePath;

}
else {

    New-AzureRmResourceGroupDeployment -ResourceGroupName $resourceGroupName -TemplateFile $templateFilePath;

}