Skip to content

Commit

Permalink
chore: adding the initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
gambol99 committed Mar 11, 2024
0 parents commit 677c5d9
Show file tree
Hide file tree
Showing 7 changed files with 407 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
name: Release

on:
push:
tags:
- "v*"

jobs:
release:
uses: appvia/appvia-cicd-workflows/.github/workflows/terraform-module-release.yml@main
name: GitHub Release
16 changes: 16 additions & 0 deletions .github/workflows/terraform.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
name: Terraform
on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
module-validation:
uses: appvia/appvia-cicd-workflows/.github/workflows/terraform-module-validation.yml@main
name: Module Validation
with:
working-directory: .
85 changes: 85 additions & 0 deletions .terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 5.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.40.0 |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_slack_notfications"></a> [slack\_notfications](#module\_slack\_notfications) | terraform-aws-modules/notify-slack/aws | 6.1.1 |
| <a name="module_sns"></a> [sns](#module\_sns) | terraform-aws-modules/sns/aws | v6.0.1 |

## Resources

| Name | Type |
|------|------|
| [aws_ce_anomaly_monitor.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ce_anomaly_monitor) | resource |
| [aws_ce_anomaly_subscription.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ce_anomaly_subscription) | resource |
| [aws_sns_topic_subscription.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
| [aws_sns_topic.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/sns_topic) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_create_sns_topic"></a> [create\_sns\_topic](#input\_create\_sns\_topic) | Indicates whether to create an SNS topic for notifications | `bool` | `true` | no |
| <a name="input_monitors"></a> [monitors](#input\_monitors) | A collection of cost anomaly monitors to create | <pre>list(object({<br> name = string<br> monitor_type = optional(string, "DIMENSIONAL")<br> monitor_dimension = optional(string, "SERVICE")<br> monitor_specification = optional(string, null)<br><br> notify = optional(object({<br> frequency = string<br> threshold_expression = optional(any, null)<br> }), {<br> frequency = "DAILY"<br> })<br> }))</pre> | n/a | yes |
| <a name="input_notification"></a> [notification](#input\_notification) | The configuration of the notification | <pre>object({<br> email = optional(object({<br> addresses = list(string)<br> }), null)<br> slack = optional(object({<br> channel = string<br> webhook_url = string<br> }), null)<br> teams = optional(object({<br> webhook_url = string<br> }), null)<br> })</pre> | n/a | yes |
| <a name="input_sns_topic_name"></a> [sns\_topic\_name](#input\_sns\_topic\_name) | The name of an existing or new SNS topic for notifications | `string` | `"cost-anomaly-notifications"` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | n/a | yes |

## Outputs

No outputs.
<!-- END_TF_DOCS -->
186 changes: 186 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#
## Related to the provisioning of services
#

locals {
# The SNS topic ARN to use for the cost anomaly detection
sns_topic_arn = var.create_sns_topic ? module.sns[0].topic_arn : data.aws_sns_topic.current[0].arn
}

#
## We need to lookup the SNS topic ARN, if it exists
#
data "aws_sns_topic" "current" {
count = var.create_sns_topic ? 0 : 1
name = var.sns_topic_name
}

#
## Provision the SNS topic for the cost anomaly detection, if required
#
module "sns" {
source = "terraform-aws-modules/sns/aws"
version = "v6.0.1"
count = var.create_sns_topic ? 1 : 0

name = var.sns_topic_name
tags = var.tags
topic_policy_statements = {
"AllowBudgetsToNotifySNSTopic" = {
actions = ["sns:Publish"]
effect = "Allow"
principals = [{
type = "Service"
identifiers = ["budgets.amazonaws.com"]
}]
}
"AllowLambda" = {
actions = [
"sns:Subscribe",
]
effect = "Allow"
principals = [{
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}]
}
}
}

#
## Provision the cost anomaly detection for services
#
resource "aws_ce_anomaly_monitor" "this" {
for_each = { for x in var.monitors : x.name => x }

name = each.value.name
monitor_type = each.value.monitor_type
monitor_dimension = each.value.monitor_dimension
#monitor_specification = each.value.monitor_specification != "" ? jsonencode(each.value.monitor_specification) : ""
tags = var.tags
}

#
## Provision the subscriptions to the anomaly detection monitors
#
resource "aws_ce_anomaly_subscription" "this" {
for_each = { for x in var.monitors : x.name => x }

name = each.value.name
frequency = each.value.notify.frequency
monitor_arn_list = [aws_ce_anomaly_monitor.this[each.key].arn]
tags = var.tags

dynamic "threshold_expression" {
for_each = [each.value.notify.threshold_expression != null ? [1] : []]
content {
dynamic "dimension" {
for_each = [for x in each.value.notify.threshold_expression : x if lookup(x, "dimension", null) != null]
content {
key = dimension.value.key
match_options = dimension.value.match_options
values = dimension.value.values
}
}

dynamic "cost_category" {
for_each = [for x in each.value.notify.threshold_expression : x if lookup(x, "cost_category", null) != null]
content {
key = cost_category.value.key
match_options = cost_category.value.match_options
values = cost_category.value.values
}
}

dynamic "tags" {
for_each = [for x in each.value.notify.threshold_expression : x if lookup(x, "tags", null) != null]
content {
key = tags.value.key
match_options = tags.value.match_options
values = tags.value.values
}
}

dynamic "and" {
for_each = [for x in each.value.notify.threshold_expression : x if lookup(x, "and", null) != null]
content {
dynamic "dimension" {
for_each = and.value.and.dimension != null ? [and.value.and.dimension] : []
content {
key = dimension.value.key
match_options = dimension.value.match_options
values = dimension.value.values
}
}
}
}

dynamic "or" {
for_each = [for x in each.value.notify.threshold_expression : x if lookup(x, "or", null) != null]
content {
dynamic "dimension" {
for_each = or.value.or.dimension != null ? [or.value.or.dimension] : []
content {
key = dimension.value.key
match_options = dimension.value.match_options
values = dimension.value.values
}
}
}
}

dynamic "not" {
for_each = [for x in each.value.notify.threshold_expression : x if lookup(x, "not", null) != null]
content {
dynamic "dimension" {
for_each = not.value.not.dimension != null ? [not.value.not.dimension] : []
content {
key = dimension.value.key
match_options = dimension.value.match_options
values = dimension.value.values
}
}
}
}
}
}

subscriber {
address = local.sns_topic_arn
type = "SNS"
}

depends_on = [module.sns]
}

#
## Provision any additional notification subscriptions (email)
#
resource "aws_sns_topic_subscription" "main" {
for_each = { for x in var.notification.email.addresses : x => x }

endpoint = each.value
protocol = "email"
topic_arn = local.sns_topic_arn
}


#
## Provision a slack notification if required
#
# tfsec:ignore:aws-lambda-enable-tracing
# tfsec:ignore:aws-lambda-restrict-source-arn
module "slack_notfications" {
count = var.notification.slack != null ? 1 : 0
source = "terraform-aws-modules/notify-slack/aws"
version = "6.1.1"

create_sns_topic = false
lambda_function_name = "cost-anomaly-detection"
slack_channel = var.notification.slack.channel
slack_username = ":aws: (Cost Anomaly Detection)"
slack_webhook_url = var.notification.slack.webhook_url
sns_topic_name = var.sns_topic_name
tags = var.tags
}

50 changes: 50 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

variable "tags" {
description = "A map of tags to add to all resources"
type = map(string)
}

variable "monitors" {
description = "A collection of cost anomaly monitors to create"
type = list(object({
name = string
monitor_type = optional(string, "DIMENSIONAL")
monitor_dimension = optional(string, "SERVICE")
monitor_specification = optional(string, null)

notify = optional(object({
frequency = string
threshold_expression = optional(any, null)
}), {
frequency = "DAILY"
})
}))
}

variable "notification" {
description = "The configuration of the notification"
type = object({
email = optional(object({
addresses = list(string)
}), null)
slack = optional(object({
channel = string
webhook_url = string
}), null)
teams = optional(object({
webhook_url = string
}), null)
})
}

variable "create_sns_topic" {
description = "Indicates whether to create an SNS topic for notifications"
type = bool
default = true
}

variable "sns_topic_name" {
description = "The name of an existing or new SNS topic for notifications"
type = string
default = "cost-anomaly-notifications"
}
Loading

0 comments on commit 677c5d9

Please sign in to comment.