diff --git a/.github/workflows/continuous-integration-tfsec.yml b/.github/workflows/continuous-integration-tfsec.yml index 5de9507..b9fe565 100644 --- a/.github/workflows/continuous-integration-tfsec.yml +++ b/.github/workflows/continuous-integration-tfsec.yml @@ -12,3 +12,4 @@ jobs: uses: aquasecurity/tfsec-pr-commenter-action@v1.3.1 with: github_token: ${{ github.token }} + working_directory: '' diff --git a/README.md b/README.md index d33e632..a0f0a2e 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ for dxw's Dalmatian hosting platform. |------|---------| | [archive](#provider\_archive) | 2.4.0 | | [aws](#provider\_aws) | 5.21.0 | +| [aws.useast1](#provider\_aws.useast1) | 5.21.0 | ## Modules @@ -50,8 +51,14 @@ for dxw's Dalmatian hosting platform. | [aws_s3_bucket_public_access_block.cloudtrail](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | | [aws_s3_bucket_server_side_encryption_configuration.cloudtrail](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | | [aws_s3_bucket_versioning.cloudtrail](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | +| [aws_sns_topic.cloudwatch_opsgenie_alerts](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource | +| [aws_sns_topic.cloudwatch_opsgenie_alerts_us_east_1](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource | | [aws_sns_topic.cloudwatch_slack_alerts](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource | +| [aws_sns_topic_policy.sns_cloudwatch_opsgenie_alerts](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_policy) | resource | +| [aws_sns_topic_policy.sns_cloudwatch_opsgenie_alerts_us_east_1](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_policy) | resource | | [aws_sns_topic_policy.sns_cloudwatch_slack_alerts](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_policy) | resource | +| [aws_sns_topic_subscription.cloudwatch_opsgenie_alerts_subscription](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource | +| [aws_sns_topic_subscription.cloudwatch_opsgenie_alerts_subscription_us_east_1](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource | | [aws_sns_topic_subscription.cloudwatch_slack_alerts_lambda_subscription](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource | | [archive_file.cloudwatch_slack_alerts_lambda](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | @@ -63,9 +70,11 @@ for dxw's Dalmatian hosting platform. | [aws\_region](#input\_aws\_region) | AWS region in which to launch resources | `string` | n/a | yes | | [cloudtrail\_log\_prefix](#input\_cloudtrail\_log\_prefix) | Cloudtrail log prefix | `string` | n/a | yes | | [cloudtrail\_log\_retention](#input\_cloudtrail\_log\_retention) | Cloudtrail log retention in days. Set to 0 to keep all logs. | `number` | n/a | yes | +| [cloudwatch\_opsgenie\_alerts\_sns\_endpoint](#input\_cloudwatch\_opsgenie\_alerts\_sns\_endpoint) | The Opsgenie SNS endpoint. https://support.atlassian.com/opsgenie/docs/integrate-opsgenie-with-incoming-amazon-sns/ | `string` | n/a | yes | | [cloudwatch\_slack\_alerts\_channel](#input\_cloudwatch\_slack\_alerts\_channel) | The Slack channel for CloudWatch alerts | `string` | n/a | yes | | [cloudwatch\_slack\_alerts\_hook\_url](#input\_cloudwatch\_slack\_alerts\_hook\_url) | The Slack webhook URL for CloudWatch alerts | `string` | n/a | yes | | [enable\_cloudtrail](#input\_enable\_cloudtrail) | Enable Cloudtrail | `bool` | n/a | yes | +| [enable\_cloudwatch\_opsgenie\_alerts](#input\_enable\_cloudwatch\_opsgenie\_alerts) | Enable CloudWatch Opsgenie alerts. This creates an SNS topic to which alerts and pipelines can send messages, which are then sent to the Opsgenie SNS endpoint. | `bool` | n/a | yes | | [enable\_cloudwatch\_slack\_alerts](#input\_enable\_cloudwatch\_slack\_alerts) | Enable CloudWatch Slack alerts. This creates an SNS topic to which alerts and pipelines can send messages, which are then picked up by a Lambda function that forwards them to a Slack webhook. | `bool` | n/a | yes | | [enable\_s3\_tfvars](#input\_enable\_s3\_tfvars) | enable\_s3\_tfvars | `bool` | n/a | yes | | [project\_name](#input\_project\_name) | Project name to be used as a prefix for all resources | `string` | n/a | yes | diff --git a/cloudwatch-opsgenie-alerts-sns.tf b/cloudwatch-opsgenie-alerts-sns.tf new file mode 100644 index 0000000..3107ae6 --- /dev/null +++ b/cloudwatch-opsgenie-alerts-sns.tf @@ -0,0 +1,116 @@ +resource "aws_kms_key" "cloudwatch_opsgenie_alerts_sns" { + count = local.cloudwatch_opsgenie_alerts_sns_kms_encryption ? 1 : 0 + + description = "This key is used to encrypt data in SNS for the CloudWatch Opsgenie Alerts (${local.project_name})" + deletion_window_in_days = 10 + enable_key_rotation = true + + policy = templatefile( + "${path.module}/policies/kms-key-policy-sns-topic.json.tpl", + { + services = jsonencode(["cloudwatch.amazonaws.com"]), + sns_topic_arn = "arn:aws:sns:${local.aws_region}:${local.aws_account_id}:${local.project_name}-cloudwatch-opsgenie-alerts" + } + ) +} + +resource "aws_kms_alias" "cloudwatch_opsgenie_alerts_sns" { + count = local.cloudwatch_opsgenie_alerts_sns_kms_encryption ? 1 : 0 + + name = "alias/${local.project_name}-cloudwatch-opsgenie-alerts-sns" + target_key_id = aws_kms_key.cloudwatch_opsgenie_alerts_sns[0].key_id +} + +resource "aws_sns_topic" "cloudwatch_opsgenie_alerts" { + count = local.enable_cloudwatch_opsgenie_alerts ? 1 : 0 + + name = "${local.project_name}-cloudwatch-opsgenie-alerts" + kms_master_key_id = local.cloudwatch_opsgenie_alerts_sns_kms_encryption ? aws_kms_alias.cloudwatch_opsgenie_alerts_sns[0].name : null +} + +resource "aws_kms_key" "cloudwatch_opsgenie_alerts_sns_us_east_1" { + count = local.cloudwatch_opsgenie_alerts_sns_kms_encryption ? 1 : 0 + + provider = aws.useast1 + + description = "This key is used to encrypt data in SNS for the CloudWatch Opsgenie Alerts (${local.project_name})" + deletion_window_in_days = 10 + enable_key_rotation = true + + policy = templatefile( + "${path.module}/policies/kms-key-policy-sns-topic.json.tpl", + { + services = jsonencode(["cloudwatch.amazonaws.com"]), + sns_topic_arn = "arn:aws:sns:us-east-1:${local.aws_account_id}:${local.project_name}-cloudwatch-opsgenie-alerts" + } + ) +} + +resource "aws_kms_alias" "cloudwatch_opsgenie_alerts_sns_us_east_1" { + count = local.cloudwatch_opsgenie_alerts_sns_kms_encryption ? 1 : 0 + + provider = aws.useast1 + + name = "alias/${local.project_name}-cloudwatch-opsgenie-alerts-sns" + target_key_id = aws_kms_key.cloudwatch_opsgenie_alerts_sns_us_east_1[0].key_id +} + + +resource "aws_sns_topic" "cloudwatch_opsgenie_alerts_us_east_1" { + count = local.enable_cloudwatch_opsgenie_alerts ? 1 : 0 + + provider = aws.useast1 + + name = "${local.project_name}-cloudwatch-opsgenie-alerts" + kms_master_key_id = local.cloudwatch_opsgenie_alerts_sns_kms_encryption ? aws_kms_alias.cloudwatch_opsgenie_alerts_sns_us_east_1[0].name : null +} + +resource "aws_sns_topic_policy" "sns_cloudwatch_opsgenie_alerts" { + count = local.enable_cloudwatch_opsgenie_alerts ? 1 : 0 + + arn = aws_sns_topic.cloudwatch_opsgenie_alerts[0].arn + policy = templatefile( + "${path.root}/policies/sns-events-policy.json.tpl", + { + sns_arn = aws_sns_topic.cloudwatch_opsgenie_alerts[0].arn + aws_account_id = local.aws_account_id + } + ) +} + +resource "aws_sns_topic_policy" "sns_cloudwatch_opsgenie_alerts_us_east_1" { + count = local.enable_cloudwatch_opsgenie_alerts ? 1 : 0 + + provider = aws.useast1 + + arn = aws_sns_topic.cloudwatch_opsgenie_alerts_us_east_1[0].arn + policy = templatefile( + "${path.root}/policies/sns-events-policy.json.tpl", + { + sns_arn = aws_sns_topic.cloudwatch_opsgenie_alerts_us_east_1[0].arn + aws_account_id = local.aws_account_id + } + ) +} + +resource "aws_sns_topic_subscription" "cloudwatch_opsgenie_alerts_subscription" { + count = local.enable_cloudwatch_opsgenie_alerts ? 1 : 0 + + topic_arn = aws_sns_topic.cloudwatch_opsgenie_alerts[0].arn + protocol = "https" + endpoint = local.cloudwatch_opsgenie_alerts_sns_endpoint + endpoint_auto_confirms = true + confirmation_timeout_in_minutes = 10 +} + +resource "aws_sns_topic_subscription" "cloudwatch_opsgenie_alerts_subscription_us_east_1" { + count = local.enable_cloudwatch_opsgenie_alerts ? 1 : 0 + + provider = aws.useast1 + + topic_arn = aws_sns_topic.cloudwatch_opsgenie_alerts_us_east_1[0].arn + protocol = "https" + endpoint = local.cloudwatch_opsgenie_alerts_sns_endpoint + endpoint_auto_confirms = true + confirmation_timeout_in_minutes = 10 +} diff --git a/locals.tf b/locals.tf index b408cb4..130881a 100644 --- a/locals.tf +++ b/locals.tf @@ -18,6 +18,10 @@ locals { cloudwatch_slack_alerts_hook_url = var.cloudwatch_slack_alerts_hook_url cloudwatch_slack_alerts_channel = var.cloudwatch_slack_alerts_channel + enable_cloudwatch_opsgenie_alerts = var.enable_cloudwatch_opsgenie_alerts + cloudwatch_opsgenie_alerts_sns_kms_encryption = var.cloudwatch_opsgenie_alerts_sns_kms_encryption && local.enable_cloudwatch_opsgenie_alerts + cloudwatch_opsgenie_alerts_sns_endpoint = var.cloudwatch_opsgenie_alerts_sns_endpoint + default_tags = { Project = local.project_name, } diff --git a/policies/kms-key-policy-sns-topic.json.tpl b/policies/kms-key-policy-sns-topic.json.tpl new file mode 100644 index 0000000..803a942 --- /dev/null +++ b/policies/kms-key-policy-sns-topic.json.tpl @@ -0,0 +1,16 @@ +{ + "Effect": "Allow", + "Principal": { + "Service": ${services} + }, + "Action": [ + "kms:Decrypt", + "kms:GenerateDataKey" + ], + "Resource": "*", + "Condition": { + "StringEquals": { + "kms:EncryptionContext:aws:sns:topicArn": "${sns_topic_arn}" + } + } +} diff --git a/providers.tf b/providers.tf index 899804b..3bada62 100644 --- a/providers.tf +++ b/providers.tf @@ -5,3 +5,12 @@ provider "aws" { tags = local.default_tags } } + +provider "aws" { + region = "us-east-1" + alias = "useast1" + + default_tags { + tags = local.default_tags + } +} diff --git a/variables.tf b/variables.tf index e0f6080..b60d66f 100644 --- a/variables.tf +++ b/variables.tf @@ -72,3 +72,18 @@ variable "cloudwatch_slack_alerts_channel" { description = "The Slack channel for CloudWatch alerts" type = string } + +variable "enable_cloudwatch_opsgenie_alerts" { + description = "Enable CloudWatch Opsgenie alerts. This creates an SNS topic to which alerts and pipelines can send messages, which are then sent to the Opsgenie SNS endpoint." + type = bool +} + +variable "cloudwatch_opsgenie_alerts_sns_endpoint" { + description = "The Opsgenie SNS endpoint. https://support.atlassian.com/opsgenie/docs/integrate-opsgenie-with-incoming-amazon-sns/" + type = string +} + +variable "cloudwatch_opsgenie_alerts_sns_kms_encryption" { + desctiption = "Use KMS encryption with the Opsgenie Alerts SNS topic" + type = bool +}