Automation–The New World of Tenant Provisioning with Windows Azure Pack (Part 3): Automated Deployment of the Identity Workload as a Tenant Admin

Aaaaaand we’re back!

Okay, I realize the title of this post is very similar to Part 2 of this blog series - and there is very good reason for that. Though they look similar, they are different. Different personas; different scripts; different context.

Once again, I will call on a little image assistance, to clear things up (the scope of this post is illustrated by the portion of the image with the red outline) :


Note Find Part 1: Intro & TOC here and Part 2: Automated Deployment of Tenant Network and Identity Workload (Isolated Tenant Virtual Network & Active Directory VM Role; from the Service Admin Persona) here .

And if that didn’t clear it up, let’s try expanding on the title for Part 3, (now with more sub-title!)

Automated Deployment of the Identity Workload as a Tenant Admin

(Active Directory VM Role; from the Tenant Admin Persona)

So, as you know, Part 2 of this blog series was all about deployment as the Service Administrator persona. Now, in Part 3, it is all about deployment as the Tenant Administrator.

I think it is time for another quick rehash (with image) :

In the above image, we see my view of these two personas, as well as what we will be getting into for this post (the first two bullets in the box on the right) .

The Options

How about another image?

This time, to visualize the high-level process for the Tenant Admin:


So, as you can see, the Tenant Admin has options.

Note Automatically adding the Isolated Tenant Virtual Network (Isolated Software Defined Network (SDN)) is completely optional, as the Service Admin can simply recommend that the Tenant Admin manually create Virtual Networks from the Tenant Portal before deploying VM Roles.

Reminder These options are the same for any VM Role Deployment, Active Directory happens to be the first one to be deployed, so it gets all the attention.

The Process

Remember that multi-step process I described in Part 2 ?

Well, it is basically the same.

So, to save you from going back and forth, here is the representative image for “The Process” :

What’s the difference?

Primarily, it is the Web Service Endpoint PORT and Authorization method.

  • As a Service Admin, you can leverage Bearer Token authorization and make WS calls against the Tenant API (non-Public), Port 30005
  • As a Tenant Admin, you leverage Certificates and make WS calls against the Public Tenant API, Port 30006

In fact, if you are familiar with Automating Windows Azure with PowerShell, the pre-requisite process has a very similar look and feel.

Care to venture a guess what we leverage to setup/use the Certificate?

The Windows Azure PowerShell Module  (It is one of the options anyway.)

That’s right. The very same – and if you haven’t checked it out, or downloaded the latest version, you may have missed all the “new-ish” commands that contain *-WAPack*

Which is a great segue to…

The Pre-Requisites

  1. Valid Account and Subscription to a Plan within WAP (have the ability to login to the WAP Tenant Portal and provision Gallery Items (VM Roles)) :
  2. The latest Windows Azure PowerShell module (contains the latest *-WAPack* Commands) :
  3. Ability to Execute Windows Azure PowerShell Commands
  4. Creation and Usage of a Management Certificate (derived from WAP Publish Settings File – see the Pre-Requisite Setup steps below)

The Pre-Requisite Setup

  1. Download and Install the Windows Azure PowerShell Commands

  2. Get the Published Settings File for your Subscription – Navigate to your version of the following URL:
    This will prompt to download the file locally – My recommendation is to download to the machine where the PowerShell Scripts will be executed.

    Note If the target portal leverages ADFS, do not include the port (30081) in the address while attempting to retrieve the .publishsettings file. At least this is what I experienced between the two environments we have, one without ADFS (required port in URL), and one with ADFS (port not used in URL).

    (Example Path: C:\LocalPath\SubscriptionName-MM-d-YYYY-credentials.publishsettings)

  3. Open PowerShell and ensure the Windows Azure PowerShell module is loaded

  4. Execute the following command (modify the command text based on the name and where the publishsettings file is located) :

    Import-WAPackPublishSettingsFile C:\WAP\Collaboration Workloads (Tenant Deployed)-3-11-2014-credentials.publishsettings

Note These steps not only import the certificate into the Management Certificates section in the Tenant Portal:


But ALSO add it to the Certificate Store for the Current User:


Resulting in the following, when Get-WAPackSubscription is executed from PowerShell:


Reminder The security best practice for the publishsettings file is to store it temporarily, and then delete it after the settings have been imported. A malicious user gaining access to the publishsettings file can edit, create, and delete your Windows Azure Pack resources.

So, there you go - All the theory and pre-requisites you can handle, right?

The good news…With these pre-requisites out of the way, we can now get on to the actual deployment script!

(as a Tenant Admin against the Public WAP API)

001002003004005006007008009010011012013014015016017018019020021022023024025026027028029030031032033034035036037038039040041042043044045046047048049050051052053054055056057058059060061062063064065066067068069070071072073074075076077078079080081082083084085086087088089090091092093094095096097098099100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 #region GetWAPConnectionData# Get WAP Subscription Information$WAPSubscription = Get-WAPackSubscription# Set Subscription$SubscriptionID = $WAPSubscription.SubscriptionId# Get Management Certificate Info$CertThumb = $WAPSubscription.Certificate.Thumbprint$CertPath = "Cert:\CurrentUser\My\{0}" -f $CertThumb$Cert = Get-Item $CertPath# Set Tenant Portal Address$TenantPortalAddress = $WAPSubscription.ServiceEndpoint.Host# Set Port$Port = $WAPSubscription.ServiceEndpoint.Port#endregion GetWAPConnectionData#region SetVariables# Set Gallery Item Name and Version for Match and Deploy$GalleryItemName = "DomainController"$GIVersion = ""# Set Common Gallery Item Parameters$UserID = ""$VMRoleNetwork = "Tenant Network ({0})" -f $UserID$CloudServiceName = "CloudService-4-{0}" -f $SubscriptionID$VMRoleName = "ActiveDirectory"$VMRoleNamePattern = "DC##"$VMRoleSize = "ExtraSmall"$VMRoleTZ = "Pacific Standard Time"$OSDisk = "Windows Server 2012 Datacenter"$OSDiskVersion = ""$Password = "Password"#endregion SetVariables#region GetResDef# Get Gallery Item Reference$GIReferenceUri = "https://{0}:{1}/{2}/Gallery/GalleryItems/$/MicrosoftCompute.VMRoleGalleryItem?api-version=2013-03" -f $TenantPortalAddress,$Port,$SubscriptionID$GIReferenceData = [xml](Invoke-WebRequest -Certificate $Cert -Uri $GIReferenceUri | Select-Object -ExpandProperty Content)$GalleryItemREF = $ | ? {$_ -match $GalleryItemName}# Get Gallery Item Resource Definition$GIResDEFUri = "https://{0}:{1}/{2}/{3}/?api-version=2013-03" -f $TenantPortalAddress,$Port,$SubscriptionID,$GalleryItemREF$GIResourceDEFJSON = Invoke-WebRequest -Certificate $Cert -Uri $GIResDEFUri | Select-Object -ExpandProperty Content#Convert ResDef JSON to Dictionary[System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions") | Out-Null$JSSerializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer$ResDef = $JSSerializer.DeserializeObject($GIResourceDEFJSON)#endregion GetResDef#region SetResDefConfig# Create Gallery Item Parameter Hashtable (for Common Data)$GIParamList = @{ VMRoleVMSize = $VMRoleSize VMRoleOSVirtualHardDiskImage = "{0}:{1}" -f $OSDisk,$OSDiskVersion VMRoleAdminCredential = "administrator:{0}" -f $Password VMRoleTimeZone = $VMRoleTZ VMRoleComputerNamePattern = $VMRoleNamePattern VMRoleNetworkRef = $VMRoleNetwork }# Add to Gallery Item Parameter Hashtable (for GI Specific Data)if ($GalleryItemName -eq "DomainController"){ $GIParamList += @{DomainControllerWindows2012DomainDNSName = $UserID.Split("@")[1]} $GIParamList += @{DomainControllerWindows2012DomainNETBIOSName = ($UserID.Split("@")[1]).Split(".")[0]} $GIParamList += @{DomainControllerWindows2012SafeModeAdminPassword = $Password}} # Convert Gallery Item Parameter Hashtable To JSON$ResDefConfigJSON = ConvertTo-Json $GIParamList#Add ResDefConfig JSON to Dictionary$ResDefConfig = New-Object 'System.Collections.Generic.Dictionary[String,Object]'$ResDefConfig.Add("Version",$GIVersion)$ResDefConfig.Add("ParameterValues",$ResDefConfigJSON)#endregion SetResDefConfig#region GenerateGIPayloadJSON# Set Gallery Item Payload Variables$GISubstate = $null$GILabel = $VMRoleName$GIName = $VMRoleName$GIProvisioningState = $null$GIInstanceView = $null# Set Gallery Item Payload Info$GIPayload = @{ "InstanceView" = $GIInstanceView "Substate" = $GISubstate "Name" = $GIName "Label" = $GILabel "ProvisioningState" = $GIProvisioningState "ResourceConfiguration" = $ResDefConfig "ResourceDefinition" = $ResDef }# Convert Gallery Item Payload Info To JSON$GIPayloadJSON = ConvertTo-Json $GIPayload -Depth 7#endregion GenerateGIPayloadJSON#region GetOrSetCloudService# Get Cloud Services$CloudServicesUri = "https://{0}:{1}/{2}/CloudServices?api-version=2013-03" -f $TenantPortalAddress,$Port,$SubscriptionID$CloudServicesData = [xml](Invoke-WebRequest -Uri $CloudServicesUri -Certificate $Cert | Select-Object -ExpandProperty Content)$CloudService = $ | ? {$_ -match $CloudServiceName}if (!$CloudService) { # Set Cloud Service Configuration $CloudServiceConfig = @{ "Name" = $CloudServiceName "Label" = $CloudServiceName } # Convert Cloud Service Configuration To JSON $CloudServiceConfigJSON = ConvertTo-Json $CloudServiceConfig $CloudServicesData = [xml](Invoke-WebRequest -Uri $CloudServicesUri -Certificate $Cert -Method Post -Body $CloudServiceConfigJSON -ContentType "application/json") $CloudService = $ | ? {$_ -match $CloudServiceName}}#endregion GetOrSetCloudService#region DeployGIVMRole# Set Gallery Item VM Role Deploy URI$GIDeployUri = "https://{0}:{1}/{2}/CloudServices/{3}/Resources/MicrosoftCompute/VMRoles/?api-version=2013-03" -f $TenantPortalAddress,$Port,$SubscriptionID,$CloudService# Deploy Gallery Item VM Role$VMRoleDeployed = Invoke-WebRequest -Uri $GIDeployUri -Certificate $Cert -Method Post -Body $GIPayloadJSON -ContentType "application/json"$VMRoleDeployed#endregion DeployGIVMRole

Note(s) I have several notes about the above script. So I will list them here:

  • This is just an example script.
  • It has been tested against our Demo/Test/Dev environment multiple times.
  • It absolutely requires the pre-requisites discussed above. And while there are alternatives to getting the required variable data based on the Get-WAPackSubscription command, I have found this to be the most efficient/dynamic method.
  • The $TenantPortalAddress Variable may need to be set to a specific string, rather than being extracted from the information available from the Get-WAPackSubscription command, specifically if the public portal address is different than the FQDN of WAP Admin Server.
  • If you are getting errors during deployment like, “Disk with Name (Windows Server 2012 Datacenter) and Version ( not found while translating Resource Definition for cloud resource.” it is likely that the $OSDisk and/or $OSDiskVersion Variables have incorrect data. The Tenant API does not care so much for the actual OS Disk name, instead, it appears to require the Family Name of the OS Disk.
  • I chose to leave all the Variable settings within the script. These could very easily be presented as Script Parameters, and fed into the script to make it a bit more generic.
  • Just like the Service Admin example script, there is a portion of the Resource Definition Configuration (ResDefConfig) that is tied directly to the ResDef/ResDefExt for a given Gallery Item VM Role. When generating the Gallery Item Parameter Hashtable, I have separated the GI specific data from the common GI data. In most cases (especially for the Gallery Item VM Roles produced by my team), the only portion of the script that has to change per VM Role is this GI specific data (and, of course any specific Variable data).
  • This script (for the Tenant Admin) should look nearly identical to the script from Part 2 (for the Service Admin). This is by design, as I wanted to keep as may synergies in play as possible. Remember, there are only subtle differences (Ports and Auth).
  • While the steps in the Gallery Item VM Role deployment process will likely remain the same, the actual script could be improved in various ways: Addition of Script Parameters, Separated into Functions, Transformed into a Set of Cmdlets, etc. If anyone takes these improvements on, I will be happy to reference / endorse the published work here in this blog.
  • Finally, as you can see, I have broken the script up into “regions”, each of which builds on the last, to eventually complete all the data collection / command execution for the final Invoke-WebRequest POST to deploy the Gallery Item VM Role. Here is an image illustrating the seven regions:

All that said, if everything goes right during the execution of this example script, you should see something very similar to this:


What’s Missing?

Reminiscent of one of the final sections in Part 2, this is where we check to see if I have “missed anything” – of course, strictly based on the in scope deliverables called out at the top.

So, let’s check those checkmarks…


Looks good to me!

But wait, WHY would I want to do this?

Great question! Here are some use cases (from both the Service Admin and Tenant Admin personas):

  • Use Case 1: As a Tenant – Simple avoidance of manual clicking to deploy Gallery Item VM Roles
  • Use Case 2: As a Tenant – Develop scripts to fully deploy a set of multiple concurrent (and/or dependent) Gallery Item VM Role Deployments (with scripts like this, you have complete control over the “what” and “when”)
  • Use Case 3: As a Service Provider (or Enterprise acting like one) – Create a custom set of cmdlets encapsulating the parameters and logic into easily consumable/executable commands
  • Use Case 4: As a Service Provider (or Enterprise acting like one) – Enabling your Tenants/End Users to automate their own Gallery Item VM Role deployments (external to any SMA efforts on the Service Admin side)

Note These are just some of the use cases I could come up with off the top of my head. I am sure you have many more scenarios in mind.

What’s Next?

Well, at this point, based on Parts 2 and 3 of this blog series, you really have everything necessary to deploy not only the Gallery Item VM Role for Active Directory, but any Gallery Item VM Role you have built (or pulled down off of WebPI (or our Blog)).

But hey, I am not going to leave you hanging. The very next blog post is all about Tenant Workloads, where I will be providing the necessary script updates (to what you have seen so far) to deploy the Gallery Item VM Roles for Lync, SharePoint, and Exchange (for both personas).

AND A VIDEO (an actual 8-Minute-Demo Video)!


How exciting….

Oh, and have you seen this blog post yet?

Windows Azure Pack–Gallery Item VM Role–References for Creation, Configuration, and Automation

If not, check it out.

And while it does cross-reference back to this post, it covers the entire Gallery Item VM Role Lifecycle (well the important bits, anyway).

Blog Series Table of Contents

  1. Part 1: Intro & TOC
  2. Part 2: Automated Deployment of Tenant Network and Identity Workload (Isolated Tenant Virtual Network & Active Directory VM Role; from the Service Admin Persona)
  3. Part 3: Automated Deployment of the Identity Workload as a Tenant Admin (Active Directory VM Role; from the Tenant Admin Persona)
  4. Part 4: Automated Deployment of Tenant Workloads (Lync, SharePoint, and Exchange) (Lync, SharePoint, and Exchange VM Roles; from both Service Admin and Tenant Admin Personas)
  5. Part 5: Working with the SQL Server resource provider, and the ITIL dilemma (by Bruno Saille)
  6. Part 6: TBD (We hope to have something around: Value Added Services/Offerings and Ongoing Automated Maintenance/Operations)

The download ( Windows Azure Pack Tenant Provisioning Automation ) includes (14) files.

For more information please refer to Part 4 of this blog series (near the bottom of the post), or in the description of the TechNet Gallery Contribution itself.

Download the Windows Azure Pack Tenant Provisioning Automation Toolkit from TechNet Gallery here:


Thanks for checking out my latest blog series! For more information, tips/tricks, and example solutions for Automation within System Center, Windows Azure Pack, Windows Azure, etc., be sure to check out the other blog posts from Building Clouds in the Automation Track!