はじめに
昨年まで AWS Budgets の自動調整予算は CloudFormation で未対応と記載がされていたのでテンプレートで作成したあとにCLIで一括更新するという非常に手間なことをやっていたのですが、いつの間にか設定できるようになっていたので複数アカウントへ一括で適用するためのテンプレートを作りました。
前提
マルチアカウント構成の AWS Budgets の設定を CloudFormation StackSets を利用して構築します。
事前準備
- Chatbot と Slack との連携済みであること
- Slack 通知先のチャネルが作成済みであること
- 予算(予測)通知用チャネル
- 予算(実績)通知用チャネル
- 構築は us-east-1 で行う(変更可)
- 適用するアカウントのエイリアス名がMappingsへ設定してあること
- 通知された予算がどのアカウントなのか分かりやすくするためにMappingsでアカウント毎にエイリアス名を設定しています
テンプレート
毎月、毎日の予算を自動調整予算で設定します。
CostTypes
はお好みで変更してください。
AWSTemplateFormatVersion: 2010-09-09
Description: AWS Budgets
Parameters:
Prefix:
Type: String
Default: "example"
Env:
Type: String
Default: "prod"
AllowedValues:
- prod
ChatbotSlackWorkspaceId:
Type: String
Description: Chatbot Workspace ID
Default: "XXXXXXXXX"
AllowedValues:
- "XXXXXXXXX"
ChatbotSlackChannelForecasted:
Type: String
Description: Slack Channel ID Forecasted
Default: "XXXXXXXXX"
AllowedValues:
- "XXXXXXXXX"
ChatbotSlackChannelActual:
Type: String
Description: Slack Channel ID Actual
Default: "XXXXXXXXX"
AllowedValues:
- "XXXXXXXXX"
Conditions:
IsUSE1: !Equals [ !Ref "AWS::Region", "us-east-1" ]
Mappings:
Budget:
"888888888888":
AccountAliasName: account-a
"999999999999":
AccountAliasName: account-b
Resources:
# --------------------------------------------------
# Budgets 予測
# --------------------------------------------------
BudgetsAlarmForecastedTopic:
Type: AWS::SNS::Topic
Condition: IsUSE1
Properties:
DisplayName: !Sub "${Prefix} [${Env}] AWS Budgets Alarm Forecasted"
TopicName: !Sub "${Prefix}-${Env}-awsbudgets-alarm-forecasted-topic"
BudgetsAlarmForecastedTopicPolicy:
Type: AWS::SNS::TopicPolicy
Condition: IsUSE1
Properties:
PolicyDocument:
Statement:
- Effect: "Allow"
Principal:
Service: "budgets.amazonaws.com"
Action: "sns:Publish"
Resource:
- !Ref BudgetsAlarmForecastedTopic
- Sid: "__default_statement_ID"
Effect: "Allow"
Principal:
AWS: "*"
Action:
- "sns:GetTopicAttributes"
- "sns:SetTopicAttributes"
- "sns:AddPermission"
- "sns:RemovePermission"
- "sns:DeleteTopic"
- "sns:Subscribe"
- "sns:ListSubscriptionsByTopic"
- "sns:Publish"
- "sns:Receive"
Resource:
- !Ref BudgetsAlarmForecastedTopic
Condition:
StringEquals:
AWS:SourceOwner: !Ref "AWS::AccountId"
Topics:
- !Ref BudgetsAlarmForecastedTopic
BudgetsAlarmForecastedTopicSubscription:
Type: AWS::SNS::Subscription
Condition: IsUSE1
Properties:
Endpoint: https://global.sns-api.chatbot.amazonaws.com
Protocol: https
RawMessageDelivery: false
Region: !Ref AWS::Region
TopicArn: !Ref BudgetsAlarmForecastedTopic
BudgetsAlarmForecastedChatbot:
Type: AWS::Chatbot::SlackChannelConfiguration
Condition: IsUSE1
Properties:
ConfigurationName: !Sub "${Prefix}-${Env}-awsbudgets-forecasted"
IamRoleArn: !GetAtt ChatbotRole.Arn
LoggingLevel: ERROR
SlackChannelId: !Ref ChatbotSlackChannelForecasted
SlackWorkspaceId: !Ref ChatbotSlackWorkspaceId
SnsTopicArns:
- !Ref BudgetsAlarmForecastedTopic
# --------------------------------------------------
# Budgets 実績
# --------------------------------------------------
BudgetsAlarmActualTopic:
Type: AWS::SNS::Topic
Condition: IsUSE1
Properties:
DisplayName: !Sub "${Prefix} [${Env}] AWS Budgets Alarm Actual"
TopicName: !Sub "${Prefix}-${Env}-awsbudgets-alarm-actual-topic"
BudgetsAlarmActualTopicPolicy:
Type: AWS::SNS::TopicPolicy
Condition: IsUSE1
Properties:
PolicyDocument:
Statement:
- Effect: "Allow"
Principal:
Service: "budgets.amazonaws.com"
Action: "sns:Publish"
Resource:
- !Ref BudgetsAlarmActualTopic
- Sid: "__default_statement_ID"
Effect: "Allow"
Principal:
AWS: "*"
Action:
- "sns:GetTopicAttributes"
- "sns:SetTopicAttributes"
- "sns:AddPermission"
- "sns:RemovePermission"
- "sns:DeleteTopic"
- "sns:Subscribe"
- "sns:ListSubscriptionsByTopic"
- "sns:Publish"
- "sns:Receive"
Resource:
- !Ref BudgetsAlarmActualTopic
Condition:
StringEquals:
AWS:SourceOwner: !Ref "AWS::AccountId"
Topics:
- !Ref BudgetsAlarmActualTopic
BudgetsAlarmActualTopicSubscription:
Type: AWS::SNS::Subscription
Condition: IsUSE1
Properties:
Endpoint: https://global.sns-api.chatbot.amazonaws.com
Protocol: https
RawMessageDelivery: false
Region: !Ref AWS::Region
TopicArn: !Ref BudgetsAlarmActualTopic
BudgetsAlarmActualChatbot:
Type: AWS::Chatbot::SlackChannelConfiguration
Condition: IsUSE1
Properties:
ConfigurationName: !Sub "${Prefix}-${Env}-awsbudgets-actual"
IamRoleArn: !GetAtt ChatbotRole.Arn
LoggingLevel: ERROR
SlackChannelId: !Ref ChatbotSlackChannelActual
SlackWorkspaceId: !Ref ChatbotSlackWorkspaceId
SnsTopicArns:
- !Ref BudgetsAlarmActualTopic
# --------------------------------------------------
# Chatbot関連
# --------------------------------------------------
ChatbotRole:
Type: AWS::IAM::Role
Condition: IsUSE1
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action: "sts:AssumeRole"
Principal:
Service: "chatbot.amazonaws.com"
Policies:
- PolicyName: inline
PolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "cloudwatch:Describe*"
- "cloudwatch:Get*"
- "cloudwatch:List*"
Effect: "Allow"
Resource: "*"
RoleName: !Sub "${Prefix}-${Env}-awsbudgets-chatbot-role"
AWSBudgetsForecastedChatbotLogGroup:
Type: AWS::Logs::LogGroup
Condition: IsUSE1
DeletionPolicy: Delete
UpdateReplacePolicy: Delete
Properties:
LogGroupName: !Sub "/aws/chatbot/${Prefix}-${Env}-awsbudgets-forecasted"
RetentionInDays: 90
AWSBudgetsActualChatbotLogGroup:
Type: AWS::Logs::LogGroup
Condition: IsUSE1
DeletionPolicy: Delete
UpdateReplacePolicy: Delete
Properties:
LogGroupName: !Sub "/aws/chatbot/${Prefix}-${Env}-awsbudgets-actual"
RetentionInDays: 90
# --------------------------------------------------
# Budgets 毎月
# --------------------------------------------------
BudgetMonthly:
Type: AWS::Budgets::Budget
Condition: IsUSE1
Properties:
Budget:
AutoAdjustData:
AutoAdjustType: HISTORICAL
HistoricalOptions:
BudgetAdjustmentPeriod: 6
BudgetName: !Sub
- "Monthly Budget ${AccountAliasName}"
- AccountAliasName: !FindInMap [Budget, !Ref AWS::AccountId, AccountAliasName]
BudgetType: COST
CostTypes:
IncludeCredit: false
IncludeDiscount: true
IncludeOtherSubscription: true
IncludeRecurring: true
IncludeRefund: false
IncludeSubscription: true
IncludeSupport: true
IncludeTax: true
IncludeUpfront: false
UseAmortized: false
UseBlended: false
TimeUnit: MONTHLY
NotificationsWithSubscribers:
# --------------------------------------------------
# 予測
# --------------------------------------------------
- Notification:
ComparisonOperator: GREATER_THAN
NotificationType: FORECASTED
Threshold: 120
ThresholdType: PERCENTAGE
Subscribers:
- Address: !Ref BudgetsAlarmForecastedTopic
SubscriptionType: SNS
# --------------------------------------------------
# 実績
# --------------------------------------------------
- Notification:
ComparisonOperator: GREATER_THAN
NotificationType: ACTUAL
Threshold: 120
ThresholdType: PERCENTAGE
Subscribers:
- Address: !Ref BudgetsAlarmActualTopic
SubscriptionType: SNS
# --------------------------------------------------
# Budgets 毎日
# --------------------------------------------------
BudgetDaily:
Type: AWS::Budgets::Budget
Condition: IsUSE1
Properties:
Budget:
AutoAdjustData:
AutoAdjustType: HISTORICAL
HistoricalOptions:
BudgetAdjustmentPeriod: 60
BudgetName: !Sub
- "Daily Budgets ${AccountAliasName}"
- AccountAliasName: !FindInMap [Budget, !Ref AWS::AccountId, AccountAliasName]
BudgetType: COST
CostTypes:
IncludeCredit: false
IncludeDiscount: true
IncludeOtherSubscription: true
IncludeRecurring: true
IncludeRefund: false
IncludeSubscription: true
IncludeSupport: true
IncludeTax: false
IncludeUpfront: false
UseAmortized: false
UseBlended: false
TimeUnit: DAILY
NotificationsWithSubscribers:
- Notification:
ComparisonOperator: GREATER_THAN
NotificationType: ACTUAL
Threshold: 120
ThresholdType: PERCENTAGE
Subscribers:
- Address: !Ref BudgetsAlarmActualTopic
SubscriptionType: SNS
自動調整予算の設定値について
TimeUnit
毎に上限値が存在します。
BudgetAdjustmentPeriod
の値をお好みの適切な値に調整してください。
TimeUnit | 最大値 |
---|---|
DAILY | 60 |
MONTHLY | 12 |
QUARTERLY | 4 |
ANNUALLY | 1 |
AutoAdjustData:
AutoAdjustType: HISTORICAL
HistoricalOptions:
BudgetAdjustmentPeriod: 60