From ed5fe2609c1b42c67cbcbcc3ff8db2ba6ebe922e Mon Sep 17 00:00:00 2001 From: Nicholas Briganti Date: Thu, 3 Apr 2025 14:07:47 -0700 Subject: [PATCH 1/2] Filter Out Resolved Resources --- handler.py | 27 +- .../Terraform_DEPLOY_AHA.tf | 964 +++++++++--------- .../Terraform_DEPLOY_AHA/terraform.tfvars | 37 +- 3 files changed, 527 insertions(+), 501 deletions(-) diff --git a/handler.py b/handler.py index 57b59fe..e8542d9 100644 --- a/handler.py +++ b/handler.py @@ -25,6 +25,7 @@ logger = logging.getLogger() logger.setLevel(os.environ.get("LOG_LEVEL", "INFO").upper()) + class CachedSecrets: def __init__(self, client): self.client = client @@ -118,7 +119,7 @@ def send_alert(event_details, affected_accounts, affected_entities, event_type): except URLError as e: print("Server connection failed: ", e.reason) pass - #Slack Notification Handling + # Slack Notification Handling if slack_url != "None": for slack_webhook_type in ["services", "triggers", "workflows"]: if ("hooks.slack.com/" + slack_webhook_type) in slack_url: @@ -136,7 +137,11 @@ def send_alert(event_details, affected_accounts, affected_entities, event_type): ) break except HTTPError as e: - print("Got an error while sending message to Slack: ", e.code, e.reason) + print( + "Got an error while sending message to Slack: ", + e.code, + e.reason, + ) except URLError as e: print("Server connection failed: ", e.reason) pass @@ -212,7 +217,7 @@ def send_org_alert( except URLError as e: print("Server connection failed: ", e.reason) pass - #Slack Notification Handling + # Slack Notification Handling if slack_url != "None": for slack_webhook_type in ["services", "triggers", "workflows"]: if ("hooks.slack.com/" + slack_webhook_type) in slack_url: @@ -230,7 +235,11 @@ def send_org_alert( ) break except HTTPError as e: - print("Got an error while sending message to Slack: ", e.code, e.reason) + print( + "Got an error while sending message to Slack: ", + e.code, + e.reason, + ) except URLError as e: print("Server connection failed: ", e.reason) pass @@ -457,6 +466,7 @@ def get_affected_entities(health_client, event_arn, affected_accounts, is_org_mo # don't list entities which are accounts (handled separately for chat applications) def get_resources_from_entities(affected_entity_array): resources = [] + filter_resolved = os.environ.get("FILTER_RESOLVED", "Yes").lower() == "yes" for entity in affected_entity_array: if entity["entityValue"] == "UNKNOWN": @@ -465,6 +475,7 @@ def get_resources_from_entities(affected_entity_array): elif ( entity["entityValue"] != "AWS_ACCOUNT" and entity["entityValue"] != entity["awsAccountId"] + and (not filter_resolved or entity.get("status", "").lower() != "resolved") ): resources.append(entity["entityValue"]) return resources @@ -514,7 +525,7 @@ def update_org_ddb( "ttl": int(sec_now) + delta_hours_sec + 86400, "statusCode": status_code, "affectedAccountIDs": affected_org_accounts, - "latestDescription": event_latestDescription + "latestDescription": event_latestDescription, # Cleanup: DynamoDB entry deleted 24 hours after last update } ) @@ -558,7 +569,7 @@ def update_org_ddb( "ttl": int(sec_now) + delta_hours_sec + 86400, "statusCode": status_code, "affectedAccountIDs": affected_org_accounts, - "latestDescription": event_latestDescription + "latestDescription": event_latestDescription, # Cleanup: DynamoDB entry deleted 24 hours after last update } ) @@ -630,7 +641,7 @@ def update_ddb( "ttl": int(sec_now) + delta_hours_sec + 86400, "statusCode": status_code, "affectedAccountIDs": affected_accounts, - "latestDescription": event_latestDescription + "latestDescription": event_latestDescription, # Cleanup: DynamoDB entry deleted 24 hours after last update } ) @@ -672,7 +683,7 @@ def update_ddb( "ttl": int(sec_now) + delta_hours_sec + 86400, "statusCode": status_code, "affectedAccountIDs": affected_accounts, - "latestDescription": event_latestDescription + "latestDescription": event_latestDescription, # Cleanup: DynamoDB entry deleted 24 hours after last update } ) diff --git a/terraform/Terraform_DEPLOY_AHA/Terraform_DEPLOY_AHA.tf b/terraform/Terraform_DEPLOY_AHA/Terraform_DEPLOY_AHA.tf index b54a0e9..0207810 100644 --- a/terraform/Terraform_DEPLOY_AHA/Terraform_DEPLOY_AHA.tf +++ b/terraform/Terraform_DEPLOY_AHA/Terraform_DEPLOY_AHA.tf @@ -6,95 +6,95 @@ data "aws_caller_identity" "current" {} provider "aws" { - region = var.aha_primary_region - default_tags { - tags = "${var.default_tags}" - } + region = var.aha_primary_region + default_tags { + tags = var.default_tags + } } # Secondary region - provider config locals { - secondary_region = "${var.aha_secondary_region == "" ? var.aha_primary_region : var.aha_secondary_region}" + secondary_region = var.aha_secondary_region == "" ? var.aha_primary_region : var.aha_secondary_region } provider "aws" { - alias = "secondary_region" - region = local.secondary_region - default_tags { - tags = "${var.default_tags}" - } + alias = "secondary_region" + region = local.secondary_region + default_tags { + tags = var.default_tags + } } # Comment below - if needed to use s3_bucket, s3_key for consistency with cf locals { - source_files = ["${path.module}/../../handler.py", "${path.module}/../../messagegenerator.py"] + source_files = ["${path.module}/../../handler.py", "${path.module}/../../messagegenerator.py"] } data "archive_file" "lambda_zip" { - type = "zip" - output_path = "${path.module}/lambda_function.zip" - source { - filename = "${basename(local.source_files[0])}" - content = file("${local.source_files[0]}") - } - source { - filename = "${basename(local.source_files[1])}" - content = file("${local.source_files[1]}") + type = "zip" + output_path = "${path.module}/lambda_function.zip" + source { + filename = basename(local.source_files[0]) + content = file("${local.source_files[0]}") + } + source { + filename = basename(local.source_files[1]) + content = file("${local.source_files[1]}") } } variable "aha_primary_region" { - description = "Primary region where AHA solution will be deployed" - type = string - default = "us-east-1" + description = "Primary region where AHA solution will be deployed" + type = string + default = "us-east-1" } variable "aha_secondary_region" { - description = "Secondary region where AHA solution will be deployed" - type = string - default = "" + description = "Secondary region where AHA solution will be deployed" + type = string + default = "" } variable "default_tags" { - description = "Tags used for the AWS resources created by this template" - type = map - default = { - Application = "AHA-Solution" - } + description = "Tags used for the AWS resources created by this template" + type = map(any) + default = { + Application = "AHA-Solution" + } } variable "dynamodbtable" { - type = string - default = "AHA-DynamoDBTable" + type = string + default = "AHA-DynamoDBTable" } variable "AWSOrganizationsEnabled" { - type = string - default = "No" - description = "You can receive both PHD and SHD alerts if you're using AWS Organizations. \n If you are, make sure to enable Organizational Health View: \n (https://docs.aws.amazon.com/health/latest/ug/aggregate-events.html) to \n aggregate all PHD events in your AWS Organization. If not, you can still \n get SHD alerts." - validation { - condition = ( - var.AWSOrganizationsEnabled == "Yes" || var.AWSOrganizationsEnabled == "No" - ) - error_message = "AWSOrganizationsEnabled variable can only accept Yes or No as values." - } + type = string + default = "No" + description = "You can receive both PHD and SHD alerts if you're using AWS Organizations. \n If you are, make sure to enable Organizational Health View: \n (https://docs.aws.amazon.com/health/latest/ug/aggregate-events.html) to \n aggregate all PHD events in your AWS Organization. If not, you can still \n get SHD alerts." + validation { + condition = ( + var.AWSOrganizationsEnabled == "Yes" || var.AWSOrganizationsEnabled == "No" + ) + error_message = "AWSOrganizationsEnabled variable can only accept Yes or No as values." + } } variable "ManagementAccountRoleArn" { - type = string - default = "" - description = "Arn of the IAM role in the top-level management account for collecting PHD Events. 'None' if deploying into the top-level management account." + type = string + default = "" + description = "Arn of the IAM role in the top-level management account for collecting PHD Events. 'None' if deploying into the top-level management account." } variable "AWSHealthEventType" { - type = string - default = "issue | accountNotification | scheduledChange" - description = "Select the event type that you want AHA to report on. Refer to \n https://docs.aws.amazon.com/health/latest/APIReference/API_EventType.html for more information on EventType." - validation { - condition = ( - var.AWSHealthEventType == "issue | accountNotification | scheduledChange" || var.AWSHealthEventType == "issue" - ) - error_message = "AWSHealthEventType variable can only accept issue | accountNotification | scheduledChange or issue as values." - } + type = string + default = "issue | accountNotification | scheduledChange" + description = "Select the event type that you want AHA to report on. Refer to \n https://docs.aws.amazon.com/health/latest/APIReference/API_EventType.html for more information on EventType." + validation { + condition = ( + var.AWSHealthEventType == "issue | accountNotification | scheduledChange" || var.AWSHealthEventType == "issue" + ) + error_message = "AWSHealthEventType variable can only accept issue | accountNotification | scheduledChange or issue as values." + } } #variable "S3Bucket" { @@ -116,57 +116,57 @@ variable "AWSHealthEventType" { #} variable "EventBusName" { - type = string - default = "" - description = "This is to ingest alerts into AWS EventBridge. Enter the event bus name if you wish to send the alerts to the AWS EventBridge. Note: By ingesting you wish to send the alerts to the AWS EventBridge. Note: By ingesting these alerts to AWS EventBridge, you can integrate with 35 SaaS vendors such as DataDog/NewRelic/PagerDuty. If you don't prefer to use EventBridge, leave the default (None)." + type = string + default = "" + description = "This is to ingest alerts into AWS EventBridge. Enter the event bus name if you wish to send the alerts to the AWS EventBridge. Note: By ingesting you wish to send the alerts to the AWS EventBridge. Note: By ingesting these alerts to AWS EventBridge, you can integrate with 35 SaaS vendors such as DataDog/NewRelic/PagerDuty. If you don't prefer to use EventBridge, leave the default (None)." } variable "SlackWebhookURL" { - type = string - default = "" - description = "Enter the Slack Webhook URL. If you don't prefer to use Slack, leave the default (empty)." + type = string + default = "" + description = "Enter the Slack Webhook URL. If you don't prefer to use Slack, leave the default (empty)." } variable "MicrosoftTeamsWebhookURL" { - type = string - default = "" - description = "Enter Microsoft Teams Webhook URL. If you don't prefer to use MS Teams, leave the default (empty)." + type = string + default = "" + description = "Enter Microsoft Teams Webhook URL. If you don't prefer to use MS Teams, leave the default (empty)." } variable "AmazonChimeWebhookURL" { - type = string - default = "" - description = "Enter the Chime Webhook URL, If you don't prefer to use Amazon Chime, leave the default (empty)." + type = string + default = "" + description = "Enter the Chime Webhook URL, If you don't prefer to use Amazon Chime, leave the default (empty)." } variable "Regions" { - type = string - default = "all regions" - description = "By default, AHA reports events affecting all AWS regions. \n If you want to report on certain regions you can enter up to 10 in a comma separated format. \n Available Regions: us-east-1,us-east-2,us-west-1,us-west-2,af-south-1,ap-east-1,ap-south-1,ap-northeast-3, \n ap-northeast-2,ap-southeast-1,ap-southeast-2,ap-northeast-1,ca-central-1,eu-central-1,eu-west-1,eu-west-2, \n eu-south-1,eu-south-3,eu-north-1,me-south-1,sa-east-1,global" + type = string + default = "all regions" + description = "By default, AHA reports events affecting all AWS regions. \n If you want to report on certain regions you can enter up to 10 in a comma separated format. \n Available Regions: us-east-1,us-east-2,us-west-1,us-west-2,af-south-1,ap-east-1,ap-south-1,ap-northeast-3, \n ap-northeast-2,ap-southeast-1,ap-southeast-2,ap-northeast-1,ca-central-1,eu-central-1,eu-west-1,eu-west-2, \n eu-south-1,eu-south-3,eu-north-1,me-south-1,sa-east-1,global" } variable "EventSearchBack" { - type = number - default = "1" - description = "How far back to search for events in hours. Default is 1 hour" + type = number + default = "1" + description = "How far back to search for events in hours. Default is 1 hour" } variable "FromEmail" { - type = string - default = "none@domain.com" - description = "Enter FROM Email Address" + type = string + default = "none@domain.com" + description = "Enter FROM Email Address" } variable "ToEmail" { - type = string - default = "none@domain.com" - description = "Enter email addresses separated by commas (for ex: abc@amazon.com, bcd@amazon.com)" + type = string + default = "none@domain.com" + description = "Enter email addresses separated by commas (for ex: abc@amazon.com, bcd@amazon.com)" } variable "Subject" { - type = string - default = "AWS Health Alert" - description = "Enter the subject of the email address" + type = string + default = "AWS Health Alert" + description = "Enter the subject of the email address" } #variable "S3Bucket" { @@ -176,9 +176,21 @@ variable "Subject" { #} variable "ExcludeAccountIDs" { - type = string - default = "" - description = "If you would like to EXCLUDE any accounts from alerting, enter a .csv filename created with comma-seperated account numbers. Sample AccountIDs file name: aha_account_ids.csv. If not, leave the default empty." + type = string + default = "" + description = "If you would like to EXCLUDE any accounts from alerting, enter a .csv filename created with comma-seperated account numbers. Sample AccountIDs file name: aha_account_ids.csv. If not, leave the default empty." +} + +variable "FilterResolvedResources" { + type = string + default = "No" + description = "Whether to filter out resources with resolved status. Set to 'Yes' to filter out resolved resources, 'No' to show all resources." + validation { + condition = ( + var.FilterResolvedResources == "Yes" || var.FilterResolvedResources == "No" + ) + error_message = "FilterResolvedResources variable can only accept Yes or No as values." + } } ##### Resources for AHA Solution created below. @@ -192,344 +204,344 @@ resource "random_string" "resource_code" { # S3 buckets creation resource "aws_s3_bucket" "AHA-S3Bucket-PrimaryRegion" { - count = "${var.ExcludeAccountIDs != "" ? 1 : 0}" - bucket = "aha-bucket-${var.aha_primary_region}-${random_string.resource_code.result}" - tags = { - Name = "aha-bucket" - } + count = var.ExcludeAccountIDs != "" ? 1 : 0 + bucket = "aha-bucket-${var.aha_primary_region}-${random_string.resource_code.result}" + tags = { + Name = "aha-bucket" + } } resource "aws_s3_bucket_acl" "AHA-S3Bucket-PrimaryRegion" { - count = var.ExcludeAccountIDs != "" ? 1 : 0 - bucket = aws_s3_bucket.AHA-S3Bucket-PrimaryRegion[0].id - acl = "private" + count = var.ExcludeAccountIDs != "" ? 1 : 0 + bucket = aws_s3_bucket.AHA-S3Bucket-PrimaryRegion[0].id + acl = "private" } resource "aws_s3_bucket" "AHA-S3Bucket-SecondaryRegion" { - count = "${var.aha_secondary_region != "" && var.ExcludeAccountIDs != "" ? 1 : 0}" - provider = aws.secondary_region - bucket = "aha-bucket-${var.aha_secondary_region}-${random_string.resource_code.result}" - tags = { - Name = "aha-bucket" - } + count = var.aha_secondary_region != "" && var.ExcludeAccountIDs != "" ? 1 : 0 + provider = aws.secondary_region + bucket = "aha-bucket-${var.aha_secondary_region}-${random_string.resource_code.result}" + tags = { + Name = "aha-bucket" + } } resource "aws_s3_bucket_acl" "AHA-S3Bucket-SecondaryRegion" { - count = "${var.aha_secondary_region != "" && var.ExcludeAccountIDs != "" ? 1 : 0}" - provider = aws.secondary_region - bucket = aws_s3_bucket.AHA-S3Bucket-SecondaryRegion[0].id - acl = "private" + count = var.aha_secondary_region != "" && var.ExcludeAccountIDs != "" ? 1 : 0 + provider = aws.secondary_region + bucket = aws_s3_bucket.AHA-S3Bucket-SecondaryRegion[0].id + acl = "private" } resource "aws_s3_object" "AHA-S3Object-PrimaryRegion" { - count = "${var.ExcludeAccountIDs != "" ? 1 : 0}" - key = var.ExcludeAccountIDs - bucket = aws_s3_bucket.AHA-S3Bucket-PrimaryRegion[0].bucket - source = var.ExcludeAccountIDs - tags = { - Name = "${var.ExcludeAccountIDs}" - } + count = var.ExcludeAccountIDs != "" ? 1 : 0 + key = var.ExcludeAccountIDs + bucket = aws_s3_bucket.AHA-S3Bucket-PrimaryRegion[0].bucket + source = var.ExcludeAccountIDs + tags = { + Name = "${var.ExcludeAccountIDs}" + } } resource "aws_s3_object" "AHA-S3Object-SecondaryRegion" { - count = "${var.aha_secondary_region != "" && var.ExcludeAccountIDs != "" ? 1 : 0}" - provider = aws.secondary_region - key = var.ExcludeAccountIDs - bucket = aws_s3_bucket.AHA-S3Bucket-SecondaryRegion[0].bucket - source = var.ExcludeAccountIDs - tags = { - Name = "${var.ExcludeAccountIDs}" - } + count = var.aha_secondary_region != "" && var.ExcludeAccountIDs != "" ? 1 : 0 + provider = aws.secondary_region + key = var.ExcludeAccountIDs + bucket = aws_s3_bucket.AHA-S3Bucket-SecondaryRegion[0].bucket + source = var.ExcludeAccountIDs + tags = { + Name = "${var.ExcludeAccountIDs}" + } } # DynamoDB table - Create if secondary region not set resource "aws_dynamodb_table" "AHA-DynamoDBTable" { - count = "${var.aha_secondary_region == "" ? 1 : 0}" - billing_mode = "PROVISIONED" - hash_key = "arn" - name = "${var.dynamodbtable}-${random_string.resource_code.result}" - read_capacity = 5 - write_capacity = 5 - stream_enabled = false - tags = { - Name = "${var.dynamodbtable}" - } + count = var.aha_secondary_region == "" ? 1 : 0 + billing_mode = "PROVISIONED" + hash_key = "arn" + name = "${var.dynamodbtable}-${random_string.resource_code.result}" + read_capacity = 5 + write_capacity = 5 + stream_enabled = false + tags = { + Name = "${var.dynamodbtable}" + } - attribute { - name = "arn" - type = "S" - } + attribute { + name = "arn" + type = "S" + } - point_in_time_recovery { - enabled = false - } + point_in_time_recovery { + enabled = false + } - timeouts {} + timeouts {} - ttl { - attribute_name = "ttl" - enabled = true - } + ttl { + attribute_name = "ttl" + enabled = true + } } # DynamoDB table - Multi region Global Table - Create if secondary region is set resource "aws_dynamodb_table" "AHA-GlobalDynamoDBTable" { - count = "${var.aha_secondary_region == "" ? 0 : 1}" - billing_mode = "PAY_PER_REQUEST" - hash_key = "arn" - name = "${var.dynamodbtable}-${random_string.resource_code.result}" - stream_enabled = true - stream_view_type = "NEW_AND_OLD_IMAGES" - tags = { - Name = "${var.dynamodbtable}" - } + count = var.aha_secondary_region == "" ? 0 : 1 + billing_mode = "PAY_PER_REQUEST" + hash_key = "arn" + name = "${var.dynamodbtable}-${random_string.resource_code.result}" + stream_enabled = true + stream_view_type = "NEW_AND_OLD_IMAGES" + tags = { + Name = "${var.dynamodbtable}" + } - attribute { - name = "arn" - type = "S" - } + attribute { + name = "arn" + type = "S" + } - point_in_time_recovery { - enabled = false - } + point_in_time_recovery { + enabled = false + } - replica { - region_name = var.aha_secondary_region - } + replica { + region_name = var.aha_secondary_region + } - timeouts {} + timeouts {} - ttl { - attribute_name = "ttl" - enabled = true - } + ttl { + attribute_name = "ttl" + enabled = true + } } # Tags for DynamoDB - secondary region resource "aws_dynamodb_tag" "AHA-GlobalDynamoDBTable" { - count = "${var.aha_secondary_region == "" ? 0 : 1}" - provider = aws.secondary_region - resource_arn = replace(aws_dynamodb_table.AHA-GlobalDynamoDBTable[count.index].arn, var.aha_primary_region, var.aha_secondary_region) - key = "Name" - value = "${var.dynamodbtable}" + count = var.aha_secondary_region == "" ? 0 : 1 + provider = aws.secondary_region + resource_arn = replace(aws_dynamodb_table.AHA-GlobalDynamoDBTable[count.index].arn, var.aha_primary_region, var.aha_secondary_region) + key = "Name" + value = var.dynamodbtable } # Tags for DynamoDB - secondary region - default_tags resource "aws_dynamodb_tag" "AHA-GlobalDynamoDBTable-Additional-tags" { - for_each = { for key, value in var.default_tags : key => value if var.aha_secondary_region != "" } - provider = aws.secondary_region - resource_arn = replace(aws_dynamodb_table.AHA-GlobalDynamoDBTable[0].arn, var.aha_primary_region, var.aha_secondary_region) - key = each.key - value = each.value + for_each = { for key, value in var.default_tags : key => value if var.aha_secondary_region != "" } + provider = aws.secondary_region + resource_arn = replace(aws_dynamodb_table.AHA-GlobalDynamoDBTable[0].arn, var.aha_primary_region, var.aha_secondary_region) + key = each.key + value = each.value } # Secrets - SlackChannelSecret resource "aws_secretsmanager_secret" "SlackChannelID" { - count = "${var.SlackWebhookURL == "" ? 0 : 1}" - name = "SlackChannelID" - description = "Slack Channel ID Secret" - recovery_window_in_days = 0 - tags = { - "HealthCheckSlack" = "ChannelID" - } - dynamic "replica" { - for_each = var.aha_secondary_region == "" ? [] : [1] - content { - region = var.aha_secondary_region - } + count = var.SlackWebhookURL == "" ? 0 : 1 + name = "SlackChannelID" + description = "Slack Channel ID Secret" + recovery_window_in_days = 0 + tags = { + "HealthCheckSlack" = "ChannelID" + } + dynamic "replica" { + for_each = var.aha_secondary_region == "" ? [] : [1] + content { + region = var.aha_secondary_region } + } } resource "aws_secretsmanager_secret_version" "SlackChannelID" { - count = "${var.SlackWebhookURL == "" ? 0 : 1}" - secret_id = "${aws_secretsmanager_secret.SlackChannelID.*.id[count.index]}" - secret_string = "${var.SlackWebhookURL}" + count = var.SlackWebhookURL == "" ? 0 : 1 + secret_id = aws_secretsmanager_secret.SlackChannelID.*.id[count.index] + secret_string = var.SlackWebhookURL } # Secrets - MicrosoftChannelSecret resource "aws_secretsmanager_secret" "MicrosoftChannelID" { - count = "${var.MicrosoftTeamsWebhookURL == "" ? 0 : 1}" - name = "MicrosoftChannelID" - description = "Microsoft Channel ID Secret" - recovery_window_in_days = 0 - tags = { - "HealthCheckMicrosoft" = "ChannelID" - "Name" = "AHA-MicrosoftChannelID" - } - dynamic "replica" { - for_each = var.aha_secondary_region == "" ? [] : [1] - content { - region = var.aha_secondary_region - } + count = var.MicrosoftTeamsWebhookURL == "" ? 0 : 1 + name = "MicrosoftChannelID" + description = "Microsoft Channel ID Secret" + recovery_window_in_days = 0 + tags = { + "HealthCheckMicrosoft" = "ChannelID" + "Name" = "AHA-MicrosoftChannelID" + } + dynamic "replica" { + for_each = var.aha_secondary_region == "" ? [] : [1] + content { + region = var.aha_secondary_region } + } } resource "aws_secretsmanager_secret_version" "MicrosoftChannelID" { - count = "${var.MicrosoftTeamsWebhookURL == "" ? 0 : 1}" - secret_id = "${aws_secretsmanager_secret.MicrosoftChannelID.*.id[count.index]}" - secret_string = "${var.MicrosoftTeamsWebhookURL}" + count = var.MicrosoftTeamsWebhookURL == "" ? 0 : 1 + secret_id = aws_secretsmanager_secret.MicrosoftChannelID.*.id[count.index] + secret_string = var.MicrosoftTeamsWebhookURL } # Secrets - EventBusNameSecret resource "aws_secretsmanager_secret" "EventBusName" { - count = "${var.EventBusName == "" ? 0 : 1}" - name = "EventBusName" - description = "EventBus Name Secret" - recovery_window_in_days = 0 - tags = { - "EventBusName" = "ChannelID" - "Name" = "AHA-EventBusName" - } - dynamic "replica" { - for_each = var.aha_secondary_region == "" ? [] : [1] - content { - region = var.aha_secondary_region - } + count = var.EventBusName == "" ? 0 : 1 + name = "EventBusName" + description = "EventBus Name Secret" + recovery_window_in_days = 0 + tags = { + "EventBusName" = "ChannelID" + "Name" = "AHA-EventBusName" + } + dynamic "replica" { + for_each = var.aha_secondary_region == "" ? [] : [1] + content { + region = var.aha_secondary_region } + } } resource "aws_secretsmanager_secret_version" "EventBusName" { - count = "${var.EventBusName == "" ? 0 : 1}" - secret_id = "${aws_secretsmanager_secret.EventBusName.*.id[count.index]}" - secret_string = "${var.EventBusName}" + count = var.EventBusName == "" ? 0 : 1 + secret_id = aws_secretsmanager_secret.EventBusName.*.id[count.index] + secret_string = var.EventBusName } # Secrets - ChimeChannelSecret resource "aws_secretsmanager_secret" "ChimeChannelID" { - count = "${var.AmazonChimeWebhookURL == "" ? 0 : 1}" - name = "ChimeChannelID" - description = "Chime Channel ID Secret" - recovery_window_in_days = 0 - tags = { - "HealthCheckChime" = "ChannelID" - "Name" = "AHA-ChimeChannelID-${random_string.resource_code.result}" - } - dynamic "replica" { - for_each = var.aha_secondary_region == "" ? [] : [1] - content { - region = var.aha_secondary_region - } + count = var.AmazonChimeWebhookURL == "" ? 0 : 1 + name = "ChimeChannelID" + description = "Chime Channel ID Secret" + recovery_window_in_days = 0 + tags = { + "HealthCheckChime" = "ChannelID" + "Name" = "AHA-ChimeChannelID-${random_string.resource_code.result}" + } + dynamic "replica" { + for_each = var.aha_secondary_region == "" ? [] : [1] + content { + region = var.aha_secondary_region } + } } resource "aws_secretsmanager_secret_version" "ChimeChannelID" { - count = "${var.AmazonChimeWebhookURL == "" ? 0 : 1}" - secret_id = "${aws_secretsmanager_secret.ChimeChannelID.*.id[count.index]}" - secret_string = "${var.AmazonChimeWebhookURL}" + count = var.AmazonChimeWebhookURL == "" ? 0 : 1 + secret_id = aws_secretsmanager_secret.ChimeChannelID.*.id[count.index] + secret_string = var.AmazonChimeWebhookURL } # Secrets - AssumeRoleSecret resource "aws_secretsmanager_secret" "AssumeRoleArn" { - count = "${var.ManagementAccountRoleArn == "" ? 0 : 1}" - name = "AssumeRoleArn" - description = "Management account role for AHA to assume" - recovery_window_in_days = 0 - tags = { - "AssumeRoleArn" = "" - "Name" = "AHA-AssumeRoleArn" - } - dynamic "replica" { - for_each = var.aha_secondary_region == "" ? [] : [1] - content { - region = var.aha_secondary_region - } + count = var.ManagementAccountRoleArn == "" ? 0 : 1 + name = "AssumeRoleArn" + description = "Management account role for AHA to assume" + recovery_window_in_days = 0 + tags = { + "AssumeRoleArn" = "" + "Name" = "AHA-AssumeRoleArn" + } + dynamic "replica" { + for_each = var.aha_secondary_region == "" ? [] : [1] + content { + region = var.aha_secondary_region } + } } resource "aws_secretsmanager_secret_version" "AssumeRoleArn" { - count = "${var.ManagementAccountRoleArn == "" ? 0 : 1}" - secret_id = "${aws_secretsmanager_secret.AssumeRoleArn.*.id[count.index]}" - secret_string = "${var.ManagementAccountRoleArn}" + count = var.ManagementAccountRoleArn == "" ? 0 : 1 + secret_id = aws_secretsmanager_secret.AssumeRoleArn.*.id[count.index] + secret_string = var.ManagementAccountRoleArn } # IAM Role for Lambda function execution resource "aws_iam_role" "AHA-LambdaExecutionRole" { - name = "AHA-LambdaExecutionRole-${random_string.resource_code.result}" - path = "/" - assume_role_policy = jsonencode( + name = "AHA-LambdaExecutionRole-${random_string.resource_code.result}" + path = "/" + assume_role_policy = jsonencode( + { + Version = "2012-10-17" + Statement = [ { - Version = "2012-10-17" - Statement = [ - { - Action = "sts:AssumeRole" - Effect = "Allow" - Principal = { - Service = "lambda.amazonaws.com" - } - }, - ] - } - ) - inline_policy { - name = "AHA-LambdaPolicy" - policy = data.aws_iam_policy_document.AHA-LambdaPolicy-Document.json - } - tags = { - "Name" = "AHA-LambdaExecutionRole" + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "lambda.amazonaws.com" + } + }, + ] } + ) + inline_policy { + name = "AHA-LambdaPolicy" + policy = data.aws_iam_policy_document.AHA-LambdaPolicy-Document.json + } + tags = { + "Name" = "AHA-LambdaExecutionRole" + } } data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { - version = "2012-10-17" + version = "2012-10-17" statement { effect = "Allow" actions = [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents", + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents", ] resources = [ - "arn:aws:logs:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:*", - "arn:aws:logs:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:*" + "arn:aws:logs:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:*", + "arn:aws:logs:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:*" ] } statement { - effect = "Allow" - actions = [ - "health:DescribeAffectedAccountsForOrganization", - "health:DescribeAffectedEntitiesForOrganization", - "health:DescribeEventDetailsForOrganization", - "health:DescribeEventsForOrganization", - "health:DescribeEventDetails", - "health:DescribeEvents", - "health:DescribeEventTypes", - "health:DescribeAffectedEntities", - "organizations:ListAccounts", - "organizations:DescribeAccount", + effect = "Allow" + actions = [ + "health:DescribeAffectedAccountsForOrganization", + "health:DescribeAffectedEntitiesForOrganization", + "health:DescribeEventDetailsForOrganization", + "health:DescribeEventsForOrganization", + "health:DescribeEventDetails", + "health:DescribeEvents", + "health:DescribeEventTypes", + "health:DescribeAffectedEntities", + "organizations:ListAccounts", + "organizations:DescribeAccount", ] - resources = [ "*" ] + resources = ["*"] } statement { - effect = "Allow" - actions = [ - "dynamodb:ListTables", + effect = "Allow" + actions = [ + "dynamodb:ListTables", ] resources = [ - "arn:aws:dynamodb:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:*", - "arn:aws:dynamodb:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:*", + "arn:aws:dynamodb:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:*", + "arn:aws:dynamodb:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:*", ] } statement { - effect = "Allow" - actions = [ - "ses:SendEmail", + effect = "Allow" + actions = [ + "ses:SendEmail", ] resources = [ - "arn:aws:ses:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:*", - "arn:aws:ses:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:*", + "arn:aws:ses:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:*", + "arn:aws:ses:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:*", ] } statement { - effect = "Allow" - actions = [ - "dynamodb:UpdateTimeToLive", - "dynamodb:PutItem", - "dynamodb:DeleteItem", - "dynamodb:GetItem", - "dynamodb:Scan", - "dynamodb:Query", - "dynamodb:UpdateItem", - "dynamodb:UpdateTable", - "dynamodb:GetRecords", + effect = "Allow" + actions = [ + "dynamodb:UpdateTimeToLive", + "dynamodb:PutItem", + "dynamodb:DeleteItem", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:Query", + "dynamodb:UpdateItem", + "dynamodb:UpdateTable", + "dynamodb:GetRecords", ] resources = [ - #aws_dynamodb_table.AHA-DynamoDBTable.arn - "arn:aws:dynamodb:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:table/${var.dynamodbtable}-${random_string.resource_code.result}", - "arn:aws:dynamodb:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:table/${var.dynamodbtable}-${random_string.resource_code.result}", + #aws_dynamodb_table.AHA-DynamoDBTable.arn + "arn:aws:dynamodb:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:table/${var.dynamodbtable}-${random_string.resource_code.result}", + "arn:aws:dynamodb:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:table/${var.dynamodbtable}-${random_string.resource_code.result}", ] } dynamic "statement" { @@ -537,15 +549,15 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "secretsmanager:GetResourcePolicy", - "secretsmanager:DescribeSecret", - "secretsmanager:ListSecretVersionIds", - "secretsmanager:GetSecretValue", + "secretsmanager:GetResourcePolicy", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecretVersionIds", + "secretsmanager:GetSecretValue", ] resources = [ - aws_secretsmanager_secret.SlackChannelID[0].arn, - "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.SlackChannelID[0].arn),6)}" -# var.aha_secondary_region != "" ? "arn:aws:secretsmanager:${var.aha_secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.SlackChannelID[0].arn),6)}" : null + aws_secretsmanager_secret.SlackChannelID[0].arn, + "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.SlackChannelID[0].arn), 6)}" + # var.aha_secondary_region != "" ? "arn:aws:secretsmanager:${var.aha_secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.SlackChannelID[0].arn),6)}" : null ] } } @@ -554,14 +566,14 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "secretsmanager:GetResourcePolicy", - "secretsmanager:DescribeSecret", - "secretsmanager:ListSecretVersionIds", - "secretsmanager:GetSecretValue", + "secretsmanager:GetResourcePolicy", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecretVersionIds", + "secretsmanager:GetSecretValue", ] resources = [ - aws_secretsmanager_secret.MicrosoftChannelID[0].arn, - "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.MicrosoftChannelID[0].arn),6)}" + aws_secretsmanager_secret.MicrosoftChannelID[0].arn, + "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.MicrosoftChannelID[0].arn), 6)}" ] } } @@ -570,14 +582,14 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "secretsmanager:GetResourcePolicy", - "secretsmanager:DescribeSecret", - "secretsmanager:ListSecretVersionIds", - "secretsmanager:GetSecretValue", + "secretsmanager:GetResourcePolicy", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecretVersionIds", + "secretsmanager:GetSecretValue", ] resources = [ - aws_secretsmanager_secret.ChimeChannelID[0].arn, - "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.ChimeChannelID[0].arn),6)}" + aws_secretsmanager_secret.ChimeChannelID[0].arn, + "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.ChimeChannelID[0].arn), 6)}" ] } } @@ -586,14 +598,14 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "secretsmanager:GetResourcePolicy", - "secretsmanager:DescribeSecret", - "secretsmanager:ListSecretVersionIds", - "secretsmanager:GetSecretValue", + "secretsmanager:GetResourcePolicy", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecretVersionIds", + "secretsmanager:GetSecretValue", ] resources = [ - aws_secretsmanager_secret.EventBusName[0].arn, - "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.EventBusName[0].arn),6)}" + aws_secretsmanager_secret.EventBusName[0].arn, + "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.EventBusName[0].arn), 6)}" ] } } @@ -602,14 +614,14 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "secretsmanager:GetResourcePolicy", - "secretsmanager:DescribeSecret", - "secretsmanager:ListSecretVersionIds", - "secretsmanager:GetSecretValue", + "secretsmanager:GetResourcePolicy", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecretVersionIds", + "secretsmanager:GetSecretValue", ] resources = [ - aws_secretsmanager_secret.AssumeRoleArn[0].arn, - "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.AssumeRoleArn[0].arn),6)}" + aws_secretsmanager_secret.AssumeRoleArn[0].arn, + "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.AssumeRoleArn[0].arn), 6)}" ] } } @@ -618,11 +630,11 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "events:PutEvents", + "events:PutEvents", ] resources = [ - "arn:aws:events:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:event-bus/${var.EventBusName}", - "arn:aws:events:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:event-bus/${var.EventBusName}" + "arn:aws:events:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:event-bus/${var.EventBusName}", + "arn:aws:events:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:event-bus/${var.EventBusName}" ] } } @@ -631,10 +643,10 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "sts:AssumeRole", + "sts:AssumeRole", ] resources = [ - "${var.ManagementAccountRoleArn}", + "${var.ManagementAccountRoleArn}", ] } } @@ -643,162 +655,164 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "s3:GetObject", + "s3:GetObject", ] resources = [ - "arn:aws:s3:::aha-bucket-${var.aha_primary_region}-${random_string.resource_code.result}/${var.ExcludeAccountIDs}", - "arn:aws:s3:::aha-bucket-${local.secondary_region}-${random_string.resource_code.result}/${var.ExcludeAccountIDs}", + "arn:aws:s3:::aha-bucket-${var.aha_primary_region}-${random_string.resource_code.result}/${var.ExcludeAccountIDs}", + "arn:aws:s3:::aha-bucket-${local.secondary_region}-${random_string.resource_code.result}/${var.ExcludeAccountIDs}", ] } } } # aws_lambda_function - AHA-LambdaFunction - Primary region resource "aws_lambda_function" "AHA-LambdaFunction-PrimaryRegion" { - description = "Lambda function that runs AHA" - function_name = "AHA-LambdaFunction-${random_string.resource_code.result}" - handler = "handler.main" - memory_size = 128 - timeout = 600 - filename = data.archive_file.lambda_zip.output_path - source_code_hash = data.archive_file.lambda_zip.output_base64sha256 -# s3_bucket = var.S3Bucket -# s3_key = var.S3Key - reserved_concurrent_executions = -1 - role = aws_iam_role.AHA-LambdaExecutionRole.arn - runtime = "python3.11" - - environment { - variables = { - "Slack" = var.SlackWebhookURL != "" ? "True" : null - "Team" = var.MicrosoftTeamsWebhookURL != "" ? "True" : null - "Chime" = var.AmazonChimeWebhookURL != "" ? "True" : null - "Eventbridge" = var.EventBusName != "" ? "True" : null - "DYNAMODB_TABLE" = "${var.dynamodbtable}-${random_string.resource_code.result}" - "EMAIL_SUBJECT" = var.Subject - "EVENT_SEARCH_BACK" = var.EventSearchBack - "FROM_EMAIL" = var.FromEmail - "HEALTH_EVENT_TYPE" = var.AWSHealthEventType - "ORG_STATUS" = var.AWSOrganizationsEnabled - "REGIONS" = var.Regions - "TO_EMAIL" = var.ToEmail - "MANAGEMENT_ROLE_ARN" = var.ManagementAccountRoleArn == "" ? "None" : var.ManagementAccountRoleArn - "ACCOUNT_IDS" = var.ExcludeAccountIDs - "S3_BUCKET" = join("",aws_s3_bucket.AHA-S3Bucket-PrimaryRegion[*].bucket) - } + description = "Lambda function that runs AHA" + function_name = "AHA-LambdaFunction-${random_string.resource_code.result}" + handler = "handler.main" + memory_size = 128 + timeout = 600 + filename = data.archive_file.lambda_zip.output_path + source_code_hash = data.archive_file.lambda_zip.output_base64sha256 + # s3_bucket = var.S3Bucket + # s3_key = var.S3Key + reserved_concurrent_executions = -1 + role = aws_iam_role.AHA-LambdaExecutionRole.arn + runtime = "python3.11" + + environment { + variables = { + "Slack" = var.SlackWebhookURL != "" ? "True" : null + "Team" = var.MicrosoftTeamsWebhookURL != "" ? "True" : null + "Chime" = var.AmazonChimeWebhookURL != "" ? "True" : null + "Eventbridge" = var.EventBusName != "" ? "True" : null + "DYNAMODB_TABLE" = "${var.dynamodbtable}-${random_string.resource_code.result}" + "EMAIL_SUBJECT" = var.Subject + "EVENT_SEARCH_BACK" = var.EventSearchBack + "FROM_EMAIL" = var.FromEmail + "HEALTH_EVENT_TYPE" = var.AWSHealthEventType + "ORG_STATUS" = var.AWSOrganizationsEnabled + "REGIONS" = var.Regions + "TO_EMAIL" = var.ToEmail + "MANAGEMENT_ROLE_ARN" = var.ManagementAccountRoleArn == "" ? "None" : var.ManagementAccountRoleArn + "ACCOUNT_IDS" = var.ExcludeAccountIDs + "S3_BUCKET" = join("", aws_s3_bucket.AHA-S3Bucket-PrimaryRegion[*].bucket) + "FILTER_RESOLVED" = var.FilterResolvedResources } + } - timeouts {} + timeouts {} - tracing_config { - mode = "PassThrough" - } - tags = { - "Name" = "AHA-LambdaFunction" - } - depends_on = [ - aws_dynamodb_table.AHA-DynamoDBTable, - aws_dynamodb_table.AHA-GlobalDynamoDBTable, - ] + tracing_config { + mode = "PassThrough" + } + tags = { + "Name" = "AHA-LambdaFunction" + } + depends_on = [ + aws_dynamodb_table.AHA-DynamoDBTable, + aws_dynamodb_table.AHA-GlobalDynamoDBTable, + ] } # aws_lambda_function - AHA-LambdaFunction - Secondary region resource "aws_lambda_function" "AHA-LambdaFunction-SecondaryRegion" { - count = "${var.aha_secondary_region == "" ? 0 : 1}" - provider = aws.secondary_region - description = "Lambda function that runs AHA" - function_name = "AHA-LambdaFunction-${random_string.resource_code.result}" - handler = "handler.main" - memory_size = 128 - timeout = 600 - filename = data.archive_file.lambda_zip.output_path - source_code_hash = data.archive_file.lambda_zip.output_base64sha256 -# s3_bucket = var.S3Bucket -# s3_key = var.S3Key - reserved_concurrent_executions = -1 - role = aws_iam_role.AHA-LambdaExecutionRole.arn - runtime = "python3.11" - - environment { - variables = { - "Slack" = var.SlackWebhookURL != "" ? "True" : null - "Team" = var.MicrosoftTeamsWebhookURL != "" ? "True" : null - "Chime" = var.AmazonChimeWebhookURL != "" ? "True" : null - "Eventbridge" = var.EventBusName != "" ? "True" : null - "DYNAMODB_TABLE" = "${var.dynamodbtable}-${random_string.resource_code.result}" - "EMAIL_SUBJECT" = var.Subject - "EVENT_SEARCH_BACK" = var.EventSearchBack - "FROM_EMAIL" = var.FromEmail - "HEALTH_EVENT_TYPE" = var.AWSHealthEventType - "ORG_STATUS" = var.AWSOrganizationsEnabled - "REGIONS" = var.Regions - "TO_EMAIL" = var.ToEmail - "MANAGEMENT_ROLE_ARN" = var.ManagementAccountRoleArn - "ACCOUNT_IDS" = var.ExcludeAccountIDs - "S3_BUCKET" = join("",aws_s3_bucket.AHA-S3Bucket-SecondaryRegion[*].bucket) - } + count = var.aha_secondary_region == "" ? 0 : 1 + provider = aws.secondary_region + description = "Lambda function that runs AHA" + function_name = "AHA-LambdaFunction-${random_string.resource_code.result}" + handler = "handler.main" + memory_size = 128 + timeout = 600 + filename = data.archive_file.lambda_zip.output_path + source_code_hash = data.archive_file.lambda_zip.output_base64sha256 + # s3_bucket = var.S3Bucket + # s3_key = var.S3Key + reserved_concurrent_executions = -1 + role = aws_iam_role.AHA-LambdaExecutionRole.arn + runtime = "python3.11" + + environment { + variables = { + "Slack" = var.SlackWebhookURL != "" ? "True" : null + "Team" = var.MicrosoftTeamsWebhookURL != "" ? "True" : null + "Chime" = var.AmazonChimeWebhookURL != "" ? "True" : null + "Eventbridge" = var.EventBusName != "" ? "True" : null + "DYNAMODB_TABLE" = "${var.dynamodbtable}-${random_string.resource_code.result}" + "EMAIL_SUBJECT" = var.Subject + "EVENT_SEARCH_BACK" = var.EventSearchBack + "FROM_EMAIL" = var.FromEmail + "HEALTH_EVENT_TYPE" = var.AWSHealthEventType + "ORG_STATUS" = var.AWSOrganizationsEnabled + "REGIONS" = var.Regions + "TO_EMAIL" = var.ToEmail + "MANAGEMENT_ROLE_ARN" = var.ManagementAccountRoleArn + "ACCOUNT_IDS" = var.ExcludeAccountIDs + "S3_BUCKET" = join("", aws_s3_bucket.AHA-S3Bucket-SecondaryRegion[*].bucket) + "FILTER_RESOLVED" = var.FilterResolvedResources } + } - timeouts {} + timeouts {} - tracing_config { - mode = "PassThrough" - } - tags = { - "Name" = "AHA-LambdaFunction" - } - depends_on = [ - aws_dynamodb_table.AHA-DynamoDBTable, - aws_dynamodb_table.AHA-GlobalDynamoDBTable, - ] + tracing_config { + mode = "PassThrough" + } + tags = { + "Name" = "AHA-LambdaFunction" + } + depends_on = [ + aws_dynamodb_table.AHA-DynamoDBTable, + aws_dynamodb_table.AHA-GlobalDynamoDBTable, + ] } # EventBridge - Schedule to run lambda resource "aws_cloudwatch_event_rule" "AHA-LambdaSchedule-PrimaryRegion" { - description = "Lambda trigger Event" - event_bus_name = "default" - state = "ENABLED" - name = "AHA-LambdaSchedule-${random_string.resource_code.result}" - schedule_expression = "rate(1 minute)" - tags = { - "Name" = "AHA-LambdaSchedule" - } + description = "Lambda trigger Event" + event_bus_name = "default" + state = "ENABLED" + name = "AHA-LambdaSchedule-${random_string.resource_code.result}" + schedule_expression = "rate(1 minute)" + tags = { + "Name" = "AHA-LambdaSchedule" + } } resource "aws_cloudwatch_event_rule" "AHA-LambdaSchedule-SecondaryRegion" { - description = "Lambda trigger Event" - count = "${var.aha_secondary_region == "" ? 0 : 1}" - provider = aws.secondary_region - event_bus_name = "default" - state = "ENABLED" - name = "AHA-LambdaSchedule-${random_string.resource_code.result}" - schedule_expression = "rate(1 minute)" - tags = { - "Name" = "AHA-LambdaSchedule" - } + description = "Lambda trigger Event" + count = var.aha_secondary_region == "" ? 0 : 1 + provider = aws.secondary_region + event_bus_name = "default" + state = "ENABLED" + name = "AHA-LambdaSchedule-${random_string.resource_code.result}" + schedule_expression = "rate(1 minute)" + tags = { + "Name" = "AHA-LambdaSchedule" + } } resource "aws_cloudwatch_event_target" "AHA-LambdaFunction-PrimaryRegion" { - arn = aws_lambda_function.AHA-LambdaFunction-PrimaryRegion.arn - rule = aws_cloudwatch_event_rule.AHA-LambdaSchedule-PrimaryRegion.name + arn = aws_lambda_function.AHA-LambdaFunction-PrimaryRegion.arn + rule = aws_cloudwatch_event_rule.AHA-LambdaSchedule-PrimaryRegion.name } resource "aws_cloudwatch_event_target" "AHA-LambdaFunction-SecondaryRegion" { - count = "${var.aha_secondary_region == "" ? 0 : 1}" - provider = aws.secondary_region - arn = aws_lambda_function.AHA-LambdaFunction-SecondaryRegion[0].arn - rule = aws_cloudwatch_event_rule.AHA-LambdaSchedule-SecondaryRegion[0].name + count = var.aha_secondary_region == "" ? 0 : 1 + provider = aws.secondary_region + arn = aws_lambda_function.AHA-LambdaFunction-SecondaryRegion[0].arn + rule = aws_cloudwatch_event_rule.AHA-LambdaSchedule-SecondaryRegion[0].name } resource "aws_lambda_permission" "AHA-LambdaSchedulePermission-PrimaryRegion" { - action = "lambda:InvokeFunction" - principal = "events.amazonaws.com" - function_name = aws_lambda_function.AHA-LambdaFunction-PrimaryRegion.arn - source_arn = aws_cloudwatch_event_rule.AHA-LambdaSchedule-PrimaryRegion.arn + action = "lambda:InvokeFunction" + principal = "events.amazonaws.com" + function_name = aws_lambda_function.AHA-LambdaFunction-PrimaryRegion.arn + source_arn = aws_cloudwatch_event_rule.AHA-LambdaSchedule-PrimaryRegion.arn } resource "aws_lambda_permission" "AHA-LambdaSchedulePermission-SecondaryRegion" { - count = "${var.aha_secondary_region == "" ? 0 : 1}" - provider = aws.secondary_region - action = "lambda:InvokeFunction" - principal = "events.amazonaws.com" - function_name = aws_lambda_function.AHA-LambdaFunction-SecondaryRegion[0].arn - source_arn = aws_cloudwatch_event_rule.AHA-LambdaSchedule-SecondaryRegion[0].arn + count = var.aha_secondary_region == "" ? 0 : 1 + provider = aws.secondary_region + action = "lambda:InvokeFunction" + principal = "events.amazonaws.com" + function_name = aws_lambda_function.AHA-LambdaFunction-SecondaryRegion[0].arn + source_arn = aws_cloudwatch_event_rule.AHA-LambdaSchedule-SecondaryRegion[0].arn } diff --git a/terraform/Terraform_DEPLOY_AHA/terraform.tfvars b/terraform/Terraform_DEPLOY_AHA/terraform.tfvars index b047ab1..ecd7479 100644 --- a/terraform/Terraform_DEPLOY_AHA/terraform.tfvars +++ b/terraform/Terraform_DEPLOY_AHA/terraform.tfvars @@ -1,35 +1,36 @@ # Input variables for Terraform_DEPLOY_AHA.tf (AHA Solution deploy using terraform) # # Customize Alerts/Notifications -aha_primary_region="us-east-1" -aha_secondary_region="" -AWSOrganizationsEnabled="No" -AWSHealthEventType="issue | accountNotification | scheduledChange" +aha_primary_region = "us-east-1" +aha_secondary_region = "" +AWSOrganizationsEnabled = "No" +AWSHealthEventType = "issue | accountNotification | scheduledChange" # Communication Channels - Slack/Microsoft Teams/Amazon Chime And/or EventBridge -SlackWebhookURL="" -MicrosoftTeamsWebhookURL="" -AmazonChimeWebhookURL="" -EventBusName="" +SlackWebhookURL = "" +MicrosoftTeamsWebhookURL = "" +AmazonChimeWebhookURL = "" +EventBusName = "" # Email Setup - For Alerting via Email -FromEmail="none@domain.com" -ToEmail="none@domain.com" -Subject="AWS Health Alert" +FromEmail = "none@domain.com" +ToEmail = "none@domain.com" +Subject = "AWS Health Alert" # More Configurations - Optional # By default, AHA reports events affecting all AWS regions. # If you want to report on certain regions you can enter up to 10 in a comma separated format. -EventSearchBack="1" -Regions="all regions" -ManagementAccountRoleArn="" -ExcludeAccountIDs="" +EventSearchBack = "1" +Regions = "all regions" +ManagementAccountRoleArn = "" +ExcludeAccountIDs = "" +FilterResolvedResources = "No" # Tags applied to all resources - using module provider. Update them per your requirement. default_tags = { - Application = "AHA-Solution" - Environment = "PROD" - auto-delete = "no" + Application = "AHA-Solution" + Environment = "PROD" + auto-delete = "no" } # commands to apply changes From 865814a9dc7b559215dbef4c7153f3034469d0ce Mon Sep 17 00:00:00 2001 From: Nicholas Briganti Date: Thu, 3 Apr 2025 21:14:22 -0700 Subject: [PATCH 2/2] Update FilterResolvedResources to use boolean type and simplify filtering logic --- handler.py | 27 +- .../Terraform_DEPLOY_AHA.tf | 967 +++++++++--------- .../Terraform_DEPLOY_AHA/terraform.tfvars | 38 +- 3 files changed, 508 insertions(+), 524 deletions(-) diff --git a/handler.py b/handler.py index e8542d9..17456be 100644 --- a/handler.py +++ b/handler.py @@ -25,7 +25,6 @@ logger = logging.getLogger() logger.setLevel(os.environ.get("LOG_LEVEL", "INFO").upper()) - class CachedSecrets: def __init__(self, client): self.client = client @@ -119,7 +118,7 @@ def send_alert(event_details, affected_accounts, affected_entities, event_type): except URLError as e: print("Server connection failed: ", e.reason) pass - # Slack Notification Handling + #Slack Notification Handling if slack_url != "None": for slack_webhook_type in ["services", "triggers", "workflows"]: if ("hooks.slack.com/" + slack_webhook_type) in slack_url: @@ -137,11 +136,7 @@ def send_alert(event_details, affected_accounts, affected_entities, event_type): ) break except HTTPError as e: - print( - "Got an error while sending message to Slack: ", - e.code, - e.reason, - ) + print("Got an error while sending message to Slack: ", e.code, e.reason) except URLError as e: print("Server connection failed: ", e.reason) pass @@ -217,7 +212,7 @@ def send_org_alert( except URLError as e: print("Server connection failed: ", e.reason) pass - # Slack Notification Handling + #Slack Notification Handling if slack_url != "None": for slack_webhook_type in ["services", "triggers", "workflows"]: if ("hooks.slack.com/" + slack_webhook_type) in slack_url: @@ -235,11 +230,7 @@ def send_org_alert( ) break except HTTPError as e: - print( - "Got an error while sending message to Slack: ", - e.code, - e.reason, - ) + print("Got an error while sending message to Slack: ", e.code, e.reason) except URLError as e: print("Server connection failed: ", e.reason) pass @@ -466,7 +457,7 @@ def get_affected_entities(health_client, event_arn, affected_accounts, is_org_mo # don't list entities which are accounts (handled separately for chat applications) def get_resources_from_entities(affected_entity_array): resources = [] - filter_resolved = os.environ.get("FILTER_RESOLVED", "Yes").lower() == "yes" + filter_resolved = os.environ.get("FILTER_RESOLVED", "false") == "true" for entity in affected_entity_array: if entity["entityValue"] == "UNKNOWN": @@ -525,7 +516,7 @@ def update_org_ddb( "ttl": int(sec_now) + delta_hours_sec + 86400, "statusCode": status_code, "affectedAccountIDs": affected_org_accounts, - "latestDescription": event_latestDescription, + "latestDescription": event_latestDescription # Cleanup: DynamoDB entry deleted 24 hours after last update } ) @@ -569,7 +560,7 @@ def update_org_ddb( "ttl": int(sec_now) + delta_hours_sec + 86400, "statusCode": status_code, "affectedAccountIDs": affected_org_accounts, - "latestDescription": event_latestDescription, + "latestDescription": event_latestDescription # Cleanup: DynamoDB entry deleted 24 hours after last update } ) @@ -641,7 +632,7 @@ def update_ddb( "ttl": int(sec_now) + delta_hours_sec + 86400, "statusCode": status_code, "affectedAccountIDs": affected_accounts, - "latestDescription": event_latestDescription, + "latestDescription": event_latestDescription # Cleanup: DynamoDB entry deleted 24 hours after last update } ) @@ -683,7 +674,7 @@ def update_ddb( "ttl": int(sec_now) + delta_hours_sec + 86400, "statusCode": status_code, "affectedAccountIDs": affected_accounts, - "latestDescription": event_latestDescription, + "latestDescription": event_latestDescription # Cleanup: DynamoDB entry deleted 24 hours after last update } ) diff --git a/terraform/Terraform_DEPLOY_AHA/Terraform_DEPLOY_AHA.tf b/terraform/Terraform_DEPLOY_AHA/Terraform_DEPLOY_AHA.tf index 0207810..daa5458 100644 --- a/terraform/Terraform_DEPLOY_AHA/Terraform_DEPLOY_AHA.tf +++ b/terraform/Terraform_DEPLOY_AHA/Terraform_DEPLOY_AHA.tf @@ -6,95 +6,95 @@ data "aws_caller_identity" "current" {} provider "aws" { - region = var.aha_primary_region - default_tags { - tags = var.default_tags - } + region = var.aha_primary_region + default_tags { + tags = "${var.default_tags}" + } } # Secondary region - provider config locals { - secondary_region = var.aha_secondary_region == "" ? var.aha_primary_region : var.aha_secondary_region + secondary_region = "${var.aha_secondary_region == "" ? var.aha_primary_region : var.aha_secondary_region}" } provider "aws" { - alias = "secondary_region" - region = local.secondary_region - default_tags { - tags = var.default_tags - } + alias = "secondary_region" + region = local.secondary_region + default_tags { + tags = "${var.default_tags}" + } } # Comment below - if needed to use s3_bucket, s3_key for consistency with cf locals { - source_files = ["${path.module}/../../handler.py", "${path.module}/../../messagegenerator.py"] + source_files = ["${path.module}/../../handler.py", "${path.module}/../../messagegenerator.py"] } data "archive_file" "lambda_zip" { - type = "zip" - output_path = "${path.module}/lambda_function.zip" - source { - filename = basename(local.source_files[0]) - content = file("${local.source_files[0]}") - } - source { - filename = basename(local.source_files[1]) - content = file("${local.source_files[1]}") + type = "zip" + output_path = "${path.module}/lambda_function.zip" + source { + filename = "${basename(local.source_files[0])}" + content = file("${local.source_files[0]}") + } + source { + filename = "${basename(local.source_files[1])}" + content = file("${local.source_files[1]}") } } variable "aha_primary_region" { - description = "Primary region where AHA solution will be deployed" - type = string - default = "us-east-1" + description = "Primary region where AHA solution will be deployed" + type = string + default = "us-east-1" } variable "aha_secondary_region" { - description = "Secondary region where AHA solution will be deployed" - type = string - default = "" + description = "Secondary region where AHA solution will be deployed" + type = string + default = "" } variable "default_tags" { - description = "Tags used for the AWS resources created by this template" - type = map(any) - default = { - Application = "AHA-Solution" - } + description = "Tags used for the AWS resources created by this template" + type = map + default = { + Application = "AHA-Solution" + } } variable "dynamodbtable" { - type = string - default = "AHA-DynamoDBTable" + type = string + default = "AHA-DynamoDBTable" } variable "AWSOrganizationsEnabled" { - type = string - default = "No" - description = "You can receive both PHD and SHD alerts if you're using AWS Organizations. \n If you are, make sure to enable Organizational Health View: \n (https://docs.aws.amazon.com/health/latest/ug/aggregate-events.html) to \n aggregate all PHD events in your AWS Organization. If not, you can still \n get SHD alerts." - validation { - condition = ( - var.AWSOrganizationsEnabled == "Yes" || var.AWSOrganizationsEnabled == "No" - ) - error_message = "AWSOrganizationsEnabled variable can only accept Yes or No as values." - } + type = string + default = "No" + description = "You can receive both PHD and SHD alerts if you're using AWS Organizations. \n If you are, make sure to enable Organizational Health View: \n (https://docs.aws.amazon.com/health/latest/ug/aggregate-events.html) to \n aggregate all PHD events in your AWS Organization. If not, you can still \n get SHD alerts." + validation { + condition = ( + var.AWSOrganizationsEnabled == "Yes" || var.AWSOrganizationsEnabled == "No" + ) + error_message = "AWSOrganizationsEnabled variable can only accept Yes or No as values." + } } variable "ManagementAccountRoleArn" { - type = string - default = "" - description = "Arn of the IAM role in the top-level management account for collecting PHD Events. 'None' if deploying into the top-level management account." + type = string + default = "" + description = "Arn of the IAM role in the top-level management account for collecting PHD Events. 'None' if deploying into the top-level management account." } variable "AWSHealthEventType" { - type = string - default = "issue | accountNotification | scheduledChange" - description = "Select the event type that you want AHA to report on. Refer to \n https://docs.aws.amazon.com/health/latest/APIReference/API_EventType.html for more information on EventType." - validation { - condition = ( - var.AWSHealthEventType == "issue | accountNotification | scheduledChange" || var.AWSHealthEventType == "issue" - ) - error_message = "AWSHealthEventType variable can only accept issue | accountNotification | scheduledChange or issue as values." - } + type = string + default = "issue | accountNotification | scheduledChange" + description = "Select the event type that you want AHA to report on. Refer to \n https://docs.aws.amazon.com/health/latest/APIReference/API_EventType.html for more information on EventType." + validation { + condition = ( + var.AWSHealthEventType == "issue | accountNotification | scheduledChange" || var.AWSHealthEventType == "issue" + ) + error_message = "AWSHealthEventType variable can only accept issue | accountNotification | scheduledChange or issue as values." + } } #variable "S3Bucket" { @@ -116,57 +116,57 @@ variable "AWSHealthEventType" { #} variable "EventBusName" { - type = string - default = "" - description = "This is to ingest alerts into AWS EventBridge. Enter the event bus name if you wish to send the alerts to the AWS EventBridge. Note: By ingesting you wish to send the alerts to the AWS EventBridge. Note: By ingesting these alerts to AWS EventBridge, you can integrate with 35 SaaS vendors such as DataDog/NewRelic/PagerDuty. If you don't prefer to use EventBridge, leave the default (None)." + type = string + default = "" + description = "This is to ingest alerts into AWS EventBridge. Enter the event bus name if you wish to send the alerts to the AWS EventBridge. Note: By ingesting you wish to send the alerts to the AWS EventBridge. Note: By ingesting these alerts to AWS EventBridge, you can integrate with 35 SaaS vendors such as DataDog/NewRelic/PagerDuty. If you don't prefer to use EventBridge, leave the default (None)." } variable "SlackWebhookURL" { - type = string - default = "" - description = "Enter the Slack Webhook URL. If you don't prefer to use Slack, leave the default (empty)." + type = string + default = "" + description = "Enter the Slack Webhook URL. If you don't prefer to use Slack, leave the default (empty)." } variable "MicrosoftTeamsWebhookURL" { - type = string - default = "" - description = "Enter Microsoft Teams Webhook URL. If you don't prefer to use MS Teams, leave the default (empty)." + type = string + default = "" + description = "Enter Microsoft Teams Webhook URL. If you don't prefer to use MS Teams, leave the default (empty)." } variable "AmazonChimeWebhookURL" { - type = string - default = "" - description = "Enter the Chime Webhook URL, If you don't prefer to use Amazon Chime, leave the default (empty)." + type = string + default = "" + description = "Enter the Chime Webhook URL, If you don't prefer to use Amazon Chime, leave the default (empty)." } variable "Regions" { - type = string - default = "all regions" - description = "By default, AHA reports events affecting all AWS regions. \n If you want to report on certain regions you can enter up to 10 in a comma separated format. \n Available Regions: us-east-1,us-east-2,us-west-1,us-west-2,af-south-1,ap-east-1,ap-south-1,ap-northeast-3, \n ap-northeast-2,ap-southeast-1,ap-southeast-2,ap-northeast-1,ca-central-1,eu-central-1,eu-west-1,eu-west-2, \n eu-south-1,eu-south-3,eu-north-1,me-south-1,sa-east-1,global" + type = string + default = "all regions" + description = "By default, AHA reports events affecting all AWS regions. \n If you want to report on certain regions you can enter up to 10 in a comma separated format. \n Available Regions: us-east-1,us-east-2,us-west-1,us-west-2,af-south-1,ap-east-1,ap-south-1,ap-northeast-3, \n ap-northeast-2,ap-southeast-1,ap-southeast-2,ap-northeast-1,ca-central-1,eu-central-1,eu-west-1,eu-west-2, \n eu-south-1,eu-south-3,eu-north-1,me-south-1,sa-east-1,global" } variable "EventSearchBack" { - type = number - default = "1" - description = "How far back to search for events in hours. Default is 1 hour" + type = number + default = "1" + description = "How far back to search for events in hours. Default is 1 hour" } variable "FromEmail" { - type = string - default = "none@domain.com" - description = "Enter FROM Email Address" + type = string + default = "none@domain.com" + description = "Enter FROM Email Address" } variable "ToEmail" { - type = string - default = "none@domain.com" - description = "Enter email addresses separated by commas (for ex: abc@amazon.com, bcd@amazon.com)" + type = string + default = "none@domain.com" + description = "Enter email addresses separated by commas (for ex: abc@amazon.com, bcd@amazon.com)" } variable "Subject" { - type = string - default = "AWS Health Alert" - description = "Enter the subject of the email address" + type = string + default = "AWS Health Alert" + description = "Enter the subject of the email address" } #variable "S3Bucket" { @@ -176,21 +176,14 @@ variable "Subject" { #} variable "ExcludeAccountIDs" { - type = string - default = "" - description = "If you would like to EXCLUDE any accounts from alerting, enter a .csv filename created with comma-seperated account numbers. Sample AccountIDs file name: aha_account_ids.csv. If not, leave the default empty." + type = string + default = "" + description = "If you would like to EXCLUDE any accounts from alerting, enter a .csv filename created with comma-seperated account numbers. Sample AccountIDs file name: aha_account_ids.csv. If not, leave the default empty." } - variable "FilterResolvedResources" { - type = string - default = "No" - description = "Whether to filter out resources with resolved status. Set to 'Yes' to filter out resolved resources, 'No' to show all resources." - validation { - condition = ( - var.FilterResolvedResources == "Yes" || var.FilterResolvedResources == "No" - ) - error_message = "FilterResolvedResources variable can only accept Yes or No as values." - } + type = bool + default = false + description = "Whether to filter out resources with resolved status. Set to true to filter out resolved resources, false to show all resources." } ##### Resources for AHA Solution created below. @@ -204,344 +197,344 @@ resource "random_string" "resource_code" { # S3 buckets creation resource "aws_s3_bucket" "AHA-S3Bucket-PrimaryRegion" { - count = var.ExcludeAccountIDs != "" ? 1 : 0 - bucket = "aha-bucket-${var.aha_primary_region}-${random_string.resource_code.result}" - tags = { - Name = "aha-bucket" - } + count = "${var.ExcludeAccountIDs != "" ? 1 : 0}" + bucket = "aha-bucket-${var.aha_primary_region}-${random_string.resource_code.result}" + tags = { + Name = "aha-bucket" + } } resource "aws_s3_bucket_acl" "AHA-S3Bucket-PrimaryRegion" { - count = var.ExcludeAccountIDs != "" ? 1 : 0 - bucket = aws_s3_bucket.AHA-S3Bucket-PrimaryRegion[0].id - acl = "private" + count = var.ExcludeAccountIDs != "" ? 1 : 0 + bucket = aws_s3_bucket.AHA-S3Bucket-PrimaryRegion[0].id + acl = "private" } resource "aws_s3_bucket" "AHA-S3Bucket-SecondaryRegion" { - count = var.aha_secondary_region != "" && var.ExcludeAccountIDs != "" ? 1 : 0 - provider = aws.secondary_region - bucket = "aha-bucket-${var.aha_secondary_region}-${random_string.resource_code.result}" - tags = { - Name = "aha-bucket" - } + count = "${var.aha_secondary_region != "" && var.ExcludeAccountIDs != "" ? 1 : 0}" + provider = aws.secondary_region + bucket = "aha-bucket-${var.aha_secondary_region}-${random_string.resource_code.result}" + tags = { + Name = "aha-bucket" + } } resource "aws_s3_bucket_acl" "AHA-S3Bucket-SecondaryRegion" { - count = var.aha_secondary_region != "" && var.ExcludeAccountIDs != "" ? 1 : 0 - provider = aws.secondary_region - bucket = aws_s3_bucket.AHA-S3Bucket-SecondaryRegion[0].id - acl = "private" + count = "${var.aha_secondary_region != "" && var.ExcludeAccountIDs != "" ? 1 : 0}" + provider = aws.secondary_region + bucket = aws_s3_bucket.AHA-S3Bucket-SecondaryRegion[0].id + acl = "private" } resource "aws_s3_object" "AHA-S3Object-PrimaryRegion" { - count = var.ExcludeAccountIDs != "" ? 1 : 0 - key = var.ExcludeAccountIDs - bucket = aws_s3_bucket.AHA-S3Bucket-PrimaryRegion[0].bucket - source = var.ExcludeAccountIDs - tags = { - Name = "${var.ExcludeAccountIDs}" - } + count = "${var.ExcludeAccountIDs != "" ? 1 : 0}" + key = var.ExcludeAccountIDs + bucket = aws_s3_bucket.AHA-S3Bucket-PrimaryRegion[0].bucket + source = var.ExcludeAccountIDs + tags = { + Name = "${var.ExcludeAccountIDs}" + } } resource "aws_s3_object" "AHA-S3Object-SecondaryRegion" { - count = var.aha_secondary_region != "" && var.ExcludeAccountIDs != "" ? 1 : 0 - provider = aws.secondary_region - key = var.ExcludeAccountIDs - bucket = aws_s3_bucket.AHA-S3Bucket-SecondaryRegion[0].bucket - source = var.ExcludeAccountIDs - tags = { - Name = "${var.ExcludeAccountIDs}" - } + count = "${var.aha_secondary_region != "" && var.ExcludeAccountIDs != "" ? 1 : 0}" + provider = aws.secondary_region + key = var.ExcludeAccountIDs + bucket = aws_s3_bucket.AHA-S3Bucket-SecondaryRegion[0].bucket + source = var.ExcludeAccountIDs + tags = { + Name = "${var.ExcludeAccountIDs}" + } } # DynamoDB table - Create if secondary region not set resource "aws_dynamodb_table" "AHA-DynamoDBTable" { - count = var.aha_secondary_region == "" ? 1 : 0 - billing_mode = "PROVISIONED" - hash_key = "arn" - name = "${var.dynamodbtable}-${random_string.resource_code.result}" - read_capacity = 5 - write_capacity = 5 - stream_enabled = false - tags = { - Name = "${var.dynamodbtable}" - } + count = "${var.aha_secondary_region == "" ? 1 : 0}" + billing_mode = "PROVISIONED" + hash_key = "arn" + name = "${var.dynamodbtable}-${random_string.resource_code.result}" + read_capacity = 5 + write_capacity = 5 + stream_enabled = false + tags = { + Name = "${var.dynamodbtable}" + } - attribute { - name = "arn" - type = "S" - } + attribute { + name = "arn" + type = "S" + } - point_in_time_recovery { - enabled = false - } + point_in_time_recovery { + enabled = false + } - timeouts {} + timeouts {} - ttl { - attribute_name = "ttl" - enabled = true - } + ttl { + attribute_name = "ttl" + enabled = true + } } # DynamoDB table - Multi region Global Table - Create if secondary region is set resource "aws_dynamodb_table" "AHA-GlobalDynamoDBTable" { - count = var.aha_secondary_region == "" ? 0 : 1 - billing_mode = "PAY_PER_REQUEST" - hash_key = "arn" - name = "${var.dynamodbtable}-${random_string.resource_code.result}" - stream_enabled = true - stream_view_type = "NEW_AND_OLD_IMAGES" - tags = { - Name = "${var.dynamodbtable}" - } + count = "${var.aha_secondary_region == "" ? 0 : 1}" + billing_mode = "PAY_PER_REQUEST" + hash_key = "arn" + name = "${var.dynamodbtable}-${random_string.resource_code.result}" + stream_enabled = true + stream_view_type = "NEW_AND_OLD_IMAGES" + tags = { + Name = "${var.dynamodbtable}" + } - attribute { - name = "arn" - type = "S" - } + attribute { + name = "arn" + type = "S" + } - point_in_time_recovery { - enabled = false - } + point_in_time_recovery { + enabled = false + } - replica { - region_name = var.aha_secondary_region - } + replica { + region_name = var.aha_secondary_region + } - timeouts {} + timeouts {} - ttl { - attribute_name = "ttl" - enabled = true - } + ttl { + attribute_name = "ttl" + enabled = true + } } # Tags for DynamoDB - secondary region resource "aws_dynamodb_tag" "AHA-GlobalDynamoDBTable" { - count = var.aha_secondary_region == "" ? 0 : 1 - provider = aws.secondary_region - resource_arn = replace(aws_dynamodb_table.AHA-GlobalDynamoDBTable[count.index].arn, var.aha_primary_region, var.aha_secondary_region) - key = "Name" - value = var.dynamodbtable + count = "${var.aha_secondary_region == "" ? 0 : 1}" + provider = aws.secondary_region + resource_arn = replace(aws_dynamodb_table.AHA-GlobalDynamoDBTable[count.index].arn, var.aha_primary_region, var.aha_secondary_region) + key = "Name" + value = "${var.dynamodbtable}" } # Tags for DynamoDB - secondary region - default_tags resource "aws_dynamodb_tag" "AHA-GlobalDynamoDBTable-Additional-tags" { - for_each = { for key, value in var.default_tags : key => value if var.aha_secondary_region != "" } - provider = aws.secondary_region - resource_arn = replace(aws_dynamodb_table.AHA-GlobalDynamoDBTable[0].arn, var.aha_primary_region, var.aha_secondary_region) - key = each.key - value = each.value + for_each = { for key, value in var.default_tags : key => value if var.aha_secondary_region != "" } + provider = aws.secondary_region + resource_arn = replace(aws_dynamodb_table.AHA-GlobalDynamoDBTable[0].arn, var.aha_primary_region, var.aha_secondary_region) + key = each.key + value = each.value } # Secrets - SlackChannelSecret resource "aws_secretsmanager_secret" "SlackChannelID" { - count = var.SlackWebhookURL == "" ? 0 : 1 - name = "SlackChannelID" - description = "Slack Channel ID Secret" - recovery_window_in_days = 0 - tags = { - "HealthCheckSlack" = "ChannelID" - } - dynamic "replica" { - for_each = var.aha_secondary_region == "" ? [] : [1] - content { - region = var.aha_secondary_region + count = "${var.SlackWebhookURL == "" ? 0 : 1}" + name = "SlackChannelID" + description = "Slack Channel ID Secret" + recovery_window_in_days = 0 + tags = { + "HealthCheckSlack" = "ChannelID" + } + dynamic "replica" { + for_each = var.aha_secondary_region == "" ? [] : [1] + content { + region = var.aha_secondary_region + } } - } } resource "aws_secretsmanager_secret_version" "SlackChannelID" { - count = var.SlackWebhookURL == "" ? 0 : 1 - secret_id = aws_secretsmanager_secret.SlackChannelID.*.id[count.index] - secret_string = var.SlackWebhookURL + count = "${var.SlackWebhookURL == "" ? 0 : 1}" + secret_id = "${aws_secretsmanager_secret.SlackChannelID.*.id[count.index]}" + secret_string = "${var.SlackWebhookURL}" } # Secrets - MicrosoftChannelSecret resource "aws_secretsmanager_secret" "MicrosoftChannelID" { - count = var.MicrosoftTeamsWebhookURL == "" ? 0 : 1 - name = "MicrosoftChannelID" - description = "Microsoft Channel ID Secret" - recovery_window_in_days = 0 - tags = { - "HealthCheckMicrosoft" = "ChannelID" - "Name" = "AHA-MicrosoftChannelID" - } - dynamic "replica" { - for_each = var.aha_secondary_region == "" ? [] : [1] - content { - region = var.aha_secondary_region + count = "${var.MicrosoftTeamsWebhookURL == "" ? 0 : 1}" + name = "MicrosoftChannelID" + description = "Microsoft Channel ID Secret" + recovery_window_in_days = 0 + tags = { + "HealthCheckMicrosoft" = "ChannelID" + "Name" = "AHA-MicrosoftChannelID" + } + dynamic "replica" { + for_each = var.aha_secondary_region == "" ? [] : [1] + content { + region = var.aha_secondary_region + } } - } } resource "aws_secretsmanager_secret_version" "MicrosoftChannelID" { - count = var.MicrosoftTeamsWebhookURL == "" ? 0 : 1 - secret_id = aws_secretsmanager_secret.MicrosoftChannelID.*.id[count.index] - secret_string = var.MicrosoftTeamsWebhookURL + count = "${var.MicrosoftTeamsWebhookURL == "" ? 0 : 1}" + secret_id = "${aws_secretsmanager_secret.MicrosoftChannelID.*.id[count.index]}" + secret_string = "${var.MicrosoftTeamsWebhookURL}" } # Secrets - EventBusNameSecret resource "aws_secretsmanager_secret" "EventBusName" { - count = var.EventBusName == "" ? 0 : 1 - name = "EventBusName" - description = "EventBus Name Secret" - recovery_window_in_days = 0 - tags = { - "EventBusName" = "ChannelID" - "Name" = "AHA-EventBusName" - } - dynamic "replica" { - for_each = var.aha_secondary_region == "" ? [] : [1] - content { - region = var.aha_secondary_region + count = "${var.EventBusName == "" ? 0 : 1}" + name = "EventBusName" + description = "EventBus Name Secret" + recovery_window_in_days = 0 + tags = { + "EventBusName" = "ChannelID" + "Name" = "AHA-EventBusName" + } + dynamic "replica" { + for_each = var.aha_secondary_region == "" ? [] : [1] + content { + region = var.aha_secondary_region + } } - } } resource "aws_secretsmanager_secret_version" "EventBusName" { - count = var.EventBusName == "" ? 0 : 1 - secret_id = aws_secretsmanager_secret.EventBusName.*.id[count.index] - secret_string = var.EventBusName + count = "${var.EventBusName == "" ? 0 : 1}" + secret_id = "${aws_secretsmanager_secret.EventBusName.*.id[count.index]}" + secret_string = "${var.EventBusName}" } # Secrets - ChimeChannelSecret resource "aws_secretsmanager_secret" "ChimeChannelID" { - count = var.AmazonChimeWebhookURL == "" ? 0 : 1 - name = "ChimeChannelID" - description = "Chime Channel ID Secret" - recovery_window_in_days = 0 - tags = { - "HealthCheckChime" = "ChannelID" - "Name" = "AHA-ChimeChannelID-${random_string.resource_code.result}" - } - dynamic "replica" { - for_each = var.aha_secondary_region == "" ? [] : [1] - content { - region = var.aha_secondary_region + count = "${var.AmazonChimeWebhookURL == "" ? 0 : 1}" + name = "ChimeChannelID" + description = "Chime Channel ID Secret" + recovery_window_in_days = 0 + tags = { + "HealthCheckChime" = "ChannelID" + "Name" = "AHA-ChimeChannelID-${random_string.resource_code.result}" + } + dynamic "replica" { + for_each = var.aha_secondary_region == "" ? [] : [1] + content { + region = var.aha_secondary_region + } } - } } resource "aws_secretsmanager_secret_version" "ChimeChannelID" { - count = var.AmazonChimeWebhookURL == "" ? 0 : 1 - secret_id = aws_secretsmanager_secret.ChimeChannelID.*.id[count.index] - secret_string = var.AmazonChimeWebhookURL + count = "${var.AmazonChimeWebhookURL == "" ? 0 : 1}" + secret_id = "${aws_secretsmanager_secret.ChimeChannelID.*.id[count.index]}" + secret_string = "${var.AmazonChimeWebhookURL}" } # Secrets - AssumeRoleSecret resource "aws_secretsmanager_secret" "AssumeRoleArn" { - count = var.ManagementAccountRoleArn == "" ? 0 : 1 - name = "AssumeRoleArn" - description = "Management account role for AHA to assume" - recovery_window_in_days = 0 - tags = { - "AssumeRoleArn" = "" - "Name" = "AHA-AssumeRoleArn" - } - dynamic "replica" { - for_each = var.aha_secondary_region == "" ? [] : [1] - content { - region = var.aha_secondary_region + count = "${var.ManagementAccountRoleArn == "" ? 0 : 1}" + name = "AssumeRoleArn" + description = "Management account role for AHA to assume" + recovery_window_in_days = 0 + tags = { + "AssumeRoleArn" = "" + "Name" = "AHA-AssumeRoleArn" + } + dynamic "replica" { + for_each = var.aha_secondary_region == "" ? [] : [1] + content { + region = var.aha_secondary_region + } } - } } resource "aws_secretsmanager_secret_version" "AssumeRoleArn" { - count = var.ManagementAccountRoleArn == "" ? 0 : 1 - secret_id = aws_secretsmanager_secret.AssumeRoleArn.*.id[count.index] - secret_string = var.ManagementAccountRoleArn + count = "${var.ManagementAccountRoleArn == "" ? 0 : 1}" + secret_id = "${aws_secretsmanager_secret.AssumeRoleArn.*.id[count.index]}" + secret_string = "${var.ManagementAccountRoleArn}" } # IAM Role for Lambda function execution resource "aws_iam_role" "AHA-LambdaExecutionRole" { - name = "AHA-LambdaExecutionRole-${random_string.resource_code.result}" - path = "/" - assume_role_policy = jsonencode( - { - Version = "2012-10-17" - Statement = [ + name = "AHA-LambdaExecutionRole-${random_string.resource_code.result}" + path = "/" + assume_role_policy = jsonencode( { - Action = "sts:AssumeRole" - Effect = "Allow" - Principal = { - Service = "lambda.amazonaws.com" - } - }, - ] + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "lambda.amazonaws.com" + } + }, + ] + } + ) + inline_policy { + name = "AHA-LambdaPolicy" + policy = data.aws_iam_policy_document.AHA-LambdaPolicy-Document.json + } + tags = { + "Name" = "AHA-LambdaExecutionRole" } - ) - inline_policy { - name = "AHA-LambdaPolicy" - policy = data.aws_iam_policy_document.AHA-LambdaPolicy-Document.json - } - tags = { - "Name" = "AHA-LambdaExecutionRole" - } } data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { - version = "2012-10-17" + version = "2012-10-17" statement { effect = "Allow" actions = [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents", + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents", ] resources = [ - "arn:aws:logs:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:*", - "arn:aws:logs:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:*" + "arn:aws:logs:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:*", + "arn:aws:logs:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:*" ] } statement { - effect = "Allow" - actions = [ - "health:DescribeAffectedAccountsForOrganization", - "health:DescribeAffectedEntitiesForOrganization", - "health:DescribeEventDetailsForOrganization", - "health:DescribeEventsForOrganization", - "health:DescribeEventDetails", - "health:DescribeEvents", - "health:DescribeEventTypes", - "health:DescribeAffectedEntities", - "organizations:ListAccounts", - "organizations:DescribeAccount", + effect = "Allow" + actions = [ + "health:DescribeAffectedAccountsForOrganization", + "health:DescribeAffectedEntitiesForOrganization", + "health:DescribeEventDetailsForOrganization", + "health:DescribeEventsForOrganization", + "health:DescribeEventDetails", + "health:DescribeEvents", + "health:DescribeEventTypes", + "health:DescribeAffectedEntities", + "organizations:ListAccounts", + "organizations:DescribeAccount", ] - resources = ["*"] + resources = [ "*" ] } statement { - effect = "Allow" - actions = [ - "dynamodb:ListTables", + effect = "Allow" + actions = [ + "dynamodb:ListTables", ] resources = [ - "arn:aws:dynamodb:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:*", - "arn:aws:dynamodb:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:*", + "arn:aws:dynamodb:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:*", + "arn:aws:dynamodb:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:*", ] } statement { - effect = "Allow" - actions = [ - "ses:SendEmail", + effect = "Allow" + actions = [ + "ses:SendEmail", ] resources = [ - "arn:aws:ses:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:*", - "arn:aws:ses:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:*", + "arn:aws:ses:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:*", + "arn:aws:ses:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:*", ] } statement { - effect = "Allow" - actions = [ - "dynamodb:UpdateTimeToLive", - "dynamodb:PutItem", - "dynamodb:DeleteItem", - "dynamodb:GetItem", - "dynamodb:Scan", - "dynamodb:Query", - "dynamodb:UpdateItem", - "dynamodb:UpdateTable", - "dynamodb:GetRecords", + effect = "Allow" + actions = [ + "dynamodb:UpdateTimeToLive", + "dynamodb:PutItem", + "dynamodb:DeleteItem", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:Query", + "dynamodb:UpdateItem", + "dynamodb:UpdateTable", + "dynamodb:GetRecords", ] resources = [ - #aws_dynamodb_table.AHA-DynamoDBTable.arn - "arn:aws:dynamodb:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:table/${var.dynamodbtable}-${random_string.resource_code.result}", - "arn:aws:dynamodb:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:table/${var.dynamodbtable}-${random_string.resource_code.result}", + #aws_dynamodb_table.AHA-DynamoDBTable.arn + "arn:aws:dynamodb:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:table/${var.dynamodbtable}-${random_string.resource_code.result}", + "arn:aws:dynamodb:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:table/${var.dynamodbtable}-${random_string.resource_code.result}", ] } dynamic "statement" { @@ -549,15 +542,15 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "secretsmanager:GetResourcePolicy", - "secretsmanager:DescribeSecret", - "secretsmanager:ListSecretVersionIds", - "secretsmanager:GetSecretValue", + "secretsmanager:GetResourcePolicy", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecretVersionIds", + "secretsmanager:GetSecretValue", ] resources = [ - aws_secretsmanager_secret.SlackChannelID[0].arn, - "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.SlackChannelID[0].arn), 6)}" - # var.aha_secondary_region != "" ? "arn:aws:secretsmanager:${var.aha_secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.SlackChannelID[0].arn),6)}" : null + aws_secretsmanager_secret.SlackChannelID[0].arn, + "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.SlackChannelID[0].arn),6)}" +# var.aha_secondary_region != "" ? "arn:aws:secretsmanager:${var.aha_secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.SlackChannelID[0].arn),6)}" : null ] } } @@ -566,14 +559,14 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "secretsmanager:GetResourcePolicy", - "secretsmanager:DescribeSecret", - "secretsmanager:ListSecretVersionIds", - "secretsmanager:GetSecretValue", + "secretsmanager:GetResourcePolicy", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecretVersionIds", + "secretsmanager:GetSecretValue", ] resources = [ - aws_secretsmanager_secret.MicrosoftChannelID[0].arn, - "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.MicrosoftChannelID[0].arn), 6)}" + aws_secretsmanager_secret.MicrosoftChannelID[0].arn, + "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.MicrosoftChannelID[0].arn),6)}" ] } } @@ -582,14 +575,14 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "secretsmanager:GetResourcePolicy", - "secretsmanager:DescribeSecret", - "secretsmanager:ListSecretVersionIds", - "secretsmanager:GetSecretValue", + "secretsmanager:GetResourcePolicy", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecretVersionIds", + "secretsmanager:GetSecretValue", ] resources = [ - aws_secretsmanager_secret.ChimeChannelID[0].arn, - "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.ChimeChannelID[0].arn), 6)}" + aws_secretsmanager_secret.ChimeChannelID[0].arn, + "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.ChimeChannelID[0].arn),6)}" ] } } @@ -598,14 +591,14 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "secretsmanager:GetResourcePolicy", - "secretsmanager:DescribeSecret", - "secretsmanager:ListSecretVersionIds", - "secretsmanager:GetSecretValue", + "secretsmanager:GetResourcePolicy", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecretVersionIds", + "secretsmanager:GetSecretValue", ] resources = [ - aws_secretsmanager_secret.EventBusName[0].arn, - "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.EventBusName[0].arn), 6)}" + aws_secretsmanager_secret.EventBusName[0].arn, + "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.EventBusName[0].arn),6)}" ] } } @@ -614,14 +607,14 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "secretsmanager:GetResourcePolicy", - "secretsmanager:DescribeSecret", - "secretsmanager:ListSecretVersionIds", - "secretsmanager:GetSecretValue", + "secretsmanager:GetResourcePolicy", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecretVersionIds", + "secretsmanager:GetSecretValue", ] resources = [ - aws_secretsmanager_secret.AssumeRoleArn[0].arn, - "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.AssumeRoleArn[0].arn), 6)}" + aws_secretsmanager_secret.AssumeRoleArn[0].arn, + "arn:aws:secretsmanager:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:secret:${element(split(":", aws_secretsmanager_secret.AssumeRoleArn[0].arn),6)}" ] } } @@ -630,11 +623,11 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "events:PutEvents", + "events:PutEvents", ] resources = [ - "arn:aws:events:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:event-bus/${var.EventBusName}", - "arn:aws:events:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:event-bus/${var.EventBusName}" + "arn:aws:events:${var.aha_primary_region}:${data.aws_caller_identity.current.account_id}:event-bus/${var.EventBusName}", + "arn:aws:events:${local.secondary_region}:${data.aws_caller_identity.current.account_id}:event-bus/${var.EventBusName}" ] } } @@ -643,10 +636,10 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "sts:AssumeRole", + "sts:AssumeRole", ] resources = [ - "${var.ManagementAccountRoleArn}", + "${var.ManagementAccountRoleArn}", ] } } @@ -655,164 +648,164 @@ data "aws_iam_policy_document" "AHA-LambdaPolicy-Document" { content { effect = "Allow" actions = [ - "s3:GetObject", + "s3:GetObject", ] resources = [ - "arn:aws:s3:::aha-bucket-${var.aha_primary_region}-${random_string.resource_code.result}/${var.ExcludeAccountIDs}", - "arn:aws:s3:::aha-bucket-${local.secondary_region}-${random_string.resource_code.result}/${var.ExcludeAccountIDs}", + "arn:aws:s3:::aha-bucket-${var.aha_primary_region}-${random_string.resource_code.result}/${var.ExcludeAccountIDs}", + "arn:aws:s3:::aha-bucket-${local.secondary_region}-${random_string.resource_code.result}/${var.ExcludeAccountIDs}", ] } } } # aws_lambda_function - AHA-LambdaFunction - Primary region resource "aws_lambda_function" "AHA-LambdaFunction-PrimaryRegion" { - description = "Lambda function that runs AHA" - function_name = "AHA-LambdaFunction-${random_string.resource_code.result}" - handler = "handler.main" - memory_size = 128 - timeout = 600 - filename = data.archive_file.lambda_zip.output_path - source_code_hash = data.archive_file.lambda_zip.output_base64sha256 - # s3_bucket = var.S3Bucket - # s3_key = var.S3Key - reserved_concurrent_executions = -1 - role = aws_iam_role.AHA-LambdaExecutionRole.arn - runtime = "python3.11" - - environment { - variables = { - "Slack" = var.SlackWebhookURL != "" ? "True" : null - "Team" = var.MicrosoftTeamsWebhookURL != "" ? "True" : null - "Chime" = var.AmazonChimeWebhookURL != "" ? "True" : null - "Eventbridge" = var.EventBusName != "" ? "True" : null - "DYNAMODB_TABLE" = "${var.dynamodbtable}-${random_string.resource_code.result}" - "EMAIL_SUBJECT" = var.Subject - "EVENT_SEARCH_BACK" = var.EventSearchBack - "FROM_EMAIL" = var.FromEmail - "HEALTH_EVENT_TYPE" = var.AWSHealthEventType - "ORG_STATUS" = var.AWSOrganizationsEnabled - "REGIONS" = var.Regions - "TO_EMAIL" = var.ToEmail - "MANAGEMENT_ROLE_ARN" = var.ManagementAccountRoleArn == "" ? "None" : var.ManagementAccountRoleArn - "ACCOUNT_IDS" = var.ExcludeAccountIDs - "S3_BUCKET" = join("", aws_s3_bucket.AHA-S3Bucket-PrimaryRegion[*].bucket) - "FILTER_RESOLVED" = var.FilterResolvedResources + description = "Lambda function that runs AHA" + function_name = "AHA-LambdaFunction-${random_string.resource_code.result}" + handler = "handler.main" + memory_size = 128 + timeout = 600 + filename = data.archive_file.lambda_zip.output_path + source_code_hash = data.archive_file.lambda_zip.output_base64sha256 +# s3_bucket = var.S3Bucket +# s3_key = var.S3Key + reserved_concurrent_executions = -1 + role = aws_iam_role.AHA-LambdaExecutionRole.arn + runtime = "python3.11" + + environment { + variables = { + "Slack" = var.SlackWebhookURL != "" ? "True" : null + "Team" = var.MicrosoftTeamsWebhookURL != "" ? "True" : null + "Chime" = var.AmazonChimeWebhookURL != "" ? "True" : null + "Eventbridge" = var.EventBusName != "" ? "True" : null + "DYNAMODB_TABLE" = "${var.dynamodbtable}-${random_string.resource_code.result}" + "EMAIL_SUBJECT" = var.Subject + "EVENT_SEARCH_BACK" = var.EventSearchBack + "FROM_EMAIL" = var.FromEmail + "HEALTH_EVENT_TYPE" = var.AWSHealthEventType + "ORG_STATUS" = var.AWSOrganizationsEnabled + "REGIONS" = var.Regions + "TO_EMAIL" = var.ToEmail + "MANAGEMENT_ROLE_ARN" = var.ManagementAccountRoleArn == "" ? "None" : var.ManagementAccountRoleArn + "ACCOUNT_IDS" = var.ExcludeAccountIDs + "S3_BUCKET" = join("",aws_s3_bucket.AHA-S3Bucket-PrimaryRegion[*].bucket) + "FILTER_RESOLVED" = var.FilterResolvedResources + } } - } - timeouts {} + timeouts {} - tracing_config { - mode = "PassThrough" - } - tags = { - "Name" = "AHA-LambdaFunction" - } - depends_on = [ - aws_dynamodb_table.AHA-DynamoDBTable, - aws_dynamodb_table.AHA-GlobalDynamoDBTable, - ] + tracing_config { + mode = "PassThrough" + } + tags = { + "Name" = "AHA-LambdaFunction" + } + depends_on = [ + aws_dynamodb_table.AHA-DynamoDBTable, + aws_dynamodb_table.AHA-GlobalDynamoDBTable, + ] } # aws_lambda_function - AHA-LambdaFunction - Secondary region resource "aws_lambda_function" "AHA-LambdaFunction-SecondaryRegion" { - count = var.aha_secondary_region == "" ? 0 : 1 - provider = aws.secondary_region - description = "Lambda function that runs AHA" - function_name = "AHA-LambdaFunction-${random_string.resource_code.result}" - handler = "handler.main" - memory_size = 128 - timeout = 600 - filename = data.archive_file.lambda_zip.output_path - source_code_hash = data.archive_file.lambda_zip.output_base64sha256 - # s3_bucket = var.S3Bucket - # s3_key = var.S3Key - reserved_concurrent_executions = -1 - role = aws_iam_role.AHA-LambdaExecutionRole.arn - runtime = "python3.11" - - environment { - variables = { - "Slack" = var.SlackWebhookURL != "" ? "True" : null - "Team" = var.MicrosoftTeamsWebhookURL != "" ? "True" : null - "Chime" = var.AmazonChimeWebhookURL != "" ? "True" : null - "Eventbridge" = var.EventBusName != "" ? "True" : null - "DYNAMODB_TABLE" = "${var.dynamodbtable}-${random_string.resource_code.result}" - "EMAIL_SUBJECT" = var.Subject - "EVENT_SEARCH_BACK" = var.EventSearchBack - "FROM_EMAIL" = var.FromEmail - "HEALTH_EVENT_TYPE" = var.AWSHealthEventType - "ORG_STATUS" = var.AWSOrganizationsEnabled - "REGIONS" = var.Regions - "TO_EMAIL" = var.ToEmail - "MANAGEMENT_ROLE_ARN" = var.ManagementAccountRoleArn - "ACCOUNT_IDS" = var.ExcludeAccountIDs - "S3_BUCKET" = join("", aws_s3_bucket.AHA-S3Bucket-SecondaryRegion[*].bucket) - "FILTER_RESOLVED" = var.FilterResolvedResources + count = "${var.aha_secondary_region == "" ? 0 : 1}" + provider = aws.secondary_region + description = "Lambda function that runs AHA" + function_name = "AHA-LambdaFunction-${random_string.resource_code.result}" + handler = "handler.main" + memory_size = 128 + timeout = 600 + filename = data.archive_file.lambda_zip.output_path + source_code_hash = data.archive_file.lambda_zip.output_base64sha256 +# s3_bucket = var.S3Bucket +# s3_key = var.S3Key + reserved_concurrent_executions = -1 + role = aws_iam_role.AHA-LambdaExecutionRole.arn + runtime = "python3.11" + + environment { + variables = { + "Slack" = var.SlackWebhookURL != "" ? "True" : null + "Team" = var.MicrosoftTeamsWebhookURL != "" ? "True" : null + "Chime" = var.AmazonChimeWebhookURL != "" ? "True" : null + "Eventbridge" = var.EventBusName != "" ? "True" : null + "DYNAMODB_TABLE" = "${var.dynamodbtable}-${random_string.resource_code.result}" + "EMAIL_SUBJECT" = var.Subject + "EVENT_SEARCH_BACK" = var.EventSearchBack + "FROM_EMAIL" = var.FromEmail + "HEALTH_EVENT_TYPE" = var.AWSHealthEventType + "ORG_STATUS" = var.AWSOrganizationsEnabled + "REGIONS" = var.Regions + "TO_EMAIL" = var.ToEmail + "MANAGEMENT_ROLE_ARN" = var.ManagementAccountRoleArn + "ACCOUNT_IDS" = var.ExcludeAccountIDs + "S3_BUCKET" = join("",aws_s3_bucket.AHA-S3Bucket-SecondaryRegion[*].bucket) + "FILTER_RESOLVED" = var.FilterResolvedResources + } } - } - timeouts {} + timeouts {} - tracing_config { - mode = "PassThrough" - } - tags = { - "Name" = "AHA-LambdaFunction" - } - depends_on = [ - aws_dynamodb_table.AHA-DynamoDBTable, - aws_dynamodb_table.AHA-GlobalDynamoDBTable, - ] + tracing_config { + mode = "PassThrough" + } + tags = { + "Name" = "AHA-LambdaFunction" + } + depends_on = [ + aws_dynamodb_table.AHA-DynamoDBTable, + aws_dynamodb_table.AHA-GlobalDynamoDBTable, + ] } # EventBridge - Schedule to run lambda resource "aws_cloudwatch_event_rule" "AHA-LambdaSchedule-PrimaryRegion" { - description = "Lambda trigger Event" - event_bus_name = "default" - state = "ENABLED" - name = "AHA-LambdaSchedule-${random_string.resource_code.result}" - schedule_expression = "rate(1 minute)" - tags = { - "Name" = "AHA-LambdaSchedule" - } + description = "Lambda trigger Event" + event_bus_name = "default" + state = "ENABLED" + name = "AHA-LambdaSchedule-${random_string.resource_code.result}" + schedule_expression = "rate(1 minute)" + tags = { + "Name" = "AHA-LambdaSchedule" + } } resource "aws_cloudwatch_event_rule" "AHA-LambdaSchedule-SecondaryRegion" { - description = "Lambda trigger Event" - count = var.aha_secondary_region == "" ? 0 : 1 - provider = aws.secondary_region - event_bus_name = "default" - state = "ENABLED" - name = "AHA-LambdaSchedule-${random_string.resource_code.result}" - schedule_expression = "rate(1 minute)" - tags = { - "Name" = "AHA-LambdaSchedule" - } + description = "Lambda trigger Event" + count = "${var.aha_secondary_region == "" ? 0 : 1}" + provider = aws.secondary_region + event_bus_name = "default" + state = "ENABLED" + name = "AHA-LambdaSchedule-${random_string.resource_code.result}" + schedule_expression = "rate(1 minute)" + tags = { + "Name" = "AHA-LambdaSchedule" + } } resource "aws_cloudwatch_event_target" "AHA-LambdaFunction-PrimaryRegion" { - arn = aws_lambda_function.AHA-LambdaFunction-PrimaryRegion.arn - rule = aws_cloudwatch_event_rule.AHA-LambdaSchedule-PrimaryRegion.name + arn = aws_lambda_function.AHA-LambdaFunction-PrimaryRegion.arn + rule = aws_cloudwatch_event_rule.AHA-LambdaSchedule-PrimaryRegion.name } resource "aws_cloudwatch_event_target" "AHA-LambdaFunction-SecondaryRegion" { - count = var.aha_secondary_region == "" ? 0 : 1 - provider = aws.secondary_region - arn = aws_lambda_function.AHA-LambdaFunction-SecondaryRegion[0].arn - rule = aws_cloudwatch_event_rule.AHA-LambdaSchedule-SecondaryRegion[0].name + count = "${var.aha_secondary_region == "" ? 0 : 1}" + provider = aws.secondary_region + arn = aws_lambda_function.AHA-LambdaFunction-SecondaryRegion[0].arn + rule = aws_cloudwatch_event_rule.AHA-LambdaSchedule-SecondaryRegion[0].name } resource "aws_lambda_permission" "AHA-LambdaSchedulePermission-PrimaryRegion" { - action = "lambda:InvokeFunction" - principal = "events.amazonaws.com" - function_name = aws_lambda_function.AHA-LambdaFunction-PrimaryRegion.arn - source_arn = aws_cloudwatch_event_rule.AHA-LambdaSchedule-PrimaryRegion.arn + action = "lambda:InvokeFunction" + principal = "events.amazonaws.com" + function_name = aws_lambda_function.AHA-LambdaFunction-PrimaryRegion.arn + source_arn = aws_cloudwatch_event_rule.AHA-LambdaSchedule-PrimaryRegion.arn } resource "aws_lambda_permission" "AHA-LambdaSchedulePermission-SecondaryRegion" { - count = var.aha_secondary_region == "" ? 0 : 1 - provider = aws.secondary_region - action = "lambda:InvokeFunction" - principal = "events.amazonaws.com" - function_name = aws_lambda_function.AHA-LambdaFunction-SecondaryRegion[0].arn - source_arn = aws_cloudwatch_event_rule.AHA-LambdaSchedule-SecondaryRegion[0].arn + count = "${var.aha_secondary_region == "" ? 0 : 1}" + provider = aws.secondary_region + action = "lambda:InvokeFunction" + principal = "events.amazonaws.com" + function_name = aws_lambda_function.AHA-LambdaFunction-SecondaryRegion[0].arn + source_arn = aws_cloudwatch_event_rule.AHA-LambdaSchedule-SecondaryRegion[0].arn } diff --git a/terraform/Terraform_DEPLOY_AHA/terraform.tfvars b/terraform/Terraform_DEPLOY_AHA/terraform.tfvars index ecd7479..959ef0f 100644 --- a/terraform/Terraform_DEPLOY_AHA/terraform.tfvars +++ b/terraform/Terraform_DEPLOY_AHA/terraform.tfvars @@ -1,36 +1,36 @@ # Input variables for Terraform_DEPLOY_AHA.tf (AHA Solution deploy using terraform) # # Customize Alerts/Notifications -aha_primary_region = "us-east-1" -aha_secondary_region = "" -AWSOrganizationsEnabled = "No" -AWSHealthEventType = "issue | accountNotification | scheduledChange" +aha_primary_region="us-east-1" +aha_secondary_region="" +AWSOrganizationsEnabled="No" +AWSHealthEventType="issue | accountNotification | scheduledChange" # Communication Channels - Slack/Microsoft Teams/Amazon Chime And/or EventBridge -SlackWebhookURL = "" -MicrosoftTeamsWebhookURL = "" -AmazonChimeWebhookURL = "" -EventBusName = "" +SlackWebhookURL="" +MicrosoftTeamsWebhookURL="" +AmazonChimeWebhookURL="" +EventBusName="" # Email Setup - For Alerting via Email -FromEmail = "none@domain.com" -ToEmail = "none@domain.com" -Subject = "AWS Health Alert" +FromEmail="none@domain.com" +ToEmail="none@domain.com" +Subject="AWS Health Alert" # More Configurations - Optional # By default, AHA reports events affecting all AWS regions. # If you want to report on certain regions you can enter up to 10 in a comma separated format. -EventSearchBack = "1" -Regions = "all regions" -ManagementAccountRoleArn = "" -ExcludeAccountIDs = "" -FilterResolvedResources = "No" +EventSearchBack="1" +Regions="all regions" +ManagementAccountRoleArn="" +ExcludeAccountIDs="" +FilterResolvedResources=false # Tags applied to all resources - using module provider. Update them per your requirement. default_tags = { - Application = "AHA-Solution" - Environment = "PROD" - auto-delete = "no" + Application = "AHA-Solution" + Environment = "PROD" + auto-delete = "no" } # commands to apply changes