Automation–Automating Hybrid Clouds with Windows Azure and PowerShell (Part 3): Public Cloud Environment Provisioning PowerShell Workflow Examples

Hi. :)

At this point, you know the drill…This is Part 3 of the Automating Hybrid Clouds with Windows Azure and PowerShell blog series – which is kind of the same thing as Part 2, but this time the deliverable is a set of PowerShell Workflow Examples.

For reference, here is Part 1 (Intro & TOC) , and here is Part 2 (Public Cloud Environment Provisioning PowerShell Examples) .

Let’s get to it.


Public Cloud Environment Provisioning PowerShell Workflow Examples

As covered in Parts 1 and 2 of this blog series, the following is the list of high level concept commands that will be covered in this post:

High Level Concept Commands

  1. Establish Windows Azure Subscription Connection ( Please Reference Part 2 for details )
  2. Create Windows Azure Affinity Group
  3. Create Windows Azure Cloud Service
  4. Create Windows Azure Storage Account
  5. Create Windows Azure Storage Container
  6. Upload “On-Prem” VHD to Windows Azure Storage Container
  7. Copy Windows Azure Blob
  8. Create Windows Azure VM Image
  9. Create Windows Azure VM

Pre-Requisites

These are the same as they were in Part 2, but again for reference, here is the short list of pre-requisites for this example solution:

  1. Windows Server
  2. A VHD file that meets the requirements to be uploaded to Windows Azure
    Also see: Converting Hyper-V .vhdx to .vhd file formats for use in Windows Azure
  3. Windows Azure PowerShell Module
    Direct Download Link: https://go.microsoft.com/?linkid=9811175&clcid=0x409

Okay, there are three new pre-requisites for this post:

  1. An Established Windows Azure Subscription Connection ( Please Reference Part 2 for details ).
    Note This portion of the script will be included in the overall script at the end of the post, just not covered separately here at the top.
  2. The Workflows that contain all the commands previously discussed in Part 2 of this blog series.
  3. An Understanding of the differences between PowerShell and PowerShell Workflow:
    Reference 1: Syntactic Differences Between Script Workflows and Scripts
    Reference 2: Windows PowerShell: PowerShell scripts versus PowerShell workflows
    Reference 3: When Windows PowerShell Met Workflow
    Note Many other references available on TechNet/MSDN.

PowerShell Workflow Differences / Notes

Here is a short list of differences and notes based on my experience creating the Public Cloud Environment Provisioning Example Solution with PowerShell Workflow:

  1. Get-AzureSubscription - There is a difference between executing the Get-AzureSubscription command within the script calling Workflows and executing the Get-AzureSubscription command within the Workflows themselves. For this reason, within the final script (which contains variables, Workflows and Workflow calls), I have placed $AzureSubscriptionForWorkflow = Get-AzureSubscription at the beginning of each Workflow. While there may be better ways to handle this, I found this the best way to ensure Workflow execution contains the necessary Windows Azure Subscription information required by the Windows Azure commands being called.
  2. Return Values - The return values are not exactly what you would expect (especially if you were expecting the same results as when executed as straight PowerShell). This difference is to be expected, with the usage of Workflow. So, what you will see in these examples is verification based on if ($Variable.OperationStatus –eq "Succeeded") logic. Obviously, you can modify what is being returned by each Workflow based on your own project requirements. This is just the method I chose for these examples.
  3. PowerShell Jobs - Because we are leveraging Workflow, we can very easily take advantage of the -AsJob and Receive-Job features. You will see that I have used Jobs for the longer running portions of this example (Uploading the VHD to Windows Azure, Creating the Windows Azure VM from Windows Azure VM Image).
  4. Regions – Nothing major here, for the sake of organization, I decided to leverage #region … #endregion functionality. You will see in the overall script I have broken everything up into 5 major regions:
    image 

Other than these few things, you should be good to go!

Speaking of Regions…

I decided it would be neat (pardon the pun) to break this blog post into these 5 regions as well. Seems like a pretty organized and symmetric idea anyway… ;)


Pre-Requisite Setup

This section of the blog posts covers (references, rather) the necessary steps for an Established Windows Azure Subscription Connection.

#region 0.PrerequisiteSetup

This region is covered 100% in Part 2 of this blog series.

image

For reference, here is the link:
Automating Hybrid Clouds with Windows Azure and PowerShell (Part 2): Public Cloud Environment Provisioning PowerShell Examples

#endregion 0.PrerequisiteSetup


Region 0 Execution Results

Steps Completed in Region 0:

  • Connection to a Windows Azure Subscription Established

Pre-Requisite Workflows

This section of the blog post will cover all the PowerShell Workflow scripts required for the Public Cloud Environment Provisioning example solution.

Note I am breaking them up into individual scripts so that you can leverage them one at a time if desired. The complete script exists at the end of this blog post.

#region 1.PrerequisiteWorkflows

001 #1.0 Establish Prerequisite Workflows

 

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 #1.1 Create Affinity Group workflow Create-AzureAffinityGroup { param ( [string]$ProjectName, [string]$AGLocation, [string]$AGLocationDesc, [string]$AGLabel ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureAffinityGroup = Get-AzureAffinityGroup -Name $ProjectName -ErrorAction SilentlyContinue if(!$AzureAffinityGroup) { $AzureAffinityGroup = New-AzureAffinityGroup -Location $AGLocation -Name $ProjectName -Description $AGLocationDesc -Label $AGLabel } Return $AzureAffinityGroup }

 

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 #1.2 Create Cloud Service workflow Create-AzureCloudService { param ( [string]$ProjectName, [string]$AGName, [string]$CloudServiceDesc, [string]$CloudServiceLabel ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureService = Get-AzureService -ServiceName $ProjectName -ErrorAction SilentlyContinue if(!$AzureService) { $AzureService = New-AzureService -AffinityGroup $AGName -ServiceName $ProjectName -Description $CloudServiceDesc -Label $CloudServiceLabel } Return $AzureService }

 

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 #1.3 Create Storage Account workflow Create-AzureStorageAccount { param ( [string]$StorageAccountName, [string]$AGName, [string]$StorageAccountDesc, [string]$StorageAccountLabel ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureStorageAccount = Get-AzureStorageAccount -StorageAccountName $StorageAccountName -ErrorAction SilentlyContinue if(!$AzureStorageAccount) { $AzureStorageAccount = New-AzureStorageAccount -AffinityGroup $AGName -StorageAccountName $StorageAccountName -Description $StorageAccountDesc -Label $StorageAccountLabel } Return $AzureStorageAccount }

 

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 #1.4 Create Storage Container workflow Create-AzureStorageContainer { param ( [string]$StorageContainerName ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureStorageContainer = Get-AzureStorageContainer -Name $StorageContainerName -ErrorAction SilentlyContinue if(!$AzureStorageContainer) { $AzureStorageContainer = New-AzureStorageContainer -Name $StorageContainerName } Return $AzureStorageContainer }

 

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 #1.5 Get Blob Info workflow Get-AzureBlobInfo { param ( [string]$StorageContainerName, [string]$VHDName ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureBlob = Get-AzureStorageBlob -Container $StorageContainerName -Blob $VHDName -ErrorAction SilentlyContinue Return $AzureBlob }

 

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 #1.6 Upload Local VHD to Storage Container workflow Upload-LocalVHDtoAzure { param ( [string]$StorageContainerName, [string]$VHDName, [string]$SourceVHDPath, [string]$DestinationBlobURI, [bool]$OverWrite ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureBlob = Get-AzureStorageBlob -Container $StorageContainerName -Blob $VHDName -ErrorAction SilentlyContinue if(!$AzureBlob -or $OverWrite) { $AzureBlob = Add-AzureVhd -LocalFilePath $SourceVHDPath -Destination $DestinationBlobURI -OverWrite:$OverWrite } Return $AzureBlob }

 

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 #1.7 Copy Blob from one Storage Container to another workflow Copy-AzureStorageBlob { param ( [string]$DestinationContainer, [string]$DestinationBlobName, [string]$SourceContainer, [string]$SourceBlobName ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureBlob = Get-AzureStorageBlob -Container $DestinationContainer -Blob $DestinationBlobName -ErrorAction SilentlyContinue if(!$AzureBlob) { $AzureBlob = Start-AzureStorageBlobCopy -DestContainer $DestinationContainer -SrcBlob $SourceBlobName -SrcContainer $SourceContainer -DestBlob $DestinationBlobName } Return $AzureBlob }

 

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 #1.8 Create Azure VM Image from Copied Blob workflow Create-AzureVMImage { param ( [string]$VMImageName, [string]$VMImageBlobContainer, [string]$VMImageBlobName, [string]$VMImageOS ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureVMImage = Get-AzureVMImage -ImageName $VMImageName -ErrorAction SilentlyContinue if(!$AzureVMImage) { $AzureBlobMediaLocation = (Get-AzureStorageBlob -Container $VMImageBlobContainer -Blob $VMImageBlobName).ICloudBlob.Uri.AbsoluteUri $AzureVMImage = Add-AzureVMImage -ImageName $VMImageName -MediaLocation $AzureBlobMediaLocation -OS $VMImageOS } Return $AzureVMImage }

 

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 #1.9 Create Azure VM from Newly Created VM Image workflow Create-AzureVM { param ( [string]$VMName, [string]$ServiceName, [string]$AdminUsername, [string]$VMImageName, [string]$Password, [bool]$Windows, [string]$VMInstanceSize, [bool]$WaitForBoot ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureVM = Get-AzureVM -Name $VMName -ServiceName $ServiceName -ErrorAction SilentlyContinue if(!$AzureVM -and $Windows) { $AzureVM = New-AzureQuickVM -AdminUsername $AdminUsername -ImageName $VMImageName -Password $Password -ServiceName $ServiceName -Windows:$Windows -InstanceSize $VMInstanceSize -Name $VMName -WaitForBoot:$WaitForBoot } Return $AzureVM }

#endregion 1.PrerequisiteWorkflows


Region 1 Execution Results

Steps Completed in Region 1:

  • All PowerShell Workflow Scripts in the Example Solution Loaded and Ready for Execution

Create New Windows Azure Environment

Now the fun can begin. This is the part of the PowerShell Script where I call all the above Workflows required to create the New Windows Azure Environment – called in a particular order, with specific logic. If you have your Orchestrator hat on, you can think of the Workflows as Sub-Runbooks, and the follow script (when compiled together) is the Process Runbook. In fact, the same exact concept exists in SMA – Process Runbook and Sub-Runbooks.

Note Once again, I am breaking up the main process script into multiple parts. This is so that it is easier to consume, and you can leverage it a piece at a time if desired. The complete script exists at the end of this blog post.

#region 2.CreateNewWindowsAzureEnvironment

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 #2.0 Setup/Configure the New Windows Azure Environment #2.1 Reset all Workflow Defined Variables $AzureAffinityGroup = $null $AzureCloudService = $null $AzureStorageAccount = $null $GenericAzureStorageContainer = $null $ProjectAzureStorageContainer = $null $AzureBlobUploadJob = $null $AzureBlob = $null $CopiedAzureBlob = $null $AzureVMImage = $null $CreateAzureVMJob = $null #2.2 Define Project Name Variable - This will be used throughout the example $ProjectName = "BCBWFDemo"

Note Just like in Part 2 of this blog series, $ProjectName is the over-arching variable that will be used (in part or whole) throughout the example. Many other variables are set based on this value. Be sure to modify this value to fit your project.

001 002 003 004 005 006 007 008 #2.3 Create Affinity Group $AGName = $ProjectName $AGLocation = "West US" $AGLocationDesc = "Affinity group for $ProjectName VMs" $AGLabel = "$AGLocation $ProjectName" $AzureAffinityGroup = Create-AzureAffinityGroup -ProjectName $AGName -AGLocation $AGLocation -AGLocationDesc $AGLocationDesc -AGLabel $AGLabel

Note Again, like in Part 2 of this blog series, $AGLocation is the Location the Affinity Group for this Environment/Project is associated with – be sure to modify this value to fit your project.

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 if ($AzureAffinityGroup.OperationStatus -eq "Succeeded") { #2.4 Create Cloud Service $CloudServiceDesc = "Service for $ProjectName VMs" $CloudServiceLabel = "$ProjectName VMs" $AzureCloudService = Create-AzureCloudService -ProjectName $ProjectName -AGName $AGName -CloudServiceDesc $CloudServiceDesc -CloudServiceLabel $CloudServiceLabel #2.5.0 Create Storage Account $StorageAccountName = $ProjectName.ToLower() $StorageAccountDesc = "Storage account for $ProjectName VMs" $StorageAccountLabel = "$ProjectName Storage" $AzureStorageAccount = Create-AzureStorageAccount -StorageAccountName $StorageAccountName -AGName $AGName -StorageAccountDesc $StorageAccountDesc -StorageAccountLabel $StorageAccountLabel }

Note No changes (unless desired) to the example variable values is required in this section.

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 if ($AzureStorageAccount.OperationStatus -eq "Succeeded") { #2.5.1 Update Set-AzureSubscription with Current Storage Account; Validate Start-Sleep -Seconds 60 $AzureStorageAccountName = $ProjectName.ToLower() Set-AzureSubscription -SubscriptionName $AzureSubscriptionName -CurrentStorageAccount $AzureStorageAccountName #2.5.2 Create Generic VHD Storage Container; VHD will be uploaded here $GenericStorageContainerName = "vhds" $GenericAzureStorageContainer = Create-AzureStorageContainer -StorageContainerName $GenericStorageContainerName #2.5.3 Create Project Storage Container; VHD will be copied here $ProjectStorageContainerName = $ProjectName.ToLower() $ProjectAzureStorageContainer = Create-AzureStorageContainer -StorageContainerName $ProjectStorageContainerName }

Note Again, no changes (unless desired) to the example variable values is required in this section.

#endregion 2.CreateNewWindowsAzureEnvironment


Region 2 Execution Results

Steps Completed in Region 2:

  • New! Windows Azure Affinity Group Created
  • New! Windows Azure Cloud Service Created
  • New! Windows Azure Storage Account Created
  • New! Windows Azure Storage Container (generic) Created
  • New! Windows Azure Storage Container (project specific) Created

The following are some screen shots I took based on the execution of Region 2 above:

New! Windows Azure Affinity Group Created

image

New! Windows Azure Cloud Service Created

image

New! Windows Azure Storage Account Created

image

New! Windows Azure Storage Container (generic) Created and

New! Windows Azure Storage Container (project specific) Created

image


Upload VHD to Windows Azure

The fun continues! Now that we have a new Windows Azure Environment, we can upload an “On-Prem” VHD.

Note This region is pretty small and self-contained, so I have not broken it into pieces. Either way, the complete script exists at the end of this blog post.

#region 3.UploadVHDtoWindowsAzure

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 #3.0 Setup/Configure Source and Destination Settings for VHD Upload to Windows Azure if ($AzureStorageAccount.OperationStatus -eq "Succeeded") { #3.1 Set Source VHD Info $SourceDiskName = "toWindowsAzure" $SourceDiskFileExt = "vhd" $SourceDiskPath = "D:\Drop\Azure\toAzure" $SourceVHDName = "{0}.{1}" -f $SourceDiskName,$SourceDiskFileExt $SourceVHDPath = "{0}\{1}" -f $SourceDiskPath,$SourceVHDName #3.2 Set Destination Blob Info $DesitnationVHDName = "{0}.{1}" -f $ProjectName,$SourceDiskFileExt $DestinationVHDPath = "https://{0}.blob.core.windows.net/{1}" -f $AzureStorageAccountName,$GenericStorageContainerName $DestinationBlobURI = "{0}/{1}" -f $DestinationVHDPath,$DesitnationVHDName $OverWrite = $false #3.3 Upload Local VHD to Windows Azure Storage Container; this will take a while $AzureBlobUploadJob = Upload-LocalVHDtoAzure -StorageContainerName $GenericStorageContainerName -VHDName $DesitnationVHDName -SourceVHDPath $SourceVHDPath -DestinationBlobURI $DestinationBlobURI -OverWrite $OverWrite -AsJob Receive-Job -Job $AzureBlobUploadJob -AutoRemoveJob -Wait -WriteEvents -WriteJobInResults }

Note I am executing the Upload-LocalVHDtoAzure Workflow with the -AsJob option. This allows me to use the Receive-Job command, to track and monitor the Job’s status.

#endregion 3.UploadVHDtoWindowsAzure


Region 3 Execution Results

Steps Completed in Region 3:

  • “On-Prem” VHD Uploaded to Windows Azure

The following are two screen shots I took based on the execution of Region 3 above:

“On-Prem” VHD Uploaded to Windows Azure

In Process (Windows PowerShell Workflow)

image

Note The output here is not as verbose as it was when directly executing the Add-AzureVhd command. I chose this for the example to keep things simple. You can certainly extend this with more script logic and output.

Results (Windows Azure Portal)

image


Copy Uploaded Windows Azure Blob from Generic to Project Container

On, and on! Now that we have the “On-Prem” VHD uploaded into the Generic Windows Azure Storage Container as a Windows Azure Blob, we can copy it over to the Project Specific Windows Azure Storage Container.

Note Again, this region is pretty small and self-contained, so I have not broken it into pieces. Either way, the complete script exists at the end of this blog post.

#region 4.CopyUploadedAzureBlobFromGenericToProjectContainer

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 #4.0 Copy Uploaded Azure Blob from Generic Storage Container to Project Storage Container #4.1 Get Azure Blob Info $AzureBlob = Get-AzureBlobInfo -StorageContainerName $GenericStorageContainerName -VHDName $DesitnationVHDName if ($AzureBlob) { #4.2 Set Source Blob Info for Copy $SourceBlobName = $DesitnationVHDName $SourceContainer = $GenericStorageContainerName #4.3 Set Destination Blob Info for Copy $DestinationContainer = $ProjectStorageContainerName $DestinationBlobName = "{0}_copy.{1}" -f $ProjectName,$SourceDiskFileExt #4.4 Copy Uploaded Blob from Generic to Project $CopiedAzureBlob = Copy-AzureStorageBlob -DestinationContainer $DestinationContainer -DestinationBlobName $DestinationBlobName -SourceContainer $SourceContainer -SourceBlobName $SourceBlobName }

Note Because some time passes during the upload, I be sure to get the Azure Blog Info as a simple check. You obviously can do a more rigorous test, but this is what I went with for the example.

#endregion 4.CopyUploadedAzureBlobFromGenericToProjectContainer


Region 4 Execution Results

Steps Completed in Region 4:

  • Uploaded VHD/Blob in Generic Container Copied to Project Specific Container

The following is a screen shot I took based on the execution of Region 4 above:

Uploaded VHD/Blob in Generic Container Copied to Project Specific Container

image


Create Windows Azure VM from Copied Azure Blob

Finally - We have arrived! Now that we have a copy of the Uploaded “On-Prem” VHD (copied from the Generic Windows Azure Storage Container to the Project Specific Windows Azure Storage Container), we can Create a new Windows Azure VM Image, and then a Windows Azure VM (again, all based on that originally Uploaded “On-Prem” VHD).

Note Finally, while this last region is small, it does have two distinct parts. I have broken the script up by these two parts. And as you know, the complete script exists at the end of this blog post.

#region 5.CreateAzureVMFromCopiedAzureBlob

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 #5.0 Create Azure VM Image from Copied Azure Blob if ($CopiedAzureBlob) { #5.1 Set VM Image Info from Copied Blob Info $VMImageName = $ProjectName $VMImageBlobContainer = $DestinationContainer $VMImageBlobName = $DestinationBlobName $VMImageOS = "Windows" #5.2 Create Azure VM Image from Copied Azure Blob $AzureVMImage = Create-AzureVMImage -VMImageName $VMImageName -VMImageBlobContainer $VMImageBlobContainer -VMImageBlobName $VMImageBlobName -VMImageOS $VMImageOS }

 

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 if ($AzureVMImage) { #5.3 Set VM Info $VMName = "{0}-001" -f $ProjectName.ToLower() $ServiceName = $ProjectName $AdminUsername = "AdminUser" $VMImageName = $AzureVMImage.ImageName $Password = "Pass@word1" $Windows = $true $VMInstanceSize = "ExtraSmall" $WaitForBoot = $true #5.4 Create Azure VM from Newly Created VM Image $CreateAzureVMJob = Create-AzureVM -VMName $VMName -ServiceName $ServiceName -AdminUsername $AdminUsername -VMImageName $VMImageName -Password $Password -Windows $Windows -VMInstanceSize $VMInstanceSize -WaitForBoot $WaitForBoot -AsJob Receive-Job -Job $CreateAzureVMJob -AutoRemoveJob -Wait -WriteEvents -WriteJobInResults }

Note Once again, like in Part 2 of this blog series, there are a few variables that you will want to change to fit your project: $VMName, $AdminUsername, $Password, $Windows, $VMInstanceSize, $WaitForBoot. For more information about these variables, see Part 2 – Create Windows Azure VM.

Note I am executing the Create-AzureVM Workflow with the -AsJob option. This allows me to use the Receive-Job command, to track and monitor the Job’s status.

#endregion 5.CreateAzureVMFromCopiedAzureBlob


Region 5 Execution Results

Steps Completed in Region 5:

  • New! Windows Azure VM Image Created
  • New! Windows Azure VM Created

The following are some screen shots I took based on the execution of Region 5 above:

New! Windows Azure VM Image Created

In Process (Windows PowerShell Workflow)

image

Note The output here is not as verbose as it was when directly executing the New-AzureQuickVM command. I chose this for the example to keep things simple. You can certainly extend this with more script logic and output.

Results (Windows Azure Portal)

New! Windows Azure VM Created

image

image


Putting it all together

It’s that time again – time to extend the length of this blog post beyond what was thought possible, by adding all the PowerShell Regions above into one script. Either way, here are all the above PowerShell script portions, rolled up into one script example (a cool 402 lines of PowerShell):

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 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 #region 0.PrerequisiteSetup #0.0. Perform Prerequisite Setup Steps #0.1. Download latest Windows Azure PowerShell Module: https://go.microsoft.com/?linkid=9811175&clcid=0x409 #0.2. Execute: Get-AzurePublishSettingsFile; Save .publishsettings file locally Get-AzurePublishSettingsFile #0.3. Execute: Import-AzurePublishSettingsFile; reference local .publishsettings file $AzurePublishSettingsFile = "C:\Your_Local_Path\Your Windows Azure Subscription Name-MM-DD-YYYY-credentials.publishsettings" Import-AzurePublishSettingsFile -PublishSettingsFile $AzurePublishSettingsFile #0.4. Execute: Set-AzureSubscription and Select-AzureSubscription (to verify Execute: Get-AzureSubscription) $AzureSubscriptionName = "Windows Azure MSDN - Visual Studio Ultimate" Set-AzureSubscription -SubscriptionName $AzureSubscriptionName Select-AzureSubscription -SubscriptionName $AzureSubscriptionName Get-AzureSubscription #endregion 0.PrerequisiteSetup #region 1.PrerequisiteWorkflows #1.0 Establish Prerequisite Workflows #1.1 Create Affinity Group workflow Create-AzureAffinityGroup { param ( [string]$ProjectName, [string]$AGLocation, [string]$AGLocationDesc, [string]$AGLabel ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureAffinityGroup = Get-AzureAffinityGroup -Name $ProjectName -ErrorAction SilentlyContinue if(!$AzureAffinityGroup) { $AzureAffinityGroup = New-AzureAffinityGroup -Location $AGLocation -Name $ProjectName -Description $AGLocationDesc -Label $AGLabel } Return $AzureAffinityGroup } #1.2 Create Cloud Service workflow Create-AzureCloudService { param ( [string]$ProjectName, [string]$AGName, [string]$CloudServiceDesc, [string]$CloudServiceLabel ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureService = Get-AzureService -ServiceName $ProjectName -ErrorAction SilentlyContinue if(!$AzureService) { $AzureService = New-AzureService -AffinityGroup $AGName -ServiceName $ProjectName -Description $CloudServiceDesc -Label $CloudServiceLabel } Return $AzureService } #1.3 Create Storage Account workflow Create-AzureStorageAccount { param ( [string]$StorageAccountName, [string]$AGName, [string]$StorageAccountDesc, [string]$StorageAccountLabel ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureStorageAccount = Get-AzureStorageAccount -StorageAccountName $StorageAccountName -ErrorAction SilentlyContinue if(!$AzureStorageAccount) { $AzureStorageAccount = New-AzureStorageAccount -AffinityGroup $AGName -StorageAccountName $StorageAccountName -Description $StorageAccountDesc -Label $StorageAccountLabel } Return $AzureStorageAccount } #1.4 Create Storage Container workflow Create-AzureStorageContainer { param ( [string]$StorageContainerName ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureStorageContainer = Get-AzureStorageContainer -Name $StorageContainerName -ErrorAction SilentlyContinue if(!$AzureStorageContainer) { $AzureStorageContainer = New-AzureStorageContainer -Name $StorageContainerName } Return $AzureStorageContainer } #1.5 Get Blob Info workflow Get-AzureBlobInfo { param ( [string]$StorageContainerName, [string]$VHDName ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureBlob = Get-AzureStorageBlob -Container $StorageContainerName -Blob $VHDName -ErrorAction SilentlyContinue Return $AzureBlob } #1.6 Upload Local VHD to Storage Container workflow Upload-LocalVHDtoAzure { param ( [string]$StorageContainerName, [string]$VHDName, [string]$SourceVHDPath, [string]$DestinationBlobURI, [bool]$OverWrite ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureBlob = Get-AzureStorageBlob -Container $StorageContainerName -Blob $VHDName -ErrorAction SilentlyContinue if(!$AzureBlob -or $OverWrite) { $AzureBlob = Add-AzureVhd -LocalFilePath $SourceVHDPath -Destination $DestinationBlobURI -OverWrite:$OverWrite } Return $AzureBlob } #1.7 Copy Blob from one Storage Container to another workflow Copy-AzureStorageBlob { param ( [string]$DestinationContainer, [string]$DestinationBlobName, [string]$SourceContainer, [string]$SourceBlobName ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureBlob = Get-AzureStorageBlob -Container $DestinationContainer -Blob $DestinationBlobName -ErrorAction SilentlyContinue if(!$AzureBlob) { $AzureBlob = Start-AzureStorageBlobCopy -DestContainer $DestinationContainer -SrcBlob $SourceBlobName -SrcContainer $SourceContainer -DestBlob $DestinationBlobName } Return $AzureBlob } #1.8 Create Azure VM Image from Copied Blob workflow Create-AzureVMImage { param ( [string]$VMImageName, [string]$VMImageBlobContainer, [string]$VMImageBlobName, [string]$VMImageOS ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureVMImage = Get-AzureVMImage -ImageName $VMImageName -ErrorAction SilentlyContinue if(!$AzureVMImage) { $AzureBlobMediaLocation = (Get-AzureStorageBlob -Container $VMImageBlobContainer -Blob $VMImageBlobName).ICloudBlob.Uri.AbsoluteUri $AzureVMImage = Add-AzureVMImage -ImageName $VMImageName -MediaLocation $AzureBlobMediaLocation -OS $VMImageOS } Return $AzureVMImage } #1.9 Create Azure VM from Newly Created VM Image workflow Create-AzureVM { param ( [string]$VMName, [string]$ServiceName, [string]$AdminUsername, [string]$VMImageName, [string]$Password, [bool]$Windows, [string]$VMInstanceSize, [bool]$WaitForBoot ) $AzureSubscriptionForWorkflow = Get-AzureSubscription $AzureVM = Get-AzureVM -Name $VMName -ServiceName $ServiceName -ErrorAction SilentlyContinue if(!$AzureVM -and $Windows) { $AzureVM = New-AzureQuickVM -AdminUsername $AdminUsername -ImageName $VMImageName -Password $Password -ServiceName $ServiceName -Windows:$Windows -InstanceSize $VMInstanceSize -Name $VMName -WaitForBoot:$WaitForBoot } Return $AzureVM } #endregion 1.PrerequisiteWorkflows #region 2.CreateNewWindowsAzureEnvironment #2.0 Setup/Configure the New Windows Azure Environment #2.1 Reset all Workflow Defined Variables $AzureAffinityGroup = $null $AzureCloudService = $null $AzureStorageAccount = $null $GenericAzureStorageContainer = $null $ProjectAzureStorageContainer = $null $AzureBlobUploadJob = $null $AzureBlob = $null $CopiedAzureBlob = $null $AzureVMImage = $null $CreateAzureVMJob = $null #2.2 Define Project Name Variable - This will be used throughout the example $ProjectName = "BCBWFDemo" #2.3 Create Affinity Group $AGName = $ProjectName $AGLocation = "West US" $AGLocationDesc = "Affinity group for $ProjectName VMs" $AGLabel = "$AGLocation $ProjectName" $AzureAffinityGroup = Create-AzureAffinityGroup -ProjectName $AGName -AGLocation $AGLocation -AGLocationDesc $AGLocationDesc -AGLabel $AGLabel if ($AzureAffinityGroup.OperationStatus -eq "Succeeded") { #2.4 Create Cloud Service $CloudServiceDesc = "Service for $ProjectName VMs" $CloudServiceLabel = "$ProjectName VMs" $AzureCloudService = Create-AzureCloudService -ProjectName $ProjectName -AGName $AGName -CloudServiceDesc $CloudServiceDesc -CloudServiceLabel $CloudServiceLabel #2.5.0 Create Storage Account $StorageAccountName = $ProjectName.ToLower() $StorageAccountDesc = "Storage account for $ProjectName VMs" $StorageAccountLabel = "$ProjectName Storage" $AzureStorageAccount = Create-AzureStorageAccount -StorageAccountName $StorageAccountName -AGName $AGName -StorageAccountDesc $StorageAccountDesc -StorageAccountLabel $StorageAccountLabel } if ($AzureStorageAccount.OperationStatus -eq "Succeeded") { #2.5.1 Update Set-AzureSubscription with Current Storage Account; Validate Start-Sleep -Seconds 60 $AzureStorageAccountName = $ProjectName.ToLower() Set-AzureSubscription -SubscriptionName $AzureSubscriptionName -CurrentStorageAccount $AzureStorageAccountName #2.5.2 Create Generic VHD Storage Container; VHD will be uploaded here $GenericStorageContainerName = "vhds" $GenericAzureStorageContainer = Create-AzureStorageContainer -StorageContainerName $GenericStorageContainerName #2.5.3 Create Project Storage Container; VHD will be copied here $ProjectStorageContainerName = $ProjectName.ToLower() $ProjectAzureStorageContainer = Create-AzureStorageContainer -StorageContainerName $ProjectStorageContainerName } #endregion 2.CreateNewWindowsAzureEnvironment #region 3.UploadVHDtoWindowsAzure #3.0 Setup/Configure Source and Destination Settings for VHD Upload to Windows Azure if ($AzureStorageAccount.OperationStatus -eq "Succeeded") { #3.1 Set Source VHD Info $SourceDiskName = "toWindowsAzure" $SourceDiskFileExt = "vhd" $SourceDiskPath = "D:\Drop\Azure\toAzure" $SourceVHDName = "{0}.{1}" -f $SourceDiskName,$SourceDiskFileExt $SourceVHDPath = "{0}\{1}" -f $SourceDiskPath,$SourceVHDName #3.2 Set Destination Blob Info $DesitnationVHDName = "{0}.{1}" -f $ProjectName,$SourceDiskFileExt $DestinationVHDPath = "https://{0}.blob.core.windows.net/{1}" -f $AzureStorageAccountName,$GenericStorageContainerName $DestinationBlobURI = "{0}/{1}" -f $DestinationVHDPath,$DesitnationVHDName $OverWrite = $false #3.3 Upload Local VHD to Windows Azure Storage Container; this will take a while $AzureBlobUploadJob = Upload-LocalVHDtoAzure -StorageContainerName $GenericStorageContainerName -VHDName $DesitnationVHDName -SourceVHDPath $SourceVHDPath -DestinationBlobURI $DestinationBlobURI -OverWrite $OverWrite -AsJob Receive-Job -Job $AzureBlobUploadJob -AutoRemoveJob -Wait -WriteEvents -WriteJobInResults } #endregion 3.UploadVHDtoWindowsAzure #region 4.CopyUploadedAzureBlobFromGenericToProjectContainer #4.0 Copy Uploaded Azure Blob from Generic Storage Container to Project Storage Container #4.1 Get Azure Blob Info $AzureBlob = Get-AzureBlobInfo -StorageContainerName $GenericStorageContainerName -VHDName $DesitnationVHDName if ($AzureBlob) { #4.2 Set Source Blob Info for Copy $SourceBlobName = $DesitnationVHDName $SourceContainer = $GenericStorageContainerName #4.3 Set Destination Blob Info for Copy $DestinationContainer = $ProjectStorageContainerName $DestinationBlobName = "{0}_copy.{1}" -f $ProjectName,$SourceDiskFileExt #4.4 Copy Uploaded Blob from Generic to Project $CopiedAzureBlob = Copy-AzureStorageBlob -DestinationContainer $DestinationContainer -DestinationBlobName $DestinationBlobName -SourceContainer $SourceContainer -SourceBlobName $SourceBlobName } #endregion 4.CopyUploadedAzureBlobFromGenericToProjectContainer #region 5.CreateAzureVMFromCopiedAzureBlob #5.0 Create Azure VM Image from Copied Azure Blob if ($CopiedAzureBlob) { #5.1 Set VM Image Info from Copied Blob Info $VMImageName = $ProjectName $VMImageBlobContainer = $DestinationContainer $VMImageBlobName = $DestinationBlobName $VMImageOS = "Windows" #5.2 Create Azure VM Image from Copied Azure Blob $AzureVMImage = Create-AzureVMImage -VMImageName $VMImageName -VMImageBlobContainer $VMImageBlobContainer -VMImageBlobName $VMImageBlobName -VMImageOS $VMImageOS } if ($AzureVMImage) { #5.3 Set VM Info $VMName = "{0}-001" -f $ProjectName.ToLower() $ServiceName = $ProjectName $AdminUsername = "AdminUser" $VMImageName = $AzureVMImage.ImageName $Password = "Pass@word1" $Windows = $true $VMInstanceSize = "ExtraSmall" $WaitForBoot = $true #5.4 Create Azure VM from Newly Created VM Image $CreateAzureVMJob = Create-AzureVM -VMName $VMName -ServiceName $ServiceName -AdminUsername $AdminUsername -VMImageName $VMImageName -Password $Password -Windows $Windows -VMInstanceSize $VMInstanceSize -WaitForBoot $WaitForBoot -AsJob Receive-Job -Job $CreateAzureVMJob -AutoRemoveJob -Wait -WriteEvents -WriteJobInResults } #endregion 5.CreateAzureVMFromCopiedAzureBlob

Finishing Up

I covered a bunch of “post deployment” topics in Part 2. I will not be repeating them in this part of the blog series. If you are interested, be sure to check out the following sections of Part 2:

  • Connecting to the Windows Azure VM
  • A Dashboard View of the created Windows Azure VM
  • Alternate Windows Azure VM Create Examples
  • Other Learnings

What’s next?

So, what’s next? This time, decreasing complexity! – Part 4 of this blog series lets you wipe the slate clean. It will be a pretty simple (in comparison) blog post that provides Windows PowerShell Examples to Deprovision all what you have created here (or with Part 2). You may be surprised how simple it is to “delete” everything!

Hey! What about SMA?

I know, I know. This blog post doesn’t say much about SMA. That is intentional. It is really about providing the Windows PowerShell Workflow version of the example solution, to get you thinking. How might this be leveraged? I mean, how might it be leveraged over the straight Windows PowerShell examples from Part 2? All great questions.

If there is interest, I am happy to put together another blog post where I actually leverage the above script(s) in SMA. At this point, this blog post is already entirely too long. So, until then, keep your comments and ideas coming. And feel free to leverage the Building Clouds Blog Forum (https://aka.ms/BuildingCloudsForum) for questions/comments/discussions/ideas/etc.!


Automating Hybrid Clouds with Windows Azure and PowerShell - Blog Series - Table of Contents

I broke this “Automating Windows Azure” topic up into four posts – primarily to make it easier to reference externally (based on varied interest levels).

As promised, the following is the link to the TechNet Contribution and Download for the examples (from all parts of the blog series).


TechNet Contribution and Download

The download (Hybrid Cloud Automation Toolkit - Windows Azure and PowerShell.zip) includes the following (4) files:

  • Provision-WindowsAzureEnvironmentResources.ps1
  • Provision-WindowsAzureEnvironmentResources-Workflow.ps1
  • Deprovision-WindowsAzureEnvironmentResources.ps1
  • Provision-Deprovision-Extras.ps1

Download the Hybrid Cloud Automation Toolkit - Windows Azure and PowerShell from TechNet Gallery here:

BC-DLButtonDark


Thanks for checking out this 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!