github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/docs/planning-behaviors.md (about) 1 # Planning Behaviors 2 3 A key design tenet for Terraform is that any actions with externally-visible 4 side-effects should be carried out via the standard process of creating a 5 plan and then applying it. Any new features should typically fit within this 6 model. 7 8 There are also some historical exceptions to this rule, which we hope to 9 supplement with plan-and-apply-based equivalents over time. 10 11 This document describes the default planning behavior of Terraform in the 12 absence of any special instructions, and also describes the three main 13 design approaches we can choose from when modelling non-default behaviors that 14 require additional information from outside of Terraform Core. 15 16 This document focuses primarily on actions relating to _resource instances_, 17 because that is Terraform's main concern. However, these design principles can 18 potentially generalize to other externally-visible objects, if we can describe 19 their behaviors in a way comparable to the resource instance behaviors. 20 21 This is developer-oriented documentation rather than user-oriented 22 documentation. See 23 [the main Terraform documentation](https://www.terraform.io/docs) for 24 information on existing planning behaviors and other behaviors as viewed from 25 an end-user perspective. 26 27 ## Default Planning Behavior 28 29 When given no explicit information to the contrary, Terraform Core will 30 automatically propose taking the following actions in the appropriate 31 situations: 32 33 - **Create**, if either of the following are true: 34 - There is a `resource` block in the configuration that has no corresponding 35 managed resource in the prior state. 36 - There is a `resource` block in the configuration that is recorded in the 37 prior state but whose `count` or `for_each` argument (or lack thereof) 38 describes an instance key that is not tracked in the prior state. 39 - **Delete**, if either of the following are true: 40 - There is a managed resource tracked in the prior state which has no 41 corresponding `resource` block in the configuration. 42 - There is a managed resource tracked in the prior state which has a 43 corresponding `resource` block in the configuration _but_ its `count` 44 or `for_each` argument (or lack thereof) lacks an instance key that is 45 tracked in the prior state. 46 - **Update**, if there is a corresponding resource instance both declared in the 47 configuration (in a `resource` block) and recorded in the prior state 48 (unless it's marked as "tainted") but there are differences between the prior 49 state and the configuration which the corresponding provider doesn't 50 explicitly classify as just being normalization. 51 - **Replace**, if there is a corresponding resource instance both declared in 52 the configuration (in a `resource` block) and recorded in the prior state 53 _marked as "tainted"_. The special "tainted" status means that the process 54 of creating the object failed partway through and so the existing object does 55 not necessarily match the configuration, so Terraform plans to replace it 56 in order to ensure that the resulting object is complete. 57 - **Read**, if there is a `data` block in the configuration. 58 - If possible, Terraform will eagerly perform this action during the planning 59 phase, rather than waiting until the apply phase. 60 - If the configuration contains at least one unknown value, or if the 61 data resource directly depends on a managed resource that has any change 62 proposed elsewhere in the plan, Terraform will instead delay this action 63 to the apply phase so that it can react to the completion of modification 64 actions on other objects. 65 - **No-op**, to explicitly represent that Terraform considered a particular 66 resource instance but concluded that no action was required. 67 68 The **Replace** action described above is really a sort of "meta-action", which 69 Terraform expands into separate **Create** and **Delete** operations. There are 70 two possible orderings, and the first one is the default planning behavior 71 unless overridden by a special planning behavior as described later. The 72 two possible lowerings of **Replace** are: 73 1. **Delete** then **Create**: first delete the existing object bound to an 74 instance, and then create a new object at the same address based on the 75 current configuration. 76 2. **Create** then **Delete**: mark the existing object bound to an instance as 77 "deposed" (still exists but not current), create a new current object at the 78 same address based on the current configuration, and then delete the deposed 79 object. 80 81 ## Special Planning Behaviors 82 83 For the sake of this document, a "special" planning behavior is one where 84 Terraform Core will select a different action than the defaults above, 85 based on explicit instructions given either by a module author, an operator, 86 or a provider. 87 88 There are broadly three different design patterns for special planning 89 behaviors, and so each "special" use-case will typically be met by one or more 90 of the following depending on which stakeholder is activating the behavior: 91 92 - [Configuration-driven Behaviors](#configuration-driven-behaviors) are 93 activated by additional annotations given in the source code of a module. 94 95 This design pattern is good for situations where the behavior relates to 96 a particular module and so should be activated for anyone using that 97 module. These behaviors are therefore specified by the module author, such 98 that any caller of the module will automatically benefit with no additional 99 work. 100 - [Provider-driven Behaviors](#provider-driven-behaviors) are activated by 101 optional fields in a provider's response when asked to help plan one of the 102 default actions given above. 103 104 This design pattern is good for situations where the behavior relates to 105 the behavior of the remote system that a provider is wrapping, and so from 106 the perspective of a user of the provider the behavior should appear 107 "automatic". 108 109 Because these special behaviors are activated by values in the provider's 110 response to the planning request from Terraform Core, behaviors of this 111 sort will typically represent "tweaks" to or variants of the default 112 planning behaviors, rather than entirely different behaviors. 113 - [Single-run Behaviors](#single-run-behaviors) are activated by explicitly 114 setting additional "plan options" when calling Terraform Core's plan 115 operation. 116 117 This design pattern is good for situations where the direct operator of 118 Terraform needs to do something exceptional or one-off, such as when the 119 configuration is correct but the real system has become degraded or damaged 120 in a way that Terraform cannot automatically understand. 121 122 However, this design pattern has the disadvantage that each new single-run 123 behavior type requires custom work in every wrapping UI or automaton around 124 Terraform Core, in order provide the user of that wrapper some way 125 to directly activate the special option, or to offer an "escape hatch" to 126 use Terraform CLI directly and bypass the wrapping automation for a 127 particular change. 128 129 We've also encountered use-cases that seem to call for a hybrid between these 130 different patterns. For example, a configuration construct might cause Terraform 131 Core to _invite_ a provider to activate a special behavior, but let the 132 provider make the final call about whether to do it. Or conversely, a provider 133 might advertise the possibility of a special behavior but require the user to 134 specify something in the configuration to activate it. The above are just 135 broad categories to help us think through potential designs; some problems 136 will require more creative combinations of these patterns than others. 137 138 ### Configuration-driven Behaviors 139 140 Within the space of configuration-driven behaviors, we've encountered two 141 main sub-categories: 142 - Resource-specific behaviors, whose effect is scoped to a particular resource. 143 The configuration for these often lives inside the `resource` or `data` 144 block that declares the resource. 145 - Global behaviors, whose effect can span across more than one resource and 146 sometimes between resources in different modules. The configuration for 147 these often lives in a separate location in a module, such as a separate 148 top-level block which refers to other resources using the typical address 149 syntax. 150 151 The following is a non-exhaustive list of existing examples of 152 configuration-driven behaviors, selected to illustrate some different variations 153 that might be useful inspiration for new designs: 154 155 - The `ignore_changes` argument inside `resource` block `lifecycle` blocks 156 tells Terraform that if there is an existing object bound to a particular 157 resource instance address then Terraform should ignore the configured value 158 for a particular argument and use the corresponding value from the prior 159 state instead. 160 161 This can therefore potentially cause what would've been an **Update** to be 162 a **No-op** instead. 163 - The `replace_triggered_by` argument inside `resource` block `lifecycle` 164 blocks can use a proposed change elsewhere in a module to force Terraform 165 to propose one of the two **Replace** variants for a particular resource. 166 - The `create_before_destroy` argument inside `resource` block `lifecycle` 167 blocks only takes effect if a particular resource instance has a proposed 168 **Replace** action. If not set or set to `false`, Terraform will decompose 169 it to **Destroy** then **Create**, but if set to `true` Terraform will use 170 the inverted ordering. 171 172 Because Terraform Core will never select a **Replace** action automatically 173 by itself, this is an example of a hybrid design where the config-driven 174 `create_before_destroy` combines with any other behavior (config-driven or 175 otherwise) that might cause **Replace** to customize exactly what that 176 **Replace** will mean. 177 - Top-level `moved` blocks in a module activate a special behavior during the 178 planning phase, where Terraform will first try to change the bindings of 179 existing objects in the prior state to attach to new addresses before running 180 the normal planning process. This therefore allows a module author to 181 document certain kinds of refactoring so that Terraform can update the 182 state automatically once users upgrade to a new version of the module. 183 184 This special behavior is interesting because it doesn't _directly_ change 185 what actions Terraform will propose, but instead it adds an extra 186 preparation step before the typical planning process which changes the 187 addresses that the planning process will consider. It can therefore 188 _indirectly_ cause different proposed actions for affected resource 189 instances, such as transforming what by default might've been a **Delete** 190 of one instance and a **Create** of another into just a **No-op** or 191 **Update** of the second instance. 192 193 This one is an example of a "global behavior", because at minimum it 194 affects two resource instance addresses and, if working with whole resource 195 or whole module addresses, can potentially affect a large number of resource 196 instances all at once. 197 198 ### Provider-driven Behaviors 199 200 Providers get an opportunity to activate some special behaviors for a particular 201 resource instance when they respond to the `PlanResourceChange` function of 202 the provider plugin protocol. 203 204 When Terraform Core executes this RPC, it has already selected between 205 **Create**, **Delete**, or **Update** actions for the particular resource 206 instance, and so the special behaviors a provider may activate will typically 207 serve as modifiers or tweaks to that base action, and will not allow 208 the provider to select another base action altogether. The provider wire 209 protocol does not talk about the action types explicitly, and instead only 210 implies them via other content of the request and response, with Terraform Core 211 making the final decision about how to react to that information. 212 213 The following is a non-exhaustive list of existing examples of 214 provider-driven behaviors, selected to illustrate some different variations 215 that might be useful inspiration for new designs: 216 217 - When the base action is **Update**, a provider may optionally return one or 218 more paths to attributes which have changes that the provider cannot 219 implement as an in-place update due to limitations of the remote system. 220 221 In that case, Terraform Core will replace the **Update** action with one of 222 the two **Replace** variants, which means that from the provider's 223 perspective the apply phase will really be two separate calls for the 224 decomposed **Create** and **Delete** actions (in either order), rather 225 than **Update** directly. 226 - When the base action is **Update**, a provider may optionally return a 227 proposed new object where one or more of the arguments has its value set 228 to what was in the prior state rather than what was set in the configuration. 229 This represents any situation where a remote system supports multiple 230 different serializations of the same value that are all equivalent, and 231 so changing from one to another doesn't represent a real change in the 232 remote system. 233 234 If all of those taken together causes the new object to match the prior 235 state, Terraform Core will treat the update as a **No-op** instead. 236 237 Of the three genres of special behaviors, provider-driven behaviors is the one 238 we've made the least use of historically but one that seems to have a lot of 239 opportunities for future exploration. Provider-driven behaviors can often be 240 ideal because their effects appear as if they are built in to Terraform so 241 that "it just works", with Terraform automatically deciding and explaining what 242 needs to happen and why, without any special effort on the user's part. 243 244 ### Single-run Behaviors 245 246 Terraform Core's "plan" operation takes a set of arguments that we collectively 247 call "plan options", that can modify Terraform's planning behavior on a per-run 248 basis without any configuration changes or special provider behaviors. 249 250 As noted above, this particular genre of designs is the most burdensome to 251 implement because any wrapping software that can ask Terraform Core to create 252 a plan must ideally offer some way to set all of the available planning options, 253 or else some part of Terraform's functionality won't be available to anyone 254 using that wrapper. 255 256 However, we've seen various situations where single-run behaviors really are the 257 most appropriate way to handle a particular use-case, because the need for the 258 behavior originates in some process happening outside of the scope of any 259 particular Terraform module or provider. 260 261 The following is a non-exhaustive list of existing examples of 262 single-run behaviors, selected to illustrate some different variations 263 that might be useful inspiration for new designs: 264 265 - The "replace" planning option specifies zero or more resource instance 266 addresses. 267 268 For any resource instance specified, Terraform Core will transform any 269 **Update** or **No-op** action for that instance into one of the 270 **Replace** actions, thereby allowing an operator to respond to something 271 having become degraded in a way that Terraform and providers cannot 272 automatically detect and force Terraform to replace that object with 273 a new one that will hopefully function correctly. 274 - The "refresh only" planning mode ("planning mode" is a single planning option 275 that selects between a few mutually-exclusive behaviors) forces Terraform 276 to treat every resource instance as **No-op**, regardless of what is bound 277 to that address in state or present in the configuration. 278 279 ## Legacy Operations 280 281 Some of the legacy operations Terraform CLI offers that _aren't_ integrated 282 with the plan and apply flow could be thought of as various degenerate kinds 283 of single-run behaviors. Most don't offer any opportunity to preview an effect 284 before applying it, but do meet a similar set of use-cases where an operator 285 needs to take some action to respond to changes to the context Terraform is 286 in rather than to the Terraform configuration itself. 287 288 Most of these legacy operations could therefore most readily be translated to 289 single-run behaviors, but before doing so it's worth researching whether people 290 are using them as a workaround for missing configuration-driven and/or 291 provider-driven behaviors. A particular legacy operation might be better 292 replaced with a different sort of special behavior, or potentially by multiple 293 different special behaviors of different genres if it's currently serving as 294 a workaround for many different unmet needs.