Azure Bastion で session 一覧を出す PowerShell cmdlet
Azure Bastion で session 一覧を出す PowerShell cmdlet
Azure Bastion には session の一覧画面があります。
bastion session list
ただ、比較的新しいのか、PowerShell にはこの一覧を出す cmdlet がありませんでした。
該当の API は Get Active Sessions です。
API があるということはあとは叩けばいいだけなので早速作っていきましょう。
ChatGPT と GitHub Copilot の力をいっぱい借りながら、こんな感じのものができました。
function Get-MyAzBastionSession {
[CmdletBinding()]
param (
[parameter(Mandatory = $true, ParameterSetName = 'ByName')]
[string]$ResourceGroupName,
[parameter(Mandatory = $true, ParameterSetName = 'ByName')]
[string]$Name,
[parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName, ParameterSetName = 'ById')]
[string]$Id
)
switch ($PSCmdlet.ParameterSetName) {
'ByName' {
$SubscriptionId = (Get-AzContext).Subscription.Id
$Path = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Network/bastionHosts/$Name/getActiveSessions?api-version=2022-09-01"
}
'ById' {
$Path = "$Id/getActiveSessions?api-version=2022-09-01"
}
}
$restResponse = Invoke-AzRestMethod -Method POST -Path $Path
$sessionResponse = $null
do {
$sessionResponse = Invoke-AzRestMethod -Method GET -Uri $restResponse.Headers.Location.AbsoluteUri
if ($sessionResponse.StatusCode -ne 200) {
Write-Debug "$(Get-Date -Format "h:MM:ss tt") - Received status code $($sessionResponse.StatusCode). Retrying in 5 seconds..."
Start-Sleep -Seconds 5
}
} while ($sessionResponse.StatusCode -ne 200)
return $sessionResponse.Content | ConvertFrom-Json
}
使い方はいたって簡単で、Resrouce group と Bastion 自体の名前を指定するパターンと、Bastion の Resource ID を指定するパターンがあります。
Get-MyAzBastionSession -ResourceGroupName files-blob-archive -Name bast-hub00 -Debug
Get-MyAzBastionSession -Id "/subscriptions/9e4d6321-d80d-4e43-8916-6f3b482d001d/resourceGroups/files-blob-archive/providers/Microsoft.Network/bastionHosts/bast-hub00" -Debug
また、こだわりのポイントとしては | (パイプ) での実行に対応しました。
注意点として、Get-AzBastion
の結果で Bastion が複数返ってきた際に、以下のような利用方法だと 1 つめの Bastion に対してのみ実行されます。
Get-AzBastion | Get-MyAzBastionSession -Debug
Get-AzBasion
の -ResourceGroupName
option を使うなどして 1 つの Bastion が返ってくるように絞るか、ForEach-Object
を使って順次実行させる必要があります。
Get-AzBastion -ResourceGroupName files-blob-archive | Get-MyAzBastionSession -Debug
Get-AzBastion | ForEach-Object { Get-MyAzBastionSession -Id $_.id}
PowerShell 的ポイント
ほとんど ChatGPT と GitHub Copilot に書いてもらったのですが、以下の点が勉強になりました。
- parameter が ResourceGroupName と Name、または Id があることを必須とする
ParameterSetName
というのを使うことで、parameter のかたまりを作り、ちょっと複雑な必須条件を書けるようです。
param (
[parameter(Mandatory = $true, ParameterSetName = 'ByName')]
[string]$ResourceGroupName,
[parameter(Mandatory = $true, ParameterSetName = 'ByName')]
[string]$Name,
[parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName, ParameterSetName = 'ById')]
[string]$Id
)
このかたまりの名前は $PSCmdlet.ParameterSetName
に入っているようなので、そのまま switch
で利用しています。
前者が ResourceGroupName と Name を指定した場合であり、後者が Id を指定した場合です。
switch ($PSCmdlet.ParameterSetName) {
'ByName' {
$SubscriptionId = (Get-AzContext).Subscription.Id
$Path = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Network/bastionHosts/$Name/getActiveSessions?api-version=2022-09-01"
}
'ById' {
$Path = "$Id/getActiveSessions?api-version=2022-09-01"
}
}
- パイプでつなげられている場合に、パイプ元の出力名と引数名を一致させて引数に一気に値を突っ込む
ValueFromPipeline = $true
がパイプから引数が渡されることを示しています。
加えて、ValueFromPipelineByPropertyName
を指定することで、Get-AzBastion
の結果に含まれる Id
を引数の Id
と直接紐づけています。
これにより、わざわざ $Id = $inputObject.Id
というようなコードを書く必要がなくなります。
[parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName, ParameterSetName = 'ById')]
[string]$Id
- HTTP response が 200 OK ではないときに、sleep して再度リクエストする
単なる idiom ではあるのですが、つい忘れちゃうのでこれも ChatGPT にやってもらいました。
当初は Invoke-AzRestMethod
だけがあったのですが、リトライ入れてー、ってお願いしたらこれです、すばらしい。
$sessionResponse = $null
do {
$sessionResponse = Invoke-AzRestMethod -Method GET -Uri $restResponse.Headers.Location.AbsoluteUri
if ($sessionResponse.StatusCode -ne 200) {
Write-Debug "$(Get-Date -Format "h:MM:ss tt") - Received status code $($sessionResponse.StatusCode). Retrying in 5 seconds..."
Start-Sleep -Seconds 5
}
} while ($sessionResponse.StatusCode -ne 200)
まとめ
実際にはこの cmdlet 自体が必要なわけではなく、これを使って、使っていない Bastion を定期的に削除するものを作りたいのでした。
まだ定期実行までは実装していないのですが、使ってなさそうな Azure Bastion を削除するという記事をこちらに書いています。
Discussion