{"action":"created","issue":{"url":"https://api.github.com/repos/hashicorp/terraform/issues/4149","labels_url":"https://api.github.com/repos/hashicorp/terraform/issues/4149/labels{/name}","comments_url":"https://api.github.com/repos/hashicorp/terraform/issues/4149/comments","events_url":"https://api.github.com/repos/hashicorp/terraform/issues/4149/events","html_url":"https://github.com/hashicorp/terraform/issues/4149","id":120062825,"number":4149,"title":"Partial/Progressive Configuration Changes","user":{"login":"apparentlymart","id":20180,"avatar_url":"https://avatars.githubusercontent.com/u/20180?v=3","gravatar_id":"","url":"https://api.github.com/users/apparentlymart","html_url":"https://github.com/apparentlymart","followers_url":"https://api.github.com/users/apparentlymart/followers","following_url":"https://api.github.com/users/apparentlymart/following{/other_user}","gists_url":"https://api.github.com/users/apparentlymart/gists{/gist_id}","starred_url":"https://api.github.com/users/apparentlymart/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apparentlymart/subscriptions","organizations_url":"https://api.github.com/users/apparentlymart/orgs","repos_url":"https://api.github.com/users/apparentlymart/repos","events_url":"https://api.github.com/users/apparentlymart/events{/privacy}","received_events_url":"https://api.github.com/users/apparentlymart/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/hashicorp/terraform/labels/core","name":"core","color":"cccccc"},{"url":"https://api.github.com/repos/hashicorp/terraform/labels/enhancement","name":"enhancement","color":"d4c5f9"},{"url":"https://api.github.com/repos/hashicorp/terraform/labels/proposal","name":"proposal","color":"fbca04"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2015-12-02T23:54:12Z","updated_at":"2015-12-03T03:50:31Z","closed_at":null,"body":"For a while now I've been wringing my hands over the issue of using computed resource properties in parts of the Terraform config that are needed during the refresh and apply phases, where the values are likely to not be known yet.\r\n\r\nThe two primary situations that I and others have run into are:\r\n* Interpolating into provider configuration blocks, as I described in #2976. This is allowed by Terraform but fails in unintuitive ways when a chicken-and-egg problem arises.\r\n* Interpolating into the `count` modifier on resource blocks, as described in #1497. Currently this permits only variables, but having it configurable from resource attributes would be desirable.\r\n\r\nAfter a number of false-starts trying to find a way to make this work better in Terraform, I believe I've found a design that builds on concepts already present in Terraform, and that makes only small changes to the Terraform workflow. I arrived at this solution by \"paving the cowpaths\" after watching my coworkers and I work around the issue in various ways.\r\n\r\n---\r\n\r\nThe crux of the proposal is to alter Terraform's workflow to support the idea of *partial application*, allowing Terraform to apply a complicated configuration over several passes and converging on the desired configuration. So from the user's perspective, it would look something like this:\r\n\r\n```bash\r\n$ terraform plan -out=tfplan\r\n... (yada yada yada) ...\r\n\r\nTerraform is not able to apply this configuration in a single step. The plan above\r\nwill partially apply the configuration, after which you should run \"terraform plan\"\r\nagain to plan the next set of changes to converge on the given configuration.\r\n\r\n$ terraform apply tfplan\r\n... (yada yada yada) ...\r\n\r\nTerraform has only partially-applied the given configuration. To converge on\r\nthe final result, run \"terraform plan\" again to plan the next set of changes.\r\n\r\n$ terraform plan -out=tfplan\r\n... (yada yada yada) ...\r\n\r\n$ terraform apply\r\n... (yada yada yada) ...\r\n\r\nSuccess! ....\r\n```\r\n\r\nFor a particularly-complicated configuration there may be three or more apply/plan cycles, but eventually the configuration should converge.\r\n\r\n`terraform apply` would also exit with a predictable exit status in the \"partial success\" case, so that Atlas can implement a smooth workflow where e.g. it could immediately plan the next step and repeat the sign-off/apply process as many times as necessary.\r\n\r\nThis workflow is intended to embrace the existing workaround of using the `-target` argument to force Terraform to apply only a subset of the config, but improve it by having Terraform itself detect the situation. Terraform can then calculate itself which resources to target to plan for the maximal subset of the graph that can be applied in a single action, rather than requiring the operator to figure this out.\r\n\r\nBy teaching Terraform to identify the problem and propose a solution itself, Terraform can guide new users through the application of trickier configurations, rather than requiring users to either have deep understanding of the configurations they are applying (so that they can target the appropriate resources to resolve the chicken-and-egg situation), or requiring infrastructures to be accompanied with elaborate documentation describing which resources to target in which order.\r\n\r\n# Implementation Details\r\n\r\nThe proposed implementation builds on the existing concept of \"computed\" values within interpolations, and introduces the new idea of a graph nodes being \"deferred\" during the plan phase.\r\n\r\n## Deferred Providers and Resources\r\n\r\nA graph node is flagged as *deferred* if any value it needs for `refresh` or `plan` is flagged as \"computed\" after interpolation. For example:\r\n\r\n* A provider is *deferred* if any of its configuration block arguments are computed.\r\n* A resource is *deferred* if its `count` value is computed. \r\n\r\nMost importantly though, a graph node is always deferred if *any* of its dependencies are deferred. \"Deferred-ness\" propagates transitively so that, for example, any resource that belongs to a deferred provider is itself deferred.\r\n\r\nAfter the graph walk for planning, the set of all deferred nodes is included in the plan. A *partial plan* is therefore signaled by the deferred node set being non-empty.\r\n\r\n## Partial Application\r\n\r\nWhen `terraform apply` is given a partial plan, it applies all of the diffs that are included in the plan and then prints a message to inform the user that it was partial before exiting with a non-successful status.\r\n\r\nAside from the different rendering in the UI, applying a partial plan proceeds and terminates just like an error occured on one of the resource operations: the state is updated to reflect what *was* applied, and then Terraform exits with a nonzero status.\r\n\r\n## Progressive Runs\r\n\r\nNo additional state is required to keep track of partial application between runs. Since the state is already resource-oriented, a subsequent `refresh` will apply to the subset of resources that have already been created, and then `plan` will find that several \"new\" resources are present in the configuration, which can be planned as normal. The new resources created by the partial application will cause the set of deferred nodes to shrink -- possibly to empty -- on the follow-up run.\r\n\r\n---\r\n\r\n# Building on this Idea\r\n\r\nThe write-up above considers the specific use-cases of computed provider configurations and computed \"count\". In addition to these, this new concept enables or interacts with some other ideas:\r\n\r\n* #3310 proposed one design for supporting \"iteration\" -- or, more accurately, \"fan out\" -- to generate a set of resource instances based on data obtained elsewhere. This proposal enables a simpler model where `foreach` could iterate over arbitrary resource globs or collections within resource attributes, without introducing a new \"generator\" concept, by deferring the planning of the multiple resource instances until the collection has been computed.\r\n* #2976 proposed the idea of allowing certain resources to be refreshed immediately, before they've been created, to allow them to exist during the initial plan. Partial planning reduces the need for this, but supporting pre-refreshed resources would still be valuable to skip an iteration just to, for example, look up a Consul key to configure a provider.\r\n* #2896 talks about rolling updates to sets of resources. This is not directly supported by the above, since it requires human intervention to describe the updates that are required, but the UX of running multiple `plan`/`apply` cycles to converge could be used for rolling updates too.\r\n* The cycles that result when mixing `create_before_destroy` with not, as documented in #2944, could get a better UX by adding some more cases where nodes are \"deferred\" such that the \"destroy\" node for the deposed resource can be deferred to a separate run from the \"create\" that deposed it.\r\n* #1819 considers allowing the `provider` attribute on resources to be interpolated. It's mainly concerned with interpolating from variables rather than resource attributes, but the partial plan idea allows interpolation to be supported more broadly without special exceptions like \"only variables are allowed here\", and so it may become easier to implement interpolation of provider.\r\n* #4084 requests \"intermediate variables\", where computed values can be given a symbolic name that can then be used in multiple places within the configuration. One way to support this would be to allow variable defaults to be interpolated and mark the variables themselves as \"deferred\" when their values are computed, though certainly other implementations are possible.\r\n"},"comment":{"url":"https://api.github.com/repos/hashicorp/terraform/issues/comments/161507564","html_url":"https://github.com/hashicorp/terraform/issues/4149#issuecomment-161507564","issue_url":"https://api.github.com/repos/hashicorp/terraform/issues/4149","id":161507564,"user":{"login":"ketzacoatl","id":10122937,"avatar_url":"https://avatars.githubusercontent.com/u/10122937?v=3","gravatar_id":"","url":"https://api.github.com/users/ketzacoatl","html_url":"https://github.com/ketzacoatl","followers_url":"https://api.github.com/users/ketzacoatl/followers","following_url":"https://api.github.com/users/ketzacoatl/following{/other_user}","gists_url":"https://api.github.com/users/ketzacoatl/gists{/gist_id}","starred_url":"https://api.github.com/users/ketzacoatl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ketzacoatl/subscriptions","organizations_url":"https://api.github.com/users/ketzacoatl/orgs","repos_url":"https://api.github.com/users/ketzacoatl/repos","events_url":"https://api.github.com/users/ketzacoatl/events{/privacy}","received_events_url":"https://api.github.com/users/ketzacoatl/received_events","type":"User","site_admin":false},"created_at":"2015-12-03T03:50:31Z","updated_at":"2015-12-03T03:50:31Z","body":"I could see this being very helpful, and a nice way to simplify situations where we are sometimes otherwise a bit stuck with one or another limitation."}}