Skip to content

Commit 1b35643

Browse files
CCM-10036 Cost Anomaly Alarms and Alerts (#535)
1 parent ca247d4 commit 1b35643

File tree

5 files changed

+117
-0
lines changed

5 files changed

+117
-0
lines changed

infrastructure/terraform/components/acct/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
| Name | Description | Type | Default | Required |
1414
|------|-------------|------|---------|:--------:|
1515
| <a name="input_aws_account_id"></a> [aws\_account\_id](#input\_aws\_account\_id) | The AWS Account ID (numeric) | `string` | n/a | yes |
16+
| <a name="input_budget_amount"></a> [budget\_amount](#input\_budget\_amount) | The budget amount in USD for the account | `number` | `500` | no |
1617
| <a name="input_component"></a> [component](#input\_component) | The variable encapsulating the name of this component | `string` | `"acct"` | no |
18+
| <a name="input_cost_alarm_recipients"></a> [cost\_alarm\_recipients](#input\_cost\_alarm\_recipients) | A list of email addresses to receive alarm notifications | `list(string)` | `[]` | no |
19+
| <a name="input_cost_anomaly_threshold"></a> [cost\_anomaly\_threshold](#input\_cost\_anomaly\_threshold) | The threshold percentage for cost anomaly detection | `number` | `10` | no |
1720
| <a name="input_default_tags"></a> [default\_tags](#input\_default\_tags) | A map of default tags to apply to all taggable resources within the component | `map(string)` | `{}` | no |
1821
| <a name="input_environment"></a> [environment](#input\_environment) | The name of the tfscaffold environment | `string` | n/a | yes |
1922
| <a name="input_group"></a> [group](#input\_group) | The group variables are being inherited from (often synonmous with account short-name) | `string` | n/a | yes |
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
resource "aws_budgets_budget" "main" {
2+
name = "${local.csi}-monthly-budget"
3+
budget_type = "COST"
4+
limit_amount = var.budget_amount
5+
limit_unit = "USD"
6+
time_unit = "MONTHLY"
7+
8+
notification {
9+
comparison_operator = "GREATER_THAN"
10+
notification_type = "FORECASTED"
11+
threshold = 100
12+
threshold_type = "PERCENTAGE"
13+
subscriber_sns_topic_arns = [aws_sns_topic.costs.arn]
14+
}
15+
16+
notification {
17+
comparison_operator = "GREATER_THAN"
18+
notification_type = "ACTUAL"
19+
threshold = 100
20+
threshold_type = "PERCENTAGE"
21+
subscriber_sns_topic_arns = [aws_sns_topic.costs.arn]
22+
}
23+
24+
notification {
25+
comparison_operator = "GREATER_THAN"
26+
notification_type = "ACTUAL"
27+
threshold = 85
28+
threshold_type = "PERCENTAGE"
29+
subscriber_sns_topic_arns = [aws_sns_topic.costs.arn]
30+
}
31+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
resource "aws_ce_anomaly_monitor" "anomaly_monitor" {
2+
name = "${local.csi}-anomaly-monitor"
3+
monitor_type = "DIMENSIONAL"
4+
monitor_dimension = "SERVICE"
5+
}
6+
7+
resource "aws_ce_anomaly_subscription" "realtime_subscription" {
8+
name = "${local.csi}-realtime-subscription"
9+
frequency = "IMMEDIATE"
10+
threshold_expression {
11+
dimension {
12+
key = "ANOMALY_TOTAL_IMPACT_PERCENTAGE"
13+
values = [var.cost_anomaly_threshold]
14+
match_options = ["GREATER_THAN_OR_EQUAL"]
15+
}
16+
}
17+
monitor_arn_list = [
18+
aws_ce_anomaly_monitor.anomaly_monitor.arn,
19+
]
20+
21+
subscriber {
22+
type = "SNS"
23+
address = aws_sns_topic.costs.arn
24+
}
25+
depends_on = [
26+
aws_sns_topic_policy.costs,
27+
]
28+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
resource "aws_sns_topic" "costs" {
2+
name = "${local.csi}-costs"
3+
kms_master_key_id = module.kms.key_id
4+
}
5+
6+
resource "aws_sns_topic_policy" "costs" {
7+
arn = aws_sns_topic.costs.arn
8+
9+
policy = data.aws_iam_policy_document.sns_costs.json
10+
}
11+
12+
data "aws_iam_policy_document" "sns_costs" {
13+
statement {
14+
sid = "AllowSNSCosts"
15+
effect = "Allow"
16+
17+
actions = [
18+
"SNS:Publish",
19+
]
20+
21+
resources = [
22+
aws_sns_topic.costs.arn,
23+
]
24+
25+
principals {
26+
type = "Service"
27+
identifiers = ["budgets.amazonaws.com", "costalerts.amazonaws.com"]
28+
}
29+
}
30+
}
31+
32+
resource "aws_sns_topic_subscription" "costs" {
33+
for_each = toset(var.cost_alarm_recipients)
34+
topic_arn = aws_sns_topic.costs.arn
35+
protocol = "email"
36+
endpoint = each.value
37+
}

infrastructure/terraform/components/acct/variables.tf

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,21 @@ variable "oam_sink_id" {
120120
type = string
121121
default = ""
122122
}
123+
124+
variable "cost_alarm_recipients" {
125+
type = list(string)
126+
description = "A list of email addresses to receive alarm notifications"
127+
default = []
128+
}
129+
130+
variable "budget_amount" {
131+
type = number
132+
description = "The budget amount in USD for the account"
133+
default = 500
134+
}
135+
136+
variable "cost_anomaly_threshold" {
137+
type = number
138+
description = "The threshold percentage for cost anomaly detection"
139+
default = 10
140+
}

0 commit comments

Comments
 (0)