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
- 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)):
- The latest Windows Azure PowerShell module(contains the latest *-WAPack* Commands):
- Ability to Execute Windows Azure PowerShell Commands
- Creation and Usage of a Management Certificate (derived from WAP Publish Settings File – see the Pre-Requisite Setup steps below)
The Pre-Requisite Setup
- Download and Install the Windows Azure PowerShell Commands
- Get the Published Settings File for your Subscription – Navigate to your version of the following URL: https://tenant.portal.com:30081/publishsettings
This will prompt to download the file locally – My recommendation is to download to the machine where the PowerShell Scripts will be executed.
(Example Path: C:\LocalPath\SubscriptionName-MM-d-YYYY-credentials.publishsettings) - Open PowerShell and ensure the Windows Azure PowerShell module is loaded
- Execute the following command (modify the command text based on the name and where the publishsettings file is located):
Import-WAPackPublishSettingsFileC:\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!
Example Active Directory Gallery Item VM Role Deployment Script
(as a Tenant Admin against the Public WAP API)
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | #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 = "1.0.0.0" # Set Common Gallery Item Parameters $UserID = "tenant@company.com" $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 = "1.0.0.0" $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 = $GIReferenceData.feed.entry.content.properties.resourcedefinitionUrl | ? {$_ -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 = $CloudServicesData.feed.entry.content.properties.Name | ? {$_ -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 = $CloudServicesData.entry.content.properties.Name | ? {$_ -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.
- 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 anyGallery 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)!
AND A TECHNET GALLERY CONTRIBUTION (for all the scripts)!
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
- Part 1: Intro & TOC
- Part 2: Automated Deployment of Tenant Network and Identity Workload(Isolated Tenant Virtual Network & Active Directory VM Role; from the Service Admin Persona)
- Part 3: Automated Deployment of the Identity Workload as a Tenant Admin(Active Directory VM Role; from the Tenant Admin Persona)
- Part 4: Automated Deployment of Tenant Workloads(Lync, SharePoint, and Exchange VM Roles; from both Service Admin and Tenant Admin Personas)
- Parts 5 & 6: TBD(We hope to have something around: Value Added Services/Offerings and Ongoing Automated Maintenance/Operations)
Note Automated Deployment of the VM Roles in these examples will include PowerShell scripts for both Service Admin and Tenant Admin personas.
Once Parts 1-4 are published, I will be creating a TechNet Gallery Contribution with a collection of all the scripts (SMA Runbooks / PowerShell Workflows). Look for a download link here in the coming weeks!
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!
enJOY!