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.