avs-autoscale/code/autoscale-out-runbook.ps1 (174 lines of code) (raw):
# Title: Azure VMware Solution Private Cloud Cluster Auto-scale Scale-Out PowerShell Runbook
# Purpose: Azure Automation PowerShell Runbook for the scale-out of an Azure VMware Solution management or resource cluster.
[OutputType("PSAzureOperationResponse")]
param (
[Parameter (Mandatory=$false)]
[object] $WebhookData
)
$ErrorActionPreference = "stop"
# Maximum number of nodes in allowed to scale up to in the cluster
# This is set to 5 for the purpose of this example. The maximum number of nodes in a cluster is 16.
$maxnodes = 5
# Array of datastore names in storage alert used to map to clustername
$vsandatastorename = 'vsanDatastore (1)','vsanDatastore (2)','vsanDatastore (3)','vsanDatastore (4)','vsanDatastore (5)','vsanDatastore (6)','vsanDatastore (7)','vsanDatastore (8)','vsanDatastore (9)','vsanDatastore (10)','vsanDatastore (11)'
if ($WebhookData) {
# Get the data object from WebhookData
$WebhookBody = (ConvertFrom-Json -InputObject $WebhookData.RequestBody)
# Get the info needed to identify the AVS Private Cloud (AVS alerts use "azureMonitorCommonAlertSchema")
$schemaId = $WebhookBody.schemaId
Write-Verbose "schemaId: $schemaId" -Verbose
if ($schemaId -eq "azureMonitorCommonAlertSchema") {
# This is the common Metric Alert schema (released March 2019)
$Essentials = [object] ($WebhookBody.data).essentials
# Get the first target only as this script doesn't handle multiple
$alertTargetIdArray = (($Essentials.alertTargetIds)[0]).Split("/")
$SubId = ($alertTargetIdArray)[2]
$ResourceGroupName = ($alertTargetIdArray)[4]
$ResourceType = ($alertTargetIdArray)[6] + "/" + ($alertTargetIdArray)[7]
$ResourceName = ($alertTargetIdArray)[-1]
$status = $Essentials.monitorCondition
$alertContext = [object] ($WebhookBody.data).alertContext
$alertContextdimensionsName = $alertContext.condition.allOf[0].dimensions[0].name
$alertContextdimensionsValue = $alertContext.condition.allOf[0].dimensions[0].value
Write-Verbose "*** alertContext: $alertContext" -Verbose
Write-Verbose "*** alertContextdimensionsName: $alertContextdimensionsName" -Verbose
Write-Verbose "*** alertContextdimensionsValue: $alertContextdimensionsValue" -Verbose
}
else {
# Schema not supported
Write-Error "The alert data schema - $schemaId - is not supported."
}
Write-Verbose "*** status: $status" -Verbose
if (($status -eq "Activated") -or ($status -eq "Fired")) {
Write-Verbose "*** resourceType: $ResourceType" -Verbose
Write-Verbose "*** resourceName: $ResourceName" -Verbose
Write-Verbose "*** resourceGroupName: $ResourceGroupName" -Verbose
Write-Verbose "*** subscriptionId: $SubId" -Verbose
# Determine code path depending on the resourceType
if ($ResourceType -eq "Microsoft.AVS/privateClouds")
{
# This is an AVS Private Cloud
Write-Verbose "*** This is an AVS Private Cloud." -Verbose
# Authenticate to Azure with Manged identity
Write-Verbose "*** Authenticating to Azure" -Verbose
Connect-AzAccount -Identity -ErrorAction Stop
Write-Verbose "Authentication successful."
Write-Verbose "Setting subscription context to $SubId"
Set-AzContext -SubscriptionId $SubId -ErrorAction Stop | Write-Verbose
# Part 1 - Detect if Cluster Type is the Management Cluster (Cluster-1) or a Resource cluster (Cluster-2 -> Cluster-12)
# Part 2 - Detect if the alert type is storage, cpu or ram and map to correct cluster name
# This is necessary because the AVS management and resource clusters use different PowerShell commands (Get-AzVMwarePrivateCloud, Update-AzVMwarePrivateCloud, Get-AzVMwareCluster, Update-AzVMwareCluster)
if (($alertContextdimensionsValue -eq "Cluster-1") -or ($alertContextdimensionsValue -eq "vsanDatastore")) {
# Management Cluster (Cluster-1) detected
$ClusterTypeManagement = $true
$ClusterName = "Cluster-1"
Write-Verbose "*** Management ($ClusterName) detected" -Verbose
}
else {
# Resource Cluster (Cluster-2 -> Cluster-12) detected
$ClusterTypeManagement = $false
Write-Verbose "*** Resource Cluster (Cluster-2 -> Cluster-12) detected" -Verbose
if ($alertContextdimensionsName -eq "clustername") {
# CPU or RAM alert detected, use existing $alertContextdimensionsValue, which has the correct clustername (Cluster-2 -> Cluster-12)
$ClusterName = $alertContextdimensionsValue
Write-Verbose "*** CPU or RAM Alert detected" -Verbose
}
else {
# Storage alert detected, search through array for datastore name (vsanDatastore (1) -> vsanDatastore (11)) and map to clustername (Cluster-2 -> Cluster-12)
for ($arrayindex = 0; $arrayindex -lt $vsandatastorename.count; $arrayindex++) {
if ($alertContextdimensionsValue -eq $vsandatastorename[$arrayindex]) {
$ClusterName = "Cluster-$($arrayindex+2)"
Write-Verbose "*** Storage Alert detected" -Verbose
Write-Verbose "*** alertContextdimensionsValue: $alertContextdimensionsValue" -Verbose
}
}
}
}
Write-Verbose "*** ClusterTypeManagement: $ClusterTypeManagement" -Verbose
Write-Verbose "*** ClusterName: $ClusterName" -Verbose
# Get Private Cloud (includes Management Cluster: Cluster-1) Object
$MgmtCluster = Get-AzVMwarePrivateCloud -SubscriptionId $SubId -ResourceGroupName $ResourceGroupName -Name $ResourceName
# Get Private Cloud Provisioning State
$PrivateCloudProvisioningState = $MgmtCluster.ProvisioningState
# Check Private Cloud Provisioning State
if ($PrivateCloudProvisioningState -eq "Succeeded") {
# Cluster Size calculation for Standard or Stretched Cluster
# We deliberately rely upon the Azure VMware Solution management & control plane to enforce the cluster size minimum (standard 3 nodes, stretched 6 nodes) and the cluster size maximum (16 nodes). This provides operational simplicity in maintaining this PS Runbook.
# Get Availability Strategy (SingleZone or DualZone)
$MgmtClusterAvailabilityType = $MgmtCluster.AvailabilityStrategy
Write-Verbose "*** MgmtClusterAvailabilityType: $MgmtClusterAvailabilityType" -Verbose
# Cluster Type Check, Cluster Size Calculation and Cluster Autoscale Execution
if ($ClusterTypeManagement -eq $true) {
# Management Cluster Size Calculation & Execution
$MgmtClusterCurrentSize = $MgmtCluster.ManagementClusterSize
if ($MgmtClusterCurrentSize -ge $maxnodes) {
# Management Cluster is already at maximum size, exit
Write-Verbose "*** Management Cluster is already at maximum size of $maxnodes nodes. No action taken." -Verbose
return
}
if ($MgmtClusterAvailabilityType -eq "DualZone") {
# DualZone requires 1 node per Availability Zone = 2
$MgmtClusterNewSize = $MgmtClusterCurrentSize + 2
}
elseif ($MgmtClusterAvailabilityType -eq "SingleZone") {
# SingleZone requires 1 node per Availability Zone = 1
$MgmtClusterNewSize = $MgmtClusterCurrentSize + 1
}
else {
# Unknown Availability Strategy Type
Write-Error "The Azure VMware Solution Availability Strategy - $MgmtClusterAvailabilityType - is not supported."
}
Write-Verbose "*** MgmtClusterNewSize: $MgmtClusterNewSize" -Verbose
# Execute Management Cluster Autoscale Command
Write-Verbose "*** Scaling PrivateCloudName: $ResourceName Management Cluster (Cluster-1) from $MgmtClusterCurrentSize nodes to $MgmtClusterNewSize nodes" -Verbose
Write-Verbose "*** PrivateCloudName: $ResourceName resides in resourceGroupName: $ResourceGroupName & subscriptionId: $SubId" -Verbose
Update-AzVMwarePrivateCloud -SubscriptionId $SubId -ResourceGroupName $ResourceGroupName -Name $ResourceName -ManagementClusterSize $MgmtClusterNewSize
}
else {
# Resource Cluster Size Calculation & Execution
$ResourceCluster = Get-AzVMwareCluster -SubscriptionId $SubId -ResourceGroupName $ResourceGroupName -PrivateCloudName $ResourceName -Name $ClusterName
$ResourceClusterCurrentSize = $ResourceCluster.Size
if ($ResourceClusterCurrentSize -ge $maxnodes) {
# Resource Cluster is already at maximum size, exit
Write-Verbose "*** Resource Cluster is already at maximum size of $maxnodes nodes. No action taken." -Verbose
return
}
if ($MgmtClusterAvailabilityType -eq "DualZone") {
# DualZone requires 1 node per Availability Zone = 2
$ResourceClusterNewSize = $ResourceClusterCurrentSize + 2
}
elseif ($MgmtClusterAvailabilityType -eq "SingleZone") {
# SingleZone requires 1 node per Availability Zone = 1
$ResourceClusterNewSize = $ResourceClusterCurrentSize + 1
}
else {
# Unknown Availability Strategy Type
Write-Error "The Azure VMware Solution Availability Strategy - $MgmtClusterAvailabilityType - is not supported."
}
Write-Verbose "*** ResourceClusterNewSize: $ResourceClusterNewSize" -Verbose
# Execute Resource Cluster Autoscale Command
Write-Verbose "*** Scaling PrivateCloudName: $ResourceName Resource Cluster $ClusterName from $ResourceClusterCurrentSize nodes to $ResourceClusterNewSize nodes" -Verbose
Write-Verbose "*** PrivateCloudName: $ResourceName resides in resourceGroupName: $ResourceGroupName & subscriptionId: $SubId" -Verbose
Update-AzVMwareCluster -SubscriptionId $SubId -ResourceGroupName $ResourceGroupName -PrivateCloudName $ResourceName -Name $ClusterName -ClusterSize $ResourceClusterNewSize
}
}
else {
# SDDC Provisioning State is not Succeeded
Write-Error "The Azure VMware Solution Private Cloud: $ResourceName provisioning state: $PrivateCloudProvisioningState is not in Succeeded state."
}
# [OutputType(PSAzureOperationResponse")]
}
else {
# ResourceType not supported
Write-Error "$ResourceType is not a supported resource type for this runbook."
}
}
else {
# The alert status was not 'Activated' or 'Fired' so no action taken
Write-Verbose ("*** No action taken. Alert status: " + $status) -Verbose
}
}
else {
# Error
Write-Error "This runbook is meant to be started from an Azure alert webhook only."
}