Terraform recreate the same resource with different state file

Terraform Version

v1.4.6

Terraform Configuration Files

resource "aws_budgets_budget" "example" {
budget_type = "COST"

limit_amount = 100
limit_unit = "USD"
time_unit = "MONTHLY"
name = "example"
}

resource "aws_sns_topic" "example" {
name = "mybudgetnotification"
}

resource "aws_sns_topic_subscription" "example" {
count = length(local.emails)
topic_arn = aws_sns_topic.example.arn
protocol = "email"
endpoint = local.emails[count.index]

}

data "aws_iam_policy_document" "example" {
policy_id = "__default_policy_ID"

statement {
actions = [
"SNS:Publish"
]


effect = "Allow"

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

resources = [
  aws_sns_topic.example.arn,
]

sid = "AWSBudgets-notification-1"


}
}

resource "aws_sns_topic_policy" "example" {
arn = aws_sns_topic.example.arn

policy = data.aws_iam_policy_document.example.json
}

Expected Behavior

Hi. I have an Account factory for Terraform on AWS codepipeline. This pipeline created resources from configuration file above (a budget and sns topic).

then I signed in to my sso locally on my pc. For testing purpose I ran terraform init, and then terraform plan + apply to create those same resources that been created by the pipeline on AWS. Now what I expect is Terraform should not be able to create those same resources, since they already existed and don’t share the same state

Actual Behavior

What happened is Terraform still created the SNS topic and it’s policy. But was not able to create the AWS budget (obviously). What just happened here. Please enlighten me. Thank you in advance!

Steps to Reproduce

  1. terraform init
  2. terraform plan
  3. terraform apply

Hi @Mysilver-cloud,

Terraform relies on its state to track the relationships between Terraform’s own addresses, like aws_sns_topic.example, and the identifiers of objects in the remote system.

If there is no entry for a particular resource instance in the state, then Terraform will always plan to create the object, because Terraform has no other information beyond its absence in the state. Whether that will succeed or not depends on whether the remote system has uniqueness rules it enforces during create.

It sounds like in your case one of the API calls succeeded despite there already being a similar object, while the other failed with an identifier collision. That difference would be caused by the underlying API design, and isn’t something Terraform can directly control. In general it’s your responsibility as the operator to make sure that each of your Terraform configurations describe totally disjoint sets of objects, or conversely that each remote object is described by only zero or one of your configurations.

If you want to apply the same module multiple times with independent states to have multiple “copies” of the same objects, you will need to design your module so each instance can declare different unique names for the remote objects.

Hi @apparentlymart ,

Thank you for the answer. 1 question. Is there a way to let terraform know that some resources already existed in that same aws account?

The terraform import command is the main way to tell Terraform to create a binding between a resource instance address and a real remote object without Terraform having been the one to create that object.

However, when you use it you must take case to not violate the rule that each remote object must be bound to at most one resource instance across all configurations and states. Terraform cannot check this itself.

In the situation you described I think the normal thing to have done would be to use the same state storage backend on your local computer as you did in CodePipeline, so that the two runs would be from the same state. Alternatively, you can use a separate state but only if you design your module to be multi-instance capable, by somehow arranging for each instance to use different names for object types that have uniqueness constraints.

if I use the same backend for local computer as Codepipeline. Is that a good practice?

My issue is, the codepipeline is very slow when run Terraform plan or apply. And every time I want to test my code, I have to merge, then run the pipeline to see the result. So I would like to test it locally before merge and run the pipeline.