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](https://qiita.com/kuroshibainu/items/d0b7e0cc9f2d66d0a052)

## Automationアカウントの作成

このドキュメントをやる
注意事項としては、サブスクリプションの所有者じゃないと[実行の時のサービスプリンシパルにアクセス許可を付与できず](https://docs.microsoft.com/ja-jp/azure/azure-resource-manager/resource-group-create-service-principal-portal#check-azure-subscription-permissions)、Runbook実行の時にコケること
[Azure Automation アカウントを作成する | Microsoft Doc](https://docs.microsoft.com/ja-jp/azure/automation/automation-quickstart-create-account)

「Azureクラシック実行アカウントの作成」に失敗している感じだけど、Runbookは実行できた
「新しいAzure実行アカウント（サービスプリンシパル）」が正常に作成されないと、Runbookは実行できなかった
![image.png](https://qiita-image-store.s3.amazonaws.com/0/123610/dbb1a127-be77-ef93-c51d-f6b827cba054.png)

## Runbookの実行

ギャラリーからやりたいことに一致するRunbookをインポートする
[Azure Automation 用の Runbook ギャラリーとモジュール ギャラリー | Microsoft Doc](https://docs.microsoft.com/ja-jp/azure/automation/automation-runbook-gallery)

これが参考になる
「入力と出力」の既定値を入力、「保存」、「公開」の流れを踏まないと「開始」、「スケジュール」ができないため、注意
[AzureAutomationを利用して、仮想マシンを平日の日中だけ起動する。 - Qiita](https://qiita.com/Vendetta1209/items/899bcd2fda98e9b70c14)

こちらはログインの処理を参考にした
[Azure Automation で VM を自動停止する | Japan Azure Technical Support Engineers' Blog](https://blogs.technet.microsoft.com/jpaztech/2017/09/01/azure-automation-で-vm-を自動停止する/)

PowerShellの戻り値は独特なので、要注意
特にメソッド内でPrintDebugしてると、そのメソッドの戻り値が変わっちゃう（だいぶんハマった）
[PowerShellにおける"戻り値"と"Return"について | しばたテックブログ](http://blog.shibata.tech/entry/2015/07/05/114903)

```powershell: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."
}
```

起動、停止とほぼ同じ

```powershell: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."
}
```