Level10

とあるSEの備忘録

AWS のメンテナンス情報で対象リソースを含めて Slack に通知する

概要

AWS における EC2 のメンテナンス情報は AWS アカウントのメールアドレスに送信されます。
メールだと気づきづらいので、Slack 通知したい!! と思うかもしれません。
その実装に関して、@hayao_k さんの記事がとても参考なります!!

qiita.com

今回は上記コードに情報を足して EC2 に関するリソース情報を含めて Slack へ通知してみたいと思います。

前提

  • Python 3.7 で動作確認
  • 対象となるのは EC2 のリソースのみです ( RDS 等は対応していません )

サンプルコード

サンプルコードはこちらになります

import json
import os
import logging
import boto3
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError

logger = logging.getLogger()
logger.setLevel(logging.INFO)
ec2client = boto3.client('ec2')
iamclient = boto3.client('iam')

def lambda_handler(event, context):
  webhook_url = os.environ['WEBHOOK_URL']
  account_alias = iamclient.list_account_aliases()['AccountAliases'][0]

  message = 'AWS Healthイベントを検知しました。対象のリソースを確認してください。' 
  
  logger.info(event)

  # リソース情報が含まれているかどうかを判定
  if 'resources' in event:
      if len(event['resources']) > 0:
          resource = event['resources'][0]
          resource_tags = 'nothing'

          # リソースが "i-" であればEC2のHostnameタグ情報を取得する
          type_check_str = resource[:2]
          if type_check_str == 'i-':
              response = ec2client.describe_instances(
                  InstanceIds=[
                      resource
                  ])
          
              for instance in response['Reservations']:
                  #print(instance['Tags'])
                  for tag in instance['Instances'][0]['Tags']:
                      #print(tag['Key'])
                      if tag['Key'] == 'Name':
                          #print(tag['Value'])
                          resource_tags = tag['Value']
      else:
          resource = 'nothing'
          resource_tags = 'nothing'
  else:
      resource = 'nothing'
      resource_tags = 'nothing'
  
  slack_message = {
    'username': 'AWS Health Event Notification',
    'icon_emoji': ':warning:',
    'text': message,
    'attachments': [
      {
        'fallback': 'AWS Health Event Description.',
        'color': 'warning',
        'title': event['detail']['eventTypeCode'],
        'title_link': 'https://phd.aws.amazon.com/phd/home',
        'fields': [
          {
            'title': 'Account ID',
            'value': event['account'],
            'short': True
          },
          {
            'title': 'Account Alias',
            'value': account_alias,
            'short': True
          },
          {
            'title': 'Region',
            'value': event['region'],
            'short': True
          },
          {
            'title': 'Service',
            'value': event['detail']['service'],
            'short': True
          },
          {
            'title': 'Start Time',
            'value': event['detail']['startTime'],
            'short': True
          },
          {
            'title': 'Resource',
            'value': resource,
            'short': True
          },
          {
            'title': 'ResourceTags',
            'value': resource_tags,
            'short': True
          },
          {
            'title': 'Description',
            'value': event['detail']['eventDescription'][0]['latestDescription'][0:511],
            'short': False
          }
        ]
      }
    ]
  }

  req = Request(webhook_url, json.dumps(slack_message).encode('utf-8'))
  try:
    response = urlopen(req)
    response.read()
    logger.info("Message posted.")
  except HTTPError as e:
    logger.error("Request failed: %d %s", e.code, e.reason)
  except URLError as e:
    logger.error("Server connection failed: %s", e.reason)

解説

下記が追加部分です。 通知メッセージに resources が含まれるかどうかを判定し、 含まれている場合には EC2 に設定されている Name タグから値を取得しています。 対象外であれば nothing を表示します。

  # リソース情報が含まれているかどうかを判定
  if 'resources' in event:
      if len(event['resources']) > 0:
          resource = event['resources'][0]
          resource_tags = 'nothing'

          # リソースが "i-" であればEC2のHostnameタグ情報を取得する
          type_check_str = resource[:2]
          if type_check_str == 'i-':
              response = ec2client.describe_instances(
                  InstanceIds=[
                      resource
                  ])
          
              for instance in response['Reservations']:
                  #print(instance['Tags'])
                  for tag in instance['Instances'][0]['Tags']:
                      #print(tag['Key'])
                      if tag['Key'] == 'Name':
                          #print(tag['Value'])
                          resource_tags = tag['Value']
      else:
          resource = 'nothing'
          resource_tags = 'nothing'
  else:
      resource = 'nothing'
      resource_tags = 'nothing'

結果

こんな感じで Slack に通知が来ます!! f:id:genkeyx:20190604001811p:plain

AccountAlias : 通知を受け取ったアカウントのエイリアスを取得して表示
Resource : 通知内のリソース情報を表示します ( 例: i-0000000000000000 )
ResourceTags ' 通知内のリソース情報に設定されている Name タグの値を表示します ( 例 : ec2-instance-name )

参考情報 : 受信 Evetnt 例

CloudWatch からは下記形式のメッセージが来ます ( 一例 Lambda の動作確認をする場合には本 JSON をもじってテストイベントとして設定見てください!!

{
  "version": "0",
  "id": "00000000-0000-0000-0000-000000000000",
  "detail-type": "AWS Health Event",
  "source": "aws.health",
  "account": "000000000000",
  "time": "YYYY-MM-DDTHH:MM:SSZ",
  "region": "ap-northeast-1",
  "resources": [
    "i-0000000000000000"
  ],
  "detail": {
    "eventArn": "arn:aws:health:ap-northeast-1::event/EC2/AWS_EC2_PERSISTENT_INSTANCE_RETIREMENT_SCHEDULED/AWS_EC2_PERSISTENT_INSTANCE_RETIREMENT_SCHEDULED",
    "service": "EC2",
    "eventTypeCode": "AWS_EC2_PERSISTENT_INSTANCE_RETIREMENT_SCHEDULED",
    "eventTypeCategory": "scheduledChange",
    "startTime": "Sun, 11 Jan 2018 11:00:00 GMT",
    "endTime": "Sun, 11 Jan 2018 11:00:00 GMT",
    "eventDescription": [
      {
        "language": "en_US",
        "latestDescription": "EC2 has detected degradation of the underlying hardware hosting your Amazon EC2 instance associated with this event in the ap-northeast-1 region. Due to this degradation, your instance could already be unreachable. After 2018-1-11 11:00 UTC your instance, which has an EBS volume as the root device, will be stopped.\\n \\nYou can see more information on your instances that are scheduled for retirement in the AWS Management Console (https://console.aws.amazon.com/ec2/v2/home?region=ap-northeast-1#Events)\\n \\n* How does this affect you?\\n \\nYour instance will be stopped after the specified retirement date, but you can start it again at any time. Note that if you have EC2 instance store volumes attached to the instance, any data on these volumes will be lost when the instance is stopped or terminated as these volumes are physically attached to the host computer\\n \\n* What do you need to do?\\n \\nYou can wait for the scheduled retirement date - when the instance is stopped - or stop the instance yourself any time before then. Once the instances has been stopped, you can start the instance again at any time. For more information about stopping and starting your instance, and what to expect when your instance is stopped, such as the effect on public, private and Elastic IP addresses associated with your instance, see Stop and Start Your Instance in the EC2 User Guide (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Stop_Start.html).\\n \\n* Why retirement?\\n \\nAWS may schedule instances for retirement in cases where there is an unrecoverable issue with the underlying hardware. For more information about scheduled retirement events please see the EC2 user guide (http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-retirement.html).\\n \\nIf you have any questions or concerns, you can contact the AWS Support Team on the community forums and via AWS Premium Support at: http://aws.amazon.com/support"
      }
    ],
    "affectedEntities": [
      {
        "entityValue": "i-00000000000000000",
        "tags": {}
      }
    ]
  }
}