はじめに
利用しているデバイスの調子が悪く、再起動すれば直った!みたいなことは普段でもよくあります。
Azure の仮想マシン (VM) を利用していても同じようなことがあります。
ということで、定期的に再起動する処理を Azure Automation を利用して実装したいと思います。
環境
- macOS:
15.0.1
- PowerShell:
7.4.0
実装にあたって調べたこと
VM 標準機能で提供されていないの?
設定したスケジュールで VMを自動で停止する機能はあるようですが、自動で起動する設定はないようです。
Start/Stop VMs v2 で実装できる
公式では Azure Function を利用して自動停止と再起動を実装する方法が紹介されています。
※ Azure Automation を利用する Start/Stop VMs v1 は非推奨になり、現在新規では利用できないようです。
この方法だと複数のサブスクリプションにまたがる Azure 仮想マシンを開始または停止することができたり、色々と便利な機能があるようですが、Azure Function をはじめ、ストレージアカウントや Application Insights といった関連リソースが作成されます。
使いこなせばすごく便利なことが多いのだろうと思うのですが、シンプルに再起動(停止と起動)だけを実装したい人(私とか)には、いろんなリソースが作られるのはなんか嫌だな。と感じたので、勉強兼ねても独自でやってみることにしました。
構成図とコンポーネントの役割
No | リソース | 役割 |
---|---|---|
1 | Azure Automation Account | Runbookの実行を管理する基盤。機能の一部として、ジョブスケジュールを提供し、仮想マシンの起動・停止のプロセスを自動化する。 |
2 | Runbook | 仮想マシンの自動起動・停止のロジックを定義したスクリプトを実行する。スケジュールやトリガーに応じてAutomation Accountから実行される。 |
3 | System Assigned Identity | 適切な権限を付与し、仮想マシンを操作できるようにする |
4 | Virtual Machine | 自動起動・停止の対象となる仮想マシン。 |
※仮想マシンはここでは作成しません。
リソースの作成
注意
Azure サービスを利用するため、コストが発生する場合があります。
コストに関しては自己責任でお願いします。
前提
- Azure アカウントを取得済であること。
- 作業ユーザーに、Automation をはじめとしたリソースを作成するリソースグループスコープで
共同作成者
ロールが割り当てられていること。 - 作業ユーザーに、VM が存在するリソースグループスコープで
所有者
orユーザーアクセス管理者
ロールが割り当てられていること。 - ローカルPC に
Azure CLI
がインストールされていること。
カスタムロールの作成
VM の電源操作を行うにあたって、Automation Account で有効にする マネージドID に対して、適切な権限を割り当てる必要があります。
共同作成者などを利用すれば必要な操作はできますが、できるだけ最小権限でロールを割り当てるのが望ましいです。
そこで、カスタムロールを利用します。VM の電源操作に関するカスタムロール作成手順については下記記事で紹介されているので、参考にしながら作成してください。
カスタムロールを作成したら、ロールの名前と定義 ID を控えるようにしてください。
後続の作業で利用します。
ロール名と定義 ID の確認場所
[アクセス制御(IAM)] > [役割] > [<作成したカスタムロール>のビュー] > [JSON]
Bicep テンプレート
Automation Account、Runbook を Bicep を利用して作成します。
マネージドID は システム割り当てを利用し、ロール割り当ても Bicep で実施します。
Runbook の種類は、 PowerShell 7.2 にしました。
下記のディレクトリ構造になるように、Bicepファイルを作成してください。
.(root)
├── modules
│ ├── automationAccounts.bicep
│ ├── jobSchedules.bicep
│ ├── roleAssignments.bicep
│ ├── runbooks.bicep
│ └── schedules.bicep
├── main.bicep
└── main.bicepparam
//
// Bicep template that implements automatic VM start/stop using Azure Automation.
// Created by nownaka.
//
// --------------------------------------------------------------------------------
// Params
// --------------------------------------------------------------------------------
@description('Resource deployment region.')
param location string = resourceGroup().location
@description('Application name.')
param appName string
@description('Environment name.')
param environment string
param suffix string?
@description('Base name of the resource. Used if there is no specification for each resource name.')
param resourceBaseName string = join(split(join(concat([appName, environment], empty(suffix) ? [] : [suffix]), '-'), '-'), '-')
@description('Resource name of Azure Automation Account.')
param automationAccountName string = 'aa-${resourceBaseName}'
@description('Identity type of Azure Automation Account')
@allowed(['None', 'SystemAssigned','SystemAssigned, UserAssigned', 'UserAssigned'])
param identityType string
@description('Name ofAccount SKU.')
@allowed([
'Basic'
'Free'
])
param automationAccountSkuName string = 'Free'
type scheduleConfig = {
description: string?
expiryTime: string?
frequency: 'Day' | 'Hour' | 'Minute' | 'Month' | 'OneTime' | 'Week'
interval: int
@description('format: yyyy-MM-ddTHH:mm:ss+09:00')
startTime: string
timeZone: string?
}
@description('The confings for VM-Start Schedules.')
param scheduleConfig_start scheduleConfig
@description('The confings for VM-Stop Schedules.')
param scheduleConfig_stop scheduleConfig
@description('Role definition to assign.')
param roleDifinitions { name: string, id: string }[] = [
{
name: '仮装マシン電源オペレーター'
id: 'cfb29df2-1cb5-43e2-8414-e9eecc6572c3'
}
]
@description('Name of the resource group containing the VM')
param vmResourceGroupName string = resourceGroup().name
// --------------------------------------------------------------------------------
// Modules
// --------------------------------------------------------------------------------
/* Azure Automation */
// Account
@description('Azure Automation Account.')
module automationAccount './modules/automationAccounts.bicep' = {
name: 'Deploy-AutomationAccount'
params: {
automationAccountLocation: location
automationAccountName: automationAccountName
identityTyoe: identityType
skuName: automationAccountSkuName
}
}
// Runbooks
@description('The confings for runbooks.')
var _runbookConfigs = [
{
name: 'Start-AzureVMs'
runbookType: 'PowerShell72'
publishContentLink: {
uri: uri('https://raw.githubusercontent.com/nownaka/azure-vms-auto-start-stop-template/refs/heads/main/runbook/', 'Start-AzureVMs.ps1')
}
}
{
name: 'Stop-AzureVMs'
runbookType: 'PowerShell72'
publishContentLink: {
uri: uri('https://raw.githubusercontent.com/nownaka/azure-vms-auto-start-stop-template/refs/heads/main/runbook/', 'Stop-AzureVMs.ps1')
}
}
]
@description('Azure Automation Runbook.')
module runbooks './modules/runbooks.bicep' = [for config in _runbookConfigs: {
name: 'Deploy-Runbook-${config.name}'
params: {
automationAccountName: automationAccount.outputs.name
runbookLocation: location
runbookName: config.name
runbookType: config.runbookType
publishContentLink: config.publishContentLink
}
}]
// Schedules
@description('The confings for schedules.')
var _scheduleConfigs = [
{
name: 'vm-start'
description: empty(scheduleConfig_start.description) ? '仮想マシンを起動するスケジュール' : scheduleConfig_start.description
expiryTime: scheduleConfig_start.expiryTime
frequency: scheduleConfig_start.frequency
interval: scheduleConfig_start.interval
startTime: scheduleConfig_start.startTime
timeZone: empty(scheduleConfig_start.timeZone) ? 'Asia/Tokyo' : scheduleConfig_start.timeZone
}
{
name: 'vm-stop'
description: empty(scheduleConfig_stop.description) ? '仮想マシンを停止するスケジュール' : scheduleConfig_stop.description
expiryTime: scheduleConfig_stop.expiryTime
frequency: scheduleConfig_stop.frequency
interval: scheduleConfig_stop.interval
startTime: scheduleConfig_stop.startTime
timeZone: empty(scheduleConfig_stop.timeZone) ? 'Asia/Tokyo' : scheduleConfig_start.timeZone
}
]
@description('Azure Automation Schedule.')
module schedules './modules/schedules.bicep' = [for config in _scheduleConfigs: {
name: 'Deploy-schedule-${config.name}'
params: {
automationAccountName: automationAccount.outputs.name
scheduleDescription: config.description
expiryTime: config.expiryTime
frequency: config.frequency
interval: config.interval
scheduleName: config.name
startTime: config.startTime
timeZone: config.timeZone
}
}]
// jobSchedules
@description('The confings for jobSchedules.')
var _jobScheduleConfigs = [
{
runbookName: _runbookConfigs[0].name
scheduleName: _scheduleConfigs[0].name
parameters: {}
}
{
runbookName: _runbookConfigs[1].name
scheduleName: _scheduleConfigs[1].name
parameters: {}
}
]
@description('jobSchedules.')
module jobSchedules './modules/jobSchedules.bicep' = [for config in _jobScheduleConfigs: {
name: 'Deploy-jobSchedule-${config.runbookName}'
params: {
automationAccountName: automationAccount.outputs.name
runbookName: config.runbookName
scheduleName: config.scheduleName
parameters: config.parameters
}
dependsOn: [
runbooks
schedules
]
}]
/* Role Assingnment */
@description('Role Assingnment.')
module roleAssignment_resourceGroup './modules//roleAssignments.bicep' = [ for (roleDifinition , index) in roleDifinitions: if( index <= 1){
name: 'RoleAssignement-${roleDifinition.name}'
params: {
principalId: automationAccount.outputs.identityId
roleDefinitionId: roleDifinition.id
}
scope: resourceGroup(vmResourceGroupName)
}]
using './main.bicep'
param location = '{The region in which you want to deploy the resource}'
param appName = 'vmautostartstop'
param environment = 'dev' // prd, stg, test , etc...
param suffix = null
// param automationAccountName = '{automation account name}'
param identityType = 'SystemAssigned'
param automationAccountSkuName = 'Free'
param scheduleConfig_start = {
frequency: 'Day'
interval: 1
startTime: '{The time you want to start the VM according to the schedule}' // exapmple format: yyyy-MM-ddTHH:mm:ss+09:00
}
param scheduleConfig_stop = {
frequency: 'Day'
interval: 1
startTime: '{The time you want to stopt the VM according to the schedule}' // exapmple format: yyyy-MM-ddTHH:mm:ss+09:00
}
param roleDifinitions = [
{
name: '{your role difinition name}'
id: '{your role difinition id}'
}
]
// param vmResourceGroupName = '{your resource group name where the target VM exists}'
//
// Reference: https://learn.microsoft.com/ja-jp/azure/templates/microsoft.automation/automationaccounts?pivots=deployment-language-bicep
//
// --------------------------------------------------------------------------------
// Params
// --------------------------------------------------------------------------------
@description('Resource name of Azure Automation Account Name.')
param automationAccountName string
@description('Resource deployment region.')
param automationAccountLocation string
@description('Resource tags.')
param tags object = {}
@description('Identity type.')
@allowed(['None', 'SystemAssigned','SystemAssigned, UserAssigned', 'UserAssigned'])
param identityTyoe string?
@description('Sets the identity property for automation account')
param identity { type: 'None' | 'SystemAssigned' | 'SystemAssigned, UserAssigned' | 'UserAssigned' | null, userAssignedIdentities: object? } = {
type: identityTyoe
}
@description('Indicates whether requests using non-AAD authentication are blocked.')
param disableLocalAuth bool = false
@description('Indicates whether traffic on the non-ARM endpoint (Webhook/Agent) is allowed from the public internet')
param publicNetworkAccess bool = true
@description('The encryption properties for the automation account.')
param encryption { identity: object, keySource: 'Microsoft.Automation' | 'Microsoft.Keyvault', keyVaultProperties: object}?
@description('Name ofAccount SKU.')
@allowed([
'Basic'
'Free'
])
param skuName string = 'Free'
// --------------------------------------------------------------------------------
// Resources
// --------------------------------------------------------------------------------
@description('Azure Automation Account.')
resource automationAccount 'Microsoft.Automation/automationAccounts@2023-11-01' = {
name: automationAccountName
location: automationAccountLocation
tags: tags
identity: identity
properties: {
disableLocalAuth: disableLocalAuth
encryption: encryption
publicNetworkAccess: publicNetworkAccess
sku: {
name: skuName
}
}
}
// --------------------------------------------------------------------------------
// Outputs
// --------------------------------------------------------------------------------
@description('Resource Id.')
output resourceId string = automationAccount.id
@description('Resource name')
output name string = automationAccount.name
@description('Identity id of SystemAssigned')
output identityId string? = automationAccount.identity.principalId
//
// Reference: https://learn.microsoft.com/ja-jp/azure/templates/microsoft.automation/automationaccounts/runbooks?pivots=deployment-language-bicep
//
// --------------------------------------------------------------------------------
// Params
// --------------------------------------------------------------------------------
@description('Resource name of parent Azure Automation Account Name.')
param automationAccountName string
@description('Runbook name.')
param runbookName string
@description('Resource deployment region.')
param runbookLocation string
@description('Resource tags.')
param tags object = {}
@description('The description of the runbook.')
param runbookDescription string?
@description('The type of the runbook.')
@allowed([
'Graph'
'GraphPowerShell'
'GraphPowerShellWorkflow'
'PowerShell'
'PowerShell72'
'PowerShellWorkflow'
'Python2'
'Python3'
'Script'
])
param runbookType string
@description('Verbose log option.')
param logVerbose bool = false
@description('Progress log option.')
param logProgress bool = false
@description('The activity-level tracing options of the runbook.')
param logActivityTrace int = 0
@description('The published runbook content link.')
param publishContentLink object?
// --------------------------------------------------------------------------------
// Resources
// --------------------------------------------------------------------------------
@description('Azure Automation Runbook.')
resource runbook 'Microsoft.Automation/automationAccounts/runbooks@2023-11-01' = {
name: '${automationAccountName}/${runbookName}'
location: runbookLocation
tags: tags
properties: {
description: runbookDescription
runbookType: runbookType
logVerbose: logVerbose
logProgress: logProgress
logActivityTrace: logActivityTrace
publishContentLink: publishContentLink
}
}
// --------------------------------------------------------------------------------
// Outputs
// --------------------------------------------------------------------------------
@description('Resource Id.')
output resourceId string = runbook.id
@description('Resource name')
output name string = runbook.name
//
// Reference: https://learn.microsoft.com/ja-jp/azure/templates/microsoft.automation/automationaccounts/schedules?pivots=deployment-language-bicep
//
// --------------------------------------------------------------------------------
// Params
// --------------------------------------------------------------------------------
@description('Resource name of Azure Automation Account Name.')
param automationAccountName string
@description('Name of the schedule to be set')
param scheduleName string
@description('the AdvancedSchedule.')
param advancedSchedule object?
@description('The description of the schedule.')
param scheduleDescription string?
@description('The end time of the schedule.')
param expiryTime string?
@description('The frequency of the schedule.')
@allowed([
'Day'
'Hour'
'Minute'
'Month'
'OneTime'
'Week'
])
param frequency string
@description('The interval of the schedule. Example: yyyy-MM-ddTHH:mm:ss+09:00')
param interval int
@description('The start time of the schedule. Example: yyyy-MM-ddTHH:mm:ss+09:00')
param startTime string
@description(' The time zone of the schedule.')
param timeZone string = 'Asia/Tokyo'
// --------------------------------------------------------------------------------
// Resources
// --------------------------------------------------------------------------------
@description('Azure Automation Schedule.')
resource schedule 'Microsoft.Automation/automationAccounts/schedules@2023-11-01' = {
name: '${automationAccountName}/${scheduleName}'
properties: {
advancedSchedule: advancedSchedule
description: scheduleDescription
expiryTime: expiryTime
frequency: frequency
interval: any(interval)
startTime: startTime
timeZone: timeZone
}
}
// --------------------------------------------------------------------------------
// Outputs
// --------------------------------------------------------------------------------
@description('Resource Id.')
output resourceId string = schedule.id
@description('Resource name')
output name string = schedule.name
//
// Reference: https://learn.microsoft.com/ja-jp/azure/templates/microsoft.automation/automationaccounts/jobschedules?pivots=deployment-language-bicep
//
// --------------------------------------------------------------------------------
// Params
// --------------------------------------------------------------------------------
@description('Resource name of Azure Automation Account Name.')
param automationAccountName string
@description('Name of the runbook to which the schedule is associated.')
param runbookName string
@description('Name of the schedule to be linked.')
param scheduleName string
@description('Parameters required for job execution.')
param parameters object = {}
@description(' the hybrid worker group that the scheduled job should run on.')
param runOn string?
// --------------------------------------------------------------------------------
// Variables
// --------------------------------------------------------------------------------
@description('The resource name')
var _jobScheduleName = guid(automationAccountName, runbookName, scheduleName, subscription().subscriptionId, resourceGroup().id)
// --------------------------------------------------------------------------------
// Resources
// --------------------------------------------------------------------------------
@description('Azure Automation JobSchedule.')
resource jobSchedules 'Microsoft.Automation/automationAccounts/jobSchedules@2023-11-01' = {
name: '${automationAccountName}/${_jobScheduleName}'
properties: {
parameters: parameters
runbook: {
name: runbookName
}
runOn: runOn
schedule: {
name: scheduleName
}
}
}
// --------------------------------------------------------------------------------
// Outputs
// --------------------------------------------------------------------------------
@description('Resource Id.')
output resourceId string = jobSchedules.id
@description('Resource name')
output name string = jobSchedules.name
//
// Reference: https://learn.microsoft.com/ja-jp/azure/templates/microsoft.authorization/roleassignments?pivots=deployment-language-bicep
//
// --------------------------------------------------------------------------------
// Params
// --------------------------------------------------------------------------------
@description('Specifies the role definition ID used in the role assignment.')
param roleDefinitionId string
@description('Specifies the principal ID assigned to the role.')
param principalId string
// --------------------------------------------------------------------------------
// Variables
// --------------------------------------------------------------------------------
@description('The resource name')
var roleAssignmentName = guid(principalId, roleDefinitionId, resourceGroup().id)
// --------------------------------------------------------------------------------
// Resources
// --------------------------------------------------------------------------------
@description('Role Assignments.')
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: roleAssignmentName
properties: {
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId)
principalId: principalId
}
}
// --------------------------------------------------------------------------------
// Outputs
// --------------------------------------------------------------------------------
@description('Resource name of Role Assignments.')
output name string = roleAssignment.name
@description('Resource Group name of Role Assignments.')
output resourceGroupName string = resourceGroup().name
@description('Resource Id of Role Assignments.')
output resourceId string = roleAssignment.id
パラメータの設定
リソース名やスケジュール設定のラメータは main.bicepparam
に入力してください。
下記の例だと、2024/11/17 00:00:00
に起動処理が、2024/11/17 23:50:00
に停止処理が動くように設定できます。
また、ロール定義についてはPortal などから取得して入力してください。
(今回は、カスタムロールを設定しています。)
using './main.bicep'
// param location = '{The region in which you want to deploy the resource}'
param appName = 'vmautostartstop'
param environment = 'idea'
param suffix = '001'
// param automationAccountName = '{automation account name}'
param identityType = 'SystemAssigned'
param automationAccountSkuName = 'Free'
param scheduleConfig_start = {
description: null
expiryTime: null
frequency: 'Day'
interval: 1
startTime: '2024-11-17T00:00:00+09:00'
timeZone: null
}
param scheduleConfig_stop = {
description: null
expiryTime: null
frequency: 'Day'
interval: 1
startTime: '2024-11-17T23:50:00+09:00'
timeZone: null
}
param roleDifinitions = [
{
name: '仮装マシン電源オペレーター'
id: 'cfb29df2-1cb5-43e2-8414-e9eecc6572c3'
}
]
// param vmResourceGroupName = '{your resource group name where the target VM exists}'
リソースデプロイ
下記コマンドを実行してリソースをデプロイします。
コマンド実行は各種ファイルが存在するルートディレクトリで行ってください。
変数の設定
# 変数の設定
resourceGroupName={your resource group name} # デプロイするリソースグループの名前
Azure ログイン
# ログイン
az login
デプロイ
# デプロイ
az deployment group create \
--name 'Deploy_VMAutoStartStop' \
--resource-group $resourceGroupName \
--template-file './main.bicep' \
--parameters './main.bicepparam'
デプロイ確認
以上でシステムの構築は終了です。
Runbook のコードについて
デフォルトでは、私の GitHub リポジトリを参照して自動でインポートされるようになっています。
# ============================================================================
# VMを停止するスクリプト
# Created by nownaka.
# ============================================================================
Write-Output "Start!"
## 認証 ##
# https://learn.microsoft.com/ja-jp/azure/automation/enable-managed-identity-for-automation#authenticate-access-with-system-assigned-managed-identity
# Ensures you do not inherit an AzContext in your runbook
Disable-AzContextAutosave -Scope Process
# Connect to Azure with system-assigned managed identity
$AzureContext = (Connect-AzAccount -Identity).context
# Set and store context
$AzureContext = Set-AzContext -SubscriptionName $AzureContext.Subscription -DefaultProfile $AzureContext
## VM 操作 ##
# VM 一覧を取得
Write-Output "Get VM List..."
$vmList = Get-AzVM | Select-Object ResourceGroupName, Id, VmId, Name
Write-Output $vmList
# VM の電源ステータスを取得
# https://learn.microsoft.com/ja-jp/azure/virtual-machines/windows/tutorial-manage-vm#vm-power-states
# Starting | 起動中
# Running | 実行中
# Stopping | 停止中
# Stopped | 停止済(コスト発生)
# Deallocating | 割り当て解除中
# Deallocated | 割り当て解除済み(コスト発生なし)
# - | 電源状態は不明
Write-Output "Get VM Power Status..."
$vmPowerStatusList = @()
foreach($vm in $vmList) {
$vmPowerStatusList += Get-AzVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Status |
Select-Object ResourceGroupName, Name, @{ n = "Status"; e = { $_.Statuses[1].Code} }
}
Write-Output $vmPowerStatusList
# VM を起動
# https://learn.microsoft.com/ja-jp/powershell/module/az.compute/start-azvm?view=azps-12.4.0
Write-Output "Start VMs..."
$statusList = @("*Stopped","*Deallocating","*Deallocated")
foreach($vm in $vmPowerStatusList) {
foreach($status in $statusList) {
$flag = $vm.Status -like $status
}
if($flag) {
Write-Output "name: $($vm.Name)"
Start-AzVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name
}
}
Write-Output "Finish!"
# ============================================================================
# VMを停止するスクリプト
# Created by nownaka.
# ============================================================================
Write-Output "Start!"
## 認証 ##
# https://learn.microsoft.com/ja-jp/azure/automation/enable-managed-identity-for-automation#authenticate-access-with-system-assigned-managed-identity
# Ensures you do not inherit an AzContext in your runbook
Disable-AzContextAutosave -Scope Process
# Connect to Azure with system-assigned managed identity
$AzureContext = (Connect-AzAccount -Identity).context
# Set and store context
$AzureContext = Set-AzContext -SubscriptionName $AzureContext.Subscription -DefaultProfile $AzureContext
## VM 操作 ##
# VM 一覧を取得
Write-Output "Get VM List..."
$vmList = Get-AzVM | Select-Object ResourceGroupName, Id, VmId, Name
Write-Output $vmList
# VM の電源ステータスを取得
# https://learn.microsoft.com/ja-jp/azure/virtual-machines/windows/tutorial-manage-vm#vm-power-states
# Starting | 起動中
# Running | 実行中
# Stopping | 停止中
# Stopped | 停止済(コスト発生)
# Deallocating | 割り当て解除中
# Deallocated | 割り当て解除済み(コスト発生なし)
# - | 電源状態は不明
Write-Output "Get VM Power Status..."
$vmPowerStatusList = @()
foreach($vm in $vmList) {
$vmPowerStatusList += Get-AzVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Status |
Select-Object ResourceGroupName, Name, @{ n = "Status"; e = { $_.Statuses[1].Code} }
}
Write-Output $vmPowerStatusList
# VM を停止
# https://learn.microsoft.com/ja-jp/powershell/module/az.compute/stop-azvm?view=azps-12.4.0
Write-Output "Stop VMs..."
$statusList = @("*Starting","*Running")
foreach($vm in $vmPowerStatusList) {
foreach($status in $statusList) {
$flag = $vm.Status -like $status
}
if($flag) {
Write-Output "name: $($vm.Name)"
Stop-AzVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Force
}
}
Write-Output "Finish!"
実際に動かしてみる
スケジュールは待つのが面倒なので、手動で実行してみます。(←)
停止
実行前
実行後
起動
実行前
実行後
大丈夫そうです。
さいごに
それぞれ利用シーンは異なると思いますので、デプロイした後に Automation の設定や Runbook の内容を変更して、カスタマイズするのがよいかと思います。
なんだかんだやっていると、Bicep の作成などで時間をとられましたが、やりたいことはできたので、よかったかなと思います。
最後まで見ていただきありがとうございます。誰かの役に立てば幸いです。
テンプレートの紹介
今回記事で紹介した内容は、テンプレートとして下記 GitHub で公開しています。
よければこちらもご確認ください。
参考