community.general icon indicating copy to clipboard operation
community.general copied to clipboard

Terraform plan output and changed state not returned when running in check mode

Open ampedandwired opened this issue 3 years ago • 12 comments

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"
}
  1. Run the playbook
  2. Change the "content" in main.tf to something else
  3. 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.

ampedandwired avatar Aug 20 '20 11:08 ampedandwired

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.

click here for bot help

ansibullbot avatar Aug 20 '20 11:08 ansibullbot

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.

ampedandwired avatar Aug 20 '20 12:08 ampedandwired

+label waiting_on_contributor

aminvakil avatar May 01 '21 15:05 aminvakil

cc @m-yosefpor click here for bot help

ansibullbot avatar May 01 '21 15:05 ansibullbot

cc @rainerleber click here for bot help

ansibullbot avatar Sep 28 '21 20:09 ansibullbot

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"

loganmc10 avatar Oct 27 '21 13:10 loganmc10

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.

geekifier avatar May 13 '22 13:05 geekifier

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?

geekifier avatar May 13 '22 13:05 geekifier

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.

felixfontein avatar May 13 '22 18:05 felixfontein

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.

Shaps avatar May 14 '22 08:05 Shaps

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.

click here for bot help

ansibullbot avatar Nov 06 '22 10:11 ansibullbot

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.

sstanovnik avatar Jan 30 '23 07:01 sstanovnik