community.general
community.general copied to clipboard
Terraform plan output and changed state not returned when running in check mode
SUMMARY
Running the terraform module in check mode does not return the terraform plan output and does not correctly report the changed state of the resource to Ansible
ISSUE TYPE
- Bug Report
COMPONENT NAME
terraform
ANSIBLE VERSION
$ ansible --version
ansible 2.9.12
config file = None
configured module search path = ['/home/charles/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/charles/tmp/ansible-test/.direnv/python-3.6.9/lib/python3.6/site-packages/ansible
executable location = /home/charles/tmp/ansible-test/.direnv/python-3.6.9/bin/ansible
python version = 3.6.9 (default, Jul 17 2020, 12:50:27) [GCC 8.4.0]
CONFIGURATION
$ ansible-config dump --only-changed
ANSIBLE_NOCOWS(env: ANSIBLE_NOCOWS) = True
OS / ENVIRONMENT
Ubuntu 18.04
STEPS TO REPRODUCE
playbook.yml:
- hosts: localhost
tasks:
- terraform:
project_path: './terraform'
state: present
terraform/main.tf
resource "local_file" "foo" {
content = "foo"
filename = "foo.txt"
}
- Run the playbook
- Change the "content" in main.tf to something else
- Run the playbook again using in check mode using
--check --verbose
EXPECTED RESULTS
The documentation for the terraform module says: "To just run a terraform plan, use check mode". So I would expect the steps above to display the change plan from Terraform and the resource to be reported as "changed" by Ansible.'
In Ansible 2.8.14 the output used to look like this:
TASK [terraform] **************************************************************************************************************************************************************************************************
ok: [localhost] => {"changed": false, "command": "/opt/bin/terraform apply -no-color -input=false -auto-approve=true -lock=true /tmp/tmpnuqnp2mz.tfplan", "outputs": {}, "state": "present", "stderr": "", "stderr_lines": [], "stdout": "Refreshing Terraform state in-memory prior to plan...\nThe refreshed state will be used to calculate this plan, but will not be\npersisted to local or remote state storage.\n\nlocal_file.foo: Refreshing state... [id=0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33]\n\n------------------------------------------------------------------------\n\nAn execution plan has been generated and is shown below.\nResource actions are indicated with the following symbols:\n-/+ destroy and then create replacement\n\nTerraform will perform the following actions:\n\n # local_file.foo must be replaced\n-/+ resource \"local_file\" \"foo\" {\n ~ content = \"foo\" -> \"foo1\" # forces replacement\n directory_permission = \"0777\"\n file_permission = \"0777\"\n filename = \"foo.txt\"\n ~ id = \"0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33\" -> (known after apply)\n }\n\nPlan: 1 to add, 0 to change, 1 to destroy.\n\n------------------------------------------------------------------------\n\nThis plan was saved to: /tmp/tmpnuqnp2mz.tfplan\n\nTo perform exactly these actions, run the following command to apply:\n terraform apply \"/tmp/tmpnuqnp2mz.tfplan\"\n\n", "stdout_lines": ["Refreshing Terraform state in-memory prior to plan...", "The refreshed state will be used to calculate this plan, but will not be", "persisted to local or remote state storage.", "", "local_file.foo: Refreshing state... [id=0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33]", "", "------------------------------------------------------------------------", "", "An execution plan has been generated and is shown below.", "Resource actions are indicated with the following symbols:", "-/+ destroy and then create replacement", "", "Terraform will perform the following actions:", "", " # local_file.foo must be replaced", "-/+ resource \"local_file\" \"foo\" {", " ~ content = \"foo\" -> \"foo1\" # forces replacement", " directory_permission = \"0777\"", " file_permission = \"0777\"", " filename = \"foo.txt\"", " ~ id = \"0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33\" -> (known after apply)", " }", "", "Plan: 1 to add, 0 to change, 1 to destroy.", "", "------------------------------------------------------------------------", "", "This plan was saved to: /tmp/tmpnuqnp2mz.tfplan", "", "To perform exactly these actions, run the following command to apply:", " terraform apply \"/tmp/tmpnuqnp2mz.tfplan\"", ""], "workspace": "default"}
ACTUAL RESULTS
The change plan from Terraform is not shown and Ansible reports the task as "changed=false". Actual output is:
TASK [terraform] **************************************************************************************************************************************************************************************************
ok: [localhost] => {"changed": false, "command": "/opt/bin/terraform apply -no-color -input=false -auto-approve=true -lock=true /tmp/tmp2uwl0dkf.tfplan", "outputs": {}, "state": "present", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": [], "workspace": "default"}
I think this commit may have stopped the plan output from being returned, as it resets out
to an empty string after the terraform plan
has been run. Indeed the plan output is returned in Ansible 2.8.14.
I'm not sure if "changed" was ever set correctly when run in check mode. It doesn't work in Ansible 2.8.14 either.
Files identified in the description:
If these files are inaccurate, please update the component name
section of the description or use the !component
bot command.
After raising this issue I have since discovered that the bug that prevented return of the plan output has been fixed in this repo (I was originally looking in the old "ansible" repo). However the "changed" state is still not being reported properly when run in check mode.
+label waiting_on_contributor
cc @m-yosefpor click here for bot help
cc @rainerleber click here for bot help
I'm seeing the same thing, when run without --check, it properly shows the changed state. but when run with --check, it always returns "OK"
With the latest 4.8.0
release of the collection, I am seeing the same issue without running it in check mode. ~When using terraform
module, when plan is changed, the module returnschanged: true
.~
Actually it turns out we ship our own version of terraform.py
in the library
folder in the role, precisely to work around the behavior of the collection's module.
When using community.general.terraform
(version 4.8.0), it returnschanged: false
for the same plan file.
This is a simple logic fix, but I think we need to get some agreement on this. IMHO changed: true
should be returned any time the plan file requires changes to the infrastructure. This is how our playbooks are currently structured - if terraform plan
task changed: true
, then we ask for approval, and run terraform apply
.
Not returning changed: true
when the plan changes is not the right behavior in my mind, though I am sure whoever designed it in this way has a different opinion :).
I am willing to work on the PR, but I would like to have some discussion on the intended behavior first.
There is no committer info for the original iteration of this line, so I can't reach out to them directly.
@felixfontein do you have any suggestions on how to proceed with this?
I agree that if a change is going to happen, and it is possible to detect this with a reasonable amount of work in check mode, the module should return changed=true
. I don't really know the code and I'm not using terraform though.
What do the module maintainers think? Also CC @Shaps as he was recently interested in this module.
I agree with @geekifier, changed: true
should be returned regardless if the module is running in check_mode or not.
Not sure why this wasn't the case in the first place.
Files identified in the description:
If these files are incorrect, please update the component name
section of the description or use the !component
bot command.
For anyone looking for a solution, this was implemented in https://github.com/ansible-collections/cloud.terraform/ in version 1.0.0
. cloud.terraform.terraform
is backwards compatible with community.general.terraform
, at least at the time of release. It also implements diffs, both in check and non-check mode, and planning for destroy operations.