Azure Resource Graph と ThreadJob を使って Azure PowerShell を高速化する
TL;DR
- Azure PowerShell を使った処理を高速化し隊
- Azure Resource Graph (ARG) はいいぞ
- PowerShell の ThreadJob はいいぞ
はじめに
Azure PowerShell を使って何か作業の効率化をするにあたって、よくあるパターンとしては Get-AzVM
してそれを foreach
で回す、みたいなのがあると思います。
シンプルな実装で分かりやすいのも重要なのですが、数百台を超えてくるとそこそこ時間がかかるようになり、効率化が求められます。
ここでは、2 つの方法を使って効率化し、かつそれをスニペットというか、コード片として残しておきます。
コピペ用
完成したものはこちらです。
$scriptBlock
と $query
を書き換えることで使いまわしができます。
PowerState/deallocated なマシンだけに対して処理をするようなイメージ
# Required Modules and Azure Login
Connect-AzAccount | Out-Null
# Define the script block for processing each VM
$scriptBlock = {
param($resource)
$VerbosePreference = 'Continue'
try {
Write-Host "Processing VM Resource ID: $($resource.resourceId)"
# Split the ResourceID to get individual components
$resourceIdParts = $resource.resourceId -split '/'
$subscriptionId = $resourceIdParts[2]
$resourceGroupName = $resourceIdParts[4]
$resourceName = $resourceIdParts[-1]
Write-Host "Azure VM: $($resourceName) in Resource Group: $($resourceGroupName) in Subscription: $($subscriptionId) is $($resource.powerState)"
} catch {
Write-Host "Error processing VM Resource ID: $($resource.resourceId). Error: $_"
}
}
# Initialize an array to hold the thread jobs
$jobs = @()
# Record the start time
$startTime = Get-Date
# Use Azure Resource Graph to retrieve only stopped VMs in the specified resource group
$query = @"
resources
| where type == 'microsoft.compute/virtualmachines'
| extend powerState = properties.extended.instanceView.powerState.code
| project-away properties
| where powerState == 'PowerState/deallocated'
"@
$resources = Search-AzGraph -Query $query
# Process each VM Resource ID in parallel using thread jobs
foreach ($resource in $resources) {
# Start a new thread job for each VM Resource ID with a throttle limit
$job = Start-ThreadJob -ScriptBlock $scriptBlock -ArgumentList $resource -ThrottleLimit 20
# Add the job to the jobs array
$jobs += $job
}
# Wait for all jobs to complete
$jobs | Wait-Job | Out-Null
# Retrieve and display the output from each job
$jobs | Receive-Job
# Clean up the jobs
$jobs | Remove-Job
# Calculate and display the duration
$endTime = Get-Date
Write-Host "Duration: $(($endTime - $startTime).TotalSeconds) seconds"
Write-Host "Process completed."
Azure Resource Graph を使った絞り込み
Get-AzVM
はシンプルでいいのですが、powerState が PowerState/deallocated
などの VM だけを取得、、というのはできません。
これは、Azure Resource Graph を使うことで解決できます。
# Use Azure Resource Graph to retrieve only stopped VMs in the specified resource group
$query = @"
resources
| where type == 'microsoft.compute/virtualmachines'
| extend powerState = properties.extended.instanceView.powerState.code
| project-away properties
| where powerState == 'PowerState/deallocated'
"@
$resources = Search-AzGraph -Query $query
Azure Resource Graph は Kusto Query Language (KQL) を使うので、分からない部分は検索したり、Microsoft Copilot for Azure に聞いて解決できます。
ThreadJob を使った並列処理
対象を絞ったところで、逐次処理ではやはり時間がかかります。
ので、ThreadJob を使った並列処理を行います。
ThreadJob については PowerShell Job を使ってみる に書いてありますので、ここでは割愛します。
Azure Resource Graph で検索した結果はオブジェクトに入っているので、それをそのまま ThreadJob に渡すことができます。
# Process each VM Resource ID in parallel using thread jobs
foreach ($resource in $resources) {
# Start a new thread job for each VM Resource ID with a throttle limit
$job = Start-ThreadJob -ScriptBlock $scriptBlock -ArgumentList $resource -ThrottleLimit 20
# Add the job to the jobs array
$jobs += $job
}
渡された ThreadJob 側で、resourceId
を分解して、Subscription の ID やリソース グループの名前などを取得して、続く処理を実行するイメージです。
ここでは、Azure VM に関して少し詳細な感じでメッセージを出していますが、あまり意味はありません。
# Define the script block for processing each VM
$scriptBlock = {
param($resource)
$VerbosePreference = 'Continue'
try {
Write-Host "Processing VM Resource ID: $($resource.resourceId)"
# Split the ResourceID to get individual components
$resourceIdParts = $resource.resourceId -split '/'
$subscriptionId = $resourceIdParts[2]
$resourceGroupName = $resourceIdParts[4]
$resourceName = $resourceIdParts[-1]
Write-Host "Azure VM: $($resourceName) in Resource Group: $($resourceGroupName) in Subscription: $($subscriptionId) is $($resource.powerState)"
} catch {
Write-Host "Error processing VM Resource ID: $($resource.resourceId). Error: $_"
}
}
まとめ
ずーっと書きたかったのですが、Get-AzVM
して foreach
して、、、というみなさまの PowerShell script が一気に高速化できるのではないか、というお話でした。
Azure Resource Graph は ARM template とほぼ同じ情報を扱えるので、フィルターとしては十分すぎる表現が可能と考えています。
また、ThreadJob という、比較的使いやすい並列処理の仕組みを使って、さらに処理を高速化できます。
参考
- PowerShell Job を使ってみる
- クイック スタート:初めての PowerShell クエリ - Azure Resource Graph
- Azure Resource Graph から変更履歴を検索する
- Log AnalyticsからAzure Resource Graphを通してアラートを上げる
Discussion