Forked from DaveRuijter/PowerBIEmbeddedScheduledAutoscaling.ps1
Last active
July 24, 2024 14:20
-
-
Save fabiocannas/eaa9aaa9a6f4e67d9425beeaff407988 to your computer and use it in GitHub Desktop.
PowerShell script to vertically scale up and down or pause/resume an Azure Power BI Embedded Capacity according to a schedule. You can use this in an Azure Automation Runbook.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .SYNOPSIS | |
| Vertically scale up and down or pause/resume an Azure Power BI Embedded Capacity according to a schedule using Azure Automation. | |
| .DESCRIPTION | |
| This Azure Automation runbook enables vertically scaling or pausing of an Azure Power BI Embedded Capacity 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. A5) on monday during peak hours, while the rest of the week the traffic is decreased, allowing you to scale down (e.g. A1). Outside business hours and during weekends you could then suspend the capacity 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 capacity 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 capacity is assigned. | |
| .PARAMETER serverName | |
| Azure Power BI Embedded Capacity name. | |
| .PARAMETER scalingSchedule | |
| PBI Capacity 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 slotsthat you defined, the server will be paused. | |
| .PARAMETER scalingScheduleTimeZone | |
| Timezone of time slots in $scalingSchedule. | |
| Available time zones: [System.TimeZoneInfo]::GetSystemTimeZones(). | |
| .EXAMPLE | |
| -resourceGroupName myResourceGroup | |
| -serverName myserver | |
| -scalingSchedule [{WeekDays:[1], StartTime:"06:59:59", StopTime:"17:59:59", Sku: "A4"}, {WeekDays:[2,3,4,5], StartTime:"06:59:59", StopTime:"17:59:59", Sku: "A2"}] | |
| -scalingScheduleTimeZone W. Europe Standard Time | |
| .NOTES | |
| Author: Dave Ruijter | |
| Last Updated: 24/07/2024 - Fabio Cannas | |
| Updated script from AzureRM to Az + fix schedule time string conversion to DateTime | |
| #> | |
| param( | |
| [parameter(Mandatory=$true)] | |
| [string] $resourceGroupName, | |
| [parameter(Mandatory=$true)] | |
| [string] $capacityName, | |
| [parameter(Mandatory=$true)] | |
| [string] $scalingSchedule, | |
| [parameter(Mandatory=$false)] | |
| [string] $scalingScheduleTimeZone = "W. Europe Standard Time" | |
| ) | |
| try | |
| { | |
| filter timestamp {"[$(Get-Date -Format G)]: $_"} | |
| Write-Output "Script started." | timestamp | |
| $VerbosePreference = "Continue" | |
| $ErrorActionPreference = "Stop" | |
| #Authentication | |
| "Logging in to Azure..." | |
| Connect-AzAccount -Identity | |
| #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)}} | |
| #Parsing to datetime specifying hours only, will cause date being one day behind if time is slightly after midnight. To avoid this, i changed the script to set the schedule's datetime to the current day. | |
| $dayObjects = $stateConfig | Where-Object {$_.WeekDays -contains $currentDayOfWeek } | ForEach-Object { | |
| $item = $_ | |
| $startTimeConversionResult = [datetime]::ParseExact($_.StartTime,"HH:mm:ss", [System.Globalization.CultureInfo]::InvariantCulture).TimeOfDay | |
| $stopTimeConversionResult = [datetime]::ParseExact($_.StopTime,"HH:mm:ss", [System.Globalization.CultureInfo]::InvariantCulture).TimeOfDay | |
| $startDate = $startTime.Date.Add($startTimeConversionResult) | |
| $endDate = $startTime.Date.Add($stopTimeConversionResult) | |
| $item | Add-Member -NotePropertyName StartTime -NotePropertyValue $startDate -Force | |
| $item | Add-Member -NotePropertyName StopTime -NotePropertyValue $endDate -Force | |
| return $item | |
| } | Select-Object Sku, Action, StartTime, StopTime | |
| # Get the PBI Capacity object | |
| $pbiEmbCap = Get-AzPowerBIEmbeddedCapacity -ResourceGroupName $resourceGroupName -Name $capacityName | |
| Write-Output "PBI Capacity name found: $($pbiEmbCap.Name)" | timestamp | |
| Write-Output "Current PBI Capacity status: $($pbiEmbCap.State), pricing tier: $($pbiEmbCap.Sku)" | 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 PBI Capacity is paused and if current pricing tier is matching..." | timestamp | |
| if($pbiEmbCap.State -eq "Paused") | |
| { | |
| Write-Output "PBI Capacity was paused. Resuming!" | timestamp | |
| $pbiEmbCap | Resume-AzPowerBIEmbeddedCapacity | |
| Write-Output "PBI Capacity resumed." | timestamp | |
| } | |
| if($pbiEmbCap.Sku -ne $matchingObject.Sku) | |
| { | |
| Write-Output "PBI Capacity is not in the pricing tier of the scaling schedule. Changing!" | timestamp | |
| $pbiEmbCap = Update-AzPowerBIEmbeddedCapacity -Name $pbiEmbCap.Name -ResourceGroupName $resourceGroupName -Sku $matchingObject.Sku | |
| Write-Output "Change to edition/tier as specified in scaling schedule initiated..." | timestamp | |
| $pbiEmbCap = Get-AzPowerBIEmbeddedCapacity -ResourceGroupName $resourceGroupName -Name $capacityName | |
| Write-Output "Current PBI Capacity state: $($pbiEmbCap.State), pricing tier: $($pbiEmbCap.Sku)" | timestamp | |
| } | |
| else | |
| { | |
| Write-Output "Current PBI Capacity pricing tier 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 PBI Capacity is paused..." | timestamp | |
| if($pbiEmbCap.State -ne "Paused") | |
| { | |
| Write-Output "PBI Capacity not paused. Pausing!" | timestamp | |
| $pbiEmbCap | Suspend-AzPowerBIEmbeddedCapacity | |
| Write-Output "PBI Capacity paused." | timestamp | |
| $pbiEmbCap = Get-AzPowerBIEmbeddedCapacity -ResourceGroupName $resourceGroupName -Name $capacityName | |
| Write-Output "Current PBI Capacity state: $($pbiEmbCap.State), pricing tier: $($pbiEmbCap.Sku)" | timestamp | |
| } | |
| else | |
| { | |
| Write-Output "PBI Capacity 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 PBI Capacity is paused..." | timestamp | |
| if($pbiEmbCap.State -ne "Paused") | |
| { | |
| Write-Output "PBI Capacity not paused. Pausing!" | timestamp | |
| $pbiEmbCap | Suspend-AzPowerBIEmbeddedCapacity | |
| Write-Output "PBI Capacity paused." | timestamp | |
| $pbiEmbCap = Get-AzPowerBIEmbeddedCapacity -ResourceGroupName $resourceGroupName -Name $capacityName | |
| Write-Output "Current PBI Capacity state: $($pbiEmbCap.State), pricing tier: $($pbiEmbCap.Sku)" | timestamp | |
| } | |
| else | |
| { | |
| Write-Output "PBI Capacity paused already. Exiting..." | timestamp | |
| } | |
| } | |
| Write-Output "Script finished." | timestamp | |
| } | |
| catch { | |
| Write-Error -Message $_.Exception | |
| throw $_.Exception | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment