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

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

  • Comments 7
  • Likes

Hello Again!

Time for Part 2 of the Automating Hybrid Clouds with Windows Azure and PowerShell blog series (find Part 1: Intro & TOC here).  And yes, this post actually has PowerShell Examples! But first, the list of what examples will be provided…


Public Cloud Environment Provisioning PowerShell Examples

As covered in the intro post of the 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
  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

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: http://go.microsoft.com/?linkid=9811175&clcid=0x409

Establish Windows Azure Subscription Connection

Automation often requires “trust”, especially between two endpoints in any given process/scenario solution. This example is no exception. The connection between the “On-Prem” Windows Server and Windows Azure need to share a “trust”. For Windows Azure (and for this example), one of the most common ways to accomplish this is through “the certificate method” (outlined in this Windows Azure tutorial: How to install and configure Windows Azure PowerShell).

Basically, it leverages the following:

  • The Get-AzurePublishSettingsFile cmdlet opens a web page on the Windows Azure Management Portal, from which you can download the subscription information. The information is contained in a .publishsettings file.
  • The Import-AzurePublishSettingsFile imports the .publishsettings file for use by the module. This file includes a management certificate that has security credentials.

Example Script (with some instructional comments)

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
#0.0. Perform Prerequisite Setup Steps

#0.1. Download latest Windows Azure PowerShell Module: http://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 (and then, to verify Execute: Get-AzureSubscription)

    $AzureSubscriptionName = "Windows Azure MSDN - Visual Studio Ultimate"
    Set-AzureSubscription -SubscriptionName $AzureSubscriptionName
    Select-AzureSubscription -SubscriptionName $AzureSubscriptionName
    Get-AzureSubscription

Note     I recommend running these commands individually (not all at once) by the groups above.

Execution Results

As part of this process, you will notice a new Management Certificate in the Windows Azure Portal:

image

If everything was successful, the Get-AzureSubscription results should look similar to this:

image


Connection Established – Moving On…

Now that we have established a good connection to Windows Azure, we can take care of the rest of the automated steps to build out the environment.

Reminder     Obviously, there are other methods and concepts to be automated for Windows Azure - these are just the ones I chose for the examples in this blog series.

Setting up the Environment with a “Project Name”

There is one variable that impacts the entire script and resulting environment. This needs to be set first, and will be used throughout:

  • $ProjectName: This 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.
001
$ProjectName = "BCBDemo"

Note     If you run this example all the way through as a test, you will see how pervasive this variable���s value actually is. Even if you just look through the “Execution Results” from each step it is apparent. This is for example purposes only. I wanted to simplify the scripts and number of variable inputs.


Create Windows Azure Affinity Group

First step for these examples is to create a Windows Azure Affinity Group within Windows Azure for the environment being built. There is one variable that you will want to consider updating to fit your environmental needs/requirements:

  • $AGLocation: The Location the Affinity Group for this Environment/Project is associated with.
001
002
003
004
005
006
007
008
009
010
011
012
$AGName = $ProjectName
$AGLocation
 = "West US"
$AGLocationDesc = "Affinity group for $ProjectName VMs"
$AGLabel = "$AGLocation $ProjectName"

$AzureAffinityGroup = Get-AzureAffinityGroup -Name $ProjectName -ErrorAction SilentlyContinue

if(!$AzureAffinityGroup) {
    $AzureAffinityGroup = New-AzureAffinityGroup -Location $AGLocation -Name $ProjectName -Description $AGLocationDesc -Label $AGLabel
}

$AzureAffinityGroup

Note     All variable values were chosen for this example only, and can be changed to fit your project needs.

Execution Results

image

image


Create Windows Azure Cloud Service

Next up, is to create a Windows Azure Cloud Service within Windows Azure for the environment being built. There are no new unique variables that require value updates.

001
002
003
004
005
006
007
008
009
010
$CloudServiceDesc = "Service for $ProjectName VMs"
$CloudServiceLabel = "$ProjectName VMs"

$AzureService = Get-AzureService -ServiceName $ProjectName -ErrorAction SilentlyContinue

if(!$AzureService) {
    $AzureService = New-AzureService -AffinityGroup $AGName -ServiceName $ProjectName -Description $CloudServiceDesc -Label $CloudServiceLabel
}

Return $AzureService

Note     This portion of the script depends on the previous two.

Execution Results

image

image


Create Windows Azure Storage Account

Next, is to create a Windows Azure Storage Account within Windows Azure for the environment being built. There are no new unique variables that require value updates.

001
002
003
004
005
006
007
008
009
010
011
$StorageAccountName = $ProjectName.ToLower()
$StorageAccountDesc = "Storage account for $ProjectName VMs"
$StorageAccountLabel = "$ProjectName Storage"

$AzureStorageAccount = Get-AzureStorageAccount -StorageAccountName $StorageAccountName -ErrorAction SilentlyContinue

if(!$AzureStorageAccount) {
    $AzureStorageAccount = New-AzureStorageAccount -AffinityGroup $AGName -StorageAccountName $StorageAccountName -Description $StorageAccountDesc -Label $StorageAccountLabel
}

$AzureStorageAccount

Note     This portion of the script depends on the previous three.

Execution Results

image

image

 


Create Windows Azure Storage Container(s)

Next, is to create Two Windows Azure Storage Containers within Windows Azure for the environment being built. There are no new unique variables that require value updates.

Reason for Two Storage Containers

  • Storage Container #1: Generic VHD Storage Container – “On-Prem” VHD will be uploaded to this container
  • Storage Container #2: Project Specific Storage Container – Uploaded VHD will be copied here (this is the VHD that will be leveraged for the rest of the example)

Note     Creation of two Storage Containers in this example was to ensure I always had a “known good” copy of the uploaded VHD/Blob.

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
#Ensure Storage Account is fully created - Sleep for 60 seconds
Start-Sleep -Seconds 60

#Update the Azure Subscription Connection with the Storage Account
#This makes it easier to connect to the storage account, without requiring the creation and usage of Storage Context


$AzureStorageAccountName = $ProjectName.ToLower()
Set-AzureSubscription -SubscriptionName $AzureSubscriptionName -CurrentStorageAccount $AzureStorageAccountName

#Create Generic VHD Storage Container; VHD will be uploaded here
$GenericStorageContainerName = "vhds"

#Create Project Storage Container; VHD will be copied here
$ProjectStorageContainerName = $ProjectName.ToLower()

$StorageContainerNames = @($GenericStorageContainerName,$ProjectStorageContainerName)

foreach ($StorageContainerName in $StorageContainerNames) {

    $AzureStorageContainer = Get-AzureStorageContainer -Name $StorageContainerName -ErrorAction SilentlyContinue

    if(!$AzureStorageContainer) {
        $AzureStorageContainer = New-AzureStorageContainer -Name $StorageContainerName
    }

    $AzureStorageContainer
}

Note     This portion of the script depends on the previous four.

Execution Results

image

image


Upload “On-Prem” VHD to Windows Azure Storage Container

Next, is to upload the “On-Prem” VHD to the Generic Windows Azure Storage Container within Windows Azure for the environment being built. This portion of PowerShell does require some customization, as it references the previously mentioned, “…VHD file that meets the requirements to be uploaded to Windows Azure”:

  • $SourceDiskName: The Name of the VHD (without .vhd extension) to be uploaded to the Generic Windows Azure Storage Container.
  • $SourceDiskPath: The Path of the VHD (without trailing "\") to be uploaded to the Generic Windows Azure Storage Container.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
#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

#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

#Upload Local VHD to the Generic VHD Windows Azure Storage Container; this will take a while
$AzureBlob = Get-AzureStorageBlob -Container $GenericStorageContainerName -Blob $DesitnationVHDName -ErrorAction SilentlyContinue
   
if(!$AzureBlob -or $OverWrite) {

    $AzureBlob = Add-AzureVhd -LocalFilePath $SourceVHDPath -Destination $DestinationBlobURI -OverWrite:$OverWrite
}

$AzureBlob

Note     This portion of the script depends on the previous five.

The following is an image (not that it is really needed, but for proof), of the file and path of the VHD I am uploading in this example:

image

Execution Results

Because this step takes a while, when you run the example PowerShell above, you have the opportunity to see the ongoing status within the PowerShell console. Here is a screenshot of the process, mid-execution:

image

Note     Over a fast connection, a file of this size (7.5GB) takes about 10 minutes to upload (as seen in the results below).

image

image


Copy Windows Azure Blob

Next, is to copy the uploaded VHD (now a Windows Azure Blob) from the Generic Container (vhds) to the Project Specific Container within Windows Azure for the environment being built. There are no new unique variables that require value updates.

Note     Usage of two Storage Containers in this example was to ensure I always had a “known good” copy of the uploaded VHD/Blob.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
#Set Source Blob Info for Copy

$SourceBlobName = 
$DesitnationVHDName
$SourceContainer
 = $GenericStorageContainerName

#Set Destination Blob Info for Copy

$DestinationContainer = 
$ProjectStorageContainerName
$DestinationBlobName
 = "{0}_copy.{1}" -f $ProjectName,
$SourceDiskFileExt

$CopiedAzureBlob
 = Get-AzureStorageBlob -Container $DestinationContainer -Blob $DestinationBlobName -ErrorAction SilentlyContinue

#Copy Uploaded Blob from Generic to Project
if(!$CopiedAzureBlob) {

    $CopiedAzureBlob = Start-AzureStorageBlobCopy -DestContainer $DestinationContainer -SrcBlob $SourceBlobName -SrcContainer $SourceContainer -DestBlob $DestinationBlobName
}
   
Return $CopiedAzureBlob

Note     This portion of the script depends on the previous six.

Execution Results

image

image


Create Windows Azure VM Image

Next, is to create an Azure VM Image from copied blob within Windows Azure for the environment being built. There are no new unique variables that require value updates as $VMImageOS likely will remain “Windows” for the VHDs uploaded.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
$VMImageName = $ProjectName
$VMImageBlobContainer
 = 
$DestinationContainer
$VMImageBlobName
 = 
$DestinationBlobName
$VMImageOS
 = "Windows"

$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

Note     This portion of the script depends on the previous seven.

Execution Results

image

image


Create Windows Azure VM

Next, (what we all have been waiting for!) is to create a Windows Azure VM from the Windows Azure VM Image within Windows Azure for the environment being built. This portion of PowerShell does require some customization:

  • $VMName: The Name of the VM being created – the example I show here is a simple numbering suffix. It is not a mask of any kind, but something that generates a sequencing integer could be created. If you are uploading a large number of VM to create Windows Azure VMs, you would likely be referencing VM Name (and most other things) from an external data source. Either way, it is up to you, this is just example naming.
  • $AdminUsername: The Name of the Admin User for initial login to the Windows Azure VM. This cannot be "Administrator".
  • $Password: The Password for the Admin User for initial login to the Windows Azure VM.
  • $Windows: The OS of Windows Azure VM. Again, this will likely remain “Windows” for the VHDs uploaded.
  • $VMInstanceSize: The VM Instance Size for the Windows Azure VM (options available, reference the following: Virtual Machine and Cloud Service Sizes for Windows Azure).
  • $WaitForBoot: The option to wait for the Windows Azure VM to finish "Provisioning" before releasing control back to the PowerShell console.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
$VMName = "{0}-001" -f $ProjectName.ToLower()
$ServiceName = 
$ProjectName
$AdminUsername
 = "AdminUser"
$VMImageName = $AzureVMImage.ImageName
$Password = "Pass@word1"
$Windows = 
$true
$VMInstanceSize
 = "ExtraSmall"
$WaitForBoot = 
$true

$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

Note     This portion of the script depends on the previous eight. Also, the method used to create the Windows Azure VM in this example was New-AzureQuickVM, there are other methods – Please see some examples below (after the "Putting it all together" section).

Execution Results

Because this step takes a while, you have the opportunity to see the ongoing status within the Windows Azure Portal. Here is a screenshot of the Windows Azure VM creation, mid-execution:

image

Note     "Provisioning" durations vary.

image

image


Connecting to the Windows Azure VM

Once provisioned, it is simple to connect directly to the Windows Azure VM to test its contents.

Step 1: Select the Windows Azure VM, Click Connect

image

Step 2: When prompted, Open the RDP file that is presented

image

Step 3: When prompted, Enter the credentials you provided within the Create Windows Azure VM PowerShell Script

image

Step 4: Wait for the RDP Session to complete and connect you to the Windows Azure VM

image

Step 5: Use/Explore the Windows Azure VM

image

Note     Obviously nothing was done to customize the network for this VM. Windows Azure Virtual Network was out of scope for this example. For more information, please refer to the following: Windows Azure Virtual Network Overview.


A Dashboard View of the created Windows Azure VM

image

image


Putting it all together

I realize this is more than “like 5 lines of PowerShell”. But in reality, if you had the Windows Azure environment already created, that number isn’t that far off. Either way, here are all the above PowerShell script portions, rolled up into one script example:

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
#0.0. Perform Prerequisite Setup Steps

#0.1. Download latest Windows Azure PowerShell Module: http://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 (and then, to verify Execute: Get-AzureSubscription)

    $AzureSubscriptionName = "Windows Azure MSDN - Visual Studio Ultimate" 
    Set-AzureSubscription -SubscriptionName $AzureSubscriptionName 
    Select-AzureSubscription -SubscriptionName $AzureSubscriptionName 
    Get-AzureSubscription

#1.0 Setting up the Environment with a “Project Name”
$ProjectName = "BCBDemo"

#2.0 Create Windows Azure Affinity Group
$AGName = $ProjectName 
$AGLocation = "West US" 
$AGLocationDesc = "Affinity group for $ProjectName VMs" 
$AGLabel = "$AGLocation $ProjectName" 

$AzureAffinityGroup = Get-AzureAffinityGroup -Name $ProjectName -ErrorAction SilentlyContinue 

if(!$AzureAffinityGroup) { 
    $AzureAffinityGroup = New-AzureAffinityGroup -Location $AGLocation -Name $ProjectName -Description $AGLocationDesc -Label $AGLabel 
} 

$AzureAffinityGroup 

#3.0 Create Windows Azure Cloud Service
$CloudServiceDesc = "Service for $ProjectName VMs" 
$CloudServiceLabel = "$ProjectName VMs" 

$AzureService = Get-AzureService -ServiceName $ProjectName -ErrorAction SilentlyContinue 

if(!$AzureService) { 
    $AzureService = New-AzureService -AffinityGroup $AGName -ServiceName $ProjectName -Description $CloudServiceDesc -Label $CloudServiceLabel 
} 

Return $AzureService 

#4.0 Create Windows Azure Storage Account
$StorageAccountName = $ProjectName.ToLower() 
$StorageAccountDesc = "Storage account for $ProjectName VMs" 
$StorageAccountLabel = "$ProjectName Storage" 

$AzureStorageAccount = Get-AzureStorageAccount -StorageAccountName $StorageAccountName -ErrorAction SilentlyContinue 

if(!$AzureStorageAccount) { 
    $AzureStorageAccount = New-AzureStorageAccount -AffinityGroup $AGName -StorageAccountName $StorageAccountName -Description $StorageAccountDesc -Label $StorageAccountLabel 
} 

$AzureStorageAccount

#5.0 Create Windows Azure Storage Container(s)
#Ensure Storage Account is fully created - Sleep for 60 seconds

Start-Sleep -Seconds 60 

#Update the Azure Subscription Connection with the Storage Account
#This makes it easier to connect to the storage account, without requiring the creation and usage of Storage Context


$AzureStorageAccountName = $ProjectName.ToLower() 
Set-AzureSubscription -SubscriptionName $AzureSubscriptionName -CurrentStorageAccount $AzureStorageAccountName 

#Create Generic VHD Storage Container; VHD will be uploaded here
$GenericStorageContainerName = "vhds" 

#Create Project Storage Container; VHD will be copied here
$ProjectStorageContainerName = $ProjectName.ToLower() 

$StorageContainerNames = @($GenericStorageContainerName,$ProjectStorageContainerName) 

foreach ($StorageContainerName in $StorageContainerNames) { 

    $AzureStorageContainer = Get-AzureStorageContainer -Name $StorageContainerName -ErrorAction SilentlyContinue 

    if(!$AzureStorageContainer) { 
        $AzureStorageContainer = New-AzureStorageContainer -Name $StorageContainerName 
    } 

    $AzureStorageContainer 
} 

#6.0 Upload “On-Prem” VHD to Windows Azure Storage Container
#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 

#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 

#Upload Local VHD to the Generic VHD Windows Azure Storage Container; this will take a while
$AzureBlob = Get-AzureStorageBlob -Container $GenericStorageContainerName -Blob $DesitnationVHDName -ErrorAction SilentlyContinue 
    
if(!$AzureBlob -or $OverWrite) { 

    $AzureBlob = Add-AzureVhd -LocalFilePath $SourceVHDPath -Destination $DestinationBlobURI -OverWrite:$OverWrite 
} 

$AzureBlob

#7.0 Copy Windows Azure Blob
#Set Source Blob Info for Copy


$SourceBlobName = $DesitnationVHDName 
$SourceContainer = $GenericStorageContainerName 

#Set Destination Blob Info for Copy

$DestinationContainer = $ProjectStorageContainerName 
$DestinationBlobName = "{0}_copy.{1}" -f $ProjectName,$SourceDiskFileExt 

$CopiedAzureBlob = Get-AzureStorageBlob -Container $DestinationContainer -Blob $DestinationBlobName -ErrorAction SilentlyContinue 

#Copy Uploaded Blob from Generic to Project
if(!$CopiedAzureBlob) { 

    $CopiedAzureBlob = Start-AzureStorageBlobCopy -DestContainer $DestinationContainer -SrcBlob $SourceBlobName -SrcContainer $SourceContainer -DestBlob $DestinationBlobName 
} 
    
Return $CopiedAzureBlob

#8.0 Create Windows Azure VM Image
$VMImageName = $ProjectName 
$VMImageBlobContainer = $DestinationContainer 
$VMImageBlobName = $DestinationBlobName 
$VMImageOS = "Windows" 

$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

#9.0 Create Windows Azure VM
$VMName = "{0}-001" -f $ProjectName.ToLower() 
$ServiceName = $ProjectName 
$AdminUsername = "AdminUser" 
$VMImageName = $AzureVMImage.ImageName 
$Password = "Pass@word1" 
$Windows = $true 
$VMInstanceSize = "ExtraSmall" 
$WaitForBoot = $true 

$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

Alternate Windows Azure VM Create Examples

Note     As stated above, the method used to create the Windows Azure VM in this example was New-AzureQuickVM, there are other methods – Please see the following for a couple of those alternate methods.

001
002
003
004
005
006
007
008
009
010
#Alternate Windows Azure VM Create: Example #1
New-AzureVMConfig -DiskName $DiskName -InstanceSize $VMInstanceSize -Name $VMName | `
    Add-AzureProvisioningConfig -AdminUsername $AdminUsername -Windows -Password $Password -ResetPasswordOnFirstLogon | `
    New-AzureVM –ServiceName $ServiceName

#Alternate Windows Azure VM Create: Example #2
$VMConfig = New-AzureVMConfig -Name $VMName -InstanceSize $VMInstanceSize -ImageName $VMImageName | `
    Add-AzureProvisioningConfig -Windows -Password $Password -AdminUsername $AdminUsername

New-AzureVM -ServiceName $ServiceName -VMs $VMConfigWaitForBoot


Community Built Alternate Windows Azure VM Create Example!

I would like to take the time to share something that came from the MVP Community recently – Another Alternate Windows Azure VM Creation Example… (Thank you Christopher Keyaert!)

This example highlights the ability to create a Windows Azure VM from a Disk with a VNet and Subnet defined.

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

########################################################
# Define Parameters
########################################################


$ProjectName = "TestProj"
$SourceDiskFileExt = "vhd"
$VMInstanceSize = "ExtraSmall"
$VMImageOS = "Windows"
$BlobName = "{0}_copy.{1}" -f $ProjectName,
$SourceDiskFileExt
$BlobContainer
 = $ProjectName.ToLower()
$DiskName = "{0}-001" -f $ProjectName.ToLower()
$DiskLabel = "BootDisk"
$VNetSiteName = "VNetSiteName"
$SubNetName = "SubnetName"
$Windows = 
$true
$ServiceName
 = 
$ProjectName
$VMName
 = "{0}-001" -f $ProjectName.ToLower()

########################################################
# Add a new Disk to the Windows Azure Disk Repository
########################################################


$AzureBlobMediaLocation = (Get-AzureStorageBlob -Container $BlobContainer -Blob $BlobName).ICloudBlob.Uri.AbsoluteUri 
Add-AzureDisk -DiskName $DiskName -MediaLocation $AzureBlobMediaLocation -Label $DiskLabel -OS $VMImageOS

########################################################
# Get Windows Azure VNet and Subnet Information
########################################################


$AzureVNet = Get-AzureVNetSite | where {$_.Name -eq $VNetSiteName}
$AzureSubNet = $AzureVNet.Subnets | where {$_.Name -eq $SubNetName}

########################################################
#Create Windows Azure VM from Disk with VNet and Subnet
########################################################


$AzureVM = Get-AzureVM -Name $VMName -ServiceName $ServiceName -ErrorAction SilentlyContinue

if(!$AzureVM -and $Windows) {

    $VMConfig = New-AzureVMConfig -Name $VMName -InstanceSize $VMInstanceSize -DiskName $VMName | Set-AzureSubnet $AzureSubNet.Name
    New-AzureVM -ServiceName $ServiceName -VMs $VMConfig -VNetName $AzureVNet.Name –WaitForBoot
}

Return $AzureVM

Note     This is an example that builds on the other scripts within this blog post. Be sure you have all pre-requisite steps completed before you attempt to implement this example.

Important     If you attempt to create a new Windows Azure VM in an existing deployment with the above script you will likely get a warning like this:

image

Warning Text: WARNING: VNetName, DnsSettings, DeploymentLabel or DeploymentName Name can only be specified on new deployments.


Execution Results

image

image

image

Once again, a big Thanks to Christopher for expanding on what I published here and suggesting that I share it with the rest of the community!


Other Learnings

During the "Conversion" process from Orchestrator Runbooks to Windows PowerShell, I ran up against a few things that I had to figure out. Here are a two of those learnings (outside what you have seen above), for just in case:

Storage Context

Before I learned about Setting the Azure Subscription with a "Current Storage Account", I tried my hand at creating and leveraging a Storage Context. Once you have created a Storage Context, you can reference it and pass it as a variable to commands requiring some level of specificity when it comes to Windows Azure Storage. While I did not use Storage Context in the examples above, I figured I would give you some example PowerShell, if you decide that is the route you want to take:

001
002
003
004
005
#Get Azure Storage Key and Create New Azure Storage Context
if(!$AzureStorageContext) {
    $AzureStorageKey = Get-AzureStorageKey -StorageAccountName $StorageAccountName
    $AzureStorageContext = New-AzureStorageContext -StorageAccountKey $AzureStorageKey.Primary -StorageAccountName $StorageAccountName
}

References


Digging Deeper: Bad Request (400)

I ran into quite a few exceptions during the build out of this example solution. Needless to say, when the PowerShell screen turns red, frustration rises just a little bit (if not more than a little bit). The following command helped me dig deeper on some of the more vague errors being returned by the command failure exceptions:

001
002
003
#Read Full Error Stream from Failed [Windows Azure] Command
$ErrorStream = New-Object System.IO.StreamReader($Error[0].Exception.InnerException.Response.GetResponseStream()) 
$ErrorStream.ReadToEnd()

Note     While it will not work in all cases, and while it is very generic, I found that if this is run right after a "Bad Request (400)", the "InnerText" of the Exception helped tremendously with what was actually going on. I found this originally referenced here.

Exception Handling Improved?

To get some proof of this working, I attempted to recreate the scenario where I needed to use the above commands. In my attempt to reproduce the "Bad Request (400)", I was met with actually useful and accurate errors:

image

That’s it! If I have any more learnings, I will add them to the upcoming blog posts.


What’s next?

So, what’s next? On to increasing complexity – Part 3 of this blog series takes what we have in this post, and transports it to a new world – Windows PowerShell Workflow!


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!


Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi Charles,

    Thank you for your post and sharing all this info !

    Just a link about how to convert a VHDX to VHD for Azure with PowerShell

    blogs.technet.com/.../converting-hyper-v-vhdx-to-vhd-file-formats-for-use-in-windows-azure.aspx

    Thx

    Christopher

  • @Christopher - Thanks!

    For inline blog reference, I have also added the link you provided to the "Pre-Requisites" section above under bullet #2.

    Thanks again.

    -Charles

  • Great :-)

    Do you see any blocking point to not consume your script with SMA ?

    Could be a great solution to replace App Controller.

  • @Christopher - No real blockers, just a little bit more work to get these scripts into PowerShell Workflow. And then, of course, testing for anything SMA specific.

    In fact, Part 3, has all the above scripts refactored as PS Workflow, which can be used with SMA.

    I did have a question, something I was wondering - What are the primary use cases for these scripts + SMA? I am happy to do another blog post to take advantage of any synergies, just trying to work through the use cases.

    I mean, from an Admin standpoint, it makes sense (we can expose the parameters to be filled in from the WAP Admin portal), but the Tenant Portal would not really be usable for execution of these scripts. Of course, a front end that calls into the WS API for SMA would be perfectly fine as well...

    Thoughts?