
AWS Chatbotと統合された AWS コスト異常検出 (Cost Anomaly Detection)を試してみた
AWS Chatbot と統合された AWS コスト異常検出 (Cost Anomaly Detection) を利用したSlack通知を AWS CloudFormation で設定してみました。
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'