Azure

Azure AutomationからAzure Virtual Machinesの起動/停止


やりたいこと


  • Azure AutomationからAzure Virtual Machinesの起動/停止をスケジュールで実行


流れ


  1. Azure Virtual Machinesの作成

  2. Automationアカウントの作成

  3. Runbookの実行


Azure Virtual Machinesの作成

こちらを参考に、ただ今回はWindowsのVMではなく、LinuxのVMを使用した(僕はVMの作成をしていないため、詳細は知らない)

Microsoft Azure入門 ~VM作成編~ | Qiita


Automationアカウントの作成

このドキュメントをやる

注意事項としては、サブスクリプションの所有者じゃないと実行の時のサービスプリンシパルにアクセス許可を付与できず、Runbook実行の時にコケること

Azure Automation アカウントを作成する | Microsoft Doc

「Azureクラシック実行アカウントの作成」に失敗している感じだけど、Runbookは実行できた

「新しいAzure実行アカウント(サービスプリンシパル)」が正常に作成されないと、Runbookは実行できなかった

image.png


Runbookの実行

ギャラリーからやりたいことに一致するRunbookをインポートする

Azure Automation 用の Runbook ギャラリーとモジュール ギャラリー | Microsoft Doc

これが参考になる

「入力と出力」の既定値を入力、「保存」、「公開」の流れを踏まないと「開始」、「スケジュール」ができないため、注意

AzureAutomationを利用して、仮想マシンを平日の日中だけ起動する。 - Qiita

こちらはログインの処理を参考にした

Azure Automation で VM を自動停止する | Japan Azure Technical Support Engineers' Blog

PowerShellの戻り値は独特なので、要注意

特にメソッド内でPrintDebugしてると、そのメソッドの戻り値が変わっちゃう(だいぶんハマった)

PowerShellにおける"戻り値"と"Return"について | しばたテックブログ


ShutdownVM

Param(

[parameter(mandatory=$true)]
[String]
$ResourceGroupName,

[parameter(mandatory=$true)]
[String]
$VmName,

[parameter(mandatory=$false)]
[Boolean]
$DryRun = $false
)

<#
Azureアカウントへログイン
#>

function LoginAzureRm {
try {
# 自動作成された接続資産(実行アカウント)を利用する
$AutomationConnectionName = "AzureRunAsConnection"
$Connection = Get-AutomationConnection -Name $AutomationConnectionName

Write-Output "# Logging in to Azure."
Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $Connection.TenantId `
-ApplicationId $Connection.ApplicationId `
-CertificateThumbprint $Connection.CertificateThumbprint
}
catch {
if (!$Connection) {
throw "Connection $AutomationConnectionName not found."
} else {
Write-Error -Message $_.Exception
throw $_.Exception
}
}
}

<#
Virtual Machineがすでに起動している場合はTrue / そうでない場合はFalse
#>

function IsVmAllocated {
param([String]$ResourceGroupName, [String]$VmName)

$Vm = Get-AzureRmVM -Name $VmName -ResourceGroupName $ResourceGroupName -Status -ErrorAction Continue

if (!$Vm) {
Throw "Virtual machine '${VmName}' not found in Resource Group '${ResourceGroupName}'."
}

$Status = $Vm.Statuses | ?{ $_.Code -eq "PowerState/deallocated" }

$Status -eq $null
}

<#
Virtual machineを停止
失敗しても3回は再度停止処理が実行される
#>

function StopVm {
param([String]$ResourceGroupName, [String]$VmName)

$TryCount = 0

Do {
$TryCount++

Write-Output "Trying to stop Virtual Machine '${VmName}', Count=${TryCount}"

$Result = Stop-AzureRmVM -ResourceGroupName $ResourceGroupName -Name $VmName -Force -ErrorAction Continue

if ($Result.IsSuccessStatusCode) {
Write-Output "Virtual Machine '${VmName}' stoped."
}
else {
Write-Output "Virtual Machine '${VmName}' cannot stop."
Start-Sleep -Seconds 15
}
} while($TryCount -le 3 -and !$Result.IsSuccessStatusCode)
}

############
# Main
############
LoginAzureRm -CredentialAssetName $CredentialAssetName -TenantIdAssetName $TenantIdAssetName

if (IsVmAllocated -ResourceGroupName $ResourceGroupName -VmName $VmName) {
if ($DryRun) {
Write-Output "DryRun mode, nothing to do (not to stop)."
} else {
StopVm -ResourceGroupName $ResourceGroupName -VmName $VmName
}
}
else {
Write-Output "Virtual machine '${VmName}' is not running."
}


起動、停止とほぼ同じ


StartUpVM

Param(

[parameter(mandatory=$true)]
[String]
$ResourceGroupName,

[parameter(mandatory=$true)]
[String]
$VmName,

[parameter(mandatory=$false)]
[Boolean]
$DryRun = $false
)

<#
Azureアカウントへログイン
#>

function LoginAzureRm {
try {
# 自動作成された接続資産(実行アカウント)を利用する
$AutomationConnectionName = "AzureRunAsConnection"
$Connection = Get-AutomationConnection -Name $AutomationConnectionName

Write-Output "# Logging in to Azure."
Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $Connection.TenantId `
-ApplicationId $Connection.ApplicationId `
-CertificateThumbprint $Connection.CertificateThumbprint
}
catch {
if (!$Connection) {
throw "Connection $AutomationConnectionName not found."
} else {
Write-Error -Message $_.Exception
throw $_.Exception
}
}
}

<#
Virtual Machineがすでに起動している場合はTrue / そうでない場合はFalse
#>

function IsVmAllocated {
param([String]$ResourceGroupName, [String]$VmName)

$Vm = Get-AzureRmVM -Name $VmName -ResourceGroupName $ResourceGroupName -Status -ErrorAction Continue

if (!$Vm) {
Throw "Virtual machine '${VmName}' not found in Resource Group '${ResourceGroupName}'."
}

$Status = $Vm.Statuses | ?{ $_.Code -eq "PowerState/deallocated" }

$Status -eq $null
}

<#
Virtual machineを停止
失敗しても3回は再度停止処理が実行される
#>

function StopVm {
param([String]$ResourceGroupName, [String]$VmName)

$TryCount = 0

Do {
$TryCount++

Write-Output "Trying to stop Virtual Machine '${VmName}', Count=${TryCount}"

$Result = Stop-AzureRmVM -ResourceGroupName $ResourceGroupName -Name $VmName -Force -ErrorAction Continue

if ($Result.IsSuccessStatusCode) {
Write-Output "Virtual Machine '${VmName}' stoped."
}
else {
Write-Output "Virtual Machine '${VmName}' cannot stop."
Start-Sleep -Seconds 15
}
} while($TryCount -le 3 -and !$Result.IsSuccessStatusCode)
}

############
# Main
############
LoginAzureRm -CredentialAssetName $CredentialAssetName -TenantIdAssetName $TenantIdAssetName

if (IsVmAllocated -ResourceGroupName $ResourceGroupName -VmName $VmName) {
if ($DryRun) {
Write-Output "DryRun mode, nothing to do (not to stop)."
} else {
StopVm -ResourceGroupName $ResourceGroupName -VmName $VmName
}
}
else {
Write-Output "Virtual machine '${VmName}' is not running."
}