AWS Chatbotと統合された AWS コスト異常検出 (Cost Anomaly Detection)を試してみた
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'