From 77d5cf0981f8472b0b7f272986ab0d79da19df56 Mon Sep 17 00:00:00 2001 From: aidanharrisnhs Date: Wed, 21 May 2025 16:30:10 +0100 Subject: [PATCH 1/5] added event bridge lambda and new vault to facilitate new cross account copy job method --- modules/aws-backup-source/backup_plan.tf | 8 +- modules/aws-backup-source/backup_vault.tf | 5 ++ modules/aws-backup-source/eventbridge.tf | 40 ++++++++++ modules/aws-backup-source/kms.tf | 1 - modules/aws-backup-source/lambda_copy_job.tf | 75 +++++++++++++++++++ .../resources/start_cross_account_copy_job.py | 42 +++++++++++ 6 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 modules/aws-backup-source/eventbridge.tf create mode 100644 modules/aws-backup-source/lambda_copy_job.tf create mode 100644 modules/aws-backup-source/resources/start_cross_account_copy_job.py diff --git a/modules/aws-backup-source/backup_plan.tf b/modules/aws-backup-source/backup_plan.tf index 19c6f26..323c042 100644 --- a/modules/aws-backup-source/backup_plan.tf +++ b/modules/aws-backup-source/backup_plan.tf @@ -16,12 +16,12 @@ resource "aws_backup_plan" "default" { cold_storage_after = rule.value.lifecycle.cold_storage_after != null ? rule.value.lifecycle.cold_storage_after : null } dynamic "copy_action" { - for_each = var.backup_copy_vault_arn != "" && var.backup_copy_vault_account_id != "" && rule.value.copy_action != null ? rule.value.copy_action : {} + for_each = rule.value.copy_action != null ? rule.value.copy_action : {} content { lifecycle { delete_after = copy_action.value } - destination_vault_arn = var.backup_copy_vault_arn + destination_vault_arn = aws_backup_vault.intermediary-vault.arn } } } @@ -47,12 +47,12 @@ resource "aws_backup_plan" "dynamodb" { cold_storage_after = rule.value.lifecycle.cold_storage_after != null ? rule.value.lifecycle.cold_storage_after : null } dynamic "copy_action" { - for_each = var.backup_copy_vault_arn != "" && var.backup_copy_vault_account_id != "" && rule.value.copy_action != null ? rule.value.copy_action : {} + for_each = rule.value.copy_action != null ? rule.value.copy_action : {} content { lifecycle { delete_after = copy_action.value } - destination_vault_arn = var.backup_copy_vault_arn + destination_vault_arn = aws_backup_vault.intermediary-vault.arn } } } diff --git a/modules/aws-backup-source/backup_vault.tf b/modules/aws-backup-source/backup_vault.tf index 49f79ca..be354ef 100644 --- a/modules/aws-backup-source/backup_vault.tf +++ b/modules/aws-backup-source/backup_vault.tf @@ -2,3 +2,8 @@ resource "aws_backup_vault" "main" { name = "${local.resource_name_prefix}-vault" kms_key_arn = aws_kms_key.aws_backup_key.arn } + +resource "aws_backup_vault" "intermediary-vault" { + name = "${local.resource_name_prefix}-intermediary-vault" + kms_key_arn = aws_kms_key.aws_backup_key.arn +} \ No newline at end of file diff --git a/modules/aws-backup-source/eventbridge.tf b/modules/aws-backup-source/eventbridge.tf new file mode 100644 index 0000000..4c38c51 --- /dev/null +++ b/modules/aws-backup-source/eventbridge.tf @@ -0,0 +1,40 @@ +module "eventbridge" { + source = "terraform-aws-modules/eventbridge/aws" + version = "3.14.3" + + create_bus = false + create_role = false + + rules = { + "Cross-Account-Copy-Job" = { + description = "Identify when a new recovery point is created in the intermediary vault" + event_pattern = jsonencode( + { + "source" : ["aws.backup"], + "account" : ["${data.aws_caller_identity.current.account_id}"], + "region" : ["eu-west-2"], + "detail" : { + "eventName" : ["RecoveryPointCreated"] + "serviceEventDetails" : { + "backupVaultName" : [{ "wildcard" : "*-intermediary-vault" }] + } + } + } + ) + enabled = true + } + } + + targets = { + "Cross-Account-Copy-Job" = [ + { + name = "start_cross_account_copy_job" + arn = "arn:aws:lambda:eu-west-2:${data.aws_caller_identity.current.account_id}:function:start_cross_account_copy_job" + } + ] + } + + tags = { + Service = "texas" + } +} \ No newline at end of file diff --git a/modules/aws-backup-source/kms.tf b/modules/aws-backup-source/kms.tf index deac337..91dea97 100644 --- a/modules/aws-backup-source/kms.tf +++ b/modules/aws-backup-source/kms.tf @@ -2,7 +2,6 @@ resource "aws_kms_key" "aws_backup_key" { description = "AWS Backup KMS Key" deletion_window_in_days = 30 enable_key_rotation = true - policy = data.aws_iam_policy_document.backup_key_policy.json } resource "aws_kms_alias" "backup_key" { diff --git a/modules/aws-backup-source/lambda_copy_job.tf b/modules/aws-backup-source/lambda_copy_job.tf new file mode 100644 index 0000000..0f6e7f7 --- /dev/null +++ b/modules/aws-backup-source/lambda_copy_job.tf @@ -0,0 +1,75 @@ +data "aws_iam_policy_document" "lambda_assume_role" { + statement { + effect = "Allow" + principals { + type = "Service" + identifiers = ["lambda.amazonaws.com"] + } + actions = ["sts:AssumeRole"] + } +} + +resource "aws_iam_role" "iam_for_lambda_copy_job" { + name = "iam_for_cross_account_copy_job_lambda" + assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json +} + +data "aws_iam_policy_document" "lambda_copy_job_permissions" { + version = "2012-10-17" + statement { + effect = "Allow" + actions = ["kms:Decrypt", "kms:GenerateDataKey"] + resources = [aws_kms_key.aws_backup_key.arn] + } + statement { + effect = "Allow" + actions = [ + "backup:StartCopyJob", + "backup:DescribeRecoveryPoint", + "backup:ListRecoveryPointsByBackupVault" + ] + resources = ["*"] + } + statement { + effect = "Allow" + actions = ["iam:PassRole"] + resources = [aws_iam_role.backup.arn] + } +} + +resource "aws_iam_role_policy" "cross_account_iam_permissions" { + name = "cross_account_iam_permissions_policy" + role = aws_iam_role.iam_for_lambda_copy_job.id + policy = data.aws_iam_policy_document.lambda_copy_job_permissions.json +} + +data "archive_file" "start_cross_account_copy_job_lambda_zip" { + type = "zip" + source_dir = "${path.module}/resources" + output_path = "${path.module}/.terraform/archive_files/start_cross_account_copy_job_lambda.zip" +} + +resource "aws_lambda_function" "start_cross_account_copy_job_lambda" { + filename = data.archive_file.start_cross_account_copy_job_lambda_zip.output_path + source_code_hash = data.archive_file.start_cross_account_copy_job_lambda_zip.output_base64sha256 + function_name = "start_cross_account_copy_job" + role = aws_iam_role.iam_for_lambda_copy_job.arn + handler = "start_cross_account_copy_job.lambda_handler" + runtime = "python3.12" + environment { + variables = { + aws_account_id = data.aws_caller_identity.current.account_id, + backup_account_id = var.backup_copy_vault_account_id, + backup_copy_vault_arn = var.backup_copy_vault_arn, + backup_role_arn = aws_iam_role.backup.arn + } + } +} + +resource "aws_lambda_permission" "allow_eventbridge" { + statement_id = "AllowExecutionFromEventbridge" + action = "lambda:InvokeFunction" + function_name = aws_lambda_function.start_cross_account_copy_job_lambda.function_name + principal = "events.amazonaws.com" + source_arn = "arn:aws:events:eu-west-2:${data.aws_caller_identity.current.account_id}:rule/Cross-Account-Copy-Job-rule" +} \ No newline at end of file diff --git a/modules/aws-backup-source/resources/start_cross_account_copy_job.py b/modules/aws-backup-source/resources/start_cross_account_copy_job.py new file mode 100644 index 0000000..fb7a680 --- /dev/null +++ b/modules/aws-backup-source/resources/start_cross_account_copy_job.py @@ -0,0 +1,42 @@ +import json +import boto3 +import logging +import os + +# Initialize AWS Backup client and logger +backup_client = boto3.client('backup') +logger = logging.getLogger() +logger.setLevel(logging.INFO) + +# Create a Secrets Manager client +region_name = os.environ.get('AWS_REGION') + +aws_account_id = os.environ.get('aws_account_id') +backup_account_id = os.environ.get('backup_account_id') +backup_copy_vault_arn = os.environ.get('backup_copy_vault_arn') +backup_role_arn = os.environ.get('terraform_role_arn') + +def lambda_handler(event, context): + # Log the incoming event for debugging purposes + logger.info(f"Received Event: {json.dumps(event)}") + + # Extract the recovery point ARN from the event + recovery_point_arn = event['detail']['serviceEventDetails']['recoveryPointArn'] + source_vault_name = event['detail']['serviceEventDetails']['backupVaultName'] + logger.info(f"Detected new recovery point in vault {source_vault_name}: {recovery_point_arn}") + + # Start the copy job to the destination vault in another AWS account + backup_client.start_copy_job( + RecoveryPointArn=recovery_point_arn, + SourceBackupVaultName=source_vault_name, + DestinationBackupVaultArn=backup_copy_vault_arn, + IamRoleArn=backup_role_arn, + Lifecycle={ + 'DeleteAfterDays': 20 + } + ) + + return { + 'statusCode': 200, + 'body': json.dumps('Copy job started successfully.') + } \ No newline at end of file From 3da646078c534624c116555ca7b2bf56d7806ca7 Mon Sep 17 00:00:00 2001 From: aidanharrisnhs Date: Wed, 21 May 2025 16:47:10 +0100 Subject: [PATCH 2/5] removed texas tags --- modules/aws-backup-source/eventbridge.tf | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/aws-backup-source/eventbridge.tf b/modules/aws-backup-source/eventbridge.tf index 4c38c51..ac7ad2a 100644 --- a/modules/aws-backup-source/eventbridge.tf +++ b/modules/aws-backup-source/eventbridge.tf @@ -33,8 +33,4 @@ module "eventbridge" { } ] } - - tags = { - Service = "texas" - } } \ No newline at end of file From d7109ce6bc59c1360247753b6859a820d726a64e Mon Sep 17 00:00:00 2001 From: aidanharrisnhs Date: Thu, 22 May 2025 16:32:40 +0100 Subject: [PATCH 3/5] added new backup plans for rds and a variable for immutable backup retention periods --- modules/aws-backup-source/backup_plan.tf | 56 ++++++++++++++- modules/aws-backup-source/lambda_copy_job.tf | 9 +-- modules/aws-backup-source/locals.tf | 2 + .../resources/start_cross_account_copy_job.py | 3 +- modules/aws-backup-source/variables.tf | 72 +++++++++++++++++++ 5 files changed, 135 insertions(+), 7 deletions(-) diff --git a/modules/aws-backup-source/backup_plan.tf b/modules/aws-backup-source/backup_plan.tf index 323c042..0318628 100644 --- a/modules/aws-backup-source/backup_plan.tf +++ b/modules/aws-backup-source/backup_plan.tf @@ -16,12 +16,12 @@ resource "aws_backup_plan" "default" { cold_storage_after = rule.value.lifecycle.cold_storage_after != null ? rule.value.lifecycle.cold_storage_after : null } dynamic "copy_action" { - for_each = rule.value.copy_action != null ? rule.value.copy_action : {} + for_each = var.backup_copy_vault_arn != "" && var.backup_copy_vault_account_id != "" && rule.value.copy_action != null ? rule.value.copy_action : {} content { lifecycle { delete_after = copy_action.value } - destination_vault_arn = aws_backup_vault.intermediary-vault.arn + destination_vault_arn = var.backup_copy_vault_arn } } } @@ -35,6 +35,36 @@ resource "aws_backup_plan" "dynamodb" { dynamic "rule" { for_each = var.backup_plan_config_dynamodb.rules + content { + recovery_point_tags = { + backup_rule_name = rule.value.name + } + rule_name = rule.value.name + target_vault_name = aws_backup_vault.main.name + schedule = rule.value.schedule + lifecycle { + delete_after = rule.value.lifecycle.delete_after != null ? rule.value.lifecycle.delete_after : null + cold_storage_after = rule.value.lifecycle.cold_storage_after != null ? rule.value.lifecycle.cold_storage_after : null + } + dynamic "copy_action" { + for_each = var.backup_copy_vault_arn != "" && var.backup_copy_vault_account_id != "" && rule.value.copy_action != null ? rule.value.copy_action : {} + content { + lifecycle { + delete_after = copy_action.value + } + destination_vault_arn = var.backup_copy_vault_arn + } + } + } + } +} + +resource "aws_backup_plan" "rds" { + count = var.backup_plan_config_rds.enable ? 1 : 0 + name = "${local.resource_name_prefix}-rds-plan" + + dynamic "rule" { + for_each = var.backup_plan_config_rds.rules content { recovery_point_tags = { backup_rule_name = rule.value.name @@ -101,3 +131,25 @@ resource "aws_backup_selection" "dynamodb" { } } } + +resource "aws_backup_selection" "rds" { + count = var.backup_plan_config_rds.enable ? 1 : 0 + iam_role_arn = aws_iam_role.backup.arn + name = "${local.resource_name_prefix}-rds-selection" + plan_id = aws_backup_plan.rds[0].id + + selection_tag { + key = var.backup_plan_config_rds.selection_tag + type = "STRINGEQUALS" + value = (var.backup_plan_config_rds.selection_tag_value == null) ? "True" : var.backup_plan_config_rds.selection_tag_value + } + condition { + dynamic "string_equals" { + for_each = local.selection_tags_rds_null_checked + content { + key = (try(string_equals.value.key, null) == null) ? null : "aws:ResourceTag/${string_equals.value.key}" + value = try(string_equals.value.value, null) + } + } + } +} \ No newline at end of file diff --git a/modules/aws-backup-source/lambda_copy_job.tf b/modules/aws-backup-source/lambda_copy_job.tf index 0f6e7f7..735248a 100644 --- a/modules/aws-backup-source/lambda_copy_job.tf +++ b/modules/aws-backup-source/lambda_copy_job.tf @@ -58,10 +58,11 @@ resource "aws_lambda_function" "start_cross_account_copy_job_lambda" { runtime = "python3.12" environment { variables = { - aws_account_id = data.aws_caller_identity.current.account_id, - backup_account_id = var.backup_copy_vault_account_id, - backup_copy_vault_arn = var.backup_copy_vault_arn, - backup_role_arn = aws_iam_role.backup.arn + aws_account_id = data.aws_caller_identity.current.account_id, + backup_account_id = var.backup_copy_vault_account_id, + backup_copy_vault_arn = var.backup_copy_vault_arn, + backup_role_arn = aws_iam_role.backup.arn + destination_vault_retention_period = var.destination_vault_retention_period } } } diff --git a/modules/aws-backup-source/locals.tf b/modules/aws-backup-source/locals.tf index 9e92405..329904f 100644 --- a/modules/aws-backup-source/locals.tf +++ b/modules/aws-backup-source/locals.tf @@ -2,6 +2,8 @@ locals { resource_name_prefix = "${data.aws_region.current.name}-${data.aws_caller_identity.current.account_id}-backup" selection_tag_value_null_checked = (var.backup_plan_config.selection_tag_value == null) ? "True" : var.backup_plan_config.selection_tag_value selection_tag_value_dynamodb_null_checked = (var.backup_plan_config_dynamodb.selection_tag_value == null) ? "True" : var.backup_plan_config_dynamodb.selection_tag_value + selection_tag_value_rds_null_checked = (var.backup_plan_config_rds.selection_tag_value == null) ? "True" : var.backup_plan_config_rds.selection_tag_value selection_tags_null_checked = (var.backup_plan_config.selection_tags == null) ? [{ "key" : var.backup_plan_config.selection_tag, "value" : local.selection_tag_value_null_checked }] : var.backup_plan_config.selection_tags selection_tags_dynamodb_null_checked = (var.backup_plan_config_dynamodb.selection_tags == null) ? [{ "key" : var.backup_plan_config_dynamodb.selection_tag, "value" : local.selection_tag_value_dynamodb_null_checked }] : var.backup_plan_config_dynamodb.selection_tags + selection_tags_rds_null_checked = (var.backup_plan_config_rds.selection_tags == null) ? [{ "key" : var.backup_plan_config_rds.selection_tag, "value" : local.selection_tag_value_rds_null_checked }] : var.backup_plan_config_rds.selection_tags } diff --git a/modules/aws-backup-source/resources/start_cross_account_copy_job.py b/modules/aws-backup-source/resources/start_cross_account_copy_job.py index fb7a680..91b5888 100644 --- a/modules/aws-backup-source/resources/start_cross_account_copy_job.py +++ b/modules/aws-backup-source/resources/start_cross_account_copy_job.py @@ -15,6 +15,7 @@ backup_account_id = os.environ.get('backup_account_id') backup_copy_vault_arn = os.environ.get('backup_copy_vault_arn') backup_role_arn = os.environ.get('terraform_role_arn') +destination_vault_retention_period = os.environ.get('destination_vault_retention_period') def lambda_handler(event, context): # Log the incoming event for debugging purposes @@ -32,7 +33,7 @@ def lambda_handler(event, context): DestinationBackupVaultArn=backup_copy_vault_arn, IamRoleArn=backup_role_arn, Lifecycle={ - 'DeleteAfterDays': 20 + 'DeleteAfterDays': destination_vault_retention_period } ) diff --git a/modules/aws-backup-source/variables.tf b/modules/aws-backup-source/variables.tf index 61f9fd0..6639f5f 100644 --- a/modules/aws-backup-source/variables.tf +++ b/modules/aws-backup-source/variables.tf @@ -71,6 +71,12 @@ variable "backup_copy_vault_account_id" { default = "" } +variable "destination_vault_retention_period" { + description = "Retention period for recovery points made with the copy job lambda" + type = number + default = 365 +} + variable "backup_plan_config" { description = "Configuration for backup plans" type = object({ @@ -211,3 +217,69 @@ variable "backup_plan_config_dynamodb" { ] } } + +variable "backup_plan_config_rds" { + description = "Configuration for backup plans with rds" + type = object({ + enable = bool + selection_tag = string + selection_tag_value = optional(string) + selection_tags = optional(list(object({ + key = optional(string) + value = optional(string) + }))) + compliance_resource_types = list(string) + rules = optional(list(object({ + name = string + schedule = string + enable_continuous_backup = optional(bool) + lifecycle = object({ + delete_after = number + cold_storage_after = optional(number) + }) + copy_action = optional(object({ + delete_after = optional(number) + })) + }))) + }) + default = { + enable = true + selection_tag = "BackupRDS" + selection_tag_value = "True" + selection_tags = [] + compliance_resource_types = ["RDS"] + rules = [ + { + name = "rds_daily_kept_5_weeks" + schedule = "cron(0 0 * * ? *)" + lifecycle = { + delete_after = 35 + } + copy_action = { + delete_after = 365 + } + }, + { + name = "rds_weekly_kept_3_months" + schedule = "cron(0 1 ? * SUN *)" + lifecycle = { + delete_after = 90 + } + copy_action = { + delete_after = 365 + } + }, + { + name = "rds_monthly_kept_7_years" + schedule = "cron(0 2 1 * ? *)" + lifecycle = { + cold_storage_after = 30 + delete_after = 2555 + } + copy_action = { + delete_after = 365 + } + } + ] + } +} From f9e5927525f560eaf446c6d0ef07a238fccf09e6 Mon Sep 17 00:00:00 2001 From: aidanharrisnhs Date: Fri, 23 May 2025 12:42:37 +0100 Subject: [PATCH 4/5] made the intermediary vault and lambda based off of if rds in enabled --- modules/aws-backup-source/backup_plan.tf | 2 +- modules/aws-backup-source/backup_vault.tf | 1 + modules/aws-backup-source/eventbridge.tf | 2 +- modules/aws-backup-source/lambda_copy_job.tf | 12 ++++++++---- .../resources/start_cross_account_copy_job.py | 4 ++-- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/modules/aws-backup-source/backup_plan.tf b/modules/aws-backup-source/backup_plan.tf index 0318628..49dd221 100644 --- a/modules/aws-backup-source/backup_plan.tf +++ b/modules/aws-backup-source/backup_plan.tf @@ -82,7 +82,7 @@ resource "aws_backup_plan" "rds" { lifecycle { delete_after = copy_action.value } - destination_vault_arn = aws_backup_vault.intermediary-vault.arn + destination_vault_arn = aws_backup_vault.intermediary-vault[0].arn } } } diff --git a/modules/aws-backup-source/backup_vault.tf b/modules/aws-backup-source/backup_vault.tf index be354ef..9fba3ea 100644 --- a/modules/aws-backup-source/backup_vault.tf +++ b/modules/aws-backup-source/backup_vault.tf @@ -4,6 +4,7 @@ resource "aws_backup_vault" "main" { } resource "aws_backup_vault" "intermediary-vault" { + count = var.backup_plan_config_rds.enable ? 1 : 0 name = "${local.resource_name_prefix}-intermediary-vault" kms_key_arn = aws_kms_key.aws_backup_key.arn } \ No newline at end of file diff --git a/modules/aws-backup-source/eventbridge.tf b/modules/aws-backup-source/eventbridge.tf index ac7ad2a..c5fd57f 100644 --- a/modules/aws-backup-source/eventbridge.tf +++ b/modules/aws-backup-source/eventbridge.tf @@ -14,7 +14,7 @@ module "eventbridge" { "account" : ["${data.aws_caller_identity.current.account_id}"], "region" : ["eu-west-2"], "detail" : { - "eventName" : ["RecoveryPointCreated"] + "eventName" : ["RecoveryPointCreated"], "serviceEventDetails" : { "backupVaultName" : [{ "wildcard" : "*-intermediary-vault" }] } diff --git a/modules/aws-backup-source/lambda_copy_job.tf b/modules/aws-backup-source/lambda_copy_job.tf index 735248a..8b012a3 100644 --- a/modules/aws-backup-source/lambda_copy_job.tf +++ b/modules/aws-backup-source/lambda_copy_job.tf @@ -10,6 +10,7 @@ data "aws_iam_policy_document" "lambda_assume_role" { } resource "aws_iam_role" "iam_for_lambda_copy_job" { + count = var.backup_plan_config_rds.enable ? 1 : 0 name = "iam_for_cross_account_copy_job_lambda" assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json } @@ -38,8 +39,9 @@ data "aws_iam_policy_document" "lambda_copy_job_permissions" { } resource "aws_iam_role_policy" "cross_account_iam_permissions" { + count = var.backup_plan_config_rds.enable ? 1 : 0 name = "cross_account_iam_permissions_policy" - role = aws_iam_role.iam_for_lambda_copy_job.id + role = aws_iam_role.iam_for_lambda_copy_job[0].id policy = data.aws_iam_policy_document.lambda_copy_job_permissions.json } @@ -50,10 +52,11 @@ data "archive_file" "start_cross_account_copy_job_lambda_zip" { } resource "aws_lambda_function" "start_cross_account_copy_job_lambda" { + count = var.backup_plan_config_rds.enable ? 1 : 0 filename = data.archive_file.start_cross_account_copy_job_lambda_zip.output_path source_code_hash = data.archive_file.start_cross_account_copy_job_lambda_zip.output_base64sha256 function_name = "start_cross_account_copy_job" - role = aws_iam_role.iam_for_lambda_copy_job.arn + role = aws_iam_role.iam_for_lambda_copy_job[0].arn handler = "start_cross_account_copy_job.lambda_handler" runtime = "python3.12" environment { @@ -61,16 +64,17 @@ resource "aws_lambda_function" "start_cross_account_copy_job_lambda" { aws_account_id = data.aws_caller_identity.current.account_id, backup_account_id = var.backup_copy_vault_account_id, backup_copy_vault_arn = var.backup_copy_vault_arn, - backup_role_arn = aws_iam_role.backup.arn + backup_role_arn = aws_iam_role.backup.arn, destination_vault_retention_period = var.destination_vault_retention_period } } } resource "aws_lambda_permission" "allow_eventbridge" { + count = var.backup_plan_config_rds.enable ? 1 : 0 statement_id = "AllowExecutionFromEventbridge" action = "lambda:InvokeFunction" - function_name = aws_lambda_function.start_cross_account_copy_job_lambda.function_name + function_name = aws_lambda_function.start_cross_account_copy_job_lambda[0].function_name principal = "events.amazonaws.com" source_arn = "arn:aws:events:eu-west-2:${data.aws_caller_identity.current.account_id}:rule/Cross-Account-Copy-Job-rule" } \ No newline at end of file diff --git a/modules/aws-backup-source/resources/start_cross_account_copy_job.py b/modules/aws-backup-source/resources/start_cross_account_copy_job.py index 91b5888..bfa833c 100644 --- a/modules/aws-backup-source/resources/start_cross_account_copy_job.py +++ b/modules/aws-backup-source/resources/start_cross_account_copy_job.py @@ -14,8 +14,8 @@ aws_account_id = os.environ.get('aws_account_id') backup_account_id = os.environ.get('backup_account_id') backup_copy_vault_arn = os.environ.get('backup_copy_vault_arn') -backup_role_arn = os.environ.get('terraform_role_arn') -destination_vault_retention_period = os.environ.get('destination_vault_retention_period') +backup_role_arn = os.environ.get('backup_role_arn') +destination_vault_retention_period = int(os.environ.get('destination_vault_retention_period')) def lambda_handler(event, context): # Log the incoming event for debugging purposes From b99f0410f71e213f10c5bf4ad21c1df6c4046130 Mon Sep 17 00:00:00 2001 From: aidanharrisnhs <148090520+aidanharrisnhs@users.noreply.github.com> Date: Wed, 18 Jun 2025 11:47:51 +0100 Subject: [PATCH 5/5] Update lambda_copy_job.tf to allow lambda to write cloudwatch logs --- modules/aws-backup-source/lambda_copy_job.tf | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/modules/aws-backup-source/lambda_copy_job.tf b/modules/aws-backup-source/lambda_copy_job.tf index 8b012a3..3b71a46 100644 --- a/modules/aws-backup-source/lambda_copy_job.tf +++ b/modules/aws-backup-source/lambda_copy_job.tf @@ -10,7 +10,7 @@ data "aws_iam_policy_document" "lambda_assume_role" { } resource "aws_iam_role" "iam_for_lambda_copy_job" { - count = var.backup_plan_config_rds.enable ? 1 : 0 + count = var.backup_plan_config_rds.enable ? 1 : 0 name = "iam_for_cross_account_copy_job_lambda" assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json } @@ -38,8 +38,14 @@ data "aws_iam_policy_document" "lambda_copy_job_permissions" { } } +resource "aws_iam_role_policy_attachment" "lambda_role_policy_attachment" { + count = var.backup_plan_config_rds.enable ? 1 : 0 + role = aws_iam_role.iam_for_lambda_copy_job[0].name + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" +} + resource "aws_iam_role_policy" "cross_account_iam_permissions" { - count = var.backup_plan_config_rds.enable ? 1 : 0 + count = var.backup_plan_config_rds.enable ? 1 : 0 name = "cross_account_iam_permissions_policy" role = aws_iam_role.iam_for_lambda_copy_job[0].id policy = data.aws_iam_policy_document.lambda_copy_job_permissions.json @@ -52,7 +58,7 @@ data "archive_file" "start_cross_account_copy_job_lambda_zip" { } resource "aws_lambda_function" "start_cross_account_copy_job_lambda" { - count = var.backup_plan_config_rds.enable ? 1 : 0 + count = var.backup_plan_config_rds.enable ? 1 : 0 filename = data.archive_file.start_cross_account_copy_job_lambda_zip.output_path source_code_hash = data.archive_file.start_cross_account_copy_job_lambda_zip.output_base64sha256 function_name = "start_cross_account_copy_job" @@ -77,4 +83,4 @@ resource "aws_lambda_permission" "allow_eventbridge" { function_name = aws_lambda_function.start_cross_account_copy_job_lambda[0].function_name principal = "events.amazonaws.com" source_arn = "arn:aws:events:eu-west-2:${data.aws_caller_identity.current.account_id}:rule/Cross-Account-Copy-Job-rule" -} \ No newline at end of file +}