scottwinkler / terraform-provider-shell

Terraform provider for executing shell commands and saving output to state file
Mozilla Public License 2.0
278 stars 61 forks source link

Provider does not work with terraform refresh #130

Open corlettb opened 8 months ago

corlettb commented 8 months ago

First of all, thanks for this project. I've used it a few times over the last few years where there has been a provider shortfall.

Recently I tried to use this provider to help bring terraform up-to-date with a resource that can sometimes change outside of terraform. Not ideal I know.

I assumed I could put logic into the read shell script so that the terraform refresh would pick up the new state.

However, this does not work. Following a plan where the read.sh has an updated output since the last terraform apply, we see a pending change (Nothing should be showing as a refresh was done).

resource "shell_script" "definition" {
 ~ dirty             = true -> false
    id                = "cmfviei42r3c0p1gni10"
    # (3 unchanged attributes hidden)

    # (1 unchanged block hidden)
}

The plan will also show a revert pending on resources that depend upon the outputs:

resource "example" "main" {
        id                                 = "1234"
      ~ definition                    = "629" -> "628"
        # (15 unchanged attributes hidden)

        # (6 unchanged blocks hidden)
    }

If you try to then do a terraform apply you get the following error:

│ Error: Provider produced inconsistent final plan
│ 
│ When expanding the plan for example.main to include new values learned so far during apply, provider "registry.example.provider" produced an invalid new value for .defintion: was
│ cty.StringVal("628"), but now cty.StringVal("629").
│ 
│ This is a bug in the provider, which should be reported in the provider's own issue tracker.

After looking through the code for read I can see the issue being around this logic in the read function.

    log.Printf("[DEBUG] previous output:|%v|", previousOutput)
    log.Printf("[DEBUG] new output:|%v|", output)
    isStateEqual := reflect.DeepEqual(output, previousOutput)
    isNewResource := d.IsNewResource()
    isUpdatedResource := stack[0] == ActionUpdate
    if !isStateEqual && !isNewResource && !isUpdatedResource {
        log.Printf("[DEBUG] Previous state not equal to new state. Marking resource as dirty to trigger update.")
        d.Set("dirty", true)
        return nil
    }

    d.Set("output", output)

    return nil

Namely during a refresh it won't update output from the shell script, it will simply update the dirty flag to true and return.

I tried a fork and removed this logic, instead just doing a d.Set("output", output) and this corrected the issue.

I'm guessing the dirty flag is there because we have no way to tell terraform the input environment variables are not the same as when this was deployed and so the resource needs to be updated. I could create a pull request to add an option to disable this functionality and just update the output in the state.

Would love your thoughts.

Thanks again.