0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Azure】Azure Automation を利用して VM を自動起動 / 停止する

Posted at

はじめに

利用しているデバイスの調子が悪く、再起動すれば直った!みたいなことは普段でもよくあります。

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 といった関連リソースが作成されます。

使いこなせばすごく便利なことが多いのだろうと思うのですが、シンプルに再起動(停止と起動)だけを実装したい人(私とか)には、いろんなリソースが作られるのはなんか嫌だな。と感じたので、勉強兼ねても独自でやってみることにしました。

構成図とコンポーネントの役割

azure-vm-auto-start-stop-template.drawio.png

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]

image.png

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
./main.bicep
//
// 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)
}]
./main.bicepparam
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}'

./modules/automationAccount.bicep
//
// 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
./modules/schedules.bicep
//
// 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
./modules/schedules.bicep
//
// 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
./modules/jobSchedules.bicep
//
// 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
./modules/roleAssignments.bicep
//
// 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'

デプロイ確認

image.png

以上でシステムの構築は終了です。

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!"

実際に動かしてみる

スケジュールは待つのが面倒なので、手動で実行してみます。(←)

停止

実行前

image.png

実行後

image.png

image.png

起動

実行前

image.png

実行後

image.png

image.png

大丈夫そうです。

さいごに

それぞれ利用シーンは異なると思いますので、デプロイした後に Automation の設定や Runbook の内容を変更して、カスタマイズするのがよいかと思います。

なんだかんだやっていると、Bicep の作成などで時間をとられましたが、やりたいことはできたので、よかったかなと思います。

最後まで見ていただきありがとうございます。誰かの役に立てば幸いです。

テンプレートの紹介

今回記事で紹介した内容は、テンプレートとして下記 GitHub で公開しています。
よければこちらもご確認ください。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?