hashicorp / terraform

Terraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.
https://www.terraform.io
Other
42.81k stars 9.57k forks source link

I would like provisioner/s to support when=update #35825

Open davidbuzz opened 1 month ago

davidbuzz commented 1 month ago

Terraform Version

Terraform v1.9.2
on linux_amd64

Use Cases

i have a : resource "terraform_data" "ssh_target" { ... }

and I "push" a shell script to the remote server:

  provisioner "file" {
    source      = "./modules/install_dependencies/depsetup.sh"
    destination = "/tmp/depsetup.sh"
  }

and I currently run 2x provisioners :

provisioner "remote-exec" {

    when = create 

    inline = [
      "echo CREATE provisioner",
      "chmod +x /tmp/depsetup.sh",
      "/tmp/depsetup.sh '${var.dependencies}'",
    ]
  }

  provisioner "remote-exec" {

    when = destroy

    inline = [
      "echo DESTROY provisioner",
      "rm -f /tmp/depsetup.sh",
    ]
  }

and it would be really helpful to be able to run the same remote script, or a different one, when = update

My script is written to be safe to re-run under all circumstances, and doesn't actually need to be removed or cleaned up, it just needs to be reliably run every time there's any sort of local change.

Attempted Solutions

right now, what seems to happen is DESTROY is always run before CREATE, and I'd prefer if i could run an UPDATE instead.

Proposal

terraform_data supports a lifecycle element replace_triggered_by=[ stuff ] it should also support a lifecycle element of update_triggered_by=[ stuff ]

eg: this doesnt work but work be great:

in this hypothetical, "replace" would run destroy+create, but update doesn't run either of them

resource "terraform_data" "mycreater" {
  triggers_create =  timestamp()
}
resource "terraform_data" "myupdater" {
  triggers_update = timestamp()
}
resource "terraform_data" "mydestroyer" {
  triggers_destroy = timestamp()
}

lifecycle {
  replace_triggered_by = [ terraform_data.mycreater,terraform_data.mydestroyer]
  update_triggered_by = [ terraform_data.myupdater]
}

References

No response

crw commented 1 month ago

Thanks for this feature request! If you are viewing this issue and would like to indicate your interest, please use the 👍 reaction on the issue description to upvote this issue. We also welcome additional use case descriptions.

Just as an FYI, new development is not likely to happen on provisioners. Please see https://developer.hashicorp.com/terraform/language/resources/provisioners/syntax#provisioners-are-a-last-resort and https://github.com/hashicorp/terraform/blob/main/.github/CONTRIBUTING.md#provisioners. I am happy to leave this issue open in case we do decide to support provisioner-like functionality outside of providers at some point in the future.

Thanks again!

davidbuzz commented 1 month ago

A well-written provisioner with current 'create' and 'destroy' capabilities is specifically hamstrung by not having "update" even made available in the ecosystem.

I have found that my current best-practive is to make my 'create' provisioner shell scripts arbitrarily re-runnable without concern [ ie enough basic if logic that they dont re-perform an action that's already been done to completion ] AND a 'destroy' provisioner that does almost nothing, but only because that's the only way to prevent execssive tear-downs and re-builds in the current no-update world.

Or to put it another way, real software engineers expect CRUD capabilities, ie Create,Read,Update,and Delete , which when mapped to TF terminology is "Create,Read,Destroy" ( whats missing )? -> Update! TF "fakes" a "update" by running "destroy+create" to mean the same thing , but in what world is that the same thing.? What that means is it artificially tears-down ( the destroy) a bunch of stuff we almost certainly didnt need to, to present a state that's more-likely to "create" friendly. but its really not a "create", its a update.

There are many reasons why supporting "when = update" on a provisioner can make more sense, and it could totally be done in a totally backward compatible way ... ie in the absence of a "when=update', it could just do the same behavior as it does now.