Azure Automation:祝日判定をして仮想マシンの自動起動/停止をする

こんにちはますのです。
タスクスケジューラやバッチ処理などの祝日判定は永遠の課題と思っていたインフラエンジニアでございます。

今回はAzur Automationで、「祝日はAzureVMを起動させない」処理が出来ないかと調べて実装した内容をメモしていきます。スクリプトはコピペするだけなのでそこまで難易度は高くない印象です。ほとんど指示通りにやって出来ました。

また、他サイトのままで実装するとバグありです。そこも改善済なのでご安心を。

Name
バグの内容は、日付が「1~9」などの一桁の場合に判定されない点でした。
PowerShellを見て、一部変更するだけで「2020/11/3」等の一桁の日付でも判定されるように改善出来たので一安心です。

祝日判定を実装する処理の流れ

ざっくりと以下の流れで処理されるようになります。

  1. 内閣府ホームページからダウンロードした「syukujitsu.csv」をインポートする
  2. 年末年始休暇等の別途決まっているお休みを追記する
  3. 今年と来年の2年分の祝日をcsvから抽出してAzureの変数に登録
  4. 自動起動Automation:祝日だった場合は実行しない
  5. 自動停止Automation:毎日停止処理をする(祝日判定はしない)

①~③の処理は年に1回、④~⑤の処理は平日実行という流れとなります。

Azure Automationアカウントの作成

  • Azureポータルへログイン>「Automation アカウント」をクリック
  • 「追加」をクリック
  • 自身の環境に合わせて設定>「作成」ボタンをクリック
    ※AzureVMを自動起動、停止するために必要なアカウントを作成する必要があるため、「Azure実行アカウントの作成:はい」を選択

Automationアカウントに祝日判定スクリプトを実装する

では実際に祝日判定スクリプトを作成していきましょう。
先ほど作成したAutomationアカウントのリソースを開きます。
手順としては「モジュールの準備」、「Rubookの作成」、「Runbookに処理を登録」、「スケジュール設定」の4ステップになります。

ステップ1:モジュールをAutomatinoアカウントにインポートする

まずはモジュール「Az.Accounts」「Az.Automation」「Az.Compute」の3つをインポートします。

  • 左カラム「共有リソース:モジュールギャラリー」をクリック
  • 検索バーに「Az.Accounts」を入力し検索>表示された「Az.Accounts」をクリック

  • 「インポート」ボタンをクリック>インポートが開始されるため「OK」をクリック
  • 同様にモジュールギャラリーより「Az.Automation」「Az.Compute」をインポート

以上でモジュールのインポート作業は完了です。

ステップ2:Automation変数を作成する

続いて祝日判定や起動終了のPowerShellで利用する変数を、Automationアカウント内に作成します。

  • 「共有リソース:変数」をクリック>「変数の追加」をクリック
  • 3つの変数「target_resource_group」「exclude_VM」「holidays_JP」を登録します。
    名前 タイプ 備考
    target_resource_group 文字列 自動起動/停止の対象となるリソースグループ名 対象としたリソースグループ内全ての仮想マシンが対象になります。
    exclude_VM 文字列 自動起動/停止を除外したい仮想マシン名 指定したリソースグループから「除外」したい仮想マシン名を入力します。
    holidays_JP 文字列 適当な値
    2020/11/3 等
    後述する「add_holidays」を動かすと自動で値が格納されます。
    作成時は任意の値を入れても問題ありません。

    ※暗号化は状況に応じて。筆者は「いいえ」を選択して実装。

    登録が終わるとこんな感じになります。

以上で変数の登録が完了となります。

ステップ3:Runbookの作成

続いて祝日判定等の処理や仮想マシンの起動/停止を行うRunbookを作成していきます。

  • 「プロセスオートメーション:Runbook」>「Runbookの作成」をクリック
  • 以下5つのRunbookを作成します。

    名前 Runbookの種類 実行スケジュール 備考
    add_holidays PowerShell 年に1回(1/1) 内閣府のWebサイトから祝日データを取得>変数「holidays_JP」へ値を上書きする。
    holiday_automation グラフィック 毎週:月~金 「not_holiday」と「start_vm」を連結させて実行する。
    not_holidays PowerShell (holiday_automationに組み込み) 実行された日時が「変数:add_holiday」内に一致する値があるか比較し、祝日判定を行う。
    start_vm PowerShell (holiday_automationに組み込み) 仮想マシンを起動する
    stop_vm PowerShell 毎日 仮想マシンを停止する

    ※ひとまず、Runbook内は記載せず、枠だけ作ってしまいます。中身の設定は後述します。

    作成後はこんな感じの一覧になるかと思います。

以上でRunbookの枠組みの作成が完了となります。

ステップ4:Runbookに処理を登録する

続いて先ほど作成したRunbookの枠組みに処理を記載していきます。
PowerShellに関してはコピペでOKです。

手順はこの流れをそれぞれ4つで実施します。
・ステップ3で作成したRunbookをクリック>「編集」>PowerShellをコピペ>「保存」>「公開」をクリック


add_holidays

#内閣府のHPから祝日一覧(csvファイル)を取得
$uri="https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv"
$importLoc=".\syukujitsu.csv"
Invoke-WebRequest -Uri $uri -OutFile $importLoc 
$HolidaysCsv= Import-Csv $importLoc -Encoding Default -Header "date","name" |Where-Object -FilterScript{$_.date -ne "国民の祝日・休日月日"}

#祝日一覧には記載されていない年末年始休暇を追加
$thisyear =(get-date).Tostring("yyyy")
$aditionalHolidays =@("/1/2","/1/3","/12/29","/12/30","/12/31")
foreach($addition in $aditionalHolidays){
    $addition= $thisyear+$addition
    $HolidaysCsv += New-Object PsObject -Property @{ date = $addition ; name = '年末年始休暇' }
}

#翌年の祝日をソートして取得
$thisyearsHolidays= $HolidaysCsv |Where-Object -FilterScript{$_.date -ge $thisyear+"/1/1"}|sort {[datetime]$_.date} 
$thisyearsHolidays|export-csv -path $importLoc -Encoding Default -NoTypeInformation

#祝日を文字列として結合
$content=""
$lastholiday=$thisyearsHolidays[-1]
$thisyearsHolidays|ForEach-Object{
    $content+=[string]$_.date
    if($_.date -ne $lastholiday.date){
    $content+=","
    }
}

#holidays_JP変数に格納
Set-AutomationVariable -Name 'holidays_JP' -Value $content

not_holidays

#holidays_JP変数に格納してある祝日を取得
$Holidays = (Get-AutomationVariable -Name 'holidays_JP')-split(",")

#今日の日付を取得(一桁の月日は先頭に0を付けない形式)
$Today = Get-Date -Format "yyyy/M/d"

#今日の日付とholidays_JP変数内の日付を突合
$not_holidays = $Holidays.contains($Today)

#今日の日付がholidays_JP変数内に存在すればTrue、存在してなければFalse
Write-Output $not_holidays

start_vm

$servicePrincipalConnection=Get-AutomationConnection -Name "AzureRunAsConnection"      
        Add-AzAccount `
            -ServicePrincipal `
            -TenantId $servicePrincipalConnection.TenantId `
            -ApplicationId $servicePrincipalConnection.ApplicationId `
            -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint 

#target_resource_group変数からリソースグループ名を取得
$Target_RG= (Get-AutomationVariable -Name 'target_resource_group')-split(",")

#上記で取得したリソースグループに所属する仮想マシンリストを取得
$VMList= @()
ForEach($rg in $Target_RG){
    $VMList += (Get-AzVM -ResourceGroupName $rg).Name
}

#除外VMリスト取得
$excludeVM =(Get-AutomationVariable -Name 'exclude_VM')-split(",")

#対象VM取得
$targetVM= Compare-Object -ReferenceObject $VMList -DifferenceObject $excludeVM -PassThru

#対象VM起動
$targetVM|ForEach-Object{
    Start-AzVM -ResourceGroupName (Get-AzVM -Name $_).ResourceGroupName -Name $_
}

stop_vm

$servicePrincipalConnection=Get-AutomationConnection -Name "AzureRunAsConnection"      
        Add-AzAccount `
            -ServicePrincipal `
            -TenantId $servicePrincipalConnection.TenantId `
            -ApplicationId $servicePrincipalConnection.ApplicationId `
            -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint 

#対象リソースグループ取得
$TargetResourceGroup= (Get-AutomationVariable -Name 'target_resource_group')-split(",")

#対象リソースグループ所属VMリスト取得
$VMList= @()
ForEach($rg in $TargetResourceGroup) {
    $VMList += (Get-AzVM -ResourceGroupName $rg).Name
}

#除外VMリスト取得
$excludeVM =(Get-AutomationVariable -Name 'exclude_VM')-split(",")

#対象VM取得
$targetVM= Compare-Object -ReferenceObject $VMList -DifferenceObject $excludeVM -PassThru

#対象VM停止
$targetVM|ForEach-Object{
    Stop-AzVM -ResourceGroupName (Get-AzVM -Name $_).ResourceGroupName -Name $_ -Force
}
参考サイトと異なる部分は「not_holidays」の[-Format]の中身になります。
簡単に変更出来て助かったのです。
・変更前:$Today = Get-Date -Format “yyyy/M/dd“ //表示→2020/1/01
・変更後:$Today = Get-Date -Format “yyyy/M/d“   //表示→2020/1/1
holiday_automation
やることは単純で処理の流れを登録します。
①祝日か確認 > ②「[No]の場合起動する」という内容を設定します。
  • 左カラム「Runbook」-「すべて」を展開>「not_holidays」と「strat_vm」の「…」をクリック>「キャンバス」に追加をそれぞれクリック
  • 赤丸の箇所から「start_vm」へ向けて矢印を引っ張る
  • 青丸部分をクリック>3箇所を設定>画面左上の「保存」>「公開」をクリック
    ・タイプ:シーケンス
    ・条件の適用:はい
    ・条件式:!$ActivityOutput[“not_holidays”]

条件式に記載する内容は「not_holidays」の値が「False」の場合、仮想マシンを起動するという内容になります。

そのため、条件式の頭に「!」が入っています。
!$ActivityOutput[“(RUNBOOK名)”]として記載する必要があるので注意ですね。

以上で祝日判定の処理~自動起動/停止処理のパーツ作成が完了となります。

ステップ4:Runbookのスケジュール登録をする

最後にRunbookのスケジュールを登録します。
祝日判定、自動起動、自動停止の合計3つが対象となります。

それぞれ以下の手順で登録となります。

  • 該当のRunbookをクリック>スケジュール>「スケジュールの追加」をクリック
  • 「スケジュールをRunbookにリンクします」をクリック
  • 「新しいスケジュールを作成します。」をクリック

名前 開始時 繰り返し 詳細設定 備考
add_holidays 翌日の任意の時間
※10:00等
定期的 ・間隔:1月
・毎月の特定曜日:月の日付
・月の指定した日に実行:1
・月の最終日に実行:いいえ
年初に1回、1年分の祝日を変数に格納するようにする。
holiday_automation 翌日のVM起動時間
※8:00等
定期的 ・間隔:1週
・設定曜日:月~金
土日は起動処理すらしない。
stop_vm VMを停止したいタイミング
※20:00等
定期的 ・間隔:1日 毎日停止処理をする。
起動と同じく、月~金でも問題無し。

設定の参考として以下のようになります。
起動時間等については各自の環境にあわせて設定するようになるかと思います。

登録されると、スケジュール画面内「次の実行」で実行時間が表示されます。

以上でスケジュール登録が完了し、祝日判定による仮想マシンの自動起動/停止が始まります。

余談:Runbookを手動で実行して動作確認する

最後に設定した内容を検証したい場合、スケジュールを待つのも良いですが手動で行うことも出来ます。
なお、検証の際は仮想マシンを事前に停止しておいてください。あと、リソースグループも落ちても良い仮想マシンだけにしておいてくださいね。
「本番機落ちたじゃないかこのやろー」など、失敗したことに対しては筆者は責任取れないのです。自己責任にてあしからずです。

  • 変数:holidays_JPの値を検証したい日付を入力する
  • Runbook:holiday_automationを開く>「開始」をクリックする
  • 停止していた仮想マシンが立ち上げってきたか確認する
「holidays_JP」の値を変更しても、上記の手順に沿って「add_holidays」を手動で実行することで再登録されます。
検証や初回設定時のあと、祝日登録がされていないので「add_holidays」は事前に手動で1回は実行しておきましょう。

日付判定のところで思わぬ地雷を踏まされましたが、筆者はこちらの設定で祝日判定での自動起動に成功しました。

また、今回は自動起動/停止としましたが、グラフィックの設定でstart_vmではないものを設定すれば他の処理にも応用が出来そうなので、何かあれば利用してみたい所存です。
皆様の参考になれば嬉しい限りです。

最新情報をチェックしよう!