hashicorp / terraform-provider-aws

The AWS Provider enables Terraform to manage AWS resources.
https://registry.terraform.io/providers/hashicorp/aws
Mozilla Public License 2.0
9.64k stars 9.03k forks source link

[Bug]: AWS Backup - Omitting lifecycle from copy_action results in selection of custom lifecycle with empty values #37862

Open tilmanmayer opened 1 month ago

tilmanmayer commented 1 month ago

Terraform Core Version

1.8.5

AWS Provider Version

5.52.0

Affected Resource(s)

Expected Behavior

Lifecycle rule defined in backup rule should be used when not explicitly defined in _copyaction ("Use the same settings from backup rule").

Actual Behavior

When _copyaction is used without lifecycle definition, in AWS console you can see that "Custom lifeycle" is chosen and "Total retention period" settings are empty in the "Copy to destination - optional)" section and "Total retention (years)" is at "10+" which could result in high backup storage cost if not noticed.

Relevant Error/Panic Output Snippet

No response

Terraform Configuration Files

resource "aws_backup_region_settings" "services" {
  resource_type_opt_in_preference = {
    "RDS"                    = true
  }
}

# Backup vaults

resource "aws_backup_vault" "custom-rds" {
  name        = "custom-rds"
  kms_key_arn = "REDACTED"
}

# Backup policies

data "aws_iam_policy_document" "default-access" {
  statement {
    effect = "Allow"

    sid = "Allow REDACTED to copy into custom-rds"

    principals {
      type        = "AWS"
      identifiers = ["arn:aws:iam::REDACTED:root"]
    }

    actions = [
      "backup:CopyIntoBackupVault",
    ]

    resources = ["*"]
  }
}

### Backup policy assignments

resource "aws_backup_vault_policy" "custom-rds" {
  backup_vault_name = aws_backup_vault.custom-rds.name
  policy            = data.aws_iam_policy_document.default-access.json
}

resource "aws_backup_vault_policy" "custom-ebs" {
  backup_vault_name = aws_backup_vault.custom-ebs.name
  policy            = data.aws_iam_policy_document.default-access.json
}

### Backup plans

resource "aws_backup_plan" "rds-backup-plan" {
  name = "rds-backup-plan"
  rule {
    rule_name                = "monthly"
    target_vault_name        = aws_backup_vault.custom-rds.name
    schedule                 = "cron(0 5 1 * ? *)"
    start_window             = 480
    completion_window        = 10080
    enable_continuous_backup = false
    lifecycle {
      cold_storage_after = 30
      delete_after       = 120
    }
    copy_action {
      #   lifecycle {
      #     cold_storage_after = 30
      #     delete_after       = 120
      #   }
      destination_vault_arn = "REDACTED"
    }
  }
}

### Backup rules

resource "aws_backup_selection" "crossbackup-rds-resources" {
  iam_role_arn = aws_iam_role.backup-role.arn
  name         = "crossbackup-rds-resources"
  plan_id      = aws_backup_plan.rds-backup-plan.id

  resources = [
    "arn:aws:rds:*:*:cluster:*",
    "arn:aws:rds:*:*:db:*"
  ]

  selection_tag {
    type  = "STRINGEQUALS"
    key   = "backup"
    value = "true"
  }
}

### IAM Service Role

resource "aws_iam_role" "backup-role" {
  name = "backup-role"

  # Terraform's "jsonencode" function converts a
  # Terraform expression result to valid JSON syntax.
  assume_role_policy = data.aws_iam_policy_document.backup_assume_role_policy.json

  tags = {
    name = "backup-role"
  }
}

data "aws_iam_policy_document" "backup_assume_role_policy" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["backup.amazonaws.com"]
    }
  }
}

resource "aws_iam_role_policy_attachment" "AWSBackupServiceRolePolicyForBackup" {
  role       = aws_iam_role.backup-role.id
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup"
}

resource "aws_iam_role_policy_attachment" "AWSBackupServiceRolePolicyForRestores" {
  role       = aws_iam_role.backup-role.id
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForRestores"
}

resource "aws_iam_role_policy_attachment" "AWSBackupServiceRolePolicyForS3Backup" {
  role       = aws_iam_role.backup-role.id
  policy_arn = "arn:aws:iam::aws:policy/AWSBackupServiceRolePolicyForS3Backup"
}

resource "aws_iam_role_policy_attachment" "AWSBackupServiceRolePolicyForS3Restore" {
  role       = aws_iam_role.backup-role.id
  policy_arn = "arn:aws:iam::aws:policy/AWSBackupServiceRolePolicyForS3Restore"
}

Steps to Reproduce

  1. Execute above terraform code
  2. Go to AWS Console
  3. AWS-Backup
  4. Backup-Plans
  5. Click on rds-backup-plan
  6. Backup rules
  7. Click on monthly
  8. Click on edit
  9. Scroll down to "Copy to destination - optional"
  10. See above described behaviour

Debug Output

No response

Panic Output

No response

Important Factoids

No response

References

No response

Would you like to implement a fix?

None

github-actions[bot] commented 1 month ago

Community Note

Voting for Prioritization

Volunteering to Work on This Issue

tilmanmayer commented 1 month ago

2024-06-06 14_48_29-Edit Backup rule _ Backup plans _ AWS Backup Screenshot from AWS console