Changing AWS resouce name

I’d like to change the data protection of a log group in AWS. Initially I used the following code pattern, but found that there was a racing issue.

The first resource - name_a already exists. name_b is a new one. The resource - name_a is used for another log group, but I’d like to change one log group only this time, which controlled by counting the variable.

resource "aws_cloudwatch_log_data_protection_policy" "name_a" {
  count      = var.protect_a ? 1:0
  log_group_name = var.log_group_name
  policy_document = file("/a/path/to/json.file")
}

resource "aws_cloudwatch_log_data_protection_policy" "name_b" {
  count      = var.protect_b ? 1:0
  log_group_name = var.log_group_name
  policy_document = file("/a/path/to/new_json.file")
}

Terraform Plan shows destruction of name_a and creation of name_b. When applying the changes occasionally destruction (name_a) completes after creation (name_b), which ends up wiping out the data protection policy. It looks like there is a racing issue.

I tried another method by adding depends_on name_a hoping that creation will wait for completion of destruction, but often I get an error - self reference name_a (destroy).

resource "aws_cloudwatch_log_data_protection_policy" "name_a" {
  count      = var.protect_a ? 1:0
  log_group_name = var.log_group_name
  policy_document = file("/a/path/to/json.file")
}

resource "aws_cloudwatch_log_data_protection_policy" "name_b" {
  count      = var.protect_b ? 1:0
  log_group_name = var.log_group_name
  policy_document = file("/a/path/to/new_json.file")
  depends_on = [
    aws_cloudwatch_log_data_protection_policy.name_a
  ]
}

I also tried adding null_resource with depends_on name_a and another depends_on null_resource in the name_b resource block. At one time it seemed to be working fine, but then started throwing up the same self reference error. Because I tried so many little variations and cannot show the exact code pattern here, but in principle I added another block of null_resource as I described.

resource "aws_cloudwatch_log_data_protection_policy" "name_a" {
  count      = var.protect_a ? 1:0
  log_group_name = var.log_group_name
  policy_document = file("/a/path/to/json.file")
}

resource "aws_cloudwatch_log_data_protection_policy" "name_b" {
  count      = var.protect_b ? 1:0
  log_group_name = var.log_group_name
  policy_document = file("/a/path/to/new_json.file")
  depends_on = [
    null_resource.wait_for_deletion
  ]
}

resouce "null_resouce" "wait_for_deletion" {
  count  = strcontains(var.log_group_name, "a_name") ? 1:0
  triggers = {
    removed = var.protect_a && !var.protect_b ? "present" : "removed"
  }
  depends_on = [
    aws_cloudwatch_log_data_protection_policy.name_a
  ]
}

A more better reliable approach would be a two-step one - terraform apply to delete the existing one then another apply to add a new one. Or, manually change the state, ie, move the existing one to name_b then apply the new json file.

But, is there a reliable way to achieve this in one run?

Appreciate your inputs and help

Is there a reason why you need to keep two separate aws_cloudwatch_log_data_protection_policy resources?

Here’s one design that works. You just need to pass in the policy name policy_a or policy_b, or null if not applying one, as the policy_name variable value.

main.tf:

variable "policy_name" {
  type     = string
  nullable = true
}

resource "aws_cloudwatch_log_group" "example" {
  name = "/example/log-group"
}

resource "aws_cloudwatch_log_data_protection_policy" "example" {
  count           = var.policy_name != null ? 1 : 0
  log_group_name  = aws_cloudwatch_log_group.example.name
  policy_document = file("${path.module}/${var.policy_name}.json")
}

policy_a.json:

{
    "Name": "PolicyA",
    "Version": "2021-06-01",
    "Statement": [
        {
            "Sid": "Audit",
            "DataIdentifier": [
                "arn:aws:dataprotection::aws:data-identifier/EmailAddress"
            ],
            "Operation": {
                "Audit": {
                    "NoFindingsDestination": {}
                }
            }
        },
        {
            "Sid": "Redact",
            "DataIdentifier": [
                "arn:aws:dataprotection::aws:data-identifier/EmailAddress"
            ],
            "Operation": {
                "Deidentify": {
                    "MaskConfig": {}
                }
            }
        }
    ]
}

policy_b.json:

{
    "Name": "PolicyB",
    "Version": "2021-06-01",
    "Statement": [
        {
            "Sid": "Audit",
            "DataIdentifier": [
                "arn:aws:dataprotection::aws:data-identifier/CreditCardNumber"
            ],
            "Operation": {
                "Audit": {
                    "NoFindingsDestination": {}
                }
            }
        },
        {
            "Sid": "Redact",
            "DataIdentifier": [
                "arn:aws:dataprotection::aws:data-identifier/CreditCardNumber"
            ],
            "Operation": {
                "Deidentify": {
                    "MaskConfig": {}
                }
            }
        }
    ]
}