Changes made outside of Terraform are missing

Hey,

I’m trying to google this without no avail yet

I have a remote state configured (aws, s3)

I’ve decided to upgrade Terraform from “1.0.11” to “1.2.3”, did delete “.terraform” folder. Now I’m missing “changes made outside of Terraform” with the list of resources. I’m assuming this information were residing under “.terraform” folder.

  • Any chance to sync this information back somehow?
  • Is it possible to keep this information remotely so next time when I delete the folder I can sync it back?

Hi @Stan,

Sorry about the change in behavior here. We got a lot of feedback about the “changes outside of Terraform” output, mostly citing that it was far too much information for the CLI, and was confusing for many users. The record of the external changes is still stored internally in the plan, however we only show the parts which may have somehow contributed to the plan itself. If there are no changes in the plan at all, then obviously none of the external changes could have contributed to the plan.

Since the goal of a -refresh-only plan is to update the state with all changes outside of terraform, a plan made with that option will still show all changes in the CLI output.

If you want to check for external changes in every plan, you can still get the full set in the json output.

Hi @jbardin .

So just trying to understand. Pretty much you’re saying if I run terraform plan -refresh-only -json it should give me that “changes made outside of Terraform”?

I’ve just tried it, the changes are not there

Hi @Stan,

I think @jbardin was offering two different alternatives:

  • If you run terraform plan -refresh-only then Terraform will make a plan to update the state to match the remote objects without changing anything else, and as part of that will produce an “objects changed outside of Terraform” report describing how the state would change if you were to apply that plan. However, there’s no obligation to actually apply the refresh-only plan, and so you can use it just to evaluate what has changed without making any changes to the persisted state.

  • Alternatively, you can run terraform plan -out=tfplan to save a plan to disk and then use terraform show -json to see a JSON representation of the data the plan is made of, in the Plan Representation format.

    I’m noticing that at the moment our example in that documentation doesn’t mention it, which we should fix, but the equivalent of the “Objects changed outside of Terraform” report is the resource_drift property, which will have the same structure as the resource_changes shown in the example.

thanks @apparentlymart for the clarification

ok, so when I run terraform plan -refresh-only I actually can see now Objects have changed outside of Terraform but this is for newly modified resources, that I did tests like 10 minutes ago.
I still can not get the list of “changed outside of Terraform” stuff before I upgrade Terraform. Is that because old plan file was deleted, so there’s no way I can get the output that I was able to get before the upgrade?

Ok, I think I partially figured out what’s happening.
Apparently while I was trying to understand what’s going on I did something like

terraform plan -out terraform.tfplan -refresh-only
terraform apply "terraform.tfplan"

so that’s why I’m not seeing “Objects changed outside of Terraform” for my old resources.

So in my state file I can see the resources I was referring to, so when I did apply on refresh-only plan those resources were merged to the state file.

Is it possible to get difference between state file and the actual configuration itself (whatever defined in tf files)?

ok, so I want to elaborate on my last post to ask the specific question
I have this

resource "aws_security_group" "stan_test" {
  name        = "stan_test"
  description = "Just a test"
  vpc_id      = module.vpc.vpc_id
}

Now I want to add an inline rule like this

resource "aws_security_group_rule" "example" {
  type              = "ingress"
  from_port         = 0
  to_port           = 65535
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  ipv6_cidr_blocks  = ["::/0"]
  security_group_id = aws_security_group.stan_test.id
}

I run plan and apply and I get what I need.

Now let’s imagine somebody makes modification of the security group outside of terraform.

I run terraform plan --refresh-only, that gives me the following

Note: Objects have changed outside of Terraform

Terraform detected the following changes made outside of Terraform since the last "terraform apply" which may have affected this plan:

  # aws_security_group.stan_test has changed
  ~ resource "aws_security_group" "stan_test" {
        id                     = "sg-0f6a8d3c80e485aae"
      ~ ingress                = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = ""
              + from_port        = 27016
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 27016
            },
            # (1 unchanged element hidden)
        ]
        name                   = "stan_test"
        tags                   = {}
        # (7 unchanged attributes hidden)
    }

I run terraform apply -refresh-only -auto-approve so terraform adds that to a state file.

At this point I no longer receive “Objects have changed outside of Terraform” notice what’s bad since i’m losing visibility of my infrastructure.

Now question. Starting from this point is there a way to ask terraform to print this aws_security_group_rule resource that was defined outside of terraform configuration but now persists in the state file?

From that point in the given situation, you have the latest version of the remote aws_security_group saved in your state, and no record of the prior state. Terraform has already warned that there was some external drift in the resource, which you have now recorded. The only values you have to compare for changes are your configuration and the stored state.

If the configuration differs from the stored state in a manner which would cause the provider to plan a change, then you can see that difference in a normal plan. If the configuration does not differ from the stored state in any meaningful way, then the provider will not plan any changes.

The provider in this case has declared that ingress is optional and computed for the resource, which means the provider is allowed to insert values there if there are none declared in the configuration. Since the ingress attribute is not declared in the configuration, the provider does not plan to remove the new rule from the instance.

If you do not want any ingres rules stored in that resource, then you could assign an empty collection to that attribute, ingress = []. I have a feeling though that a problem is going to be what is stored behind # (1 unchanged element hidden) – if the provider is storing the other externally added aws_security_group_rule values inside this resource, you may end up with a conflict which the provider cannot resolve. That unfortunately would be a design problem with the provider itself, and not something you could work around via configuration alone. If adding ingress = [] causes the removal of the wrong rules, or results in a perpetual diff, then that would need to be resolved by the provider. I did not go through the open issues, but there seem to be a number already containing combinations of “aws_security_group_rule”, “aws_security_group” and “ingress”.

got it, thank you @jbardin @apparentlymart ! appreciate your help
best