Start/Stop VMs v2を使いこなせるように頑張ってみた話（スケジュール設定編）

# はじめに

2年ぐらい前に一生懸命検証したAzure Automationを使った自動起動/停止スケジュール機能の「Start/Stop VMs v1」が近日中に廃止されるとのことなので、新たにリリースされたStart/Stop VMs v2を検証しました。

とりあえず、v1で実装していた内容をそのままv2で作り替えられそうなくらいまでは理解したので、忘れないように備忘録。

Start Stop VMs v2の概要は以下を参照
・参考資料 : [Start/Stop VMs v2 の概要](https://learn.microsoft.com/ja-jp/azure/azure-functions/start-stop-vms/overview)

## 前提条件
Start/Stop VMs v2がデプロイされていること。

本記事はデプロイ後の起動/停止スケジュール設定編です。
デプロイ方法については以下記事参照。

[Start/Stop VMs v2を使いこなせるように頑張ってみた話（デプロイ編）](https://qiita.com/mikanchaaan/items/2f0f7bd86c293393a8c2)


# 目次
[1.スケジュールが1つの場合](#1-スケジュールが1つの場合)
[2.スケジュールが2つの場合](#2-スケジュールが2つの場合)
[3.スケジュールが3つ以上の場合](#3-スケジュールが3つ以上の場合)

# 1. スケジュールが1つの場合
デプロイ時に作成されたLogic Appsにスケジュールを設定します。
まずは、起動/停止スケジュールが1つの場合のみの設定方法。
例）サーバA、B、Cを9:00起動で設定したい。

## 指定したVMを一括で起動/停止する場合
「ststv2_vms_Scheduled_start」「ststv2_vms_Scheduled_stop」のロジックアプリを利用します。

### 設定方法（1つの場合）
1. Azure Portalにログインして、ロジックアプリの画面を開く。
![007.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609611/29028f22-9b93-fa9a-9222-3424811de1d2.jpeg)

1.  「ststv2_vms_Scheduled_start」をクリック
![008.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609611/da8133a9-db59-13ce-c678-d275e814f95e.jpeg)

1. 画面右側のメニューから「ロジックアプリデザイナー」を選択して、「Recurrence」画面を展開
![009.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609611/b94ce2db-36d1-5c22-1e52-1bd1e63ffd90.jpeg)

1. VMを起動するスケジュールを設定
   * 曜日を指定したい場合は、頻度を「週」にすると、「設定曜日」という項目が追加されます。
   * 「開始時刻」の値を設定しないと、トリガーの起動時刻にずれが発生し、正常にジョブが動作しない場合があります。<br>また、Azureの仕様上、トリガーは1秒程度前倒して実行される可能性があるため、「開始時刻」の秒数は「00」ではなく、「05」等にしておくことをおすすめします。（スケジュールを2つ以上にしたときに嵌りました...）<br>参考資料：[「繰り返し (Recurrence)」トリガーの起動時刻の差異について](https://jpazinteg.github.io/blog/LogicApps/ScheduleTriggerStrattime/)
   
   ![010.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609611/9dfc9c7c-da17-6325-290c-68a92e68ecfe.jpeg)

1. 右上の「・・・」をクリックし、「設定」を選択
![011.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609611/c548e95b-0627-4db9-53ed-75257c388af1.jpeg)

1. 「トリガーの起動条件」に以下の関数を設定して、「保存」をクリック
   ※設定しなくても動作しますが、設定しておかないと、有効化した際にジョブが動作してスケジュールで指定している時刻以外でも起動/停止が動作します。
   ```
     @and(equals(int(addSeconds(convertFromUtc(utcNow(), 'Tokyo Standard Time'), 5,'HH')), 9), equals(int(addSeconds(convertFromUtc(utcNow(), 'Tokyo Standard Time'),5 ,'mm')), 00))
     # 上記は起動時刻に9:00を指定している場合
     # 'HH'の後の数字と'mm’の後の数字を変更して起動時刻を指定する 
   ```  
      ![012.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609611/859cbbfc-18ac-e980-4bc0-18f8a0fce6e9.jpeg)
1. 「Function-Try」の画面を展開し、「Scheduled」に以下を設定
   ```   
     {
      # 起動の場合は"start"、停止の場合は"stop"を指定
      "Action": "start", 　
      "EnableClassic": false,
      "RequestScopes": {
      "ExcludedVMLists": [],
      # 複数台対象VMが存在する場合は、[]の中をカンマ区切りで増やしていく
      "VMLists": [
        "<起動/停止対象のVMのリソースID>", 
       ]
      }
     }
   ``` 
   ![013.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609611/93b49f9d-5c3c-c6cf-e16f-61f9a0c5e1c3.jpeg)

1. 「保存」をクリック
![014.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609611/9a0b472e-a91e-3c3c-751f-756f8b0c9e09.jpeg)

1. ロジックアプリが無効化されている場合は「有効」をクリックして有効化する
![017.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609611/250b5d55-5684-8b04-6cb6-757a901b89e8.jpeg)


「ststv2_vms_Scheduled_stop」のロジックアプリに停止スケジュールを設定すれば、同様に停止も動作するようになります。

## 指定したVMに順序をつけて起動/停止する場合
「ststv2_vms_Sequenced_start」「ststv2_vms_Sequenced_stop」のロジックアプリを利用します。

### 設定方法
1. 上記の「ststv2_vms_Sequenced_start」「ststv2_vms_Sequenced_stop」にて設定した内容を「ststv2_vms_Sequenced_start」「ststv2_vms_Sequenced_stop」に対して設定

1. 起動/停止対象のVMに以下のタグを付与
   |タグ名  |説明  |設定値  |
   |---|---|---|
   |Sequencestart  |VM起動順序を指定|（起動順序の数値）
   |Sequencestop  |VM停止順序を指定|（停止順序の数値）

![015.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609611/c92dfbd6-3c44-7e03-5bb1-637fac640c7c.jpeg)

上記設定をすると、ロジックアプリで指定した時刻にVMに設定したタグの順序で起動/停止します。
例）サーバAに「Sequencestart:1」、サーバBに「Sequencestart:2」のタグが付与されており、
ロジックアプリの起動スケジュールが9:00に指定されている場合、9:00にサーバA→サーバBの順でVMが起動します。

# 2. スケジュールが2つの場合
続いて、起動/停止スケジュールが2つになった場合の設定方法。（この辺りから非常に使いづらい...）
例）サーバA、B、Cが存在している場合に、サーバAとBは9:00起動、サーバCは9:30起動で設定したい。

### 設定方法（2つの場合）
基本的には、[スケジュールが1つの場合の設定手順](#1-スケジュールが1つの場合)と同じですが、以下が変更となります。

* 手順6:「トリガーの起動条件」に設定する関数が以下になる
   ```
     @or(and(equals(int(addSeconds(convertFromUtc(utcNow(), 'Tokyo Standard Time'), 5,'HH')), 9), equals(int(addSeconds(convertFromUtc(utcNow(), 'Tokyo Standard Time'),5 ,'mm')), 00)),and(equals(int(addSeconds(convertFromUtc(utcNow(), 'Tokyo Standard Time'), 5,'HH')), 9), equals(int(addSeconds(convertFromUtc(utcNow(), 'Tokyo Standard Time'),5 ,'mm')), 30)))
     # 上記は起動時刻に9:00と9:30を指定している場合（9:00と9:30以外にはジョブが動作しないようにする）
     # 'HH'の後の数字と'mm’の後の数字を変更して起動時刻を指定する 
   ```  
* 手順7の前に「パラメーター」をクリックして、起動/停止対象のVMのリソースIDを登録する
   今回は"ServerA"にサーバAのリソースID、"ServerB"にサーバBのリソースID、"ServerC"にサーバCのリソースIDをそれぞれ登録したとします。<br>また、1つのパラメータに2台以上のリソースIDを登録しても動作しないので、対象VM1台に付き1つのパラメータを設定してください。
![016.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609611/68e34776-0ecc-8c54-3030-09465559c387.jpeg)


* 手順7の「Scheduled」に設定する関数が以下になる

   ```   
     {
      # 起動の場合は"start"、停止の場合は"stop"を指定
      "Action": "start", 　
      "EnableClassic": false,
      "RequestScopes": {
      "ExcludedVMLists": [],
      # if文を使って、9:30にトリガーが動いた場合はサーバCを起動、それ以外の時間にトリガーが動いた場合はサーバAとBを起動する設定にする
      # 対象サーバが1台の場合はarray関数を使ってパラメータを指定、2台以上の場合はcreateArray関数を使ってパラメータを指定する
      "VMLists": @if(equals(int(addSeconds(convertFromUtc(utcNow(), 'Tokyo Standard Time'), 5,'mm')), 30),array(parameters('ServerC')),createArray(parameters('ServerA'),parameters('ServerB')))
       ]
      }
     }
   ``` 
![018.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609611/0867ceed-de7d-1a3c-0eb9-21a403df2054.jpeg)
 
# 3. スケジュールが3つ以上の場合
最後は3つ以上の場合です。
と言っても、あとはif文を入れ子にして、条件分岐を増やしていくだけなので、スケジュールが2つの時と設定方法は変わりません。
（ただし、スケジュールが増えれば増えるほど、if文が長くなっていくだけなのでただただ管理が複雑になります。。）
例）サーバA、B、Cが存在している場合に、サーバAは9:00起動、サーバBは9:30起動、サーバCは9:45起動で設定したい。

### 設定方法（3つの場合）

こちらも、基本的には[スケジュールが1つの場合の設定手順](#1-スケジュールが1つの場合)と同じですが、以下が変更となります。

* 手順6:「トリガーの起動条件」に設定する関数が以下になる
   ```
     @or(and(equals(int(addSeconds(convertFromUtc(utcNow(), 'Tokyo Standard Time'), 5,'HH')), 17), equals(int(addSeconds(convertFromUtc(utcNow(), 'Tokyo Standard Time'),5 ,'mm')), 55)),and(equals(int(addSeconds(convertFromUtc(utcNow(), 'Tokyo Standard Time'), 5,'HH')), 18), equals(int(addSeconds(convertFromUtc(utcNow(), 'Tokyo Standard Time'),5 ,'mm')), 0)),and(equals(int(addSeconds(convertFromUtc(utcNow(), 'Tokyo Standard Time'), 5,'HH')), 18), equals(int(addSeconds(convertFromUtc(utcNow(), 'Tokyo Standard Time'),5 ,'mm')), 5)))
     # 上記は起動時刻に9:00と9:30と9:45を指定している場合（9:00と9:30と9:45以外にはジョブが動作しないようにする）
     # 'HH'の後の数字と'mm’の後の数字を変更して起動時刻を指定する 
   ```  
* 手順7の前に「パラメーター」をクリックして、起動/停止対象のVMのリソースIDを登録する
   今回は"ServerA"にサーバAのリソースID、"ServerB"にサーバBのリソースID、"ServerC"にサーバCのリソースIDをそれぞれ登録したとします。<br>また、1つのパラメータに2台以上のリソースIDを登録しても動作しないので、対象VM1台に付き1つのパラメータを設定してください。


* 手順7の「Scheduled」に設定する関数が以下になる

   ```   
     {
      # 起動の場合は"start"、停止の場合は"stop"を指定
      "Action": "start", 　
      "EnableClassic": false,
      "RequestScopes": {
      "ExcludedVMLists": [],
      # if文を使って、9:30にトリガーが動いた場合はサーバCを起動、それ以外の時間にトリガーが動いた場合はサーバAとBを起動する設定にする
      # 対象サーバが1台の場合はarray関数を使ってパラメータを指定、2台以上の場合はcreateArray関数を使ってパラメータを指定する
      "VMLists": @if(equals(int(addSeconds(convertFromUtc(utcNow(), 'Tokyo Standard Time'), 5,'mm')), 00),array(parameters('ServerA')),if(equals(int(addSeconds(convertFromUtc(utcNow(), 'Tokyo Standard Time'), 5,'mm')), 30),array(parameters('ServerB')),array(parameters('ServerC'))))
       ]
      }
     }
   ```  

# おわりに
こんな感じで、Azureの機能を使ってVMの自動起動停止の実装はできそうです。
ただ、v1のときみたいにGUIでスケジュール管理できるわけじゃないので、コード書くのすこぶる苦手な私にはとてもとてもとても使いにくいなと感じました。
あと、スケジュールが増えれば増えるほどif文の条件分岐が長くなって管理が煩雑になるので、しっかり動作確認してから導入しないと、ちょっとしたミスで正常に動作してなかった...とかが増えそうなので注意かなと思います。（※あくまで個人的な意見です。）

***