Azure Analysis Services scheduled autoscaling and pausing

Posted by

One of the major benefits of Azure is scalability, the cloud is built to be elastic, to be as cost-effective as possible. Azure solutions can be scaled in ways that would never be possible with your own hardware. You are able to scale up and out when the demand arises, and more importantly you can scale down and in, or even pause, when you don’t need all the resources anymore, thereby saving a lot of money.

Autoscaling based on a schedule allows you to scale your solution according to predictable resource demand. For example you could require the highest capacity on Monday during peak hours, while the rest of the week the traffic is decreased allowing you to scale down and minimizing costs. Outside business hours and during weekends you could then pause the service, after which no charges will be applied at all.

I recently blogged about Azure SQL Database scheduled autoscaling and in this post I will apply the same solution to Azure Analysis Services. Scaling Azure AS works similar to Azure SQL, but the amount of money you can save is higher because in general Azure AS is more expensive, and you have the possibility to pause the instance.

Azure AS currently offers three service tiers: Developer, Basic and Standard. Each instance can contain one or multiple tabular models. You can change between instance tiers and performance levels easily and without downtime.
Azure Analysis Services Tiers

Scheduled autoscaling with Azure Automation

To automate scaling of Azure Analysis Services based on a schedule I have created an Azure Automation PowerShell Runbook called AzureAnalysisServicesScheduledAutoscaling. You can easily import this runbook into your Azure Automation account and then execute it against your instance by entering values for a few parameters that specify your database details, your Automaton Run As Account and an instance-specific scaling schedule. A step-by-step guide can be found at the bottom of this post.

Scheduling of the runbook itself can be done by creating a recurring hourly Automation Schedule. A single runbook can have multiple jobs running at one time. This means it is possible to create multiple schedules for the AzureAnalysisServicesScheduledAutoscaling runbook, enabling scaling of multiple instances simultaneously.

Scaling of the instance to a specified tier/performance level will be performed in case the runbook execution moment matches a day/time slot specified in the scaling schedule parameter. If the execution moment does not match a day/time slot the service will be paused. If the runbook executes and the instance is in the desired tier/performance level already, no work will be done. Don’t worry about the costs of Azure Automation, the first 500 run minutes are free and after that you pay €0.002 / minute (as per september 2017).

Azure Analysis Services Vertical Scaling using Azure Automation

When you start using this solution, keep the following in mind:

  • The length of time for a switch of tier/performance level varies, but is generally under a couple of minutes.
  • Behind the scenes, changing the tier/performance level creates a replica of the original instance at the new tier/performance level, and then switches connections over to the replica. No data is lost during this process. During the switch over to the replica, connections to the database are disabled which can cause in flight queries to fail but they should be retried.
  • Scaling from a lower tier to a higher tier is supported, but NOT vice-versa. For example, you can move from Basic to Standard tier or from Developer to Basic tier, but you can’t move from Standard to Basic tier or from Basic to Developer tier.
  • To scale an instance down, the total size of the models must be smaller than the maximum allowed size of the target performance level.
  • The Standard tier supports scale out instances. Additional instances can be added to a primary instance for faster data and query processing. In this PowerShell solution only vertical scaling is supported.


The Runbook PowerShell script

The PowerShell script below can be used in an Azure Automation Runbook with type “PowerShell”.

The script contains the following logic:

  1. Authenticate with Azure Automation Run As account.
  2. Get the current day of week and current time, and convert it to the time zone that is specified.
  3. Check which of the specified ‘slots’ in the scaling schedule is currently active.
  4. Scale instance to tier/performance level that is specified in the scaling schedule.
    If no schedule for the current day/time is found, pause instance.
    If instance is already in target edition/tier, do nothing.

.SYNOPSIS
Vertically scale up and down or pause/resume an Azure Analysis
Services server according to a schedule using Azure Automation.

.DESCRIPTION
This Azure Automation runbook enables vertically scaling or
pausing of an Azure Analysis Services server according to a
schedule. Autoscaling based on a schedule allows you to scale
your solution according to predictable resource demand. For
example you could require a high capacity (e.g. S1) on monday
during peak hours, while the rest of the week the traffic is
decreased allowing you to scale down (e.g. S0). Outside business
hours and during weekends you could then pause the server so no
charges will be applied. This runbook can be scheduled to run
hourly. The code checks the scalingSchedule parameter to decide
if scaling needs to be executed, or if the server is in the
desired state already and no work needs to be done. The script
is time zone aware.

.PARAMETER resourceGroupName
Name of the resource group to which the server is assigned.

.PARAMETER azureRunAsConnectionName
Azure Automation Run As account name. Needs to be able to access
the $serverName.

.PARAMETER serverName
Azure Analysis Services server name.

.PARAMETER scalingSchedule
Server Scaling Schedule. It is possible to enter multiple
comma separated schedules: [{},{}]
Weekdays start at 0 (sunday) and end at 6 (saturday).
If the script is executed outside the scaling schedule time slots
that you defined, the server will be paused.

.PARAMETER scalingScheduleTimeZone
Time Zone of time slots in $scalingSchedule.
Available time zones: [System.TimeZoneInfo]::GetSystemTimeZones().

.EXAMPLE
-resourceGroupName myResourceGroup
-azureRunAsConnectionName AzureRunAsConnection
-serverName myserver
-scalingSchedule [{WeekDays:[1], StartTime:”06:59:59″,
StopTime:”17:59:59″, Sku: “B2″},
{WeekDays:[2,3,4,5], StartTime:”06:59:59″,
StopTime:”17:59:59”, Sku: “B1”}]
-scalingScheduleTimeZone W. Europe Standard Time

.NOTES
Author: Jorg Klein
Last Updated: 10/10/2017

param(
[parameter(Mandatory=$true)]
[string] $resourceGroupName,

[parameter(Mandatory=$false)]
[string] $azureRunAsConnectionName = "AzureRunAsConnection",

[parameter(Mandatory=$true)]
[string] $serverName,

[parameter(Mandatory=$true)]
[string] $scalingSchedule,

[parameter(Mandatory=$false)]
[string] $scalingScheduleTimeZone = "W. Europe Standard Time"
)

filter timestamp {"[$(Get-Date -Format G)]: $_"}

Write-Output "Script started." | timestamp

$VerbosePreference = "Continue"
$ErrorActionPreference = "Stop"

#Authenticate with Azure Automation Run As account (service principal)
$runAsConnectionProfile = Get-AutomationConnection `
-Name $azureRunAsConnectionName
Add-AzureRmAccount -ServicePrincipal `
-TenantId $runAsConnectionProfile.TenantId `
-ApplicationId $runAsConnectionProfile.ApplicationId `
-CertificateThumbprint ` $runAsConnectionProfile.CertificateThumbprint | Out-Null
Write-Output "Authenticated with Automation Run As Account." | timestamp

#Get current date/time and convert to $scalingScheduleTimeZone
$stateConfig = $scalingSchedule | ConvertFrom-Json
$startTime = Get-Date
Write-Output "Azure Automation local time: $startTime." | timestamp
$toTimeZone = [System.TimeZoneInfo]::FindSystemTimeZoneById($scalingScheduleTimeZone)
Write-Output "Time zone to convert to: $toTimeZone." | timestamp
$newTime = [System.TimeZoneInfo]::ConvertTime($startTime, $toTimeZone)
Write-Output "Converted time: $newTime." | timestamp
$startTime = $newTime

#Get current day of week based on converted start time
$currentDayOfWeek = [Int]($startTime).DayOfWeek
Write-Output "Current day of week: $currentDayOfWeek." | timestamp

# Get the scaling schedule for the current day of week
$dayObjects = $stateConfig | Where-Object {$_.WeekDays -contains $currentDayOfWeek } `
|Select-Object Sku, `
@{Name="StartTime"; Expression = {[datetime]::ParseExact($_.StartTime,"HH:mm:ss", [System.Globalization.CultureInfo]::InvariantCulture)}}, `
@{Name="StopTime"; Expression = {[datetime]::ParseExact($_.StopTime,"HH:mm:ss", [System.Globalization.CultureInfo]::InvariantCulture)}}

# Get the server object
$asSrv = Get-AzureRmAnalysisServicesServer -ResourceGroupName $resourceGroupName -Name $serverName
Write-Output "AAS server name: $($asSrv.Name)" | timestamp
Write-Output "Current server status: $($asSrv.State), sku: $($asSrv.Sku.Name)" | timestamp

if($dayObjects -ne $null) { # Scaling schedule found for this day
    # Get the scaling schedule for the current time. If there is more than one available, pick the first
    $matchingObject = $dayObjects | Where-Object { ($startTime -ge $_.StartTime) -and ($startTime -lt $_.StopTime) } | Select-Object -First 1
    if($matchingObject -ne $null)
    {
        Write-Output "Scaling schedule found. Check if server is paused and if current sku is matching..." | timestamp
        if($asSrv.State -eq "Paused")
        {
            Write-Output "Server was paused. Resuming!" | timestamp
            $asSrv | Resume-AzureRmAnalysisServicesServer
            Write-Output "Server resumed." | timestamp
        }
        if($asSrv.Sku.Name -ne $matchingObject.Sku)
        {
            Write-Output "Server is not in the sku of the scaling schedule. Changing!" | timestamp
            $asSrv = Set-AzureRmAnalysisServicesServer -Name $asSrv.Name -ResourceGroupName $resourceGroupName -Sku $matchingObject.Sku
            Write-Output "Change to edition/tier as specified in scaling schedule initiated..." | timestamp
            $asSrv = Get-AzureRmAnalysisServicesServer -ResourceGroupName $resourceGroupName -Name $serverName
            Write-Output "Current server state: $($asSrv.State), sku: $($asSrv.Sku.Name)" | timestamp
        }
        else
        {
            Write-Output "Current server sku matches the scaling schedule already. Exiting..." | timestamp
        }
    }
    else { # Scaling schedule not found for current time
        Write-Output "No matching scaling schedule time slot for this time found. Check if the server is paused..." | timestamp
        if($asSrv.State -ne "Paused")
        {
            Write-Output "Server not paused. Pausing!" | timestamp
            $asSrv | Suspend-AzureRmAnalysisServicesServer
            Write-Output "Server paused." | timestamp
            $asSrv = Get-AzureRmAnalysisServicesServer -ResourceGroupName $resourceGroupName -Name $serverName
            Write-Output "Current server sate: $($asSrv.State), sku: $($asSrv.Sku.Name)" | timestamp
        }
        else
        {
            Write-Output "Server paused already. Exiting..." | timestamp
        }
    }
}
else # Scaling schedule not found for this day
{
    Write-Output "No matching scaling schedule for this day found.  Check if the server is paused..." | timestamp
    if($asSrv.State -ne "Paused")
    {
        Write-Output "Server not paused. Pausing!" | timestamp
        $asSrv | Suspend-AzureRmAnalysisServicesServer
        Write-Output "Server paused." | timestamp
        $asSrv = Get-AzureRmAnalysisServicesServer -ResourceGroupName $resourceGroupName -Name $serverName
        Write-Output "Current server state: $($asSrv.State), sku: $($asSrv.Sku.Name)" | timestamp
    }
    else
    {
        Write-Output "Server paused already. Exiting..." | timestamp
    }
}  

Write-Output "Script finished." | timestamp


Step-by-step setup guide

Navigate to the Azure portal and create an Azure Automation account in the same Azure Subscription as where the Azure Analysis Services instance that you want to scale resides. Make sure the Run As account has been created. When you create a new Automation account in the Azure portal, it automatically creates a new service principal (Run As account) and assigns it the Contributor role-based access control (RBAC) role in the subscription by default. These rights are sufficient to scale Azure SQL Databases.

A pre-requisite to using my script is to import the AzureRM.Profile and AzureRM.AnalysisServices PowerShell modules. Navigate to your Automation Account and click the “Modules Gallery” menu item. Search for “AzureRM.Profile” first since the Azure AS module depends on it. Open the item from the gallery and click Import.
Azure Automation Import Module

Next, import the “AzureRM.AnalysisServices” module.
Azure Automation Import Module AnalysisServices

I have uploaded my PowerShell script to TechNet Script Center so you can easily import it as Runbook. Navigate to your Automation Account if required and click the “Runbooks Gallery” menu item. Make sure you filter the Gallery Source on “Script Center” and search for “Azure Analysis Services – Scale and Pause or Resume using scheduled autoscaling”. Open the item from the gallery and click Import.
Azure Automation Import Runbook

Before you can use the runbook you have to publish it first. Click on “Overview” if you are not already on that page and then click “Edit”.
Edit Runbook

Now click “Publish”.
Publish Runbook

The runbook is now ready to use. Next step is to schedule it to run hourly, of course with your desired parameters. Click “Schedules” and then “Add a schedule”.
Schedule Runbook

Give your schedule a name, set the start time on a whole hour, the Recurrence on “Recurring”, Recur every “1 Hour” and Set expiration to “No”.
New Runbook Schedule

Now configure the parameters. Explaination can be found under “The Runbook PowerShell script” earlier in this article.
Schedule Runbook Parameters

When you now navigate to “Jobs” you get an overview of all executions.
Runbook Jobs

When you open a job and look at the Output, you will see all details of that particular run. For example, I can see my server was resumed on friday at 07:00.
Job Output

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s