🍏

[terraform] RDSイベントをEventBridgeでNew Relicに送信する

に公開

🌱 はじめに

この記事では、Terraform
EventBridge → New Relic のイベント連携を自動構築する手順をまとめました。

構成のベースは、サーバーワークス様のブログ記事
👉 Amazon RDSイベントをEventBridge経由でNew Relicに送信する方法 を参考にしています🙇‍♀️

🧩 全体構成

EventBridge の API Destination 機能を使うと、
AWSから外部サービス(今回は New Relic)へ直接HTTPSリクエストを送信できます。

構成の流れは次のとおりです。

  1. EventBridge Connection を作成し、New RelicへのAPIキーを登録
  2. API Destination を設定して送信先エンドポイントを定義
  3. IAMロールとポリシー を作成(EventBridgeが呼び出せるようにする)
  4. RDSイベントルール を作成し、トリガーを定義
  5. Event Target でNew Relic APIへ送信

⚙️ 事前準備:変数定義

本記事で利用している主要な変数は以下の通りです。

variables.tf
variable "sysname" {
  description = "システム名"
  type        = string
}

variable "env" {
  description = "環境名"
  type        = string
}

variable "newrelic_license_key" {
  description = "New Relic INGEST-LICENSE APIキー"
  type        = string
  sensitive   = true
}

variable "rds_event_types" {
  description = "RDS Event Types"
  type        = list(string)
  default     = null
}

💡ポイント

  • newrelic_license_key には、New Relic のAPIキーのKey type:「INGEST - LICENSE」を指定します。
  • rds_event_types には、対象とするRDSイベントカテゴリを配列で指定しています。

例:

rds_event_types = ["RDS DB Instance Event", "RDS DB Cluster Event"]

RDSが発行するイベントカテゴリを基に設定する必要があります。何も指定しない場合には、全てのRDSイベントが通知されます。
参考🔗:Amazon RDS イベントカテゴリとイベントメッセージ

1️⃣ EventBridge Connectionの作成

まず、New Relicへの接続を定義します。

resource "aws_cloudwatch_event_connection" "newrelic" {
  name               = "${var.sysname}-${var.env}-newrelic-connection"
  description        = "Connection to New Relic for events"
  authorization_type = "API_KEY"

  auth_parameters {
    api_key {
      key   = "X-License-Key"
      value = var.newrelic_license_key
    }
  }
}

EventBridgeのConnectionを作成すると、var.newrelic_license_keyで渡されたNew RelicのAPIキーの情報が/aws/events!connection/<connection名> という名前でSecrets Managerに自動的に保存されます

2️⃣ API Destinationの作成

New Relicのログやイベント受信APIを呼び出すための宛先を定義します。

resource "aws_cloudwatch_event_api_destination" "newrelic" {
  name                = "${var.sysname}-${var.env}-newrelic-api-destination"
  description         = "API destination for New Relic events"
  http_method         = "POST"
  invocation_endpoint = "https://log-api.newrelic.com/log/v1"
  invocation_rate_limit_per_second = 10
  connection_arn      = aws_cloudwatch_event_connection.newrelic.arn
}

これでEventBridgeがNew RelicのログインジェストAPIにアクセスできるようになります。

3️⃣ IAMロールとポリシーの設定

EventBridgeがAPI Destinationを呼び出せるようにIAMロールを作成します。

resource "aws_iam_role" "eventbridge" {
  name = "${var.sysname}-${var.env}-newrelic-eventbridge-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "events.amazonaws.com"
        }
      }
    ]
  })
}

次に、Secrets ManagerおよびAPI Destinationを呼び出す権限を付与します。

data "aws_caller_identity" "current" {}

resource "aws_iam_role_policy" "eventbridge" {
  name = "${var.sysname}-${var.env}-newrelic-eventbridge-policy"
  role = aws_iam_role.eventbridge.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = ["events:InvokeApiDestination"]
        Resource = aws_cloudwatch_event_api_destination.newrelic.arn
      },
      {
        Effect = "Allow"
        Action = ["secretsmanager:GetSecretValue"]
        Resource = "arn:aws:secretsmanager:*:${data.aws_caller_identity.current.account_id}:secret:events!connection/${aws_cloudwatch_event_connection.newrelic.name}/*"
      }
    ]
  })
}

secretsmanager:GetSecretValue
EventBridgeがSecrets Managerに保存されたNew RelicのAPIキーの情報を取得するために必要な権限を付与しています。

4️⃣ RDSイベントルールの定義

どのイベントをトリガーとして送信するかを指定します。

resource "aws_cloudwatch_event_rule" "rds" {
  name        = "${var.sysname}-${var.env}-newrelic-rds-rule"
  description = "Capture RDS events and send to New Relic"

  event_pattern = jsonencode({
    source      = ["aws.rds"]
    detail-type = var.rds_event_types
  })
}

本記事では監視対象のRDSイベントタイプを配列でパラメータで渡しています。

5️⃣ Event Targetの設定

最後に、キャッチしたイベントをNew Relic API DestinationへPOSTするターゲットを設定します。

resource "aws_cloudwatch_event_target" "newrelic" {
  rule       = aws_cloudwatch_event_rule.rds.name
  arn        = aws_cloudwatch_event_api_destination.newrelic.arn
  role_arn   = aws_iam_role.eventbridge.arn

  http_target {
    header_parameters = {
      "Content-Type" = "application/json"
    }
  }

  depends_on = [aws_iam_role_policy.eventbridge]
}

これで、RDSイベント発生時にEventBridgeが自動でNew Relic APIにリクエストを送信します。
送信されたイベントは、New Relicのログ画面やイベントビューアで確認できます 📊

🔔 おまけ:New Relic→SlackへRDSイベントを通知する

New Relic側でNRQLアラートを設定して、RDSイベントをSlackへ通知するようにしました。

以下はアラートポリシーのTerraformコードの例です。

NRQLで detail.EventID"rds" を含むイベントを検知したらwarning通知を出すようにしています。

resource "newrelic_nrql_alert_condition" "db_event" {
  account_id = <NEWRELIC_ACCOUNT_ID>
  policy_id  = <NEWRELIC_POLICY_ID>
  type       = "static"
  name       = "DB EVENT"

  description = trimspace(<<-EOT
  target is : {{tags.detail.SourceIdentifier}}
  message is : {{tags.detail.Message}}
  EOT
  )

  enabled = true
  violation_time_limit_seconds = 259200

  nrql {
    query = "SELECT COUNT(*) FROM Log WHERE detail.EventID LIKE %rds% FACET detail.SourceIdentifier, detail.Message"
  }

  warning {
    operator              = "above_or_equals"
    threshold             = 1
    threshold_duration    = 300
    threshold_occurrences = "at_least_once"
  }

  fill_option           = "none"
  aggregation_window    = 60
  aggregation_method    = "event_timer"
  aggregation_timer     = 60
  title_template        = "{{conditionName}}: {{tags.detail.Message}} - {{tags.detail.SourceIdentifier}}"
}

💡ポイント

  • detail.SourceIdentifierdetail.Message は、EventBridgeから転送されたRDSイベントのフィールドです。
  • title_template を設定することで、通知メッセージにイベント内容を直接含められます。

🚀 さいごに

上記の構成を1ファイルにまとめたTerraformコード例をまとめて終わりたいと思います。
この記事がどなたかの参考になれば幸いです。
えみり〜でした|ωΦ)ฅ

terraform コード全文
main.tf
variable "sysname" {
  description = "システム名"
  type        = string
}

variable "env" {
  description = "環境名"
  type        = string
}

variable "newrelic_license_key" {
  description = "New Relic INGEST-LICENSE APIキー"
  type        = string
  sensitive   = true
}

variable "rds_event_types" {
  description = "RDS Event Types"
  type        = list(string)
  default     = ["RDS DB Instance Event", "RDS DB Cluster Event"]
}

data "aws_caller_identity" "current" {}

resource "aws_cloudwatch_event_connection" "newrelic" {
  name               = "${var.sysname}-${var.env}-newrelic-connection"
  description        = "Connection to New Relic for events"
  authorization_type = "API_KEY"

  auth_parameters {
    api_key {
      key   = "X-License-Key"
      value = var.newrelic_license_key
    }
  }
}

resource "aws_cloudwatch_event_api_destination" "newrelic" {
  name                = "${var.sysname}-${var.env}-newrelic-api-destination"
  description         = "API destination for New Relic events"
  http_method         = "POST"
  invocation_endpoint = "https://log-api.newrelic.com/log/v1"
  invocation_rate_limit_per_second = 10
  connection_arn      = aws_cloudwatch_event_connection.newrelic.arn
}

resource "aws_iam_role" "eventbridge" {
  name = "${var.sysname}-${var.env}-newrelic-eventbridge-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "events.amazonaws.com"
        }
      }
    ]
  })
}

resource "aws_iam_role_policy" "eventbridge" {
  name = "${var.sysname}-${var.env}-newrelic-eventbridge-policy"
  role = aws_iam_role.eventbridge.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = ["events:InvokeApiDestination"]
        Resource = aws_cloudwatch_event_api_destination.newrelic.arn
      },
      {
        Effect = "Allow"
        Action = ["secretsmanager:GetSecretValue"]
        Resource = "arn:aws:secretsmanager:*:${data.aws_caller_identity.current.account_id}:secret:events!connection/${aws_cloudwatch_event_connection.newrelic.name}/*"
      }
    ]
  })
}

resource "aws_cloudwatch_event_rule" "rds" {
  name        = "${var.sysname}-${var.env}-newrelic-rds-rule"
  description = "Capture RDS events and send to New Relic"

  event_pattern = jsonencode({
    source      = ["aws.rds"]
    detail-type = var.rds_event_types
  })
}

resource "aws_cloudwatch_event_target" "newrelic" {
  rule       = aws_cloudwatch_event_rule.rds.name
  arn        = aws_cloudwatch_event_api_destination.newrelic.arn
  role_arn   = aws_iam_role.eventbridge.arn

  http_target {
    header_parameters = {
      "Content-Type" = "application/json"
    }
  }

  depends_on = [aws_iam_role_policy.eventbridge]
}

🔗 参考資料

Discussion