Lavieleaf

おもったことをなんでも書くことろ

EventBus で RDS のスナップショットの共有を検知する


はじめに

Amazon RDS にはイベントを検知する機能がありますが、スナップショット共有のイベントは検知できないようです。

スナップショット共有は ModifyDBSnapshotAttribute API が使用されます。 CloudTrail で API コールを検知することができるため、スナップショット共有を検知できます。 また、アカウント間のイベントは EventBridge の EventBus を利用することで別アカウントへイベント送信することができます。

今回は、AWS アカウント間でスナップショット共有したときに自動でスナップショットをリストアする仕組みが欲しかったので、 EventBridge を使ってスナップショット共有を検知する仕組みを作ってみました。

RDS スナップショット共有の検知

CloudTrail の API コールを検知するための EventBridge のルールを作成します。 イベントルールは、以下のように作成します。

{
  "source": ["aws.rds"],
  "detail-type": ["AWS API Call via CloudTrail"],
  "detail": {
    "eventSource": ["rds.amazonaws.com"],
    "eventName": ["ModifyDBClusterSnapshotAttribute"],

    "requestParameters": {
      "valuesToAdd": [{ "exists": true }]
    },
    "responseElements": {
      "dBClusterSnapshotIdentifier": [{ "prefix": "sample-cluster" }]
    }
  }
}

いくつかの重要なポイントがあります。 CloudTrail で API コールを検知するときには、 AWS API Call via CloudTrail の detail-type を指定する必要があります。

ModifyDBClusterSnapshotAttribute だけでは、あらゆる変更によってイベントが発火してしまいます。 requestParametersvaluesToAdd がある場合、共有先のアカウント ID が含まれています。共有を解除する場合は、 valuesToRemove が含まれますのでイベントは発火しません。

また、responseElementsdBClusterSnapshotIdentifier にプレフィックスを指定して特定のスナップショットのみイベントを発火するようにしています。

EventBus の作成

EventBridge を用いたイベントの送信には、まず EventBus の作成が必要です。 共有先のアカウントで作成するリソースを Terraform で作成すると以下のようになります。

resource "aws_cloudwatch_event_bus" "event_bus" {
  name = "rds-snapshot-share-event-bus"
}

data "aws_iam_policy_document" "policy" {
  statement {
    sid    = "OtherAccountAccess"
    effect = "Allow"
    actions = [
      "events:PutEvents",
    ]
    resources = [
      aws_cloudwatch_event_bus.event_bus.arn,
    ]

    principals {
      type = "AWS"
      # 共有元の AWS アカウント ID
      identifiers = ["123456789012"]
    }
  }
}

resource "aws_cloudwatch_event_bus_policy" "event_bus" {
  event_bus_name = aws_cloudwatch_event_bus.event_bus.name
  policy         = data.aws_iam_policy_document.policy.json
}

resource "aws_cloudwatch_event_rule" "event_bus" {
  name           = "rds-snapshot-share"
  event_bus_name = aws_cloudwatch_event_bus.event_bus.name

  event_pattern = jsonencode({
    source      = ["aws.rds"]
    detail-type = ["AWS API Call via CloudTrail"]
    detail = {
      eventSource = ["rds.amazonaws.com"]
      eventName   = ["ModifyDBClusterSnapshotAttribute"]

      requestParameters = {
        valuesToAdd = [{ exists = true }],
      }
      responseElements = {
        dBClusterSnapshotIdentifier = [{ prefix = "sample-cluster" }]
      }
    }
  })
}

resource "aws_cloudwatch_event_target" "event_bus" {
  event_bus_name = aws_cloudwatch_event_bus.event_bus.name
  rule           = aws_cloudwatch_event_rule.event_bus.name
  arn            = <共有されたスナップショットのリストア処理>
  role_arn       = aws_iam_role.event_bridge.arn
}

共有元の EventBridge の作成

共有元のアカウントでイベントを送信するためには、 EventBus の ARN を指定してルールを作成します。

resource "aws_cloudwatch_event_rule" "share_snapshot" {
  name        = "rds-modify-db-cluster-snapshot-attribute"

  event_pattern = jsonencode({
    source      = ["aws.rds"]
    detail-type = ["AWS API Call via CloudTrail"]
    detail = {
      eventSource = ["rds.amazonaws.com"]
      eventName   = ["ModifyDBClusterSnapshotAttribute"]
      requestParameters = {
        valuesToAdd = [{ exists = true }],
      }
      responseElements = {
        dBClusterSnapshotIdentifier = [{ prefix = "sample-cluster" }]
      }
    }
  })
}

resource "aws_cloudwatch_event_target" "share_snapshot" {
  rule     = aws_cloudwatch_event_rule.share_snapshot.name
  arn      = <共有先の EventBus  ARN>
  role_arn = aws_iam_role.event_bridge.arn
}

さいごに

AWS の EventBridge と CloudTrail を組み合わせることで、データベースのスナップショット共有を効率的に監視することが可能です。 この機能を活用することで、スナップショットの共有を自動的に検知し、即時にリストアするシステムを構築できます。