NewRelicのアラートをEventBridgeを通してLambdaで受ける

awsnewrelic

NewRelic のアラートの Destination には Slack や PagerDuty などがあり、Workflows でこれらを指定し NRQL で追加のデータを付加することもできる。ただアラート文面をカスタマイズしたり、別の処理を行ったりするには Lambda などでハンドリングする必要がある。Webhook & Custom header で受けることもできるが EventBridge だと外にエンドポイントを開く必要がない。

EventBridge の destination を作成し、

Workflows の Notify 先に設定すると Evnet source の選択があり、まだ作られていない場合 EventBridge のパートナーイベントソースが作られる。

デフォルトで event template は次のような値になっている。

{
	"id": {{ json issueId }},
	"issueUrl": {{ json issuePageUrl }},
	"title": {{ json annotations.title.[0] }},
	"priority": {{ json priority }},
	"impactedEntities": {{json entitiesData.names}},
	"totalIncidents": {{json totalIncidents}},
	"state": {{ json state }},
	"trigger": {{ json triggerEvent }},
	"isCorrelated": {{ json isCorrelated }},
	"createdAt": {{ createdAt }},
	"updatedAt": {{ updatedAt }},
	"sources": {{ json accumulations.source }},
	"alertPolicyNames": {{ json accumulations.policyName }},
	"alertConditionNames": {{ json accumulations.conditionName }},
	"workflowName": {{ json workflowName }}
}

パートナーイベントソースをイベントバスに関連づけると同名のイベントバスができ、Schema discovery するとスキーマを確認できるので、

対応する struct を定義する。

type Event struct {
	Account    string    `json:"account"`
	DetailType string    `json:"detail-type"`
	ID         string    `json:"id"`
	Region     string    `json:"region"`
	Resources  []string  `json:"resources"`
	Source     string    `json:"source"`
	Time       time.Time `json:"time"`
	Version    string    `json:"version"`
	Detail     struct {
		ID                  string   `json:"id"`
		IssueURL            string   `json:"issueURL"`
		Title               string   `json:"title"`
		Priority            string   `json:"priority"`
		ImpactedEntities    []string `json:"impactedEntities"`
		TotalIncidents      int      `json:"totalIncidents"`
		State               string   `json:"state"`
		Trigger             string   `json:"trigger"`
		IsCorrelated        bool     `json:"isCorrelated"`
		CreatedAt           int64    `json:"createdAt"`
		UpdatedAt           int64    `json:"updatedAt"`
		Sources             []string `json:"sources"`
		AlertPolicyNames    []string `json:"alertPolicyNames"`
		AlertConditionNames []string `json:"alertConditionNames"`
		WorkloadNames       []string `json:"workloadNames"`
	}
}

func handler(ctx context.Context, event Event) (interface{}, error) {
	fmt.Printf("Received: %+v\n", event)
	...
}

あとは EventBridge から Lambda を呼べるようにして EventBus にルールを作成するとアラートが発火した際に実行されるようになる。

import * as events from 'aws-cdk-lib/aws-events'
import * as targets from 'aws-cdk-lib/aws-events-targets'
    
fn.grantInvoke(new iam.ServicePrincipal('events.amazonaws.com'));

new events.Rule(this, 'NewRelicEventBusRule', {
  eventBus: events.EventBus.fromEventBusArn(
    this,
    'NewRelicEventBus',
    'arn:aws:events:ap-northeast-1:xxxxxx:event-bus/aws.partner/newrelic.com/xxxxxx',
  ),
   eventPattern: {
    source: events.Match.prefix('aws.partner/newrelic.com'),
  },
  targets: [new targets.LambdaFunction(fn, {
    retryAttempts: 1,
  })],
})

参考

AWS EventBridgeとの連携によるインシデント対応の効率化(架電編)

[AWS CDK tips]EventBridge (L2) で Content based filtering を記述する