Azure: Definiowanie alertu w oparciu o metryki w PowerShell

19-wrz-2022

Azure Monitor pozwala na konfigurację alertów, które zostaną wyzwolone w przypadku zwiększonego wykorzystania zasobów wybranego obiektu infrastruktury, np. przeciążonego CPU, dysku sieci, ale także w oparciu o przekroczenie licznika pewnych zdarzeń, jak np. liczba nieudanych logowań w określonym czasie. Poniżej możesz zobaczyć, jak stworzyć taki alert przy pomocy powershella.

Na początek należy się zalogować do Azure i w razie potrzeby wybrać subskrypcję:

Connect-AZAccount
$subscription_name = 'XXX enter subscription name here XXX'
Select-AzSubscription -SubscriptionName "$subscription_name" | Out-Null
Przyda się też kilka zmiennych przechowujących pomocnicze dane:
$action_group_name =  'DBAADMIN' # limit 12 characters
$emails = ('one@example.com', 'two@example.com')
$description = "Please have a look at this issue!"

Teraz pora na zdefiniowanie kilku metryk. Dla własnej wygody robię to poprzez zdefiniowanie listy słowników:

$metric_alerts = @()
$condition = @{
    'metric_name' = 'CPU Credits Consumed';
    'threshold'   = 80;
    'window_size' = New-TimeSpan -Minutes 5;
    'frequency'   = New-TimeSpan -Minutes 1;
    'aggregation' = 'Average';
    'severity'    = 3
}
$metric_alerts += $condition

$condition = @{
    'metric_name' = 'CPU Credits Consumed';
    'threshold'   = 90;
    'window_size' = New-TimeSpan -Minutes 5;
    'aggregation' = 'Average';    
    'frequency'   = New-TimeSpan -Minutes 1;
    'severity'    = 3
}
$metric_alerts += $condition

Mamy tutaj informacje o metryce, którą należy zmierzyć, o progowej wartości tej metryki oraz jak ją mierzyć. Pomiaru dokonuje się w określonym czasie (window_size), z określoną częstością (frequency) i w zależności od metryki dokonuje się pewnej agregacji (aggregation). Mając różne warunki, można tworzyć alerty o różnych priorytetach, stąd w słowniku zapisuję też severity.
Alerty będą skonfigurowane dla określonego obiektu, dlatego potrzebne też są zmienne definiujące ten obiekt. Tutaj jest to nazwa grupy zasobów i nazwa serwera z tej grupy zasobów:
$object_RG = 'XXX enter servers resource group here XXX'
$object_name = 'XXX enter servers name here XXX'
$object = Get-AzVM -Name $object_name  -ResourceGroupName $object_RG
Teraz pora na zabawę – przechodzimy przez każdy warunek, który powinien mieć zdefiniowany alert i uruchamiamy funkcję definiującą alert:
foreach($condition in $metric_alerts){
   set-metric_alert -object_RG $object_RG `
                    -object_name $object_name `
                    -object_id $object.Id `
                    -condition $condition `
                    -description $description `
                    -action_group_name $action_group_name `
                    -emails $emails
}
Fajnie wygląda, ale funkcję set-metric_alert trzeba zdefiniować. Oto definicja funkcji, a jej opis umieszczam poniżej:
function set-metric_alert{
    param(
        [string]$object_RG,
        [string]$object_name,
        [string]$object_id,
        $condition,
        [string]$action_group_name,  # limit 12 characters
        [string[]]$emails,
        [string]$description
    )

    Write-Output "Setting $object_name $($condition['metric_name']) $($condition['threshold'])"

    # Condition
    $metric_name = $condition['metric_name']
    $threshold   = $condition['threshold']
    $window_size = $condition['window_size']
    $aggregation = $condition['aggregation']
    $frequency   = $condition['frequency']
    $severity    = $condition['severity']

    # Alert
    $alert_RG = $object_RG
    $alert_rule_name = "$metric_name $threshold ($severity $object_name)"

    # Action
    Write-Output "     getting receiver"
    $email_receiver_list = @()
    foreach ($address in $emails){
        $email_receiver_list += New-AzActionGroupReceiver -Name "Notify $address" `
                                                    -EmailReceiver -EmailAddress $address `
                                                    -UseCommonAlertSchema
    }

    # Action group
    Write-Output "     getting action group"
    $action_group = Set-AzActionGroup -Name $action_group_name `
                                      -ResourceGroupName $alert_RG `
                                      -ShortName $action_group_name `
                                      -Receiver $email_receiver_list `
                                      -WarningAction SilentlyContinue 
                                       
    #Creates an ActionGroup reference object in memory
    Write-Output "     getting action group id"
    $action_group_id = New-AzActionGroup  -ActionGroupId $action_group.id

    # Condition
    Write-Output "     getting condition"
    $condition_rule = New-AzMetricAlertRuleV2Criteria -MetricName $metric_name `
                                                      -TimeAggregation $aggregation `
                                                      -Operator GreaterThan `
                                                      -Threshold $threshold `
                                                      -WarningAction SilentlyContinue

    # Alert
    Write-Output "     setting alert"
    Add-AzMetricAlertRuleV2 -Name $alert_rule_name `
                            -ResourceGroupName $alert_RG `
                            -WindowSize $window_size `
                            -Frequency $frequency `
                            -Description $description `
                            -TargetResourceId $object_id `
                            -Condition $condition_rule `
                            -ActionGroup $action_group_id `
                            -Severity $severity `
                            -WarningAction SilentlyContinue  | Out-null
}

Oto komentarz do poszczególnych kroków:

  • Na początku przyjmujemy parametry. Na uwagę zasługuje ewentualnie adres email – u nas jest to lista napisów [string[]]
  • Dla wygody wyciągam definicję warunku wyzwolenia alertu do zmiennych lokalnych
  • Każdy alert musi mieć nazwę i musi być umieszczony w jakiejś grupie zasobów. Tutaj założyłem, że alert powstanie w tej samej grupie zasobów, co monitorowany zasób, a nazwa będzie ustalona dynamicznie
  • Alert będzie wysyłał powiadomienia, a tych powiadomień może być więcej. Wszystkie one będą umieszczone w action group. Dlatego:
    • definiuję 'action group receiver’ polegającą na powiadomieniu administratora o sytuacji zaistniałej na serwerze. Tutaj jest to zrobione dla adresu email, ale możliwe jest dodanie także powiadomienia przez SMS, a idąc jeszcze dalej zamiast zwykłego powiadamiania, możnaby uruchamiać job-a, który być może zaistniały problem jest w stanie rozwiązać
    • tworzę  'action group’, który łączy w sobie aktywności, które należy wykonać. Ze względów technicznych potrzebny jest obiekt w pamięci wskazujący na action group. Można go uzyskać za pomocą polecenia New-AzActionGroup
  • W oparciu o przesłane warunki, budujemy obiekt Alert Rule. Podczas tworzenia tego obiektu, może być wyświetlany dodatkowy komunikat o zależnościach między obiektami, które są ukryte przed nami. Jeśli nie chcesz widzieć tych ostrzeżeń, dodaj do polecenia WarningAction
  • Kluczowa sprawa – utworzenie alertu. Jedno polecenie, które łączy w sobie warunki wyzwolenia alertu z action group, obiektem, który ma być monitorowany, sposobem pomiaru wartości

I to w sumie tyle – od tej pory, gdy zostanie przekroczona wartość metryki, wyzwoli się alert, który wyśle maila. Oto cała zawartość skryptu:

function set-metric_alert{
    param(
        [string]$object_RG,
        [string]$object_name,
        [string]$object_id,
        $condition,
        [string]$action_group_name,  # limit 12 characters
        [string[]]$emails,
        [string]$description
    )

    Write-Output "Setting $object_name $($condition['metric_name']) $($condition['threshold'])"
    # Condition
    $metric_name = $condition['metric_name']
    $threshold   = $condition['threshold']
    $window_size = $condition['window_size']
    $aggregation = $condition['aggregation']
    $frequency   = $condition['frequency']
    $severity    = $condition['severity']
    # Alert
    $alert_RG = $object_RG
    $alert_rule_name = "$metric_name $threshold ($severity $object_name)"

    # Action
    Write-Output "     getting receiver"

    $email_receiver_list = @()
    foreach ($address in $emails){
        $email_receiver_list += New-AzActionGroupReceiver -Name "Notify $address" `
                                                    -EmailReceiver -EmailAddress $address `
                                                    -UseCommonAlertSchema
    }

    # Action group
    Write-Output "     getting action group"

    $action_group = Set-AzActionGroup -Name $action_group_name `
                                      -ResourceGroupName $alert_RG `
                                      -ShortName $action_group_name `
                                      -Receiver $email_receiver_list `
                                      -WarningAction SilentlyContinue                                    

    #Creates an ActionGroup reference object in memory
    Write-Output "     getting action group id"
    $action_group_id = New-AzActionGroup  -ActionGroupId $action_group.id

    # Condition
    Write-Output "     getting condition"
    $condition_rule = New-AzMetricAlertRuleV2Criteria -MetricName $metric_name `
                                                      -TimeAggregation $aggregation `
                                                      -Operator GreaterThan `
                                                      -Threshold $threshold `
                                                      -WarningAction SilentlyContinue
    # Alert
    Write-Output "     setting alert"
    Add-AzMetricAlertRuleV2 -Name $alert_rule_name `
                            -ResourceGroupName $alert_RG `
                            -WindowSize $window_size `
                            -Frequency $frequency `
                            -Description $description `
                            -TargetResourceId $object_id `
                            -Condition $condition_rule `
                            -ActionGroup $action_group_id `
                            -Severity $severity `
                            -WarningAction SilentlyContinue  | Out-null

}

Connect-AZAccount
$subscription_name = 'XXX enter subscription name XXX'
Select-AzSubscription -SubscriptionName "$subscription_name" | Out-Null
$action_group_name =  'DBAADMIN' # limit 12 characters
$emails = ('one@example.com', 'two@example.com')
$description = "Please have a look at this issue!"
$metric_alerts = @()
$condition = @{
    'metric_name' = 'CPU Credits Consumed';
    'threshold'   = 80;
    'window_size' = New-TimeSpan -Minutes 5;
    'frequency'   = New-TimeSpan -Minutes 1;
    'aggregation' = 'Average';
    'severity'    = 3
}
$metric_alerts += $condition

$condition = @{
    'metric_name' = 'CPU Credits Consumed';
    'threshold'   = 90;
    'window_size' = New-TimeSpan -Minutes 5;
    'aggregation' = 'Average';    
    'frequency'   = New-TimeSpan -Minutes 1;
    'severity'    = 3
}

$metric_alerts += $condition
$object_RG = 'XXX enter VM resource group XXX'
$object_name = 'XXX enter VM name'

$object = Get-AzVM -Name $object_name  -ResourceGroupName $object_RG

foreach($condition in $metric_alerts){
   set-metric_alert -object_RG $object_RG `
                    -object_name $object_name `
                    -object_id $object.Id `
                    -condition $condition `
                    -description $description `
                    -action_group_name $action_group_name `
                    -emails $emails
}

Komentarze są wyłączone

Autor: Rafał Kraik