|
| 1 | +# Terraform Core Resource Destruction Notes |
| 2 | + |
| 3 | +This document intends to describe some of the details and complications |
| 4 | +involved in the destructions of resources. It covers the ordering defined for |
| 5 | +related create and destroy operations, as well as changes to the lifecycle |
| 6 | +ordering imposed by `create_before_destroy`. It is not intended to enumerate |
| 7 | +all possible combinations of dependency ordering, only to outline the basics |
| 8 | +and document some of the more complicated aspects of resource destruction. |
| 9 | + |
| 10 | +The graph diagrams here will continue to use the inverted graph structure used |
| 11 | +internally by Terraform, where edges represent dependencies rather than order |
| 12 | +of operations. |
| 13 | + |
| 14 | +## Simple Resource Creation |
| 15 | + |
| 16 | +In order to describe resource destruction, we first need to create the |
| 17 | +resources and define their order. The order of creation is that which fulfills |
| 18 | +the dependencies for each resource. In this example, `A` has no dependencies, |
| 19 | +`B` depends on `A`, and `C` depends on `B`, and transitively depends on `A`. |
| 20 | + |
| 21 | + |
| 22 | +<!-- |
| 23 | +digraph create { |
| 24 | + subgraph nodes { |
| 25 | + rank=same; |
| 26 | + a [label="A create"]; |
| 27 | + b [label="B create"]; |
| 28 | + c [label="C create"]; |
| 29 | + b -> c [dir=back]; |
| 30 | + a -> b [dir=back]; |
| 31 | + } |
| 32 | +} |
| 33 | +--> |
| 34 | + |
| 35 | +## Resource Updates |
| 36 | + |
| 37 | +An existing resource may be updated with references to a newly created |
| 38 | +resource. The ordering here is exactly the same as one would expect for |
| 39 | +creation. |
| 40 | + |
| 41 | + |
| 42 | +<!-- |
| 43 | +digraph update { |
| 44 | + subgraph nodes { |
| 45 | + rank=same; |
| 46 | + a [label="A create"]; |
| 47 | + b [label="B update"]; |
| 48 | + c [label="C update"]; |
| 49 | + b -> c [dir=back]; |
| 50 | + a -> b [dir=back]; |
| 51 | + } |
| 52 | +} |
| 53 | +--> |
| 54 | + |
| 55 | +## Simple Resource Destruction |
| 56 | + |
| 57 | +The order for destroying resource is exactly the inverse used to create them. |
| 58 | +This example shows the graph for the destruction of the same nodes defined |
| 59 | +above. While destroy nodes will not contain attribute references, we will |
| 60 | +continue to use the inverted edges showing dependencies for destroy, so the |
| 61 | +operational ordering is still opposite the flow of the arrows. |
| 62 | + |
| 63 | + |
| 64 | +<!-- |
| 65 | +digraph destroy { |
| 66 | + subgraph nodes { |
| 67 | + rank=same; |
| 68 | + a [label="A destroy"]; |
| 69 | + b [label="B destroy"]; |
| 70 | + c [label="C destroy"]; |
| 71 | + a -> b; |
| 72 | + b -> c; |
| 73 | + } |
| 74 | +} |
| 75 | +--> |
| 76 | + |
| 77 | + |
| 78 | +## Resource Replacement |
| 79 | + |
| 80 | +Resource replacement is the logical combination of the above scenarios. Here we |
| 81 | +will show the replacement steps involved when `B` depends on `A`. |
| 82 | + |
| 83 | + |
| 84 | +In this first example, we simultaneously replace both `A` and `B`. Here `B` is |
| 85 | +destroyed before `A`, then `A` is recreated before `B`. |
| 86 | + |
| 87 | + |
| 88 | +<!-- |
| 89 | +digraph replacement { |
| 90 | + subgraph create { |
| 91 | + rank=same; |
| 92 | + a [label="A create"]; |
| 93 | + b [label="B create"]; |
| 94 | + a -> b [dir=back]; |
| 95 | + } |
| 96 | + subgraph destroy { |
| 97 | + rank=same; |
| 98 | + a_d [label="A destroy"]; |
| 99 | + b_d [label="B destroy"]; |
| 100 | + a_d -> b_d; |
| 101 | + } |
| 102 | +
|
| 103 | + a -> a_d; |
| 104 | + b -> a_d [style=dotted]; |
| 105 | + b -> b_d; |
| 106 | +} |
| 107 | +--> |
| 108 | + |
| 109 | + |
| 110 | +This second example replaces only `A`, while updating `B`. Resource `B` is only |
| 111 | +updated once `A` has been destroyed and recreated. |
| 112 | + |
| 113 | + |
| 114 | +<!-- |
| 115 | +digraph replacement { |
| 116 | + subgraph create { |
| 117 | + rank=same; |
| 118 | + a [label="A create"]; |
| 119 | + b [label="B update"]; |
| 120 | + a -> b [dir=back]; |
| 121 | + } |
| 122 | + subgraph destroy { |
| 123 | + rank=same; |
| 124 | + a_d [label="A destroy"]; |
| 125 | + } |
| 126 | +
|
| 127 | + a -> a_d; |
| 128 | + b -> a_d [style=dotted]; |
| 129 | +} |
| 130 | +--> |
| 131 | + |
| 132 | +While the dependency edge from `B update` to `A destroy` isn't necessary in |
| 133 | +these examples, it is shown here as an implementation detail which will be |
| 134 | +mentioned later on. |
| 135 | + |
| 136 | +## Create Before Destroy |
| 137 | + |
| 138 | +Currently, the only user-controllable method for changing the ordering of |
| 139 | +create and destroy operations is with the `create_before_destroy` resource |
| 140 | +`lifecycle` attribute. This has the obvious effect of causing a resource to be |
| 141 | +created before it is destroyed when replacement is required, but has a couple |
| 142 | +of other effects we will detail here. |
| 143 | + |
| 144 | +Taking the previous replacement examples, we can change the behavior of `A` to |
| 145 | +be that of `create_before_destroy`. |
| 146 | + |
| 147 | + |
| 148 | +<!-- |
| 149 | +digraph replacement { |
| 150 | + subgraph create { |
| 151 | + rank=same; |
| 152 | + a [label="A create"]; |
| 153 | + b [label="B create"]; |
| 154 | + a -> b [dir=back]; |
| 155 | + } |
| 156 | + subgraph destroy { |
| 157 | + rank=same; |
| 158 | + a_d [label="A destroy"]; |
| 159 | + b_d [label="B destroy"]; |
| 160 | + a_d -> b_d; |
| 161 | + } |
| 162 | +
|
| 163 | + a -> a_d [dir=back]; |
| 164 | + b -> a_d [dir=back]; |
| 165 | + b -> b_d; |
| 166 | +} |
| 167 | +--> |
| 168 | + |
| 169 | +Note that in this first example, the creation of `B` is inserted in between the |
| 170 | +creation of `A` and the destruction of `A`. This becomes more important in the |
| 171 | +update example below. |
| 172 | + |
| 173 | + |
| 174 | + |
| 175 | +<!-- |
| 176 | +digraph replacement { |
| 177 | + subgraph create { |
| 178 | + rank=same; |
| 179 | + a [label="A create"]; |
| 180 | + b [label="B update"]; |
| 181 | + a -> b [dir=back]; |
| 182 | + } |
| 183 | + subgraph destroy { |
| 184 | + rank=same; |
| 185 | + a_d [label="A destroy"]; |
| 186 | + } |
| 187 | +
|
| 188 | + a -> a_d [dir=back]; |
| 189 | + b -> a_d [dir=back]; |
| 190 | +} |
| 191 | +--> |
| 192 | + |
| 193 | +Here we can see clearly how `B` is updated after the creation of `A` and before |
| 194 | +the destruction of `A`. To introduce another term, the prior resource `A` is |
| 195 | +sometimes referred to as "deposed" before it is destroyed, to disambiguate it |
| 196 | +from the newly created `A`. |
| 197 | + |
| 198 | +The transformation used to create these graphs is also where we use the extra |
| 199 | +edges mentioned above connecting `B` to `A destroy`. The algorithm to change a |
| 200 | +resource from the default ordering to `create_before_destroy` simply inverts |
| 201 | +any incoming edges from other resources, which automatically creates the |
| 202 | +necessary dependency ordering for dependent updates. This also ensures that |
| 203 | +reduced versions of this example still adhere to the same ordering rules, such |
| 204 | +as when the dependency is only being removed: |
| 205 | + |
| 206 | + |
| 207 | +<!-- |
| 208 | +digraph update { |
| 209 | + subgraph create { |
| 210 | + rank=same; |
| 211 | + b [label="B update"]; |
| 212 | + } |
| 213 | + subgraph destroy { |
| 214 | + rank=same; |
| 215 | + a_d [label="A destroy"]; |
| 216 | + } |
| 217 | +
|
| 218 | + b -> a_d [dir=back]; |
| 219 | +} |
| 220 | +--> |
| 221 | + |
| 222 | + |
| 223 | +### Forced Create Before Destroy |
| 224 | + |
| 225 | +In the previous examples, only resource `A` was being used as is it were |
| 226 | +`create_before_destroy`. The minimal graphs used show that it works in |
| 227 | +isolation, but that is only when the `create_before_destroy` resource has no |
| 228 | +dependencies of it own. When a `create_before_resource` depends on another |
| 229 | +resource, that dependency is "infected" by the `create_before_destroy` |
| 230 | +lifecycle attribute. |
| 231 | + |
| 232 | +This example demonstrates why forcing `create_before_destroy` is necessary. `B` |
| 233 | +has `create_before_destroy` while `A` does not. If we only invert the ordering |
| 234 | +for `B`, we can see that results in a cycle. |
| 235 | + |
| 236 | + |
| 237 | +<!-- |
| 238 | +digraph replacement { |
| 239 | + subgraph create { |
| 240 | + rank=same; |
| 241 | + a [label="A create"]; |
| 242 | + b [label="B create"]; |
| 243 | + a -> b [dir=back]; |
| 244 | + } |
| 245 | + subgraph destroy { |
| 246 | + rank=same; |
| 247 | + a_d [label="A destroy"]; |
| 248 | + b_d [label="B destroy"]; |
| 249 | + a_d -> b_d; |
| 250 | + } |
| 251 | +
|
| 252 | + a -> a_d; |
| 253 | + b -> a_d [style=dotted]; |
| 254 | + b -> b_d [dir=back]; |
| 255 | +} |
| 256 | +--> |
| 257 | + |
| 258 | +In order to resolve these cycles, and all resources that precede a resource |
| 259 | +with `create_before_destroy` must in turn be handled in the same manner. |
| 260 | +Reversing the incoming edged to `A destroy` resolves the problem: |
| 261 | + |
| 262 | + |
| 263 | +<!-- |
| 264 | +digraph replacement { |
| 265 | + subgraph create { |
| 266 | + rank=same; |
| 267 | + a [label="A create"]; |
| 268 | + b [label="B create"]; |
| 269 | + a -> b [dir=back]; |
| 270 | + } |
| 271 | + subgraph destroy { |
| 272 | + rank=same; |
| 273 | + a_d [label="A destroy"]; |
| 274 | + b_d [label="B destroy"]; |
| 275 | + a_d -> b_d; |
| 276 | + } |
| 277 | +
|
| 278 | + a -> a_d [dir=back]; |
| 279 | + b -> a_d [dir=back]; |
| 280 | + b -> b_d [dir=back]; |
| 281 | +} |
| 282 | +--> |
| 283 | + |
| 284 | +This also demonstrates why `create_before_destry` cannot be overridden when |
| 285 | +it is inherited; changing the behaviour here isn't possible without removing |
| 286 | +the initial reason for `create_before_destroy`; otherwise cycles are always |
| 287 | +introduced into the graph. |
0 commit comments