Health APIでメンテナンス必要な件数をMackerelで可視化

はじめに

AWSでメンテナンスが行われる場合、それぞれのアカウントに対してメールで通知が行われます。
対象のアカウントが多いと、メールも大量になり、重要なメンテナンス通知を漏らすことがあります。
(メールの文面が変わってしまったりすることもあり、完全にフィルタリングを行うことは難しいです。。。)

Health APIを用いると、APIで必要なメンテナンス情報を取得することができるので、対応漏れを防ぐことができます。

ドキュメントにあるように、Healthイベントのステータス変化をイベントとして、再起動や通知を行ったりもできますが、今回は対処が必要なリソースが、どのアカウントで何件あるのかをMackerelで確認できるようにしたいと思います。

構成

簡単ですが構成は以下の通りです。
日次でCloudWatch EventsからLambdaをスケジュール起動し、各アカウントのメンテナンス情報を収集します。
収集した結果をサービスメトリックとしてMackerlに投稿するようにしました。

Health APIへのアクセスにはhealth:Describe*の許可が必要です。
各アカウントの環境にアクセスできるように、実行環境hへのAssumeRoleを設定しています。

コード

MackerelのAPIKEYはひとまず環境変数に持たせています。
ターゲットアカウントのロールと、Mackerlへ投稿する際のサービス、メトリック名をパラメータとして渡すことにしています。

testparam.png

main.go
main.go
package main

import (
    "log"
    "os"
    "time"

    "github.com/aws/aws-lambda-go/lambda"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/credentials/stscreds"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/health"
    "github.com/aws/aws-sdk-go/service/sts"
    mackerel "github.com/mackerelio/mackerel-client-go"
)

var (
    client  = mackerel.NewClient(os.Getenv("APIKEY"))
    nowTime = time.Now()
)

// Request is argument specified at call
type Request struct {
    TargetList []TargetAccount `json:"TargetList"`
}

// TargetAccount is target account
type TargetAccount struct {
    Service string `json:"Service"`
    Name    string `json:"Name"`
    Role    string `json:"Role"`
}

func handler(req Request) (string, error) {

    sess := session.Must(session.NewSession())
    assumeRoler := sts.New(sess)

    eventParam := &health.DescribeEventsInput{
        Filter: &health.EventFilter{
            EventStatusCodes: []*string{
                aws.String(health.EventStatusCodeOpen),
                aws.String(health.EventStatusCodeUpcoming),
            },
            EventTypeCategories: []*string{
                aws.String(health.EventTypeCategoryScheduledChange),
            },
        },
    }

    for _, target := range req.TargetList {
        creds := stscreds.NewCredentialsWithClient(assumeRoler, target.Role)
        svc := health.New(sess, aws.NewConfig().WithRegion("us-east-1").WithCredentials(creds))

        var arns []*string
        err := svc.DescribeEventsPages(eventParam, func(resp *health.DescribeEventsOutput, lastPage bool) bool {
            for _, event := range resp.Events {
                arns = append(arns, event.Arn)
            }
            return true
        })

        if err != nil {
            log.Println(err.Error())
            continue
        }

        entityParam := &health.DescribeAffectedEntitiesInput{
            Filter: &health.EntityFilter{
                EventArns: arns,
            },
        }

        var entities []*health.AffectedEntity
        err = svc.DescribeAffectedEntitiesPages(entityParam, func(resp *health.DescribeAffectedEntitiesOutput, lastPage bool) bool {
            entities = append(entities, resp.Entities...)
            return true
        })

        if err != nil {
            log.Println(err.Error())
            continue
        }

        entities = removeUnknown(entities)

        err = client.PostServiceMetricValues(target.Service, []*mackerel.MetricValue{
            &mackerel.MetricValue{
                Name:  "monitor-maintenance." + target.Name,
                Time:  nowTime.Unix(),
                Value: len(entities),
            },
        })
        if err != nil {
            log.Println(err.Error())
            continue
        }
    }
    return "ok", nil
}

func removeUnknown(e []*health.AffectedEntity) []*health.AffectedEntity {
    result := []*health.AffectedEntity{}
    for _, v := range e {
        if *v.EntityValue == health.EntityStatusCodeUnknown {
            continue
        }
        result = append(result, v)
    }
    return result
}

func main() {
    lambda.Start(handler)
}

  • Health APIの実行にはビジネス、またはエンタープライズサポートプランが必要です
  • AWS Healthのエンドポイントは米国東部(バージニア北部)リージョンのみです
  • 試してる間にメンテナンスが発生していなくて、ちゃんとテストできていません。。。
    • 後日問題あれば修正します。スミマセン<( ̄∇ ̄)ゞ

実行結果(想定)

mackerl_sample-service.png
このように描かれるんじゃないかなーと思っています。。

最後に

いよいよ週明け、12/23(月)は5周年イベントのMackerel Day#2ですね!
すごく楽しみです!
https://mackerelio.connpass.com/event/152583/

Why do not you register as a user and use Qiita more conveniently?
  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
Sign up for free and join this conversation.
If you already have a Qiita account