CloudFormationでCloudWatch Logsの保存期間をAWS Configでチェックして自動設定する

前回ご紹介した、AWS ConfigによるCloudWatch Logsの保持期間のチェック設定をCloudFormationで簡単に導入できるようにしてみました。もしよろしければご活用ください!
2020.11.06

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

前回の記事で、CloudWatch Logsの保持期間をAWS Configでチェックして指定期間に修正するという内容をご紹介しました。

前回はコンソールで作業していましたが、それも面倒なのでCloudFormationで設定できるようにしてみました。

CloudFormationテンプレート

早速ですが、テンプレートは以下のとおりです。

---
AWSTemplateFormatVersion: '2010-09-09'
Description: check and set retention days of the CloudWatch Logs log group by AWS Config
###############################################################
# Metadata
###############################################################
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
# ------------------------------------------------------------#
# Requeired Parameters
# ------------------------------------------------------------#
    - Label:
        default: Required Parameters
      Parameters:
      - ExecutionFrequency
      - NotificationEmailAddress

# ------------------------------------------------------------#
# Option Parameters
# ------------------------------------------------------------#
    - Label:
        default: Option Parameters
      Parameters:
      - RetentionInDays
      - BaseMessage
      - BaseNotificationSubject

###############################################################
# Parameters
###############################################################
Parameters:
  # EB application & Environment
  RetentionInDays:
    Description: "(Option) select cloudwatch logs retention days"
    Default: "Default"
    Type: String
    AllowedValues:
      - Default
      - 1
      - 3
      - 5
      - 7
      - 14
      - 30
      - 60
      - 90
      - 120
      - 150
      - 180
      - 365
      - 400
      - 545
      - 731
      - 1827
      - 3653
  BaseMessage:
    Description: "(Option) enter messages for notification"
    Default: "Default"
    Type: String
  BaseNotificationSubject:
    Description: "(Option) enter notification subject"
    Default: "Default"
    Type: String
  ExecutionFrequency:
    Description: "(Required) select execution frequency which AWS Config runs evaluations"
    Default: TwentyFour_Hours
    Type: String
    AllowedValues:
      - One_Hour
      - Six_Hour
      - Three_Hour
      - Twelve_Hours
      - TwentyFour_Hours
  NotificationEmailAddress:
    Description: "(Required) Enter e-mail address for notification"
    Type: String

###############################################################
# Conditions
###############################################################
Conditions:
  SetRetentionInDays: !Not [ !Equals [ !Ref RetentionInDays, "Default" ] ]
  SetBaseMessage: !Not [ !Equals [ !Ref BaseMessage, "Default" ] ]
  SetBaseNotificationSubject: !Not [ !Equals [ !Ref BaseNotificationSubject, "Default" ] ]

###############################################################
# Resources
###############################################################
Resources:
# ------------------------------------------------------------#
# IAM Role
# ------------------------------------------------------------#
  IamRole:
    Type: AWS::IAM::Role
    Properties: 
      RoleName: ssm-automation-role-for-put-cloudwatch-logs-retention-policy
      Description: "role for ssm automation"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement: 
          - Effect: "Allow"
            Principal: 
              Service: "ssm.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies: 
        - PolicyName: sns-publish
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              Effect: "Allow"
              Action: "sns:Publish"
              Resource: "*"
        - PolicyName: ssm-automation-put-cloudwatch-logs-retention-days
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              Effect: "Allow"
              Action: "logs:PutRetentionPolicy"
              Resource: "*"

# ------------------------------------------------------------#
# SSM Automation Document
# ------------------------------------------------------------#
  SetCloudWatchLoggroupRetentionDaysDocument:
    Type: AWS::SSM::Document
    Properties: 
      Content:
        description: Set CloudWatch Log Group Retention period
        schemaVersion: '0.3'
        assumeRole: '{{ AutomationAssumeRole }}'
        parameters:
          AutomationAssumeRole:
            type: String
            description: (Required) The ARN of the role that allows Automation to perform the actions on your behalf.
          logGroupName:
            type: String
            description: (Required) The name of CloudWatch Loggroup.
          retentionInDays:
            type: Integer
            description: (Optional) Retention Days of cloudwatch loggroup. default 365 days.
            default: 365
          BasePublishMessage:
            type: String
            description: (Optional) The message to include in the SNS notification.
            default: There is no retentionInDays set for the cloudwatch logs. And set RetentionInDays the logs.
          SnsTopicARN:
            type: String
            description: (Required) The ARN of the SNS topic to publish the notification to.
          BaseNotificationSubject:
            type: String
            description: (Optional) Subject of the notification.
            default: CloudWatch Logs Retention Check
        mainSteps:
          - name: PutRetentionPolicy
            action: 'aws:executeAwsApi'
            inputs:
              Service: logs
              Api: PutRetentionPolicy
              logGroupName: '{{logGroupName}}'
              retentionInDays: '{{retentionInDays}}'
          - name: PublishMessage
            action: 'aws:executeAwsApi'
            inputs:
              Service: sns
              Api: Publish
              Message: '{{logGroupName}} : {{ BasePublishMessage }}'
              TopicArn: '{{ SnsTopicARN }}'
              Subject: '{{ BaseNotificationSubject }} - {{logGroupName}}'
            isEnd: true
      DocumentType: Automation
      Name: SetCloudWatchLoggroupRetentionDays

# ------------------------------------------------------------#
# AWS Config Rule
# ------------------------------------------------------------#
  ConfigRule:
    Type: AWS::Config::ConfigRule
    Properties: 
      ConfigRuleName: cw-loggroup-retention-period-check
      Description: "Checks whether Amazon CloudWatch LogGroup retention period is set to specific number of days. The rule is NON_COMPLIANT if the retention period is not set or is less than the configured retention period."
      MaximumExecutionFrequency: !Ref ExecutionFrequency
      Source: 
        Owner: AWS
        SourceIdentifier: CW_LOGGROUP_RETENTION_PERIOD_CHECK
      Scope:
        ComplianceResourceTypes: 
          - "AWS::Logs::LogGroup"

# ------------------------------------------------------------#
# AWS Config Remediation Action Configuration
# ------------------------------------------------------------#
  RemediationConfiguration:
    Type: AWS::Config::RemediationConfiguration
    Properties: 
      Automatic: true
      ConfigRuleName: !Ref ConfigRule
      MaximumAutomaticAttempts: 5
      RetryAttemptSeconds: 60
      TargetId: !Ref SetCloudWatchLoggroupRetentionDaysDocument
      TargetType: "SSM_DOCUMENT"
      TargetVersion: "1"
      Parameters:
        AutomationAssumeRole:
          StaticValue:
            Values:
              - !GetAtt [IamRole, "Arn"]
        logGroupName:
          ResourceValue:
            Value: RESOURCE_ID
        retentionInDays:
          StaticValue:
            Values:
              - !If [ SetRetentionInDays, !Ref RetentionInDays, !Ref AWS::NoValue ]
        BasePublishMessage:
          StaticValue:
            Values:
              - !If [ SetBaseMessage, !Ref BaseMessage, !Ref AWS::NoValue ]
        SnsTopicARN:
          StaticValue:
            Values:
              - !Ref NotificationSnsTopic
        BaseNotificationSubject:
          StaticValue:
            Values:
              - !If [ SetBaseNotificationSubject, !Ref BaseNotificationSubject, !Ref AWS::NoValue ]

# ------------------------------------------------------------#
# Amazon SNS
# ------------------------------------------------------------#
  NotificationSnsTopic:
    Type: AWS::SNS::Topic
    Properties: 
      #DisplayName: String
      #KmsMasterKeyId: String
      Subscription: 
        - Endpoint: !Ref NotificationEmailAddress
          Protocol: "email"
      TopicName: cloudwatch-logs-retention-day-check

Conditionsの利用

このテンプレートのポイントはConditionsの利用です。

Systems ManagerのAutomationドキュメントでは、パラメータとして必須のものとオプションのもので2種類があります。オプションのパラメータは、AWS Configの修復アクションでは必須ではありません。

必須ではないので、「何も指定しなければAutomationドキュメントのデフォルトが利用」されます。

これをCloudFormationでも実現したかったので、Conditionsを利用してスタック作成時にオプションのパラメータの内容を変更していない場合は、Automationドキュメントのデフォルトを利用するようにしています。

使い方

CloudFormationのコンソールから上記テンプレートファイルをアップロードします。
パラメータの指定は下記図のようにRequiredと記載している項目のみでOKです。

01-create-stack

各パラメーターの意味は下記の通りです。

パラメータ 内容 備考
ExecutionFrequency Twelve_Hours AWS Configのチェック間隔の指定
NotificationEmailAddress 非準拠リソース検知時の通知先のメールアドレス
RetentionInDays (オプション)ロググループの保持期間(日)の指定 デフォルトで365日
BaseMessage (オプション)メール通知するメッセージ内容
BaseNotificationSubject (オプション)通知メールの件名

オプションとしている項目(上記画像でDefaultと表示されている項目)は、カスタマイズしたい場合に、その内容を記載してください。デフォルトのままの場合は、Automationドキュメントで指定されているデフォルト値が設定されます。

今回はログの保持期間 (RetentionInDays) をデフォルトの 365 から 400 に変更してみました。

スタックを作成するとAmazon SNSも作成されるので、SNSのサブスクライブ確認のメールが届きます。そのまま「Confirm subsctiption」をクリックして通知が届くようにしておきましょう。

02-sns-subscription

確認

スタックの作成が完了したら AWS Configの画面から確認してみます。デフォルトから変更した保持期間がちゃんと「400日」になっていますね。また、デフォルトから変更していない項目は、修復アクションのパラメータには何も設定されていないことが確認できました。

03-remediation-action

2つのロググループに保持期間が設定されていないことも検知しています。

04-no-retention-day

再評価してみます。

05-re-evaluate

正常に修復アクションが実行されました。

06-complete-action

修復アクションにより ロググループの保持期間が400日(13ヶ月)に変更されました!

07-retention-days

最後に

これで簡単にログの保持期間のチェックが設定できるようになりました。もしよろしければご活用ください。

以上です。