Sospendere automaticamente un processo con PowerShell e Funzioni di Azure o Automazione di Azure

Alcune applicazioni richiedono un approccio di elaborazione del flusso, reso semplice con Analisi di flusso di Azure , ma non è strettamente necessario eseguirlo in modo continuo. I motivi sono diversi:

  • Dati di input in arrivo su un pianificare (inizio dell'ora...)
  • Un volume sparse o basso di dati in ingresso (pochi record al minuto)
  • Processi aziendali che traggono vantaggio dalle funzionalità di time-windowing, ma vengono eseguiti in batch per essenza (Finance o HR...)
  • Dimostrazioni, prototipi o test che coinvolgono processi a esecuzione prolungata su larga scala

Il vantaggio di non eseguire questi processi in modo continuo sarà un risparmio sui costi, perché i processi di Analisi di flusso vengono fatturati per unità di streaming nel tempo.

Questo articolo illustra come configurare la sospensione automatica per un processo di Analisi di flusso di Azure. Viene configurata un'attività che sospende e riprende automaticamente un processo in un pianificare. Se si usa il termine pausa, lo statoeffettivo del processo viene arrestato, in modo da evitare la fatturazione.

Verrà illustrata prima la progettazione complessiva, quindi verranno esaminati i componenti necessari e infine verranno illustrati alcuni dettagli di implementazione.

Nota

Ci sono aspetti negativi per la sospensione automatica di un processo. I principali sono la perdita delle funzionalità a bassa latenza/in tempo reale e i potenziali rischi derivanti dal consentire al backlog degli eventi di input di aumentare le prestazioni non supervisionate durante la sospensione di un processo. La sospensione automatica non deve essere considerata per la maggior parte degli scenari di produzione in esecuzione su larga scala

Progettazione

Per questo esempio, si vuole che il processo venga eseguito per N minuti, prima di sospendere il processo per M minuti. Quando il processo viene sospeso, i dati di input non verranno utilizzati, accumulando upstream. Dopo l'avvio del processo, il processo verrà aggiornato con tale backlog, elaborando i dati che si inseguiranno prima di essere arrestati di nuovo.

Diagram that illustrates the behavior of the auto-paused job over time

Durante l'esecuzione, l'attività non deve arrestare il processo finché le metriche non sono integre. Le metriche di interesse saranno il backlog di input e la filigrana. Si verificherà che entrambi si trovino nella linea di base per almeno N minuti. Questo comportamento si traduce in due azioni:

  • Un processo arrestato viene riavviato dopo m minuti
  • Un processo in esecuzione viene arrestato in qualsiasi momento dopo N minuti, non appena il relativo backlog e le metriche della filigrana sono integre

Diagram that shows the possible states of the job

Si consideri ad esempio N = 5 minuti e M = 10 minuti. Con queste impostazioni, un processo ha almeno 5 minuti per elaborare tutti i dati ricevuti in 15. Il potenziale risparmio sui costi è fino al 66%.

Per riavviare il processo, si userà l'opzione When Last Stoppedstart. Questa opzione indica ad ASA di elaborare tutti gli eventi di cui è stato eseguito il backup upstream dopo l'arresto del processo. Ci sono due avvertenze in questa situazione. In primo luogo, il processo non può rimanere arrestato più a lungo del periodo di conservazione del flusso di input. Se il processo viene eseguito una sola volta al giorno, è necessario assicurarsi che il periodo di conservazione dell'hub eventi sia più di un giorno. In secondo luogo, il processo deve essere stato avviato almeno una volta per l'accettazione della modalità When Last Stopped (altrimenti non è stato letteralmente mai arrestato prima). Pertanto, la prima esecuzione di un processo deve essere manuale oppure è necessario estendere lo script per coprire tale caso.

L'ultima considerazione è quella di rendere queste azioni idempotenti. In questo modo, possono essere ripetuti senza effetti collaterali, sia per facilità d'uso che per resilienza.

Componenti

Chiamate API

Si prevede la necessità di interagire con ASA sugli aspetti seguenti:

  • Ottenere lo stato del processo corrente (Gestione risorse ASA)
    • Se è in esecuzione
      • Ottenere l'ora dall'avvio (log)
      • Ottenere i valori delle metriche correnti (metriche)
      • Se applicabile, arrestare il processo (Gestione risorse ASA)
    • Se arrestato
      • Ottenere il tempo trascorso dall'arresto (log)
      • Se applicabile, avviare il processo (Gestione risorse ASA)

Per Gestione risorse ASA, è possibile usare l'API REST, .NET SDK o una delle librerie dell'interfaccia della riga di comando (Interfaccia della riga di comando di Azure, PowerShell).

Per metriche e log, in Azure tutto è centralizzato in Monitoraggio di Azure, con una scelta simile di superfici API. È necessario ricordare che i log e le metriche sono sempre da 1 a 3 minuti quando si eseguono query sulle API. L'impostazione di N a 5 indica in genere che il processo verrà eseguito da 6 a 8 minuti in realtà. Un altro aspetto da considerare è che le metriche vengono sempre generate. Quando il processo viene arrestato, l'API restituisce record vuoti. Sarà necessario pulire l'output delle chiamate API per esaminare solo i valori pertinenti.

Linguaggio di scripting

Per questo articolo si è deciso di implementare la sospensione automatica in PowerShell. Il primo motivo è che PowerShell è ora multipiattaforma. Può essere eseguito in qualsiasi sistema operativo, semplificando così le distribuzioni. Il secondo motivo è che accetta e restituisce oggetti anziché stringhe. Gli oggetti semplificano l'analisi e l'elaborazione per le attività di automazione.

In PowerShell si userà il modulo Az PowerShell , che imbarca Az.Monitor e Az.StreamAnalytics, per tutto il necessario:

Servizio di hosting

Per ospitare l'attività di PowerShell, è necessario un servizio che offre esecuzioni pianificate. Esistono molte opzioni, ma esaminando quelle serverless:

  • Funzioni di Azure, un motore di calcolo serverless in grado di eseguire quasi qualsiasi parte di codice. Le funzioni offrono un trigger timer che può essere eseguito fino a ogni secondo
  • Automazione di Azure, un servizio gestito creato per i carichi di lavoro e le risorse del cloud operativi. Che si adatta alla fattura, ma il cui intervallo minimo di pianificare è 1 ora (meno con soluzioni alternative).

Se non si pensa alla soluzione alternativa, Automazione di Azure è il modo più semplice per distribuire l'attività. Ma per poter confrontare, in questo articolo verrà prima scritto uno script locale. Dopo aver creato uno script funzionante, verrà distribuito sia in Funzioni che in un account di Automazione.

Strumenti per sviluppatori

È consigliabile usare VSCode per lo sviluppo locale, sia per Funzioni che per AsA. L'uso di un IDE locale consente di usare il controllo del codice sorgente e di ripetere facilmente le distribuzioni. Ma per brevità, qui verrà illustrato il processo nella portale di Azure.

Scrittura dello script di PowerShell in locale

Il modo migliore per sviluppare lo script è localmente. PowerShell multipiattaforma, lo script può essere scritto e testato in qualsiasi sistema operativo. In Windows è possibile usare Terminale Windows con PowerShell 7 e Az PowerShell.

Lo script finale che verrà usato è disponibile per Funzioni (e Automazione di Azure). È diverso da quello illustrato di seguito, essendo stato collegato all'ambiente di hosting (Funzioni o Automazione). Questo aspetto verrà illustrato più avanti. Prima di tutto, esaminiamo una versione di che viene eseguita solo in locale.

Questo script viene scritto intenzionalmente in una forma semplice, quindi può essere compreso da tutti.

Nella parte superiore vengono impostati i parametri obbligatori e viene controllato lo stato iniziale del processo:


# Setting variables
$restartThresholdMinute = 10 # This is M
$stopThresholdMinute = 5 # This is N

$maxInputBacklog = 0 # The amount of backlog we tolerate when stopping the job (in event count, 0 is a good starting point)
$maxWatermark = 10 # The amount of watermark we tolerate when stopping the job (in seconds, 10 is a good starting point at low SUs)

$subscriptionId = "<Replace with your Subscription Id - not the name>"
$resourceGroupName = "<Replace with your Resource Group Name>"
$asaJobName = "<Replace with your ASA job name>"

$resourceId = "/subscriptions/$($subscriptionId )/resourceGroups/$($resourceGroupName )/providers/Microsoft.StreamAnalytics/streamingjobs/$($asaJobName)"

# If not already logged, uncomment and run the 2 following commands
# Connect-AzAccount
# Set-AzContext -SubscriptionId $subscriptionId

# Check current ASA job status
$currentJobState = Get-AzStreamAnalyticsJob  -ResourceGroupName $resourceGroupName -Name $asaJobName | Foreach-Object {$_.JobState}
Write-Output "asaRobotPause - Job $($asaJobName) is $($currentJobState)."

Quindi, se il processo è in esecuzione, verificare se il processo è in esecuzione almeno N minuti, il relativo backlog e la relativa filigrana.


# Switch state
if ($currentJobState -eq "Running")
{
    # First we look up the job start time with Get-AzActivityLog
    ## Get-AzActivityLog issues warnings about deprecation coming in future releases, here we ignore them via -WarningAction Ignore
    ## We check in 1000 record of history, to make sure we're not missing what we're looking for. It may need adjustment for a job that has a lot of logging happening.
    ## There is a bug in Get-AzActivityLog that triggers an error when Select-Object First is in the same pipeline (on the same line). We move it down.
    $startTimeStamp = Get-AzActivityLog -ResourceId $resourceId -MaxRecord 1000 -WarningAction Ignore | Where-Object {$_.EventName.Value -like "Start Job*"}
    $startTimeStamp = $startTimeStamp | Select-Object -First 1 | Foreach-Object {$_.EventTimeStamp}

    # Then we gather the current metric values
    ## Get-AzMetric issues warnings about deprecation coming in future releases, here we ignore them via -WarningAction Ignore
    $currentBacklog = Get-AzMetric -ResourceId $resourceId -TimeGrain 00:01:00 -MetricName "InputEventsSourcesBacklogged" -DetailedOutput -WarningAction Ignore
    $currentWatermark = Get-AzMetric -ResourceId $resourceId -TimeGrain 00:01:00 -MetricName "OutputWatermarkDelaySeconds" -DetailedOutput -WarningAction Ignore

    # Metric are always lagging 1-3 minutes behind, so grabbing the last N minutes means checking N+3 actually. This may be overly safe and fined tune down per job.
    $Backlog =  $currentBacklog.Data |
                    Where-Object {$_.Maximum -ge 0} | # We remove the empty records (when the job is stopped or starting)
                    Sort-Object -Property Timestamp -Descending |
                    Where-Object {$_.Timestamp -ge $startTimeStamp} | # We only keep the records of the latest run
                    Select-Object -First $stopThresholdMinute | # We take the last N records
                    Measure-Object -Sum Maximum # We sum over those N records
    $BacklogSum = $Backlog.Sum

    $Watermark = $currentWatermark.Data |
                    Where-Object {$_.Maximum -ge 0} |
                    Sort-Object -Property Timestamp -Descending |
                    Where-Object {$_.Timestamp -ge $startTimeStamp} |
                    Select-Object -First $stopThresholdMinute |
                    Measure-Object -Average Maximum # Here we average
    $WatermarkAvg = [int]$Watermark.Average # Rounding the decimal value casting it to integer

    # Since we called Get-AzMetric with a TimeGrain of a minute, counting the number of records gives us the duration in minutes
    Write-Output "asaRobotPause - Job $($asaJobName) is running since $($startTimeStamp) with a sum of $($BacklogSum) backlogged events, and an average watermark of $($WatermarkAvg) sec, for $($Watermark.Count) minutes."

    # -le for lesser or equal, -ge for greater or equal
    if (
        ($BacklogSum -ge 0) -and ($BacklogSum -le $maxInputBacklog) -and ` # is not null and is under the threshold
        ($WatermarkAvg -ge 0) -and ($WatermarkAvg -le $maxWatermark) -and ` # is not null and is under the threshold
        ($Watermark.Count -ge $stopThresholdMinute) # at least N values
        )
    {
        Write-Output "asaRobotPause - Job $($asaJobName) is stopping..."
        Stop-AzStreamAnalyticsJob -ResourceGroupName $resourceGroupName -Name $asaJobName
    }
    else {
        Write-Output "asaRobotPause - Job $($asaJobName) is not stopping yet, it needs to have less than $($maxInputBacklog) backlogged events and under $($maxWatermark) sec watermark for at least $($stopThresholdMinute) minutes."
    }
}

Se il processo viene arrestato, si esamina il log quando è stata l'ultima azione "Arresta processo":


elseif ($currentJobState -eq "Stopped")
{
    # First we look up the job start time with Get-AzActivityLog
    ## Get-AzActivityLog issues warnings about deprecation coming in future releases, here we ignore them via -WarningAction Ignore
    ## We check in 1000 record of history, to make sure we're not missing what we're looking for. It may need adjustment for a job that has a lot of logging happening.
    ## There is a bug in Get-AzActivityLog that triggers an error when Select-Object First is in the same pipeline (on the same line). We move it down.
    $stopTimeStamp = Get-AzActivityLog -ResourceId $resourceId -MaxRecord 1000 -WarningAction Ignore | Where-Object {$_.EventName.Value -like "Stop Job*"}
    $stopTimeStamp = $stopTimeStamp | Select-Object -First 1 | Foreach-Object {$_.EventTimeStamp}

    # Get-Date returns a local time, we project it to the same time zone (universal) as the result of Get-AzActivityLog that we extracted above
    $minutesSinceStopped = ((Get-Date).ToUniversalTime()- $stopTimeStamp).TotalMinutes

    # -ge for greater or equal
    if ($minutesSinceStopped -ge $restartThresholdMinute)
    {
        Write-Output "asaRobotPause - Job $($jobName) was paused $([int]$minutesSinceStopped) minutes ago, set interval is $($restartThresholdMinute), it is now starting..."
        Start-AzStreamAnalyticsJob -ResourceGroupName $resourceGroupName -Name $asaJobName -OutputStartMode LastOutputEventTime
    }
    else{
        Write-Output "asaRobotPause - Job $($jobName) was paused $([int]$minutesSinceStopped) minutes ago, set interval is $($restartThresholdMinute), it will not be restarted yet."
    }
}
else {
    Write-Output "asaRobotPause - Job $($jobName) is not in a state I can manage: $($currentJobState). Let's wait a bit, but consider helping is that doesn't go away!"
}

Al termine, registriamo il completamento del processo:


# Final ASA job status check
$newJobState = Get-AzStreamAnalyticsJob  -ResourceGroupName $resourceGroupName -Name $asaJobName | Foreach-Object {$_.JobState}
Write-Output "asaRobotPause - Job $($asaJobName) was $($currentJobState), is now $($newJobState). Job completed."

Opzione 1: Hosting dell'attività in Funzioni di Azure

Per riferimento, il team di Funzioni di Azure gestisce una guida completa per gli sviluppatori di PowerShell.

Per prima cosa è necessaria una nuova app per le funzioni. Un'app per le funzioni è simile a una soluzione che può ospitare più funzioni.

La procedura completa è qui, ma il gist consiste nel portale di Azure e creare una nuova app per le funzioni con:

  • Pubblica: Codice
  • Runtime: PowerShell Core
  • Versione: 7+

Dopo aver effettuato il provisioning, iniziamo con la relativa configurazione complessiva.

Identità gestita per Funzioni di Azure

La funzione richiede le autorizzazioni per avviare e arrestare il processo ASA. Queste autorizzazioni verranno assegnate tramite un'identità gestita.

Il primo passaggio consiste nell'abilitare un'identità gestita assegnata dal sistema per la funzione, seguendo questa procedura.

Ora è possibile concedere le autorizzazioni corrette a tale identità nel processo ASA che si vuole sospendere automaticamente. Per questo, nel portale per il processo ASA (non la funzione), in Controllo di accesso (IAM) aggiungere un'assegnazione di ruolo al ruolo Collaboratore per un membro di tipo Identità gestita, selezionando il nome della funzione precedente.

Screenshot of IAM settings for the ASA job

Nello script di PowerShell è possibile aggiungere un controllo che garantisce che l'identità gestita sia impostata correttamente (lo script finale è disponibile qui)


# Check if managed identity has been enabled and granted access to a subscription, resource group, or resource
$AzContext = Get-AzContext -ErrorAction SilentlyContinue
if (-not $AzContext.Subscription.Id)
{
    Throw ("Managed identity is not enabled for this app or it has not been granted access to any Azure resources. Please see /azure/app-service/overview-managed-identity for additional details.")
}

Si aggiungeranno anche alcune informazioni di registrazione per assicurarsi che la funzione stia attivando:


$currentUTCtime = (Get-Date).ToUniversalTime()

# Write an information log with the current time.
Write-Host "asaRobotPause - PowerShell timer trigger function is starting at time: $currentUTCtime"

Parametri per Funzioni di Azure

Il modo migliore per passare i parametri allo script in Funzioni consiste nell'usare le impostazioni dell'applicazione app per le funzioni come variabili di ambiente.

A tale scopo, il primo passaggio è nella pagina App per le funzioni, per definire i parametri come App Impostazioni seguendo questa procedura. È necessario:

Nome Valore
maxInputBacklog La quantità di backlog tollerabile quando si arresta il processo (nel numero di eventi, 0 è un buon punto di partenza)
maxWatermark La quantità di filigrana tollerabile quando si arresta il processo (in secondi, 10 è un buon punto di partenza a bassa SUS)
restartThresholdMinute M: tempo (in minuti) fino al riavvio di un processo arrestato
stopThresholdMinute N: tempo (in minuti) di raffreddamento fino a quando non viene arrestato un processo in esecuzione. Il backlog di input dovrà rimanere a 0 durante quel periodo
subscriptionId SubscriptionId (non il nome) del processo ASA da sospendere automaticamente
resourceGroupName Nome gruppo di risorse del processo ASA da sospendere automaticamente
asaJobName Nome del processo ASA da sospendere

In seguito sarà necessario aggiornare lo script di PowerShell per caricare di conseguenza le variabili:

$maxInputBacklog = $env:maxInputBacklog
$maxWatermark = $env:maxWatermark

$restartThresholdMinute = $env:restartThresholdMinute
$stopThresholdMinute = $env:stopThresholdMinute

$subscriptionId = $env:subscriptionId
$resourceGroupName = $env:resourceGroupName
$asaJobName = $env:asaJobName

Requisiti del modulo di PowerShell

Analogamente, è necessario installare Az PowerShell in locale per usare i comandi ASA (ad esempio Start-AzStreamAnalyticsJob), sarà necessario aggiungerlo all'host dell'app per le funzioni.

A tale scopo, è possibile passare alla Functions>App files pagina App per le funzioni, selezionare e annullare requirements.psd1ilcommentazione della riga 'Az' = '6.*'. Affinché tale modifica venga eseguita, l'intera app dovrà essere riavviata.

Screenshot of the app files settings for the Function App

Creazione della funzione

Una volta completata questa configurazione, è possibile creare la funzione specifica, all'interno dell'app per le funzioni, che eseguirà lo script.

Nel portale verrà sviluppata una funzione attivata su un timer (ogni minuto con 0 */1 * * * *, che legge "al secondo 0 di ogni 1 minuto"):

Screenshot of creating a new timer trigger function in the function app

Se necessario, è possibile modificare il valore timer in Integration, aggiornando il pianificare:

Screenshot of the integration settings of the function

Code + TestIn è quindi possibile copiare lo script in run.ps1 e testarlo. Lo script completo può essere copiato da qui, la logica di business è stata spostata in un'istruzione TRY/CATCH per generare errori appropriati in caso di errore durante l'elaborazione.

Screenshot of Code+Test for the function

È possibile verificare che tutti gli elementi vengano eseguiti correttamente tramite test/esecuzione nel Code + Test riquadro. È anche possibile esaminare il Monitor riquadro, ma è sempre tardi di un paio di esecuzioni.

Screenshot of the output of a successful run

Impostazione di un avviso sull'esecuzione della funzione

Infine, si vuole ricevere una notifica tramite un avviso se la funzione non viene eseguita correttamente. Gli avvisi hanno un costo minore, ma possono impedire situazioni più costose.

Nella pagina App per le funzioni eseguire Logsla query seguente che restituisce tutte le esecuzioni non riuscite negli ultimi 5 minuti:

requests
| where success == false
| where timestamp > ago(5min)
| summarize failedCount=sum(itemCount) by operation_Name
| order by failedCount desc

Nell'editor di query selezionare New alert rule. Nella schermata seguente definire la misura come:

  • Misura: failedCount
  • Tipo di aggregazione: Totale
  • Granularità aggregazione: 5 minuti

Configurare quindi la logica di avviso come segue:

  • Operator: Maggiore di
  • Valore soglia: 0
  • Frequenza di valutazione: 5 minuti

Da qui, riutilizzare o creare un nuovo gruppo di azioni, quindi completare la configurazione.

Per verificare che l'avviso sia stato configurato correttamente, è possibile aggiungere throw "Testing the alert" ovunque nello script di PowerShell e attendere 5 minuti per ricevere un messaggio di posta elettronica.

Opzione 2: Hosting dell'attività in Automazione di Azure

Prima di tutto sarà necessario un nuovo account di automazione. Un account di automazione è simile a una soluzione che può ospitare più runbook.

La procedura è qui. In questo caso è possibile selezionare per usare un'identità gestita assegnata dal sistema direttamente nella advanced scheda.

Per informazioni di riferimento, il team di Automazione ha una buona esercitazione per iniziare a usare i runbook di PowerShell.

Parametri per Automazione di Azure

Con un runbook è possibile usare la sintassi dei parametri classica di PowerShell per passare argomenti:

Param(
    [string]$subscriptionId,
    [string]$resourceGroupName,
    [string]$asaJobName,

    [int]$restartThresholdMinute,
    [int]$stopThresholdMinute,

    [int]$maxInputBacklog,
    [int]$maxWatermark
)

Identità gestita per Automazione di Azure

L'account di automazione deve avere ricevuto un'identità gestita durante il provisioning. Tuttavia, se necessario, è possibile abilitare una procedura.

Come per la funzione, sarà necessario concedere le autorizzazioni corrette per il processo ASA che si vuole sospendere automaticamente.

Per questo, nel portale per il processo ASA (non nella pagina automazione ), in Controllo di accesso (IAM) aggiungere un'assegnazione di ruolo al ruolo Collaboratore per un membro di tipo Identità gestita, selezionando il nome dell'account di automazione precedente.

Screenshot of IAM settings for the ASA job

Nello script di PowerShell è possibile aggiungere un controllo che garantisce che l'identità gestita sia impostata correttamente (lo script finale è disponibile qui)

# Ensures you do not inherit an AzContext in your runbook
Disable-AzContextAutosave -Scope Process | Out-Null

# Connect using a Managed Service Identity
try {
        $AzureContext = (Connect-AzAccount -Identity).context
    }
catch{
        Write-Output "There is no system-assigned user identity. Aborting.";
        exit
    }

Creazione del Runbook

Al termine della configurazione, è possibile creare il runbook specifico, all'interno dell'account di automazione, che eseguirà lo script. Qui non è necessario aggiungere Az PowerShell come requisito, è già integrato.

Nel portale, in Automazione processi selezionare , quindi selezionare , selezionare , selezionare RunbooksCreate a runbookcome tipo di runbook e qualsiasi versione precedente 7 come versione (al momento 7.1 (preview)).PowerShell

È ora possibile incollare lo script e testarlo. Lo script completo può essere copiato da qui, la logica di business è stata spostata in un'istruzione TRY/CATCH per generare errori appropriati in caso di errore durante l'elaborazione.

Screenshot of the runbook script editor in Azure Automation

È possibile verificare che tutto sia cablato correttamente nell'oggetto Test Pane.

Dopo aver necessario il Publish processo, che consentirà di collegare il runbook a un pianificare. La creazione e il collegamento della pianificare è un processo semplice che non verrà discusso qui. Ora è un buon momento per ricordare che esistono soluzioni alternative per ottenere intervalli di pianificare sotto 1 ora.

Infine, è possibile configurare un avviso. Il primo passaggio consiste nell'abilitare i log tramite le impostazioni di diagnostica dell'account di automazione. Il secondo passaggio consiste nell'acquisire errori tramite una query simile a quella eseguita per Funzioni.

Risultato

Esaminando il nostro processo ASA, possiamo notare che tutto è in esecuzione come previsto in due posizioni.

Nel log attività:

Screenshot of the logs of the ASA job

E tramite le metriche:

Screenshot of the metrics of the ASA job

Una volta compreso lo script, è semplice rielaborarlo per estendere l'ambito. Può essere facilmente aggiornato per indirizzare un elenco di processi anziché uno singolo. È possibile definire e elaborare ambiti più grandi tramite tag, gruppi di risorse o anche intere sottoscrizioni.

Supporto

Per ulteriori assistenza, provare la pagina delle domande di Microsoft Q&A per Analisi di flusso di Azure.

Passaggi successivi

Sono state apprese le nozioni di base sull'uso di PowerShell per automatizzare la gestione dei processi di Analisi di flusso di Azure. Per altre informazioni, vedere gli articoli seguenti: