RDSイベントを取得してCloudWatchLogsに出力するハンズオン

## はじめに
RDSのイベントについてもログで管理したいという思いから、EventBridgeを利用して、RDSのイベントが発生したログをCloudWatchLogsに出力するまでの構築をCFnで構築してみました。

------------

## 構成図
①VPC作成
②RDS作成
③EventBridge及びCloudWatchLogs作成
上記順番で構築をしていきます。
![](https://cloud5.jp/wp-content/uploads/2022/11/スクリーンショット-2022-11-20-10.25.30-640x383.png)

------------

## ハンズオン

### 1.VPC作成
以前記載したブログ[CloudFormationを使ってVPC構築](https://cloud5.jp/cfn_vpc/)に沿って、VPCを構築していきます。
後続の作業でVPCのCIDRが必要になるので、`Outputs`に下記`追加部分`を足しておきます。
```yaml
Outputs:
# VPC
  VPC:
    Value: !Ref VPC
    Export:
      Name: !Sub "${PJName}-vpc"
###追加部分##############################
 VpcCidr:
    Value: !Ref VpcCidr
    Export:
      Name: !Sub "${PJName}-vpc-cidr"
###ここまで###############################
```
### 2.RDS作成
検証用のMySQLを作成していきます。
そのためDBインスタンスクラスも`db.t2.micro`と最小限です。
ただし今後もテンプレートは活用できそうなので、拡張できるようパラメータなどの記載は多め。

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Description:
  RDS for  MySQL Create

# ------------------------------------------------------------#
# Metadatas
# ------------------------------------------------------------# 
Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "Project Name Prefix"
        Parameters:
          - PJPrefix
      - Label:
          default: "RDS Configuration"
        Parameters:
          - DBInstanceName
          - MySQLMajorVersion
          - MySQLMinorVersion
          - DBInstanceClass
          - DBInstanceStorageSize
          - DBInstanceStorageType
          - DBName
          - DBMasterUserName
          - DBPassword
          - MultiAZ
    ParameterLabels:
      DBInstanceName:
        default: "DBInstanceName"
      MySQLMajorVersion:
        default: "MySQLMajorVersion"
      MySQLMinorVersion:
        default: "MySQLMinorVersion"
      DBInstanceClass:
        default: "DBInstanceClass"
      DBInstanceStorageSize:
        default: "DBInstanceStorageSize"
      DBInstanceStorageType:
        default: "DBInstanceStorageType"
      DBName:
        default: "DBName"
      DBMasterUserName:
        default: "DBUserName"
      DBPassword:
        default: "DBPassword"
      MultiAZ:
        default: "MultiAZ"
      CopyTagsToSnapshot:
        default: "CopyTagsToSnapshot"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  PJPrefix:
    Type: String
    Default: "cfn-inamura"
  DBInstanceName:
    Type: String
    Default: "mysql"
  MySQLMajorVersion:
    Type: String
    Default: "8.0"
  MySQLMinorVersion:
    Type: String
    Default: "28"
    AllowedValues: [ "31", "30", "28", "27", "26", "25", "23" ]
  DBInstanceClass:
    Type: String
    Default: "db.t2.micro" 
  DBInstanceStorageSize:
    Type: String
    Default: "20"
  DBInstanceStorageType:
    Type: String
    Default: "gp2"
  DBName:
    Type: String
    Default: "db"
  DBMasterUserName:
    Type: String
    Default: "dbuser"
    NoEcho: true
    MinLength: 1
    MaxLength: 16
    AllowedPattern: "[a-zA-Z][a-zA-Z0-9]*"
    ConstraintDescription: "must begin with a letter and contain only alphanumeric characters."
  DBPassword: 
    Default: "dbpassword"
    NoEcho: true
    Type: String
    MinLength: 8
    MaxLength: 41
    AllowedPattern: "[a-zA-Z0-9]*"
    ConstraintDescription: "must contain only alphanumeric characters."
  MultiAZ: 
    Default: "false"
    Type: String
    AllowedValues: [ "true", "false" ]
  CopyTagsToSnapshot:
    Default: "false"
    Type: String
    AllowedValues: [ "true", "false" ]

Resources: 
# ------------------------------------------------------------#
#  DBInstance MySQL
# ------------------------------------------------------------#
  DBInstance: 
    Type: "AWS::RDS::DBInstance"
    Properties: 
      DBInstanceIdentifier: !Sub "${PJPrefix}-${DBInstanceName}"
      Engine: MySQL
      EngineVersion: !Sub "${MySQLMajorVersion}.${MySQLMinorVersion}"
      DBInstanceClass: !Ref DBInstanceClass
      AllocatedStorage: !Ref DBInstanceStorageSize
      StorageType: !Ref DBInstanceStorageType
      DBName: !Ref DBName
      MasterUsername: !Ref DBMasterUserName
      MasterUserPassword: !Ref DBPassword
      DBSubnetGroupName: !Ref DBSubnetGroup
      PubliclyAccessible: false
      MultiAZ: !Ref MultiAZ
      PreferredBackupWindow: "18:00-18:30"
      PreferredMaintenanceWindow: "sat:19:00-sat:19:30"
      AutoMinorVersionUpgrade: false
      DBParameterGroupName: !Ref DBParameterGroup  
      VPCSecurityGroups:
        - !Ref RDSSecurityGroup
      CopyTagsToSnapshot: !Ref CopyTagsToSnapshot
      BackupRetentionPeriod: 7
      Tags: 
        - Key: "Name"
          Value: !Ref DBInstanceName
    DeletionPolicy: "Delete"

# ------------------------------------------------------------#
#  DBParameterGroup
# ------------------------------------------------------------#
  DBParameterGroup:
    Type: "AWS::RDS::DBParameterGroup"
    Properties:
      Family: !Sub "MySQL${MySQLMajorVersion}"
      Description: !Sub "${PJPrefix}-${DBInstanceName}-parm"

# ------------------------------------------------------------#
#  SecurityGroup for RDS(MySQL)
# ------------------------------------------------------------#
  RDSSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: !ImportValue cfn-inamura-vpc
      GroupName: !Sub "${PJPrefix}-${DBInstanceName}-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-${DBInstanceName}-sg"
# Rule
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          CidrIp: !ImportValue cfn-inamura-vpc-cidr

# ------------------------------------------------------------#
#  DBSubnetGroup
# ------------------------------------------------------------#
  DBSubnetGroup: 
    Type: "AWS::RDS::DBSubnetGroup"
    Properties: 
      DBSubnetGroupName: !Sub "${PJPrefix}-${DBInstanceName}-subnet"
      DBSubnetGroupDescription: "-"
      SubnetIds: 
        - !ImportValue cfn-inamura-private-subneta
        - !ImportValue cfn-inamura-private-subnetc

# ------------------------------------------------------------#
# Output Parameters
# ------------------------------------------------------------#                
Outputs:
#DBInstance
  DBInstanceID:
    Value: !Ref DBInstance
    Export:
      Name: !Sub "${PJPrefix}-${DBInstanceName}-id"

  DBInstanceEndpoint:
    Value: !GetAtt DBInstance.Endpoint.Address
    Export:
      Name: !Sub "${PJPrefix}-${DBInstanceName}-endpoint"

  DBName:
    Value: !Ref DBName
    Export:
      Name: !Sub "${PJPrefix}-${DBInstanceName}-dbname"

```

### 3.EventBridge、ログ出力先CloudwatchLogs作成
リソースポリシーを作成してEventBridgeに、CloudWatchlogsへログを出力させます。
当初はIAMロールをつくって、どうしたらいいのか分からずハマりました。

AWS公式：[AWS::Logs::ResourcePolicy
](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-resourcepolicy.html)

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Description:
  EventBridge gets RDS events and sends them to CloudWatchlogs
  
# ------------------------------------------------------------#
# Metadatas
# ------------------------------------------------------------# 
Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "Project Name Prefix"
        Parameters:
          - PJPrefix
      - Label:
          default: "CloudWatchLogs Configuration"
        Parameters:
          - LogGroupName
      - Label:
          default: "EventBridge Configuration"
        Parameters:
          - EventBridgeRuleName
          - TargetRDSName

    ParameterLabels:
      LogGroupName:
        default: "LogGroupName"

      EventBridgeRuleName:
        default: "EventBridgeRuleName"

      TargetRDSName:
        default: "TargetRDSName"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  PJPrefix:
    Type: String
    Default: "cfn-inamura"

  LogGroupName:
    Type: String
    Default: "/aws/events/rdbevents"

  EventBridgeRuleName:
    Type: String
    Default: "SendCloudWatchlogs"

  TargetRDSName:
    Type: String
    Default: "cfn-inamura-mysql"

# ------------------------------------------------------------#
#  ResourcePolicy
# ------------------------------------------------------------#
Resources:
  ResourcePolicyForEventsToLogs:
    Type: AWS::Logs::ResourcePolicy
    Properties:
      PolicyName: !Sub "${PJPrefix}-evb-resourcepolicy"
      PolicyDocument: !Sub 
        - |
          {
            "Version": "2012-10-17",
            "Statement": [
              {
                "Effect": "Allow",
                "Principal": {
                  "Service": "events.amazonaws.com"
                },
                "Action": [
                  "logs:CreateLogStream",
                  "logs:PutLogEvents"
                ],
                "Resource": "${CloudWatchLogsLogGroupArn}"
              }
            ]
          }
        - CloudWatchLogsLogGroupArn: !GetAtt RDSEventsLogGroup.Arn

# ------------------------------------------------------------#
#  CloudWatchLogs
# ------------------------------------------------------------#
  RDSEventsLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "${LogGroupName}"

# ------------------------------------------------------------#
#  EventBridge
# ------------------------------------------------------------#
  RDSEventsRule:
    Type: AWS::Events::Rule
    DependsOn: ResourcePolicyForEventsToLogs
    Properties:
      Name: !Sub "${PJPrefix}-evb-${EventBridgeRuleName}"
      Description: "Get RDS events and send them to CloudWatchlogs"
      State: ENABLED
      EventPattern:
        source: 
          - aws.rds
        detail-type:
          - "RDS DB Instance Event"
        resources:
          -  !Sub "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:db:${TargetRDSName}"
      Targets:
        - Arn: !GetAtt RDSEventsLogGroup.Arn
          Id: !Sub "RDSEventsLogGroup"
```

### 挙動の確認
①RDBを再起動する
![](https://cloud5.jp/wp-content/uploads/2022/11/スクリーンショット-2022-11-20-10.28.12-640x136.png)
②再起動後CloudWatchLogsの`/aws/events/rdbevents`を確認すると、ログストリームに2つのログが確認できる。
![](https://cloud5.jp/wp-content/uploads/2022/11/スクリーンショット-2022-11-20-10.28.41-640x327.png)

・下記画面は、新しいタイムスタンプ詳細内容を表示したもので`"Message": "DB instance restarted"`のイベント内容が確認できる。
![](https://cloud5.jp/wp-content/uploads/2022/11/スクリーンショット-2022-11-20-10.38.12-640x392.png)

③EventBridgeのモニタリング（CloudWatchメトリクス）でもオレンジ点で起動していることを確認できる。
![](https://cloud5.jp/wp-content/uploads/2022/11/スクリーンショット-2022-11-20-10.29.44-640x291.png)

------------

## さいごに
リソースポリシーの構築及び、CFnの書き方など勉強すべき部分は多々あるので、こういった構築の方法を自分で考えながら手を動かして作っていくようにしていきます。

## 参考
[CloudWatch Logsのリソースポリシー作成がCloudFormationに対応していた！](https://go-to-k.hatenablog.com/entry/2022/01/31/070000)