AWS Chatbotと統合された AWS コスト異常検出 (Cost Anomaly Detection)を試してみた

AWS Chatbotと統合された AWS コスト異常検出 (Cost Anomaly Detection)を試してみた

AWS Chatbot と統合された AWS コスト異常検出 (Cost Anomaly Detection) を利用したSlack通知を AWS CloudFormation で設定してみました。
Clock Icon2022.04.19 09:50

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

AWSチームのすずきりょうです。

2022年3月、AWS コスト異常検出 (Cost Anomaly Detection) のアップデートで、AWS Chatbot との統合がサポートされました。

AWS コスト異常検出の結果を、AWS Chatbotを利用して Slackに通知する設定を CloudFormation で行う機会がありましたので、紹介させていただきます。

CloudFormation設定

リージョン

AWS コスト異常検出はグローバルサービスとなるため、バージニアリージョン(us-east-1)のCloudFormationを利用しました。

AnomalyMonitor

  • AWSのサービスを対象とした、コスト異常検出モニターを作成しました。
  AnomalyServiceMonitor:
    Type: AWS::CE::AnomalyMonitor
    Properties:
      MonitorName: AWS services
      MonitorType: DIMENSIONAL
      MonitorDimension: SERVICE
  • AWSサービスを対象としたモニターは、1アカウントにつき1件のみ作成可能です。

カスタムモニター

  • 一括請求(コンソリデーテッドビリング)のマスターアカウントでは、連結アカウント、コストカテゴリ、コスト分類タグを対象とした、カスタム(CUSTOM)モニターが作成可能です。

  • コスト分類タグを利用したカスタムモニター例

        Type: AWS::CE::AnomalyMonitor
        Properties:
          MonitorName: !Sub 'Tags-CmBillingGroup'
          MonitorType: CUSTOM
          MonitorSpecification: '{"Tags":{"Key":"CmBillingGroup","Values":["web"]}}'
    

AnomalySubscription

  • 1ドル以上のコスト影響をSNSトピックに通知するサブスクリプションを作成しました。
  • 通知の閾値(Threshold)は、検出履歴の結果や、システム規模に応じ調整してご利用ください。

  AnomalySubscriptionLow:
    Type: AWS::CE::AnomalySubscription
    Properties:
      SubscriptionName: Subscription-low-1usd
      Threshold: 1
      Frequency: IMMEDIATE
      MonitorArnList:
        - !Ref 'AnomalyServiceMonitor'
      Subscribers:
        - Type: SNS
          Address: !Ref 'TopicLow'

  TopicLow:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName: !Sub '${AWS::StackName}-low'

ChatBot

以下記事で紹介された Chatbot 設定を踏襲しました。

Slackワークスペースの認証や、ワークスペースID、スラックチャネルIDの取得などは、当記事を参照ください。

Parameters:
  SlackWorkspaceId:
    Description: SlackWorkspaceId
    Type: String
    Default: T0XXXXXXXXX
  SlackChannelIdLow:
    Description: SlackChannelId (low)
    Type: String
    Default: C0XXXXXXXX3
Resources:
  SlackChannelLow:
    Type: AWS::Chatbot::SlackChannelConfiguration
    Properties:
      ConfigurationName: !Sub '${AWS::StackName}-low'
      IamRoleArn: !GetAtt 'IamRole.Arn'
      LoggingLevel: INFO
      SlackChannelId: !Ref 'SlackChannelIdLow'
      SlackWorkspaceId: !Ref 'SlackWorkspaceId'
      SnsTopicArns:
        - !Ref 'TopicLow'
  IamRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - chatbot.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      Policies: !Ref 'AWS::NoValue'
      RoleName: !Ref 'AWS::NoValue'
  NotificationPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: Chatbot-Notification-Policy
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - cloudwatch:Describe*
              - cloudwatch:Get*
              - cloudwatch:List*
            Resource:
              - '*'
      Roles:
        - !Ref 'IamRole'

動作例

今回設定を試みたアカウントでは、1ヶ月で11件のコスト異常検出が記録されていました。

  • コスト異常検出の検出履歴

  • Slack通知サンプル

データベース(Aurora)のメンテナンスに伴う、インポート、エクスポートなどに伴うストレージコストの増加が検出されていました。

まとめ

AWS コスト異常検出を利用する事で、AWS Budgetsの予算管理では補足できない AWSアカウントのコスト増加や、異常な利用の発生を 早期に検出出来る場合があります。

今回利用した AWS コスト異常検出、 AWS Chatbot は、利用するSNSなどのAWSサービスの実費のみで利用可能です。ぜひご活用ください。

参考テンプレート

  • AWS コスト異常検出と、AWS Budgets の月額予算の釣果を Chatbot経由で通知します。
AWSTemplateFormatVersion: '2010-09-09'
Description: Chatbot (slack) notification template for cost anomaly detection

Parameters:
  SlackWorkspaceId:
    Description: SlackWorkspaceId
    Type: String
    Default: T0XXXXXXXXX
  SlackChannelIdHigh:
    Description: SlackChannelId (high)
    Type: String
    Default: C0XXXXXXXX1
  SlackChannelIdMid:
    Description: SlackChannelId (mid)
    Type: String
    Default: C0XXXXXXXX2
  SlackChannelIdLow:
    Description: SlackChannelId (low)
    Type: String
    Default: C0XXXXXXXX3

Resources:
  # CE AnomalyMonitor
  AnomalyServiceMonitor:
    Type: AWS::CE::AnomalyMonitor
    Properties:
      MonitorName: AWS services
      MonitorType: DIMENSIONAL
      MonitorDimension: SERVICE

  AnomalySubscriptionHigh:
    Type: AWS::CE::AnomalySubscription
    Properties:
      SubscriptionName: Subscription-high-30usd
      Threshold: 30
      Frequency: IMMEDIATE
      MonitorArnList:
        - !Ref 'AnomalyServiceMonitor'
      Subscribers:
        - Type: SNS
          Address: !Ref 'TopicHigh'
  AnomalySubscriptionMid:
    Type: AWS::CE::AnomalySubscription
    Properties:
      SubscriptionName: Subscription-mid-10usd
      Threshold: 10
      Frequency: IMMEDIATE
      MonitorArnList:
        - !Ref 'AnomalyServiceMonitor'
      Subscribers:
        - Type: SNS
          Address: !Ref 'TopicMid'
  AnomalySubscriptionLow:
    Type: AWS::CE::AnomalySubscription
    Properties:
      SubscriptionName: Subscription-low-1usd
      Threshold: 1
      Frequency: IMMEDIATE
      MonitorArnList:
        - !Ref 'AnomalyServiceMonitor'
      Subscribers:
        - Type: SNS
          Address: !Ref 'TopicLow'

  # SNS
  TopicHigh:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName: !Sub '${AWS::StackName}-high'
  TopicMid:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName: !Sub '${AWS::StackName}-mid'
  TopicLow:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName: !Sub '${AWS::StackName}-low'

  # Budget
  Budget:
    Type: AWS::Budgets::Budget
    Properties:
      Budget:
        BudgetLimit:
          Amount: 50
          Unit: USD
        TimeUnit: MONTHLY
        BudgetType: COST
      NotificationsWithSubscribers:
        - Notification:
            NotificationType: ACTUAL
            ComparisonOperator: GREATER_THAN
            Threshold: 120
          Subscribers:
            - SubscriptionType: SNS
              Address: !Ref 'TopicHigh'
        - Notification:
            NotificationType: ACTUAL
            ComparisonOperator: GREATER_THAN
            Threshold: 80
          Subscribers:
            - SubscriptionType: SNS
              Address: !Ref 'TopicMid'
        - Notification:
            NotificationType: ACTUAL
            ComparisonOperator: GREATER_THAN
            Threshold: 60
          Subscribers:
            - SubscriptionType: SNS
              Address: !Ref 'TopicLow'

  # Chatbot
  ## SlackChannelConfiguration
  SlackChannelHigh:
    Type: AWS::Chatbot::SlackChannelConfiguration
    Properties:
      ConfigurationName: !Sub '${AWS::StackName}-high'
      IamRoleArn: !GetAtt 'IamRole.Arn'
      LoggingLevel: INFO
      SlackChannelId: !Ref 'SlackChannelIdHigh'
      SlackWorkspaceId: !Ref 'SlackWorkspaceId'
      SnsTopicArns:
        - !Ref 'TopicHigh'
  SlackChannelMid:
    Type: AWS::Chatbot::SlackChannelConfiguration
    Properties:
      ConfigurationName: !Sub '${AWS::StackName}-mid'
      IamRoleArn: !GetAtt 'IamRole.Arn'
      LoggingLevel: INFO
      SlackChannelId: !Ref 'SlackChannelIdMid'
      SlackWorkspaceId: !Ref 'SlackWorkspaceId'
      SnsTopicArns:
        - !Ref 'TopicMid'
  SlackChannelLow:
    Type: AWS::Chatbot::SlackChannelConfiguration
    Properties:
      ConfigurationName: !Sub '${AWS::StackName}-low'
      IamRoleArn: !GetAtt 'IamRole.Arn'
      LoggingLevel: INFO
      SlackChannelId: !Ref 'SlackChannelIdLow'
      SlackWorkspaceId: !Ref 'SlackWorkspaceId'
      SnsTopicArns:
        - !Ref 'TopicLow'

  ## Chatbot:LogGroup
  LogGroupSlackChannelHigh:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/aws/chatbot/${AWS::StackName}-high'
      RetentionInDays: 7

  LogGroupSlackChannelMid:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/aws/chatbot/${AWS::StackName}-mid'
      RetentionInDays: 7

  LogGroupSlackChannelLow:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/aws/chatbot/${AWS::StackName}-low'
      RetentionInDays: 7

  ## Chatbot:IAM
  IamRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - chatbot.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      Policies: !Ref 'AWS::NoValue'
      RoleName: !Ref 'AWS::NoValue'

  NotificationPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: Chatbot-Notification-Policy
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - cloudwatch:Describe*
              - cloudwatch:Get*
              - cloudwatch:List*
            Resource:
              - '*'
      Roles:
        - !Ref 'IamRole'

  ReadonlyCommandsPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: Chatbot-ReadonlyCommands-Policy
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Deny
            Action:
              - iam:*
              - s3:GetBucketPolicy
              - ssm:*
              - sts:*
              - kms:*
              - cognito-idp:GetSigningCertificate
              - ec2:GetPasswordData
              - ecr:GetAuthorizationToken
              - gamelift:RequestUploadCredentials
              - gamelift:GetInstanceAccess
              - lightsail:DownloadDefaultKeyPair
              - lightsail:GetInstanceAccessDetails
              - lightsail:GetKeyPair
              - lightsail:GetKeyPairs
              - redshift:GetClusterCredentials
              - storagegateway:DescribeChapCredentials
            Resource:
              - '*'
      Roles:
        - !Ref 'IamRole'

この記事をシェアする

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.