RDSインスタンの状態変化に伴うアラーム作成

## ◆概要
AuroraをマルチAZで使用。
Auroraインスタンスは、何かしらの原因で落ちたとしても、設定上自動で回復してくる。
マルチAZ方式をとっている、とっていないで回復してい来る時間は大幅異なっては来るが。

只、データベースインスタンスが何かしらの原因で落ちた場合、自動回復してくるとはいえ
いつ落ちたかなどをしっかり把握しておきたい。

そこで、RDSインスタンスが止まる、起動する、フェールオーバーなどした際にアラームを
発報するようにしたいと思い、EventBrige, StepFuction, SNSを使用してアラームを
作成した。


## ◆構成図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689346/f2937887-3c25-c9e3-a300-be9fa8031e71.png)

１．例えば、Auroraインスタンスがdeleteされた
２．EventBrigeで、AuroraのEventCategory(delete)を検知
３．StepFunctionでSNSに飛ばすメールの作成
４．SNSで任意の送信先にアラームを飛ばす


## ◆作成方法
Terraformを使って作成



## ◆terraformのコード
terraformのコードは、EvntBrige、StepFunction、SNSのみを記載
<br>

#### ★EvntBrige
先ずはEvntBrigeから

```.go
/*---------------------------------------------
 EventBrige Rule                                                                  
---------------------------------------------*/
resource "aws_cloudwatch_event_rule" "rds_event" {
  is_enabled  = true
  description = "RDS State Change Event"
  name        = "RdsStateChangeEvent"

  event_pattern = <<EOF
{
  "source": ["aws.rds"],
  "detail-type": ["RDS DB Cluster Event", "RDS DB Instance Event"],
  "detail": {
    "EventCategories": ["configuration change", "failover", "maintenance", "creation", "deletion"]
  }
}
EOF
}
/*
「event_pattern」
 Auroraの場合は、クラスターで検知するので detail-typeの箇所に[RDS DB Instance Event]は、
 恐らくいらないと思うけど一応入れています。
 また今回のイベント検知条件は、設定変更[configuration change]、フェールオーバー[failover]、
 メンテナンス[maintenance]、インスタンの作成・削除[creation, deletion]
 がされた際にアラームを発報するようにしています。
 EventCategoriesは、RDSのイベントサブスクリプションで特定のイベントカテゴリが見れますので
 そこを参照して好きなカテゴリを選択してみてはと思います。
 */



/*---------------------------------------------
 EventBrige Targets                                                                  
---------------------------------------------*/
resource "aws_cloudwatch_event_target" "rds_event" {
  target_id      = "RDSEventMonitoring"
  event_bus_name = "default"
  arn            = aws_sfn_state_machine.rds_event.arn  // この後作成するStepFunctionのARN
  rule           = aws_cloudwatch_event_rule.rds_event.name  // 先程作成したEventBrigeのRule
  role_arn       = aws_iam_role.events_role.arn         // この後作成するIAM RoleのARN

  input_transformer {
    // ここは別途下記で説明
    input_paths = {
      "account"         = "$.account"
      "region"          = "$.region"
      "time"            = "$.time"
      "SourceArn"       = "$.detail.SourceArn"
      "Message"         = "$.detail.Message"
      "EventCategories" = "$.detail.EventCategories[0]"
    }

    
    input_template = <<EOF
    {
      "subject" : "RDS DB Instance Changed \"<Message> !\".",
      "message" : " At <time> UTC. \n\n The Changed Status of Your RDS Instance !. \n\n RDS Details: \n - Account : <account>. \n - AWS Region : <region>. \n - Source : <SourceArn>. \n - EventCategories : <EventCategories>. \n - Message : <Message>."
    }
    EOF
  }
}
/*
「input_template」
 ターゲット(StepFunction)に送信されるデータをカスタマイズするためのテンプレート
 この時点でメール題名・本文をカスタマイズする(どのような内容の文にしたいか決める)
  < 変数名 >でinput_pathsで指定した変数を使う事が出来る                     
*/


/*---------------------------------------------
 IAM Role                                                                  
---------------------------------------------*/
/* EventBrigeに付与するロール */
resource "aws_iam_role" "events_role" {
  name               = "RdsStateChangeEvent"
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "events.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

/*---------------------------------------------
 IAM Policy                                                                  
---------------------------------------------*/
/*
上記のIAMロールに付与する IAMポリシー
StepFunctionの起動を許可する設定を付与
*/
resource "aws_iam_role_policy" "events_policy" {
  name   = "RdsStateChangeEvent"
  role   = aws_iam_role.events_role.id
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
         "states:*"
        ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}
EOF
}

```

<br>

##### ◇input_paths
ここでは、Input_Path(入力パス) は、Input_Templateで使用したい変数を定義するために
使用されます。JSONパスを使用してイベント内の項目(下記の画像を参照)を参照し、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689346/0c9b8bb5-0dc0-e8e8-895d-3ddf8390059b.png)
それらの値を変数に格納します。

上記画像はEventBrigeのサンプルイベントから参照しています。
RDS DB Cluster Event イベントの例の値です。

変数の格納の仕方は下記のように書きます

```
{
  "timestamp" : "$.time",
  "Account" : "$.account", 
  "Source" : "$.detail.SourceArn",
  "Category : "$.detail.EventCategories[0]"  // EventCategoriesは配列なので[0]が必要
}

```

<br>

#### ★ StepFunction
次は、StepFunction


```.go
/*---------------------------------------------
 StepFunction                                                               
---------------------------------------------*/
resource "aws_sfn_state_machine" "rds_event" {
  name     = "RDS-Event"
  role_arn = aws_iam_role.sfn_role.arn   // この後作成するIAM RoleのARN

  definition = <<EOF
{
  "StartAt": "PublishSns",  // スタート(初めの)のステートマシンを記載する、ここでは「PublishSns」というかこれしかないので
  "States": {
    "PublishSns": {  // ステートマシンの名前を記載する(何でもOKだけどここを変更した場合 StartAtの箇所も変更が必要)
      "End": true,   // これは最後のステートマシンという事を表すもの、今回1個しかないのでここに記載
      "Type": "Task", // ここはドキュメント参考推奨(種類がいっぱいあるのでドキュメント見た方いいと思う)
      "Resource": "arn:aws:states:::aws-sdk:sns:publish",  // これはこういう書き方なので、ここもドキュメント参考推奨
      "Parameters": {
        "TopicArn": "${aws_sns_topic.rds_event.arn}",  // この後作成するSNSのARN
        "Message.$": "$.message",  // EventBrige Targets の input_templateで作成した message変数代入
        "Subject.$": "$.subject"   // EventBrige Targets の input_templateで作成した subject変数代入
      }
    }
  }
}
EOF
}

/*---------------------------------------------
 IAM Role                                                               
---------------------------------------------*/
/* StepFunctionに付与するIAM Role */
resource "aws_iam_role" "sfn_role" {
  name               = "Event-sfn"
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "states.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

/*---------------------------------------------
 IAM Policy                                                               
---------------------------------------------*/
/* 
上記のIAMロールに付与する IAMポリシー
SNSを起動する許可設定 と StepFunctionのログを格納する為のCloudWatch Logsの許可設定
StepFunctionのログを格納をする必要がない場合いらないです
*/
resource "aws_iam_role_policy" "sfn_policy" {
  name   = "Event-sfn"
  role   = aws_iam_role.sfn_role.id
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
          "cloudwatch:*",
          "logs:*",
          "SNS:*"
        ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}
EOF
}

```

<br>

#### ★ SNS
最後は、SNS


```.go
/*---------------------------------------------
 SNS TOPIC                                 
---------------------------------------------*/
resource "aws_sns_topic" "rds_event" {
  name            = "RDS-Event"
  delivery_policy = <<EOF  // DeliveryPolicyは、ほぼほぼデフォルトの設定
{
  "http": {
    "defaultHealthyRetryPolicy": {
      "minDelayTarget": 2,
      "maxDelayTarget": 20,
      "numRetries": 10,
      "numMaxDelayRetries": 3,
      "numNoDelayRetries": 3,
      "numMinDelayRetries": 2,
      "backoffFunction": "exponential"
    },
    "disableSubscriptionOverrides": false,
    "defaultThrottlePolicy": {
      "maxReceivesPerSecond": 10
    }
  }
}
EOF
}

resource "aws_sns_topic_policy" "rds_event" {
  arn = aws_sns_topic.rds_event.arn  // 先程作成したSNSのARN
  policy = jsonencode(
    {
      Id = "default_policy"
      Statement = [
        {  // ここはデフォルトで入っているアクセスポリシーの設定なので一応記載しています
          Action = [
            "SNS:GetTopicAttributes",
            "SNS:SetTopicAttributes",
            "SNS:AddPermission",
            "SNS:RemovePermission",
            "SNS:DeleteTopic",
            "SNS:Subscribe",
            "SNS:ListSubscriptionsByTopic",
            "SNS:Publish",
            "SNS:Receive",
          ]
          Condition = {
            StringEquals = {
              "AWS:SourceOwner" = "各自アカウントID"  // ここのアカウントIDは各自のもの記載
            }
          }
          Effect = "Allow"
          Principal = {
            AWS = "*"
          }
          Resource = aws_sns_topic.rds_event.arn  // // 先程作成したSNSのARN
          Sid      = "default_statement_ID"
        },
        {  // ここからアクセスポリシーに追記した許可設定
          Action = [
            "sns:Publish",
          ]
          Effect = "Allow"
          Principal = {
            Service = [
              "events.amazonaws.com",  // これはいらないと思うけど一応いれてます
              "states.amazonaws.com"   // こっちは必ず必要です
            ]
          }
          Resource = aws_sns_topic.rds_event.arn  // 先程作成したSNSのARN
          Sid      = "RDS-Event"
        },
      ]
      Version = "2008-10-17"
    }
  )
}

resource "aws_sns_topic_subscription" "rds_event" {
  protocol  = "email"
  topic_arn = aws_sns_topic.rds_event.arn  // 先程作成したSNSのARN
  endpoint  = "各自の送信メールアドレス"  // ここの各自のメールアドレスを記載  
}

```

<br>

## ◆最後に
今回は、RDSインスタンスの状態変化に伴うアラーム作成しました。
勿論、EC2インスタンスも作成出来ますので、EventBrige Ruleの変更を
すれば後は同じコードいれるばずです。
また、このログを取りたい場合は、Lambdaを使用すれば出来ますので是非試してみて下さい。

拙い説明でしたが、どこかの誰かのお役に立てれば幸いです。