How to ensure terraform fails if ansible fails

in terraform, local-exec will march on even if a single ansible playbook fails.

I’d like to ensure a local-exec fails immediately with an appropriate exit code as soon as as that happens. I think this should be default behaviour but if you have any ideas I’d love to know.

here is an example-

resource "null_resource" "provision_1" {
  provisioner "local-exec" {
    command = <<EOT
      set -x
      echo 'test'
      ansible-playbook ansible-fail.yml
      echo 'continuing'
EOT

  }
}

resource "null_resource" "provision_2" {
  provisioner "local-exec" {
    command = <<EOT
      set -x
      echo 'continuing as well'
EOT

  }
}

ansible-fail.yml

- hosts: localhost

  tasks:
  - fail:
      msg: enforce failure.

…and the output showing that ‘continuing’ is being printed when it shouldn’t.

terraform apply --auto-approve
null_resource.provision_1: Creating...
null_resource.provision_2: Creating...
null_resource.provision_1: Provisioning with 'local-exec'...
null_resource.provision_2: Provisioning with 'local-exec'...
null_resource.provision_1 (local-exec): Executing: ["/bin/sh" "-c" "      set -x\n      echo 'test'\n      ansible-playbook ansible-fail.yml\n      echo 'continuing'\n"]
null_resource.provision_2 (local-exec): Executing: ["/bin/sh" "-c" "      set -x\n      echo 'continuing as well'\n"]
null_resource.provision_1 (local-exec): + echo test
null_resource.provision_1 (local-exec): test
null_resource.provision_2 (local-exec): + echo 'continuing as well'
null_resource.provision_1 (local-exec): + ansible-playbook ansible-fail.yml
null_resource.provision_2 (local-exec): continuing as well
null_resource.provision_2: Creation complete after 0s [id=76518244843484622]
null_resource.provision_1 (local-exec):  [WARNING]: No inventory was parsed, only implicit localhost is available
null_resource.provision_1 (local-exec):  [WARNING]: provided hosts list is empty, only localhost is available. Note
null_resource.provision_1 (local-exec): that the implicit localhost does not match 'all'

null_resource.provision_1 (local-exec): PLAY [localhost] ***************************************************************

null_resource.provision_1 (local-exec): TASK [Gathering Facts] *********************************************************
null_resource.provision_1 (local-exec): ok: [localhost]

null_resource.provision_1 (local-exec): TASK [fail] ********************************************************************
null_resource.provision_1 (local-exec): fatal: [localhost]: FAILED! => {"changed": false, "msg": "enforce failure."}

null_resource.provision_1 (local-exec): PLAY RECAP *********************************************************************
null_resource.provision_1 (local-exec): localhost                  : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

null_resource.provision_1 (local-exec): + echo continuing
null_resource.provision_1 (local-exec): continuing
null_resource.provision_1: Creation complete after 4s [id=5512796884102997803]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

I posted this in the github thread https://github.com/hashicorp/terraform/issues/15469
This is my current solution that just emerged
i’ve started to find that using a local-exec with simply

echo "Failed Message" >&2
exit 1

is working well for me.

…extended to ansible in local-exec I’m doing this now-

resource "null_resource" "fail-test" {
  provisioner "local-exec" {
    command = <<EOT
      exit_test () {
        RED='\033[0;31m' # Red Text
        GREEN='\033[0;32m' # Green Text
        BLUE='\033[0;34m' # Blue Text
        NC='\033[0m' # No Color
        if [ $? -eq 0 ]; then
          printf "\n $GREEN Playbook Succeeded $NC \n"
        else
          printf "\n $RED Failed Playbook $NC \n" >&2
          exit 1
        fi
      }
      ansible-playbook ansible-fail.yml; exit_test
  
EOT

  }
}

I think it would be cool if terraform had a mode to detect any non zero exit codes for each line though. It would be useful.

Hi @queglay,

From Terraform’s perspective, the command value is just an opaque string to be passed to the shell, so it’s the shell’s responsibility to decide how to handle errors.

If you are using bash then you should be able to arrange for the behavior you want by adding the following to the start of your script:

set -e

(With that said, do be sure to consider the caveats about -e.)

As you’ve seen, Terraform will halt processing if the result is unsuccessful, but the definition of what is unsuccessful is up to the shell that is processing the commands, not up to Terraform itself. Depending on what you’re running, you might also consider using -o pipefail and other bash options to select behaviors appropriate for what your script is expecting.

I’d like to slightly necro this, as I’m struggling even after following the advice on this thread.

I’ve got the following provisioner:

provisioner "local-exec" {
    command    = <<EOT
     if [[ var.region = 'us-west-2' || var.region = 'us-east-2' ]]
     then
      echo Region Valid
     else
      exit 1
     fi
  EOT
  }

Seemingly no matter what I do in the conditional, the terraform run continues on failure. I’ve validated, the conditions, and can conform that the script is definitely exiting 1, yet terraform just keeps on keeping on. Has the default behavior for local-exec changed? I’ve also tried set -e and place a command that will fail in my then block, but still no dice.