Tales About Working With The Azure Service Management API – Chapter II

After I elaborated a bit on one of the base requirements to work with the Azure Service Management API at all which is enabling a valid certificate to authenticate your calls to the API I want today share some thoughts about how to automate tasks related with Azure deployments. As already pointed out in Chapter I you probably want to use Windows Powershell together with the Windows Azure Service Management CmdLets which will make your life in interacting with the Windows Azure Fabric from a shell environment much easier. The commandlets support most of the relevant tasks to do deployments and undeployments.

So in my example here I will show how those commandlets can be used in scripts to optimize the consumption of Windows Azure compute services. As you might know the compute service is billed based on service hours which basically means that as long as a service is deployed it will produce cost although the service may not be used at all during certain times. And as there may be quite some solutions where there are known time windows in which the service isn’t used (e.g. non busines hours) it may come in handy if there would be the possibility to undeploy and deploy the services based on their availability needs.

To have this work the only thing needed is two Windows Powershell scripts where on does the deployment and the other one the undeployment. The script for undeploying a service could look like the following:

 <#
     .Synopsis
         Will suspend and undeploy a Windows Azure Service
     .Description
         Will suspend and undeploy a Windows Azure Service        
     .Parameter servicename
         The name of the Azure service that holds the deployment
     .Parameter thumbprint
         The thumbprint of a locally installed and valid certificate for accessing
         the Windows Azure Management Service
     .Parameter subscriptionid
         The ID for the subscription of the Azure Account hosting the respective service
     .Parameter slot
         The deployment slot in which the package to be undeployed is physically situated
     .Example
         undeployAzureService.ps1 -thumbprint "5643060E8F0029FB8C6DB76AAE905C7FA697" -servicename "MyService" -subscriptionid "00000000-0000-0000-0000-000000000000" -slot "Production"
 #>
  
 param([PARAMETER(Mandatory=$true)][string]$servicename, [PARAMETER(Mandatory=$true)][string]$thumbprint, [PARAMETER(Mandatory=$true)][string]$subscriptionid, [PARAMETER(Mandatory=$true)][string]$slot)
 if(!($snapin = Get-PSSnapin AzureManagementToolsSnapIn -erroraction silentlycontinue))
 {
     Write-Host "Adding ManagementTools SnapIn...please wait"
     Add-PSSnapin AzureManagementToolsSnapIn
 }
 $cert = Get-Item cert:\CurrentUser\My\$thumbprint
 $service = Get-HostedService $servicename -Certificate $cert -SubscriptionId $subscriptionid
 if($service)
 {
     $deployment = Get-Deployment -Slot $slot -ServiceName $servicename -SubscriptionId $subscriptionid -Certificate $cert -erroraction silentlycontinue
     if($deployment.Name)
     {
         Write-Host "Suspending deployment...please wait"
         Get-Deployment -Slot $slot -ServiceName $servicename -SubscriptionId $subscriptionid -Certificate $cert |
         Set-DeploymentStatus 'Suspended' |
         Get-OperationStatus -WaitToComplete
         
         Write-Host "Removing deployment...please wait"
         Get-Deployment -Slot $slot -ServiceName $servicename -SubscriptionId $subscriptionid -Certificate $cert |
         Remove-Deployment |
         Get-OperationStatus -WaitToComplete
     } else
     {
         Write-Host "No deployment found for the service named $servicename"
     }
 } else
 {
     Write-Host "No service found with the name $servicename"
 }

After creating this script you then can create a scheduled job using the Windows Scheduler. When creating the command line for the scheduled task you have to be carefull about two things:

  1. Set the execution policy for powershell so that it allows to run scripts. Depending on your environment and security policies you should set the execution policy to either “AllSigned”, “RemoteSigned” or “Unrestricted”. For more information consult the powershell documentation. Example: Set-ExecutionPolicy Unrestricted
  2. If your path to the powershell script which you need to pass over to powershell as an argument does contain blanks you need to use double quotes surrounding the whole argument including the ampersand (which is the call operator in Windows Powershell) and inside enclose the path information with single quotes. So a correct statement would look like this: powershell.exe -command "& 'C:\My Scripts\undeployAzureService.ps1'"

To create the scheduled task using the wizard or use a XML template like the one below, insert your data and import it into the scheduler.

  1. <?xml version="1.0" encoding="utf-16"?>
  2. <Task version="1.3" xmlns="https://schemas.microsoft.com/windows/2004/02/mit/task">
  3.   <RegistrationInfo>
  4.     <Date>2010-02-12T13:44:02.910461</Date>
  5.     <Author>[userid]</Author>
  6.     <Description>Timed undeployment of Azure Service during non business hours to optimize Azure consumption of service compute hours.</Description>
  7.   </RegistrationInfo>
  8.   <Triggers>
  9.     <CalendarTrigger>
  10.       <StartBoundary>2010-02-12T19:00:00Z</StartBoundary>
  11.       <Enabled>true</Enabled>
  12.       <ScheduleByDay>
  13.         <DaysInterval>1</DaysInterval>
  14.       </ScheduleByDay>
  15.     </CalendarTrigger>
  16.   </Triggers>
  17.   <Principals>
  18.     <Principal id="Author">
  19.       <UserId>[userid</UserId>
  20.       <LogonType>Password</LogonType>
  21.       <RunLevel>LeastPrivilege</RunLevel>
  22.     </Principal>
  23.   </Principals>
  24.   <Settings>
  25.     <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
  26.     <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
  27.     <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
  28.     <AllowHardTerminate>true</AllowHardTerminate>
  29.     <StartWhenAvailable>true</StartWhenAvailable>
  30.     <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
  31.     <IdleSettings>
  32.       <StopOnIdleEnd>true</StopOnIdleEnd>
  33.       <RestartOnIdle>false</RestartOnIdle>
  34.     </IdleSettings>
  35.     <AllowStartOnDemand>true</AllowStartOnDemand>
  36.     <Enabled>false</Enabled>
  37.     <Hidden>false</Hidden>
  38.     <RunOnlyIfIdle>false</RunOnlyIfIdle>
  39.     <DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession>
  40.     <UseUnifiedSchedulingEngine>false</UseUnifiedSchedulingEngine>
  41.     <WakeToRun>false</WakeToRun>
  42.     <ExecutionTimeLimit>P3D</ExecutionTimeLimit>
  43.     <Priority>7</Priority>
  44.   </Settings>
  45.   <Actions Context="Author">
  46.     <Exec>
  47.       <Command>powershell</Command>
  48.       <Arguments>-command "&amp; 'C:\my scripts\undeployAzureService.ps1'" -servicename "MyService" -thumbprint "5643060E8F0029FB8C6DB76AAE905C7FA697" -subscriptionid "00000000-0000-0000-0000-000000000000" -slot "Production" &gt; log.txt</Arguments>
  49.       <WorkingDirectory>C:\deployments\logs</WorkingDirectory>
  50.     </Exec>
  51.   </Actions>
  52. </Task>

Then do the same for the deployment process. A respective Windows Powershell script could look like this:

 <#
     .Synopsis
         Will deploy and start a Windows Azure Service
     .Description
         Will deploy and start a Windows Azure Service        
     .Parameter servicename
         The name of the Azure service that will hold the deployment
     .Parameter thumbprint
         The thumbprint of a locally installed and valid certificate for accessing
         the Windows Azure Management Service
     .Parameter subscriptionid
         The ID for the subscription of the Azure Account hosting the respective service
     .Parameter slot
         The deployment slot in which the package shall be deployed <Production | Staging>
     .Parameter storageservicename
         The storage account that can be used to upload the deployment package
     .Parameter package
         The full qualified path to the deployment package
     .Parameter config
         The full qualified path to the configuration file for this deployment
     .Example
         deployAzureService.ps1 -servicename "MyService" -thumbprint "5643060E8F0029FB8C6DB76AAE905C7FA697" -subscriptionid "00000000-0000-0000-0000-000000000000" -slot "Production" -storageservicename "StorageService" -package "C:\deployment\HelloWorld.cspkg" -config "C:\deployments\ServiceConfiguration.cscfg" -label "MyFirstDeployment"
 #>
  
 param([PARAMETER(Mandatory=$true)][string]$servicename, [PARAMETER(Mandatory=$true)][string]$thumbprint, [PARAMETER(Mandatory=$true)][string]$subscriptionid, [PARAMETER(Mandatory=$true)][string]$slot, [PARAMETER(Mandatory=$true)][string]$storageservicename, [PARAMETER(Mandatory=$true)][string]$package, [PARAMETER(Mandatory=$true)][string]$config, [PARAMETER(Mandatory=$true)][string]$label)
 if(!($snapin = Get-PSSnapin AzureManagementToolsSnapIn -erroraction silentlycontinue))
 {
     Write-Host "Adding ManagementTools SnapIn...please wait"
     Add-PSSnapin AzureManagementToolsSnapIn
 }
 $cert = Get-Item cert:\CurrentUser\My\$thumbprint
 $service = Get-HostedService $servicename -Certificate $cert -SubscriptionId $subscriptionid
 if($service)
 {
     $deployment = Get-Deployment -Slot $slot -ServiceName $servicename -SubscriptionId $subscriptionid -Certificate $cert -erroraction silentlycontinue
     $name = $deployment.Name
     if(!$deployment.Name)
     {
         Write-Host "Creating new deployment...please wait"
         New-Deployment -Slot $slot -ServiceName $servicename -SubscriptionId $subscriptionid -Certificate $cert -Package $package -Configuration $config -Label $label -StorageServiceName $storageservicename |
         Get-OperationStatus -WaitToComplete
         
         Write-Host "Starting the new deployment...please wait"
         Get-Deployment -Slot $slot -ServiceName $servicename -SubscriptionId $subscriptionid -Certificate $cert |
         Set-DeploymentStatus "Running" |
         Get-OperationStatus -WaitToComplete
     } else
     {
         Write-Host "Service $servicename already has an active deployment with the name $name...please remove the active deployment first"
     }
 } else
 {
     Write-Host "No service found with the name $servicename"
 }

And the template for the scheduler could look like this:

  1. <?xml version="1.0" encoding="UTF-16"?>
  2. <Task version="1.2" xmlns="https://schemas.microsoft.com/windows/2004/02/mit/task">
  3.   <RegistrationInfo>
  4.     <Date>2010-02-15T09:50:14.2255756</Date>
  5.     <Author>[userid]</Author>
  6.   </RegistrationInfo>
  7.   <Triggers>
  8.     <CalendarTrigger>
  9.       <StartBoundary>2010-02-15T07:00:00</StartBoundary>
  10.       <Enabled>true</Enabled>
  11.       <ScheduleByDay>
  12.         <DaysInterval>1</DaysInterval>
  13.       </ScheduleByDay>
  14.     </CalendarTrigger>
  15.   </Triggers>
  16.   <Principals>
  17.     <Principal id="Author">
  18.       <UserId>[userid]</UserId>
  19.       <LogonType>Password</LogonType>
  20.       <RunLevel>HighestAvailable</RunLevel>
  21.     </Principal>
  22.   </Principals>
  23.   <Settings>
  24.     <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
  25.     <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
  26.     <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
  27.     <AllowHardTerminate>true</AllowHardTerminate>
  28.     <StartWhenAvailable>false</StartWhenAvailable>
  29.     <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
  30.     <IdleSettings>
  31.       <StopOnIdleEnd>true</StopOnIdleEnd>
  32.       <RestartOnIdle>false</RestartOnIdle>
  33.     </IdleSettings>
  34.     <AllowStartOnDemand>true</AllowStartOnDemand>
  35.     <Enabled>true</Enabled>
  36.     <Hidden>false</Hidden>
  37.     <RunOnlyIfIdle>false</RunOnlyIfIdle>
  38.     <WakeToRun>false</WakeToRun>
  39.     <ExecutionTimeLimit>P3D</ExecutionTimeLimit>
  40.     <Priority>7</Priority>
  41.   </Settings>
  42.   <Actions Context="Author">
  43.     <Exec>
  44.       <Command>powershell</Command>
  45.       <Arguments>-command "&amp; 'C:\My Scripts\deployAzureService.ps1'" -servicename "MyService" -thumbprint "5643060E8F0029FB8C6DB76AAE905C7FA697" -subscriptionid "00000000-0000-0000-0000-000000000000" -slot "Production" -storageservicename "mystorageservice" -package "'C:\deployments\packages\HelloWorld.cspkg'" -config "'C:\deployments\packages\ServiceConfiguration.cscfg'" -label "MyFirstDeployment" &gt; deployment.log</Arguments>
  46.       <WorkingDirectory>C:\deployments\packages\logs</WorkingDirectory>
  47.     </Exec>
  48.   </Actions>
  49. </Task>

This is all to automate your deployment tasks. One thing however is to remember here. As the New-Deployment commandlet uploads your deployment package into the blob storage of the specified Azure Storage account you should check your blob storage from time and do a clean up otherwise this will also produce unnecessary cost.

Bookmark Digg Bookmark Del.icio.us Bookmark Facebook Bookmark Reddit Bookmark StumbleUpon Bookmark Yahoo Bookmark Google Bookmark Technorati Bookmark Twitter