📝

CloudWatchの請求アラームを作成するCloudFormationテンプレートを作ってみた

2021/07/16に公開

以前、
CloudWatchで請求アラームを作成する
マルチアカウントで各アカウントの請求アラームを作成してみた
という記事を書いたときに、コンソールでの同じ作業の繰り返しが多かったので、CloudFormationテンプレートを作りたいと思っていました。

今回はそのテンプレートを作ったので紹介します。

CloudWatchで請求アラームを作成するテンプレート

まずは、単一のアカウント内で請求アラームを作成するテンプレートです。
構成図や細かい手順などはCloudWatchで請求アラームを作成するをご覧ください。

テンプレート

BillingAlarm.json
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "Create Billing Alarm",
  "Parameters": {
    "EmailAddress": {
      "Description": "Enter your EmailAddress",
      "Type": "String"
    },
    "Threshold": {
      "Description": "Enter Threshold",
      "Type": "Number",
      "Default": 10
    }
  },
  "Resources": {
    "CloudWatchAlarm": {
      "Type": "AWS::CloudWatch::Alarm",
      "Properties": {
        "AlarmName": "BillingAlarm",
        "ActionsEnabled": true,
        "AlarmActions": [
          {
            "Ref": "SNSTopic"
          }
        ],
        "MetricName": "EstimatedCharges",
        "Namespace": "AWS/Billing",
        "Statistic": "Maximum",
        "Dimensions": [
          {
            "Name": "Currency",
            "Value": "USD"
          }
        ],
        "Period": 21600,
        "EvaluationPeriods": 1,
        "DatapointsToAlarm": 1,
        "Threshold": {
          "Ref": "Threshold"
        },
        "ComparisonOperator": "GreaterThanThreshold",
        "TreatMissingData": "missing"
      }
    },
    "SNSTopic": {
      "Type": "AWS::SNS::Topic",
      "Properties": {
        "DisplayName": "",
        "TopicName": "Default_CloudWatch_Alarms_Topic"
      }
    },
    "SNSTopicPolicy": {
      "Type": "AWS::SNS::TopicPolicy",
      "Properties": {
        "PolicyDocument": {
          "Fn::Sub": "{\"Version\":\"2008-10-17\",\"Id\":\"__default_policy_ID\",\"Statement\":[{\"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\":\"${SNSTopic}\",\"Condition\":{\"StringEquals\":{\"AWS:SourceOwner\":\"${AWS::AccountId}\"}}}]}"
        },
        "Topics": [
          {
            "Ref": "SNSTopic"
          }
        ]
      }
    },
    "SNSSubscription": {
      "Type": "AWS::SNS::Subscription",
      "Properties": {
        "TopicArn": {
          "Ref": "SNSTopic"
        },
        "Endpoint": {
          "Ref": "EmailAddress"
        },
        "Protocol": "email",
        "Region": {
          "Ref": "AWS::Region"
        }
      }
    }
  }
}

簡単な説明

  • CloudWatchアラーム
    デフォルトで「10USDを超えたら」という条件を指定しています。
    アラーム状態になったらSNSトピックに通知します。
  • SNSトピック
    Default_CloudWatch_Alarms_Topicという名前のトピックを作成します。
  • SNSサブスクリプション
    Default_CloudWatch_Alarms_Topicにメールアドレスを紐づけます。
    メールアドレスは、CloudFormationスタック作成時にパラメーターで入力します。
  • SNSトピックポリシー
    コンソールでトピックを作成したときに生成されるものと同じです。

注意点

  • 請求アラームはバージニア北部リージョンでしか作成できませんので、CloudFormationスタックもバージニア北部リージョンで作成してください。
  • スタック作成後、指定したメールアドレスの承認が必要なので、メールを確認し、承認を行ってください。

マルチアカウントで各アカウントの請求アラームを作成

続いて、マルチアカウント運用している場合に、マスターアカウントで一括で各アカウントのアラームを作成するテンプレートです。
構成図や細かい手順などはマルチアカウントで各アカウントの請求アラームを作成してみたをご覧ください。

テンプレート

MultiAccountBillingAlarm.json
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "Create LinkedAccount Billing Alarm",
  "Parameters": {
    "Threshold1": {
      "Description": "Enter Threshold",
      "Type": "Number",
      "Default": 10
    },
    "LinkedAccount1": {
      "Description": "Enter LinkedAccount1",
      "Type": "String"
    },
    "Threshold2": {
      "Description": "Enter Threshold",
      "Type": "Number",
      "Default": 10
    },
    "LinkedAccount2": {
      "Description": "Enter LinkedAccount2",
      "Type": "String"
    },
    "Threshold3": {
      "Description": "Enter Threshold",
      "Type": "Number",
      "Default": 10
    },
    "LinkedAccount3": {
      "Description": "Enter LinkedAccount3",
      "Type": "String"
    },
    "SnsTopicArn": {
      "Description": "Enter your SNS Topic ARN",
      "Type": "String"
    }
  },
  "Metadata": {
    "AWS::CloudFormation::Interface": {
      "ParameterGroups": [
        {
          "Label": {
            "default": "Account A"
          },
          "Parameters": ["LinkedAccount1", "Threshold1"]
        },
        {
          "Label": {
            "default": "Account B"
          },
          "Parameters": ["LinkedAccount2", "Threshold2"]
        },
        {
          "Label": {
            "default": "Account C"
          },
          "Parameters": ["LinkedAccount3", "Threshold3"]
        },
        {
          "Label": {
            "default": "SNS Topic Arn"
          },
          "Parameters": ["SnsTopicArn"]
        }
      ]
    }
  },
  "Resources": {
    "CloudWatchAlarm1": {
      "Type": "AWS::CloudWatch::Alarm",
      "Properties": {
        "AlarmName": "BillingAlarm1",
        "AlarmDescription": "アカウントAの料金アラーム",
        "ActionsEnabled": true,
        "AlarmActions": [
          {
            "Ref": "SnsTopicArn"
          }
        ],
        "MetricName": "EstimatedCharges",
        "Namespace": "AWS/Billing",
        "Statistic": "Maximum",
        "Dimensions": [
          {
            "Name": "Currency",
            "Value": "USD"
          },
          {
            "Name": "LinkedAccount",
            "Value": {
              "Ref": "LinkedAccount1"
            }
          }
        ],
        "Period": 21600,
        "EvaluationPeriods": 1,
        "DatapointsToAlarm": 1,
        "Threshold": {
          "Ref": "Threshold1"
        },
        "ComparisonOperator": "GreaterThanOrEqualToThreshold",
        "TreatMissingData": "missing"
      }
    },
    "CloudWatchAlarm2": {
      "Type": "AWS::CloudWatch::Alarm",
      "Properties": {
        "AlarmName": "BillingAlarm2",
        "AlarmDescription": "アカウントBの料金アラーム",
        "ActionsEnabled": true,
        "AlarmActions": [
          {
            "Ref": "SnsTopicArn"
          }
        ],
        "MetricName": "EstimatedCharges",
        "Namespace": "AWS/Billing",
        "Statistic": "Maximum",
        "Dimensions": [
          {
            "Name": "Currency",
            "Value": "USD"
          },
          {
            "Name": "LinkedAccount",
            "Value": {
              "Ref": "LinkedAccount2"
            }
          }
        ],
        "Period": 21600,
        "EvaluationPeriods": 1,
        "DatapointsToAlarm": 1,
        "Threshold": {
          "Ref": "Threshold2"
        },
        "ComparisonOperator": "GreaterThanOrEqualToThreshold",
        "TreatMissingData": "missing"
      }
    },
    "CloudWatchAlarm3": {
      "Type": "AWS::CloudWatch::Alarm",
      "Properties": {
        "AlarmName": "BillingAlarm3",
        "AlarmDescription": "アカウントCの料金アラーム",
        "ActionsEnabled": true,
        "AlarmActions": [
          {
            "Ref": "SnsTopicArn"
          }
        ],
        "MetricName": "EstimatedCharges",
        "Namespace": "AWS/Billing",
        "Statistic": "Maximum",
        "Dimensions": [
          {
            "Name": "Currency",
            "Value": "USD"
          },
          {
            "Name": "LinkedAccount",
            "Value": {
              "Ref": "LinkedAccount3"
            }
          }
        ],
        "Period": 21600,
        "EvaluationPeriods": 1,
        "DatapointsToAlarm": 1,
        "Threshold": {
          "Ref": "Threshold3"
        },
        "ComparisonOperator": "GreaterThanOrEqualToThreshold",
        "TreatMissingData": "missing"
      }
    }
  }
}

簡単な説明

作成するリソースはCloudWatchアラーム3つです。
基本的に以下のパラメーターを設定するだけです。

  • Threshold1~3
    各アカウントのしきい値を設定
  • LinkedAccount1~3
    各アカウントIDを設定
  • SnsTopicArn
    既存のSNSトピックのARNを設定

注意点

  • AWS Organizationsのマスターアカウントのバージニア北部リージョンでスタックを作成してください。
  • パラメーター名やアラーム名は適当なので自由に変えてください。
  • 上記テンプレートでは3つのアラームを作成しますが、パラメーターやアラームをコピペで追加したり、不要であれば削除して数を調整してください。

その他

「きっとCDKでやるといいのかなあ」
とか思ってますが、「CDKなにそれおいしいの」レベルなので、今回は使用していません。

まとめ

今回はCloudWatchの請求アラームを作成するCloudFormationテンプレートを作ってみました。
いずれのテンプレートも、大枠はFormer2を使用して作成し、細かいところは手を加えたというかんじです。
どなたかの参考になれば幸いです。
そしてFormer2最高!!

Discussion