VM のメンテナンス再起動を Scheduled Events によって検知し、SendGrid でメール送信する

こんにちは、Azure サポート部の石井です。

 

今回は、VM にメンテナンス、あるいはポータル・PowerShell/CLI などのコマンドによる再起動が発生したときに、その通知メールを送信するという処理を Scheduled Events の機能を使って行うサンプルをご紹介します。

Scheduled Events についての前段となるご紹介については、以下を参照してください。簡単にいうと、VM の中から、自身が再起動される、あるいはメンテナンスにより一時停止する、といったイベントとその発生時間を、最大 15 分前に取得できるという API です。

 

自分の VM に何が起きるか把握する Scheduled Events (Instance Metadata Service) のご紹介
https://blogs.technet.microsoft.com/jpaztech/2017/10/04/instance-metadata-service/

 

 

今回は、SendGrid の無償アカウントを作成し、SendGrid 経由で外部にメール送信します。

 

Azure Marketplace (ポータル左上の "リソースの作成") から、"SendGrid Email Delivery" を選択し、F1 Free プランを使って作成しています。Azure 経由では、毎月 25,000 通まではメールを無償で作成できる、というプランとなります。

 

※ クラウド ソリューション プロバイダー (CSP) サブスクリプションでは、Azure 経由でのサードパーティのリソース購入に一部制限がある場合がありますが、SendGrid.com から直接サブスクリプションを作成していただくことで、同様のことが実現できます。同じく無償サブスクリプションが存在しますのでご安心ください。

 

このとき設定するパスワードは、メール送信の際の認証情報になるため忘れないようにお気を付け下さい。

 

SendGrid リソースを Azure サブスクリプションから作成した場合、以下のように、[SendGrid Accounts] - [アカウント名] - [Manage] から SendGrid.com のポータルにジャンプ出来ます。(シングルサインオンとなります。)

 

 

 

ジャンプした先は、SendGrid のアカウント ポータルです。様々な設定が出来るようですが、今回はひとまず、自身の SendGrid アカウント名の確認をしましょう。

画面をスクロール ダウンすると、以下のように azure_*****@azure.com という形式のアカウントが作成されています。このアカウント名と、リソース作成時に指定したパスワードが SendGrid  経由のメール送信に使う認証情報になります。

 

 

上記までが出来たら、メールが送信できるようになります。

 

前回のポストの続きとして、Windows VM において、PowerShell を使って「再起動 (Reboot)」 のイベントが取得された時にだけメールを送る PowerShell スクリプトをサンプルとしてご紹介します。

テキスト エディターにコピーし、適宜編集してから、.ps1 形式にして保存して PowerShell ウィンドウから実行して下さい。

 

 while($true){
    $message = curl https://169.254.169.254/metadata/scheduledevents?api-version=2017-04-02
  $message | Out-File C:\test\metadata.txt -append
    $message.Content | Out-File C:\test\metadata.txt -append
    $MyVmName = hostname

    # 再起動の処理が予定されている場合のルーチンに入る。自分の VM 名のメンテナンスであることもチェックする。
 if ($message.Content.Contains('"EventType":"Reboot"') -And $message.Content.Contains($MyVmName) ){
          $log = "==============   リブートを伴う処理が予定されています。  ========================================"
     #Write-Output($log)
         $log | Out-File C:\test\emaildelivery.txt -append
           $message | Out-File C:\test\emaildelivery.txt -append
           $message.Content | Out-File C:\test\emaildelivery.txt -append

           # NotBefore の後の日時を取得し、$strSchedule に入力
      $ptrScheduleFrom = $message.Content.IndexOf('"NotBefore"')
      $ptrScheduleTo = $message.Content.IndexOf('GMT"')
       $ptrScheduleFrom = $ptrScheduleFrom + 13
        $strSchedule = $message.Content.Substring($ptrScheduleFrom, ($ptrScheduleTo - $ptrScheduleFrom))

        # スケジュールのテキストを形成しておく
        $rebootScheduleMessage = "当 VM は GMT " + $strSchedule + "に再起動されます。"

         $rebootScheduleMessage | Out-File C:\test\emaildelivery.txt -append

     # コンソール出力 (確認用)
     Write-Output($rebootScheduleMessage)

        # メール送信処理を開始
        # SendGrid リソースを作った際のアカウント名
     $Username ="azure_****@azure.com"

       # SendGrid リソースを作った際のパスワード
      $Password = ConvertTo-SecureString "パスワード" -AsPlainText -Force
      $credential = New-Object System.Management.Automation.PSCredential $Username, $Password
     $SMTPServer = "smtp.sendgrid.net"

       # 送信元メールアドレス (任意のメールアドレスが入力可能。SendGrid のアカウント名の形式でなくてもよい。)
      $EmailFrom = "test@foobar.com"

      ## 単体の送信の場合
     #[string[]]$EmailTo = "test@contoso.com"

        # 複数のメールアドレスへの送信の場合
     [string[]]$EmailTo = "test1@contoso.com","test2@contoso.com","test3@contoso.com"
        
        $Subject = "Azure VM:" + $MyVmName + " に再起動を伴うメンテナンスが発生します。"
        
        $Body = $MyVmName + " にメンテナンスが予定されています。" + $rebootScheduleMessage
       
        Send-MailMessage -Encoding UTF8 -smtpServer $SMTPServer -Credential $credential -Usessl -Port 587 -from $EmailFrom -to $EmailTo -subject $Subject -Body $Body -BodyAsHtml
       
        Write-Output "メールを送信しました" | Out-File C:\test\emaildelivery.txt -append 
     Write-Output "From: " $EmailFrom | Out-File C:\test\emaildelivery.txt -append 
      Write-Output "To: " $EmailTo| Out-File C:\test\emaildelivery.txt -append
        Write-Output "Subject: " $Subject | Out-File C:\test\emaildelivery.txt -append
      Write-Output "Body:" $Body| Out-File C:\test\emaildelivery.txt -append

          # メールを繰り返し送らないよう Wait するか、Exit する。念のため、何度か送信するようにしても OK。
            Start-Sleep -seconds 90000

  #メール送信のルーチン完了
   }

   # メールを送る必要がなければ再度ループ
    Write-Output "メンテナンス予定は現時点ではありません。ループします。"
  Start-Sleep -seconds 10
}

 

さすがに、大規模メンテナンスをテストすることはできないので、Azure ポータルから当 VM を選択して、[再起動] をクリックして下さい。
しばらくすると Reboot のイベントが取得できるため、メール送信のロジックに入ります。

 

 

 

 

- さらなる応用

ドキュメント Azure Metadata Service: Windows VM のスケジュールされたイベント (プレビュー) に以下の記載がありますとおり、当通知は、同じクラウド サービス (クラシック デプロイメントの場合)、可用性セット、スケール セット (同じクラスター) の配置グループ VM 全ての通知が取得できます。

 

<引用> スケジュールされたイベントの配信先は次のとおりです。

・クラウド サービス内のすべての Virtual Machines

・可用性セット内のすべての Virtual Machines

・スケール セットの配置グループ内のすべての Virtual Machines

 

このため、サンプルでは、単に Reboot かどうかの文字列をチェックするだけでなく、自分の VM 名についての再起動であるかチェックするため、$message.Content.Contains($MyVmName)  という確認を冒頭の if 文に入れています。

応用をすると、Active/Passive 構成の 2 VM において、可用性セットの相方のメンテナンスを検知し、自身のほうでサービスを稼働させる (フェールオーバーさせる) ということも出来るということです。

 

※ 今回、メンテナンス再起動のうち、強制再起動期間を想定して "Reboot" を条件にしていますが、セルフサービスのメンテナンスや、管理者ユーザー側が行う管理上の操作としては "Redeploy" が行われる可能性がありますので、冒頭の if 文でそちらもカバーするのも良いかもしれません。

 

- 参考情報

PowerShell 経由で SendGrid にメールをする部分のスクリプトは以下 Technet Gallery を参考にしました。

Send email from Azure SendGrid using PowerShell
https://gallery.technet.microsoft.com/Send-email-from-Azure-eabb6d26