Help us understand the problem. What is going on with this article?

AWSからメンテ通知が来たら5秒でインスタンスを再起動させる方法(Slackメッセージボタンでの事前確認付き)

More than 1 year has passed since last update.

AWSから不定期に届くメンテ通知や障害メールの対応を、PCやブラウザを使わずにSlackで完結させます。

完成図

できあがったものがこれです。

  • 作成所要時間 約15分 2017-11-20 10_00_16-aws-mentenance.json - FrontOps.png

※定型対応フローが簡単に作れるアプリFrontOps を使っています。

はじめに

ある日、こんな件名のメールが届きます。

  • Amazon EC2 Maintenance - Maintenance
  • [Retirement Notification]

インスタンスが動いている物理サーバをメンテするから、いつまでにインスタンスを移動してください。
インスタンスの状態が異常なので、インスタンスを確認して再起動してください。

といった内容です。
このメール、たまにしか来ないことに加え、英語なこともあり他の迷惑メールや広告メールに紛れて見落とすことがよくありました。

そこで、このメールを受信したらSlackに通知させて見落としを防止することにしました。

またこのメール。メール文中にはインスタンスID の記載しかないので、インスタンス名を確認しないと影響判断ができません。
ただ、インスタンス名を確認するにはいちいちAWSのマネジメントコンソールにログインする必要があって面倒です。

そこでSlack通知の際に、インスタンス名を補足させることにします。

また、このメールの対応としてはインスタンスを再起動するしかありません。
機械的に再起動させてもいいのですが、
再起動前に事前作業が必要なインスタンスもあるので、
事前にSlackのメッセージボタンを使って承認を挟むことにします。

必要なもの

  • FrontOps (https://frontops.exhands.org)
    • 無料アカウントで、アプリをダウンロードして利用することができます
  • AWS CLIが使えてSSHでログインできるサーバ
    • あらかじめaws configureでクレデンシャル等は設定しておきます。
  • 通知先のSlackチャンネル

設定説明

全体フローは冒頭にあげた通りです。以下では各ノードの設定を説明します。

メール定期受信

メールを定期的に受信させます。1分ごとにしました。
2017-11-21 08_15_52-aws-mentenance.json - FrontOps.png

件名でフィルタ

メールの件名は、msg.topicに入ってきます。msg.topicに文字列が含まれるかどうかを設定します。
条件に該当する場合は次のノードに進みます。
条件に該当しない場合はここで処理が終わります。
2017-11-20 23_48_12-aws-mentenance.json - FrontOps.png

インスタンスID取得

メールの本文は、msg.payloadに入ってきます。
ここからJSONATAを使ってインスタンスIDを抽出して、msg.instanceIdに設定します。

$match(payload, /^(i-\w+)/m).groups

2017-11-21 07_41_19-aws-mentenance.json - FrontOps.png

インスタンス名取得

インスタンス名は、ふだん使っているサーバにSSHログインしてAWS CLIで取得します。

取得した結果は、msg.instanceNameに設定します。
コマンド部にはmustacheが使えるので、{{{プロパティ}}}msgオブジェクトのプロパティを展開できます。
ここでは先に取得したインスタンスIDを展開させています。

aws ec2 describe-instances --instance-id {{{instanceId}}} --query 'Reservations[*].Instances[*].[InstanceId,State.Name,Tags[?Key==`Name`].Value]' --output text

2017-11-20 23_48_51-aws-mentenance.json - FrontOps.png

Slack通知

Slackのgeneralチャンネルに

メールの件名(msg.topic)
メールの本文(msg.payload)
インスタンス名(msg.instanceName.stdout)

を通知します。

2017-11-20 23_49_09-aws-mentenance.json - FrontOps.png

再起動実施確認

インスタンスの停止・起動をメッセージボタンで確認します。
よく使うYes/Noの形式なので、情報ウィンドウに表示されているテンプレートをコピペして文言だけ直して使います。

2017-11-20 23_49_28-aws-mentenance.json - FrontOps.png

復唱

どのメッセージボタンが押されたかを復唱させます。この情報はmsg.answerに入ってきます。
これもよくあるパターンなので、情報ウィンドウに表示されているテンプレートをコピペして使います。
2017-11-20 23_49_41-aws-mentenance.json - FrontOps.png

承認結果判定

ボタン操作の結果に応じて分岐を作ります。
2017-11-20 23_49_55-aws-mentenance.json - FrontOps.png

対応終了

2017-11-20 23_50_10-aws-mentenance.json - FrontOps.png

対応開始宣言

2017-11-20 23_50_23-aws-mentenance.json - FrontOps.png

インスタンス停止

インスタンス停止は、AWS CLIでおこないます。
インスタンスを停止させて、ステータスがstoppedになるまで待ちます。
タイムアウトは60秒にしました。
2017-11-20 23_52_15-aws-mentenance.json - FrontOps.png

インスタンス起動

インスタンス起動は、AWS CLIでおこないます。
インスタンスを起動させて、ステータスがrunningになるまで待ちます。
タイムアウトはちょっと長めに90秒にしておきます。
2017-11-21 08_09_04-aws-mentenance.json - FrontOps.png

対応完了報告

対応完了を報告させて終了します。
この前にさらに正常性確認を追加してもいいかもしれません。
2017-11-20 23_51_24-aws-mentenance.json - FrontOps.png

例外処理

タイムアウトやその他エラーが発生した時には、ログに記録を残させると同時にSlackに通知させています。
電話で通知させるパターンもよくあります。

フローデータ

以下をコピーして、[読み込み]>[クリップボード] でフローを取り込むことができます。

[{"id":"67e11027.4cc2c","type":"command","z":"bd84d76c.2d93f8","connectionconfig":"fd7512e2.f22ec","command":"aws ec2 describe-instances --instance-id {{{instanceId}}} --query 'Reservations[*].Instances[*].[InstanceId,State.Name,Tags[?Key==`Name`].Value]' --output text","timer":"15","oldrc":false,"env":"","pty":false,"trim":false,"field":"instanceName","fieldType":"msg","name":"インスタンス名取得","x":190,"y":240,"wires":[["b0addf6c.85c41"]]},{"id":"10686c8e.7ab4d3","type":"e-mail in","z":"bd84d76c.2d93f8","name":"メール定期受信","protocol":"IMAP","server":"imap.gmail.com","useSSL":true,"port":"993","box":"INBOX","disposition":"Read","repeat":"60","x":130,"y":160,"wires":[["20849217.8d4fae"]]},{"id":"20849217.8d4fae","type":"switch","z":"bd84d76c.2d93f8","name":"件名でフィルタ","property":"topic","propertyType":"msg","rules":[{"t":"cont","v":"Amazon EC2 Maintenance - Maintenance","vt":"str"},{"t":"cont","v":"[Retirement Notification]","vt":"str"}],"checkall":"true","outputs":2,"x":310,"y":160,"wires":[["dfb73593.bb2f08"],["dfb73593.bb2f08"]]},{"id":"dfb73593.bb2f08","type":"change","z":"bd84d76c.2d93f8","name":"インスタンスID取得","rules":[{"t":"set","p":"instanceId","pt":"msg","to":"$match(payload, /^(i-\\w+)/m).groups[0]\t","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":160,"wires":[["67e11027.4cc2c"]]},{"id":"b0addf6c.85c41","type":"slack-say","z":"bd84d76c.2d93f8","slack":"","channel":"general","msg":"*{{{topic}}}*\n\n```\n{{{payload}}}\n```\n{{{instanceName.stdout}}}","output":"str","gonext":true,"name":"Slack通知","x":380,"y":240,"wires":[["f6e76317.5bd3"]]},{"id":"f6e76317.5bd3","type":"slack-ask","z":"bd84d76c.2d93f8","slack":"","channel":"general","msg":"{\n  \"text\": \"インスタンスを停止・起動していいですか?\",\n  \"attachments\": [\n    {\n      \"title\": \"承認依頼\",\n      \"text\": \"実行してよろしいですか\",\n      \"fallback\": \"このクライアントでは返答できません\",\n      \"callback_id\": \"approve_123\",\n      \"color\": \"#3AA3E3\",\n      \"attachment_type\": \"default\",\n      \"actions\": [\n        {\n          \"name\": \"answer\",\n          \"text\": \"承認\",\n          \"value\": \"yes\",\n          \"type\": \"button\",\n          \"style\": \"primary\"\n        },\n        {\n          \"name\": \"answer\",\n          \"text\": \"否認\",\n          \"value\": \"no\",\n          \"type\": \"button\"\n        }\n      ]\n    }\n  ]\n}","timeout":"30","timeoutUnits":"minutes","name":"再起動実施確認","x":200,"y":300,"wires":[["225facba.2a5cf4"]]},{"id":"225facba.2a5cf4","type":"slack-say","z":"bd84d76c.2d93f8","slack":"","channel":"general","msg":"> {{{payload.original_message.text}}}\n{{{answer.value}}} by {{{payload.user.name}}}","output":"str","gonext":true,"name":"復唱","x":370,"y":300,"wires":[["75b36d.ac831c94"]]},{"id":"75b36d.ac831c94","type":"switch","z":"bd84d76c.2d93f8","name":"承認結果判定","property":"answer.value","propertyType":"msg","rules":[{"t":"eq","v":"no","vt":"str"},{"t":"eq","v":"yes","vt":"str"}],"checkall":"true","outputs":2,"x":540,"y":300,"wires":[["eb240803.79d998"],["351ce544.8e522a"]]},{"id":"3c6efba0.b994e4","type":"catch","z":"bd84d76c.2d93f8","name":"例外処理","scope":["b0addf6c.85c41","75b36d.ac831c94","dfb73593.bb2f08","cd9f1df.a3aede","67e11027.4cc2c","c274bafd.cddfe8","20849217.8d4fae","f6e76317.5bd3","10cc5de3.b72572","eb240803.79d998","351ce544.8e522a","225facba.2a5cf4","b4948d24.29e29"],"x":160,"y":460,"wires":[["a640fbbb.d2ee18","25f25187.2b4cbe","6fdbe105.9ed53"]]},{"id":"eb240803.79d998","type":"slack-say","z":"bd84d76c.2d93f8","slack":"","channel":"general","msg":"後の対応をお願いします。","output":"str","gonext":false,"name":"対応終了","x":800,"y":300,"wires":[]},{"id":"351ce544.8e522a","type":"slack-say","z":"bd84d76c.2d93f8","slack":"","channel":"general","msg":"対応を始めます","output":"str","gonext":true,"name":"対応開始宣言","x":200,"y":360,"wires":[["cd9f1df.a3aede"]]},{"id":"cd9f1df.a3aede","type":"command","z":"bd84d76c.2d93f8","connectionconfig":"fd7512e2.f22ec","command":"aws ec2 stop-instances --instance-id {{{instanceId}}}\naws ec2 wait instance-stopped --instance-id {{{instanceId}}}","timer":"60","oldrc":false,"env":"","pty":false,"trim":false,"field":"payload","fieldType":"msg","name":"インスタンス停止","x":400,"y":360,"wires":[["b4948d24.29e29","c274bafd.cddfe8"]]},{"id":"b4948d24.29e29","type":"slack-say","z":"bd84d76c.2d93f8","slack":"","channel":"general","msg":"インスタンスを停止しました。\nインスタンスを起動します。","output":"str","gonext":false,"name":"途中報告","x":600,"y":400,"wires":[]},{"id":"c274bafd.cddfe8","type":"command","z":"bd84d76c.2d93f8","connectionconfig":"fd7512e2.f22ec","command":" aws ec2 start-instances --instance-id {{{instanceId}}}\naws ec2 wait instance-running --instance-id {{{instanceId}}}","timer":"90","oldrc":false,"env":"","pty":false,"trim":false,"field":"payload","fieldType":"msg","name":"インスタンス起動","x":620,"y":360,"wires":[["10cc5de3.b72572"]]},{"id":"10cc5de3.b72572","type":"slack-say","z":"bd84d76c.2d93f8","slack":"","channel":"general","msg":"インスタンスを起動しました。\n対応が完了しました。","output":"str","gonext":false,"name":"対応完了報告","x":820,"y":360,"wires":[]},{"id":"a640fbbb.d2ee18","type":"slack-say","z":"bd84d76c.2d93f8","slack":"","channel":"general","msg":"異常が発生しました。対応をお願いします。\n{{{error.message}}}\n```\n{{{payload}}}\n```\n","output":"str","gonext":false,"name":"異常発生連絡","x":400,"y":460,"wires":[]},{"id":"25f25187.2b4cbe","type":"debug","z":"bd84d76c.2d93f8","name":"","active":true,"console":"false","complete":"true","x":370,"y":560,"wires":[]},{"id":"6fdbe105.9ed53","type":"file","z":"bd84d76c.2d93f8","name":"ログ追記","filename":"c:\\Temp\\aws-mentenance.log","appendNewline":true,"createDir":true,"overwriteFile":"false","x":377.1000061035156,"y":510.20001220703125,"wires":[]},{"id":"d20cc86f.3c0dd8","type":"comment","z":"bd84d76c.2d93f8","name":"AWSのメンテ通知の対応","info":"","x":140,"y":100,"wires":[]},{"id":"fd7512e2.f22ec","type":"connection","z":"","name":"util server","host":"35.185.201.106","username":"sakazuki"}]

最後に

FrontOps を使うと、複数のツール・ソフトを簡単につなげて制御することができます。
少人数でシステム運用・保守をされている方に是非活用いただきたいアプリです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした