Fix ‘Objects have changed outside of Terraform’ with invoke wrapper

Note: From v1.2 (not released yet), this issue probably shouldn’t be that bad, comment

Nicolai Antiferov
3 min readMar 27, 2022

Last May Hashicorp introduced new feature in path version (0.15.4). And while on paper it looked nice, in reality it became one hell of a mess unfortunately. And since it was available in soon released v1.x, they said that even option to disable it won’t be added to not break compatibility, comment.

So before that, plan/apply showed diff between state+refresh and code. Since 0.15.4, terraform plan/apply shows 2 diffs: first between state and refresh (objects current state from API) and then diff between state+refresh and code as usual. Afaik this intended to show you drift in your infrastructure without usage of external tools like driftctl.

Depends on your state size, terraform and provider version now you can get hundreds of lines at first stage and it’s quite easy to accidentally think that it’s a real diff, even when you’re aware of this. There were diffs from updated latest_restorable_time in RDS resources, IAM diffs caused by different sort order of statements, etc.

Example (in this case new SG rule was added manually):

Note: Objects have changed outside of TerraformTerraform detected the following changes made outside of Terraform since the last "terraform apply":# aws_security_group.this has been changed
~ resource "aws_security_group" "this" {
id = "sg-23307b4a"
~ ingress = [
+ {
+ cidr_blocks = [
+ "0.0.0.0/0",
]
+ description = "test"
+ from_port = 80
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = "tcp"
+ security_groups = []
+ self = false
+ to_port = 80
},
# (2 unchanged elements hidden)
]
name = "default"
tags = {}
# (7 unchanged attributes hidden)
# (1 unchanged block hidden)
}
Unless you have made equivalent changes to your configuration, or ignored the relevant attributes using ignore_changes, the following plan may include
actions to undo or respond to these changes.
────────────────────────────────────────────────────────────────────Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:# aws_security_group.this will be updated in-place
~ resource "aws_security_group" "this" {
id = "sg-23307b4a"
~ ingress = [
- {
- cidr_blocks = [
- "0.0.0.0/0",
]
- description = "test"
- from_port = 80
- ipv6_cidr_blocks = []
- prefix_list_ids = []
- protocol = "tcp"
- security_groups = []
- self = false
- to_port = 80
},
# (2 unchanged elements hidden)
]
name = "default"
tags = {}
# (7 unchanged attributes hidden)
# (1 unchanged block hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.

Probably the easiest fix would look like: terraform plan|sed -n '/Objects have changed outside of Terraform/,/─────────────────────────────────────────────────────────────────────────────/!p', but sed could have different options/behaviour on different OS and you have to remember always add this pipe to your terraform commands.

I really enjoy using invoke to make wrappers for existing commands. It’s like better make written in python, check article for details.

I will use it to create wrapper for plan/apply commands, so it’ll be possible to use just inv plan/apply instead of teraform plan/apply directly.

Step by step:

  • plan and apply tasks use filter_run function to run according terraform commands
  • filter_run function uses Popen from subprocess to run cmd in background
  • till stdout is finished, it reads line by line output from commands and ignores lines from begin to end function parameter.

Check full code in PR #6.

--

--