インフラエンジニアの隙間時間 Infrastructure Engineer's Spare Time
今回の検証を行った背景
プロジェクトで、Azure Virtual Desktop(以下AVD)のセッションホスト(以下仮想マシン)を定期的に再起動させる必要があった。
AVDには、スケーリングプランという、仮想マシンを定期的に起動/シャットダウンする仕組みがあるが、再起動は存在していない。
Azure Automationを使用すれば、仮想マシンを定期的に再起動できるのではないかと考えた。
ただし、Azure Automationには、仮想マシンを定期的に起動/シャットダウンするテンプレートはあるが、再起動するテンプレートは存在していない。
なお、Azure AutomationのRunbook作成時には、AutomationがAzureにサインインするための仕組み(サービスプリンシパルもしくはユーザーIDなど)が必要だった。
→ Azure Automationから、サービスプリンシパルを使用して仮想マシンを定期的に再起動するRunbookを作成すれば解決できるのではとの結論に至った。
本格的に検証を行う前の基盤知識
Azure Automationとは?
- Azure環境とAzure以外の環境にまたがるクラウドベースの自動化及び構成サービス。
- PowerShell/Pythonスクリプト(Runbook)を定期的に実行することで、タスクを自動化できる。
- Azure上のリソースだけではなく、オンプレミスや他クラウドの環境も管理できる。
- 数多くのテンプレートが用意されているので、それを活用してスクリプトを作成することも、自身の固有スクリプトを作成することも可能。
サービスプリンシパルとは?
- アプリケーションやサービスがAzureリソースにアクセスするための「ID」。
- ユーザーが自身のIDとパスワードを使用して様々なサービスにアクセスするのと同じく、アプリケーションやサービスはサービスプリンシパルを使用してAzureリソースにアクセスする。
- 今回の場合は、AutomationというサービスがAzureリソース(仮想マシン)にアクセスするために、Automationのサービスプリンシパルを作成。
準備するもの
- Azure アカウント
- アカウントは、サービスプリンシパルを作成するために、Application.ReadWrite.All 権限を持つ必要がある。
- なお、サブスクリプションから必要なRBACロールを割り当てるために、RoleAssignment.WriteまたはMicrosoft.Authorization/roleAssignments/write権限を持つ必要がある。
手順の流れ
1)サービスプリンシパルの作成
2)サブスクリプションからサービスプリンシパルへの仮想マシン操作権限付与
3)Automation アカウント作成
4)RunBook作成
5)Automation アカウントの資格情報及び変数設定
6)スケジュール作成
7)公開
8)スケジュールとのリンク
9)動作確認
1. サービスプリンシパルの作成
1) Azure Portalにサインインする。 2) Microsoft Entra ID → 管理 → アプリの登録 → 新規登録にアクセスする。 3) アプリケーションの登録画面から、以下の項目を入力して登録をクリックする。 - 名前 - どのようなアプリケーションかわかりやすい名前を入力する。 - サポートされているアカウントの種類 - この組織ディレクトリのみに含まれるアカウントを選択する。

4) 作成したアプリケーションの画面が表示される。今後Azure Automationで使用するために、以下の項目をメモする。
アプリケーション (クライアント) ID
- AutomationでサービスプリンシパルがAzureにサインインする時にIDとして使用される。
ディレクトリ (テナント) ID
5) 管理 → 証明書とシークレットに移動する。
6) 新しいクライアント シークレットをクリックし、以下の項目を入力して追加をクリックする。
説明
- どのようなシークレットかわかりやすい説明を入力する。
有効期限
- クライアント シークレットの有効期限を設定する。
※ クライアント シークレットの有効期限が切れると、サービスプリンシパルがAzureにアクセスできなくなるので、再度登録してアプリケーションに登録したクライアントシークレットを修正しなければならない。クライアント シークレットを自動に更新するような仕組みがあればベストであるが、今回は割愛する。

7) 作成されたクライアント シークレットが表示されるので、そこから以下の項目をメモする。
値
- クライアント シークレットの値は、AutomationでサービスプリンシパルがAzureにサインインする時にパスワードとして使用される。
※ 値は、クライアント シークレットを作成した直後のページでしか表示されない。そこから他のページに移動すると二度と表示されなくなるのでページから離れる前に必ずメモしておく必要がある。 もし、誤って他のページに移動してしまった場合は、元のクライアント シークレットを削除して再度作成する必要がある。
2. サブスクリプションからサービスプリンシパルへの仮想マシン操作権限付与
サービスプリンシパルは、作成した直後は何も権限を持っていないので、当然ながら仮想マシンを操作する権限も持っていない。以下の手順を実施することにより、サービスプリンシパルが仮想マシンを操作できる権限が割り当てられる。
1) Azure Portal → サブスクリプション → Azure Automationを使用するサブスクリプションにアクセス
2) 該当サブスクリプション画面から以下をメモする。
- サブスクリプション ID
3) アクセス制御 (IAM)にアクセスする。
4) アクセス制御 (IAM)画面から追加 → ロールの割り当ての追加をクリックする。
5) ロールの割り当ての追加 - ロール画面から、仮想マシン共同作成者ロールをクリックして次へをクリックする。
6) ロールの割り当ての追加 - メンバー画面から、以下を選択して次へをクリックする。
アクセスの割り当て先
- ユーザー、グループ、またはサービス プリンシパルを選択する。
メンバー
- サービスプリンシパルの作成手順で作成したサービス プリンシパルを選択する。

7) ロールの割り当ての追加 - レビューと割り当て画面からレビューと割り当てをクリックする。
8) アクセス制御 (IAM) → ロールの割り当てにアクセスし、サービス プリンシパルに権限が割り当てられていることを確認する。
3. Automation アカウント作成
1) Azure Portal → Automation アカウントにアクセスし、作成をクリックする。
2) Automation アカウントの作成 - 基本から以下の項目を入力して次へをクリックする。
リソース グループ
- Automation アカウントを作成するリソース グループを選択する。
Automation アカウント名
- Automation アカウント名を入力する。
地域
- Automation アカウントを作成するリージョンを選択する。
3) Automation アカウントの作成 - マネージドIDから以下の項目を入力して次へをクリックする。
マネージドID
- システム割り当てにチェックを入れる(デフォルト設定)
※ ユーザー割り当てにチェックを入れる場合、ユーザー割り当て IDの作成が必要になる。
4) Automation アカウントの作成 - ネットワークから以下の項目を入力して次へをクリックする。
接続の構成
- パブリック アクセスを選択する。
※ プライベート アクセスを選択する場合は、プライベート エンドポイントの作成が必要になる。
5) Automation アカウントの作成 - 確認および作成から設定した内容を確認し、問題なければ作成をクリックすると、Automation アカウントが作成される。

4. Runbook作成
Runbookは、Azureが提供している自動化スクリプトの実行環境で、ここにスクリプトを入力する形となる。実際には、直観的にワークフロー形式でRunbookを作成できるグラフィカルPowerShellワークフローなど、いくつかの作成の仕方があるが、今回は伝統的なPowerShellスクリプトの形でRunbookを作成した。
1) Azure Portal → Automation アカウント → 3.で作成したAutomation アカウント → プロセス オートメーション → Runbookにアクセスする。
2) Runbook の作成をクリックする。
3) Runbook の作成 - 基本から、以下の項目を入力して次へをクリックする。
名前
- 作成するRunbook名を入力する。
Runbook の種類
- PowerShellを選択する。
ランタイム バージョン
- 7.2を選択する。
説明
- このRunbookの用途をわかりやすく記載する。省略可能。

4) Runbook の作成 - タグは必要であれば作成し、次へをクリックする。
5) Runbook の作成 - 確認および作成から設定した内容を確認し、問題なければ作成をクリックすると、Runbookが作成される。
6) 作成したRunbookの編集画面が表示される。今回は、以下のスクリプトを入力した。
param(
[Parameter(Mandatory=$true)]
[string]$vmNames,
[Parameter(Mandatory=$true)]
[string]$TargetResourceGroup
)
$credentialAssetName = Get-AutomationVariable -Name "AzureCredentialAssetName"
$TargetTenantID = Get-AutomationVariable -Name "TargetTenantID"
$TargetSubscriptionId = Get-AutomationVariable -Name "TargetSubscriptionID"
$cred = Get-AutomationPSCredential -Name $credentialAssetName
Connect-AzAccount -ServicePrincipal -Credential $cred -TenantId $TargetTenantID
Set-AzContext -SubscriptionId $TargetSubscriptionId
$vmNameArray = @()
if ($vmNames -eq "All") {
Write-Output "Retrieving all VMs in resource group: $TargetResourceGroup"
$vms = Get-AzVM -ResourceGroupName $TargetResourceGroup
$vmNameArray = $vms.Name
} else {
$vmNameArray = $vmNames -split ','
}
foreach ($vmname in $vmNameArray) {
$vmName = $vmName.Trim()
Write-Output "Restarting VM: $vmName in Resource Group: $TargetResourceGroup"
try {
Restart-AzVM -Name $vmName -ResourceGroupName $TargetResourceGroup
Write-Output "Successfully restarted $vmName"
} catch {
Write-Error "Failed to restart $vmName"
}
}
コードの説明
仮想マシンおよび仮想マシンが入っているリソースグループ名を変数として入力し、再起動をかける。複数の仮想マシン名を入力する場合は、コンマ(,)を使用して区分する。
仮想マシン名を「All」と入力された場合は、リソースグループ内の全ての仮想マシンを再起動する。
再起動をかけた際に、仮想マシンが問題なく再起動できたら成功のメッセージ、何らかの問題により再起動できなかった場合は失敗のメッセージを出力する。
RunbookがAzureにアクセスするための資格情報(サービスプリンシパル、テナントID、サブスクリプションIDなど)は、セキュリティの観点からAutomation アカウント内の共有リソースから呼び出して使用する。
7) スクリプトの入力が完了したら、保存をクリックしてRunbookを保存する。

5. Automation アカウントの資格情報及び変数設定
作成したRunbookがAzureにサインインするためにはサービスプリンシパルの資格情報を使用しなければならない。ただし、Runbookの中にハードコーディングされている状態で資格情報が入力されているのは、セキュリティ上非常に危険であり、スクリプトの変更時にも影響を及ぼすので、今回はサービスプリンシパルの資格情報をAzure Automationの共有リソースとして保存し、Runbookでは変数として記載して、Runbookが実行されるたびにAutomationの共有リソースから資格情報を呼び出して使用することでセキュリティを確保するようにした。
1) Azure Portal → Automation アカウント → 3.で作成したAutomation アカウント → 共有リソース → 資格情報にアクセスする。
2) 資格情報の追加をクリックし、以下を入力して作成をクリックする。
名前
- 資格情報の名前。どのような資格情報を使用するのかわかりやすい名前を記載する。後ほど別の手順で使用される。ここでは、「AutomationServicePrincipal」という名前を入力した。
ユーザー名
- 1.4)でメモしたアプリケーション (クライアントID)を入力する。
パスワード
- 1.7)でメモしたクライアント シークレットを入力する。
3) 共有リソース → 変数にアクセスする。
4) 変数の追加をクリックし、以下の変数を各々作成する。以下の変数は、スクリプトで使用する変数名をそのまま作成しなければならない。
(1)
変数名:AzureCredentialAssetName
タイプ:文字列
値:5.2)で作成した資格情報の名前。ここでは「AutomationServicePrincipal」を入力した。
暗号化:はい
(2)
変数名:TargetSubscriptionId
タイプ:文字列
値:2.2)でメモしたサブスクリプションIDを入力する。
暗号化:はい
(3)
変数名:TargetTenantId
タイプ:文字列
値:1.3)でメモしたテナントIDを入力する。
暗号化:はい

※ 上記の変数名まで作成が完了したら、Runbookのテストが可能になる。Runbookのテストがしたい場合は、作成したRunbook → 編集 → ポータルで編集 → テスト ウインドウにアクセスし、再起動をテストしたい仮想マシン名(すべての仮想マシンの場合はAllを入力)とリソースグループを入力してテストを実施する。問題ない場合はエラーメッセージ(赤いメッセージで表示される)などはなく、完了メッセージおよびログが表示される。

6. スケジュール作成
1) Azure Portal → Automation アカウント → 3.で作成したAutomation アカウント → 共有リソース → スケジュールにアクセスする。
2) スケジュールの追加をクリックし、以下の項目を入力して作成をクリックする。
名前
- スケジュール名を入力する。
開始時
- スケジュールをいつ開始するかを入力する。スケジュールを作成している時間から最低5分以降から入力できる。
タイム ゾーン
- スケジュールを実行するタイムゾーンを入力する。ここでは、Japan - Japan Timeを選択した。
繰り返し
- 単発性で実行するか、定期的に実行するかを設定する。ここでは、定期的、間隔は1時間、有効期限はなしを選択した。
3) 作成したスケジュールが表示されるので、指定した設定通りなっているか確認する。

7. 公開
1) Azure Portal → Automation アカウント → 3.で作成したAutomation アカウント → プロセス オートメーション → Runbook → 4.7で作成したRunbook → 編集 → ポータルで編集にアクセスする。
2) PowerShell Runbook の編集画面から、公開をクリックする。Runbook の発行ウインドウが表示されるので、はいをクリックするとRunbookが公開される。

8. スケジュールとのリンク
1) Azure Portal → Automation アカウント → 3.で作成したAutomation アカウント → プロセス オートメーション → Runbook → 4.7で作成したRunbook → リソース → スケジュールにアクセスする。
3) スケジュールの追加をクリックし、以下の項目を入力してOKをクリックする。
スケジュール
- 6.で作成したスケジュールを選択する。
パラメーターと実行設定
- 定期的に再起動を実行する仮想マシン名および仮想マシンが入っているリソースグループを入力する。

9. 動作確認
1) 最初にスケジュールを実行する日時になったら、Azure Portal → Automation アカウント → 3.で作成したAutomation アカウント → プロセス オートメーション → Runbook → 4.7で作成したRunbookにアクセスする。
2) 最近のジョブから、新しいジョブが作成されていることを確認する。ジョブの状態が、キューに挿入済みもしくは実行中の場合はそのまま待機し、完了になっている場合は完了をクリックする。
3) ジョブの状態が表示される。すべてのログをクリックする。
4) すべてのログから、再起動を指定した仮想マシンがすべて再起動に成功したというメッセージを確認する。再起動に失敗した仮想マシンがある場合は、仮想マシンのログを確認して対応する。

これですべての手順が完了する。
最後に
結構長くてややこしい手順ではあるが、検証は特に問題なく成功した。 サービスプリンシパルについて、前に軽く触ったことはあったが、いつもこれはそもそもどんなもので、どうやって使用すればいいんだろうという疑問が常に心の隅に存在していた。それが今回の検証を通じてなんとなく理解できた気がする。もちろん、もっとも簡単な使い方しかしていないので本当はもっといろんな使い方があるとは思うが、今後もっと深堀していこうかなと思う。
余談。
今回はサービスプリンシパルを利用することが中心だったので特に気にしてはいなかったが、RunbookからAzureのサインインに本当はマネージドIDも使用できるそう。Azure間のリソースであればマネージドIDのセキュリティ性がもっと高いので、時間があればそれについても触れてみたいと思う。 なお、今回使用したスクリプトの多くはCopilotから生成したコードであり、自身がいろいろいじった部分はあったが、昔みたいに一からスクリプトを作成しなくてもよくなり、生成型AIの威力を実感するきっかけとなった。うまく使えば、業務効率化に非常に役立つだろうと思った。 もちろんセキュリティはしっかり気にしないといけないけど、、
