この記事は公開されてから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'