github.com/jaredpalmer/terraform@v1.1.0-alpha20210908.0.20210911170307-88705c943a03/docs/architecture.md (about) 1 # Terraform Core Architecture Summary 2 3 This document is a summary of the main components of Terraform Core and how 4 data and requests flow between these components. It's intended as a primer 5 to help navigate the codebase to dig into more details. 6 7 We assume some familiarity with user-facing Terraform concepts like 8 configuration, state, CLI workflow, etc. The Terraform website has 9 documentation on these ideas. 10 11 ## Terraform Request Flow 12 13 The following diagram shows an approximation of how a user command is 14 executed in Terraform: 15 16  17 18 Each of the different subsystems (solid boxes) in this diagram is described 19 in more detail in a corresponding section below. 20 21 ## CLI (`command` package) 22 23 Each time a user runs the `terraform` program, aside from some initial 24 bootstrapping in the root package (not shown in the diagram) execution 25 transfers immediately into one of the "command" implementations in 26 [the `command` package](https://pkg.go.dev/github.com/hashicorp/terraform/internal/command). 27 The mapping between the user-facing command names and 28 their corresponding `command` package types can be found in the `commands.go` 29 file in the root of the repository. 30 31 The full flow illustrated above does not actually apply to _all_ commands, 32 but it applies to the main Terraform workflow commands `terraform plan` and 33 `terraform apply`, along with a few others. 34 35 For these commands, the role of the command implementation is to read and parse 36 any command line arguments, command line options, and environment variables 37 that are needed for the given command and use them to produce a 38 [`backend.Operation`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/backend#Operation) 39 object that describes an action to be taken. 40 41 An _operation_ consists of: 42 43 * The action to be taken (e.g. "plan", "apply"). 44 * The name of the [workspace](https://www.terraform.io/docs/state/workspaces.html) 45 where the action will be taken. 46 * Root module input variables to use for the action. 47 * For the "plan" operation, a path to the directory containing the configuration's root module. 48 * For the "apply" operation, the plan to apply. 49 * Various other less-common options/settings such as `-target` addresses, the 50 "force" flag, etc. 51 52 The operation is then passed to the currently-selected 53 [backend](https://www.terraform.io/docs/backends/index.html). Each backend name 54 corresponds to an implementation of 55 [`backend.Backend`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/backend#Backend), using a 56 mapping table in 57 [the `backend/init` package](https://pkg.go.dev/github.com/hashicorp/terraform/internal/backend/init). 58 59 Backends that are able to execute operations additionally implement 60 [`backend.Enhanced`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/backend#Enhanced); 61 the command-handling code calls `Operation` with the operation it has 62 constructed, and then the backend is responsible for executing that action. 63 64 Most backends do _not_ implement this interface, and so the `command` package 65 wraps these backends in an instance of 66 [`local.Local`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/backend/local#Local), 67 causing the operation to be executed locally within the `terraform` process 68 itself, which (at the time of writing) is currently the only way an operation 69 can be executed. 70 71 ## Backends 72 73 A _backend_ has a number of responsibilities in Terraform: 74 75 * Execute operations (e.g. plan, apply) 76 * Store state 77 * Store workspace-defined variables (in the future; not yet implemented) 78 79 As described above, the `local.Local` implementation -- named `local` from the 80 user's standpoint -- is the only backend which implements _all_ functionality. 81 Backends that cannot execute operations (at the time of writing, all except 82 `local`) can be wrapped inside `local.Local` to perform operations locally 83 while storing the [state](https://www.terraform.io/docs/state/index.html) 84 elsewhere. 85 86 To execute an operation locally, the `local` backend uses a _state manager_ 87 (either 88 [`statemgr.Filesystem`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/states/statemgr#Filesystem) if the 89 local backend is being used directly, or an implementation provided by whatever 90 backend is being wrapped) to retrieve the current state for the workspace 91 specified in the operation, then uses the _config loader_ to load and do 92 initial processing/validation of the configuration specified in the 93 operation. It then uses these, along with the other settings given in the 94 operation, to construct a 95 [`terraform.Context`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#Context), 96 which is the main object that actually performs Terraform operations. 97 98 The `local` backend finally calls an appropriate method on that context to 99 begin execution of the relevant command, such as 100 [`Plan`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#Context.Plan) 101 or 102 [`Apply`](), which in turn constructs a graph using a _graph builder_, 103 described in a later section. 104 105 ## Configuration Loader 106 107 The top-level configuration structure is represented by model types in 108 [package `configs`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/configs). 109 A whole configuration (the root module plus all of its descendent modules) 110 is represented by 111 [`configs.Config`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/configs#Config). 112 113 The `configs` package contains some low-level functionality for constructing 114 configuration objects, but the main entry point is in the sub-package 115 [`configload`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/configs/configload]), 116 via 117 [`configload.Loader`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/configs/configload#Loader). 118 A loader deals with all of the details of installing child modules 119 (during `terraform init`) and then locating those modules again when a 120 configuration is loaded by a backend. It takes the path to a root module 121 and recursively loads all of the child modules to produce a single 122 [`configs.Config`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/configs#Config) 123 representing the entire configuration. 124 125 Terraform expects configuration files written in the Terraform language, which 126 is a DSL built on top of 127 [HCL](https://github.com/hashicorp/hcl). Some parts of the configuration 128 cannot be interpreted until we build and walk the graph, since they depend 129 on the outcome of other parts of the configuration, and so these parts of 130 the configuration remain represented as the low-level HCL types 131 [`hcl.Body`](https://pkg.go.dev/github.com/hashicorp/hcl/v2/#Body) 132 and 133 [`hcl.Expression`](https://pkg.go.dev/github.com/hashicorp/hcl/v2/#Expression), 134 allowing Terraform to interpret them at a more appropriate time. 135 136 ## State Manager 137 138 A _state manager_ is responsible for storing and retrieving snapshots of the 139 [Terraform state](https://www.terraform.io/docs/language/state/index.html) 140 for a particular workspace. Each manager is an implementation of 141 some combination of interfaces in 142 [the `statemgr` package](https://pkg.go.dev/github.com/hashicorp/terraform/internal/states/statemgr), 143 with most practical managers implementing the full set of operations 144 described by 145 [`statemgr.Full`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/states/statemgr#Full) 146 provided by a _backend_. The smaller interfaces exist primarily for use in 147 other function signatures to be explicit about what actions the function might 148 take on the state manager; there is little reason to write a state manager 149 that does not implement all of `statemgr.Full`. 150 151 The implementation 152 [`statemgr.Filesystem`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/states/statemgr#Filesystem) is used 153 by default (by the `local` backend) and is responsible for the familiar 154 `terraform.tfstate` local file that most Terraform users start with, before 155 they switch to [remote state](https://www.terraform.io/docs/language/state/remote.html). 156 Other implementations of `statemgr.Full` are used to implement remote state. 157 Each of these saves and retrieves state via a remote network service 158 appropriate to the backend that creates it. 159 160 A state manager accepts and returns a state snapshot as a 161 [`states.State`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/states#State) 162 object. The state manager is responsible for exactly how that object is 163 serialized and stored, but all state managers at the time of writing use 164 the same JSON serialization format, storing the resulting JSON bytes in some 165 kind of arbitrary blob store. 166 167 ## Graph Builder 168 169 A _graph builder_ is called by a 170 [`terraform.Context`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#Context) 171 method (e.g. `Plan` or `Apply`) to produce the graph that will be used 172 to represent the necessary steps for that operation and the dependency 173 relationships between them. 174 175 In most cases, the 176 [vertices](https://en.wikipedia.org/wiki/Vertex_(graph_theory)) of Terraform's 177 graphs each represent a specific object in the configuration, or something 178 derived from those configuration objects. For example, each `resource` block 179 in the configuration has one corresponding 180 [`GraphNodeConfigResource`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#GraphNodeConfigResource) 181 vertex representing it in the "plan" graph. (Terraform Core uses terminology 182 inconsistently, describing graph _vertices_ also as graph _nodes_ in various 183 places. These both describe the same concept.) 184 185 The [edges](https://en.wikipedia.org/wiki/Glossary_of_graph_theory_terms#edge) 186 in the graph represent "must happen after" relationships. These define the 187 order in which the vertices are evaluated, ensuring that e.g. one resource is 188 created before another resource that depends on it. 189 190 Each operation has its own graph builder, because the graph building process 191 is different for each. For example, a "plan" operation needs a graph built 192 directly from the configuration, but an "apply" operation instead builds its 193 graph from the set of changes described in the plan that is being applied. 194 195 The graph builders all work in terms of a sequence of _transforms_, which 196 are implementations of 197 [`terraform.GraphTransformer`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#GraphTransformer). 198 Implementations of this interface just take a graph and mutate it in any 199 way needed, and so the set of available transforms is quite varied. Some 200 important examples include: 201 202 * [`ConfigTransformer`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#ConfigTransformer), 203 which creates a graph vertex for each `resource` block in the configuration. 204 205 * [`StateTransformer`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#StateTransformer), 206 which creates a graph vertex for each resource instance currently tracked 207 in the state. 208 209 * [`ReferenceTransformer`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#ReferenceTransformer), 210 which analyses the configuration to find dependencies between resources and 211 other objects and creates any necessary "happens after" edges for these. 212 213 * [`ProviderTransformer`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#ProviderTransformer), 214 which associates each resource or resource instance with exactly one 215 provider configuration (implementing 216 [the inheritance rules](https://www.terraform.io/docs/language/modules/develop/providers.html)) 217 and then creates "happens after" edges to ensure that the providers are 218 initialized before taking any actions with the resources that belong to 219 them. 220 221 There are many more different graph transforms, which can be discovered 222 by reading the source code for the different graph builders. Each graph 223 builder uses a different subset of these depending on the needs of the 224 operation that is being performed. 225 226 The result of graph building is a 227 [`terraform.Graph`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#Graph), which 228 can then be processed using a _graph walker_. 229 230 ## Graph Walk 231 232 The process of walking the graph visits each vertex of that graph in a way 233 which respects the "happens after" edges in the graph. The walk algorithm 234 itself is implemented in 235 [the low-level `dag` package](https://pkg.go.dev/github.com/hashicorp/terraform/internal/dag#AcyclicGraph.Walk) 236 (where "DAG" is short for [_Directed Acyclic Graph_](https://en.wikipedia.org/wiki/Directed_acyclic_graph)), in 237 [`AcyclicGraph.Walk`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/dag#AcyclicGraph.Walk). 238 However, the "interesting" Terraform walk functionality is implemented in 239 [`terraform.ContextGraphWalker`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#ContextGraphWalker), 240 which implements a small set of higher-level operations that are performed 241 during the graph walk: 242 243 * `EnterPath` is called once for each module in the configuration, taking a 244 module address and returning a 245 [`terraform.EvalContext`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#EvalContext) 246 that tracks objects within that module. `terraform.Context` is the _global_ 247 context for the entire operation, while `terraform.EvalContext` is a 248 context for processing within a single module, and is the primary means 249 by which the namespaces in each module are kept separate. 250 251 Each vertex in the graph is evaluated, in an order that guarantees that the 252 "happens after" edges will be respected. If possible, the graph walk algorithm 253 will evaluate multiple vertices concurrently. Vertex evaluation code must 254 therefore make careful use of concurrency primitives such as mutexes in order 255 to coordinate access to shared objects such as the `states.State` object. 256 In most cases, we use the helper wrapper 257 [`states.SyncState`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/states#SyncState) 258 to safely implement concurrent reads and writes from the shared state. 259 260 ## Vertex Evaluation 261 262 The action taken for each vertex during the graph walk is called 263 _execution_. Execution runs a sequence of arbitrary actions that make sense 264 for a particular vertex type. 265 266 For example, evaluation of a vertex representing a resource instance during 267 a plan operation would include the following high-level steps: 268 269 * Retrieve the resource's associated provider from the `EvalContext`. This 270 should already be initialized earlier by the provider's own graph vertex, 271 due to the "happens after" edge between the resource node and the provider 272 node. 273 274 * Retrieve from the state the portion relevant to the specific resource 275 instance being evaluated. 276 277 * Evaluate the attribute expressions given for the resource in configuration. 278 This often involves retrieving the state of _other_ resource instances so 279 that their values can be copied or transformed into the current instance's 280 attributes, which is coordinated by the `EvalContext`. 281 282 * Pass the current instance state and the resource configuration to the 283 provider, asking the provider to produce an _instance diff_ representing the 284 differences between the state and the configuration. 285 286 * Save the instance diff as part of the plan that is being constructed by 287 this operation. 288 289 Each execution step for a vertex is an implementation of 290 [`terraform.Execute`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/erraform#Execute). 291 As with graph transforms, the behavior of these implementations varies widely: 292 whereas graph transforms can take any action against the graph, an `Execute` 293 implementation can take any action against the `EvalContext`. 294 295 The implementation of `terraform.EvalContext` used in real processing 296 (as opposed to testing) is 297 [`terraform.BuiltinEvalContext`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#BuiltinEvalContext). 298 It provides coordinated access to plugins, the current state, and the current 299 plan via the `EvalContext` interface methods. 300 301 In order to be executed, a vertex must implement 302 [`terraform.GraphNodeExecutable`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#GraphNodeExecutable), 303 which has a single `Execute` method that handles. There are numerous `Execute` 304 implementations with different behaviors, but some prominent examples are: 305 306 * [NodePlannableResource.Execute](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#NodePlannableResourceInstance.Execute), which handles the `plan` operation. 307 308 * [`NodeApplyableResourceInstance.Execute`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#NodeApplyableResourceInstance.Execute), which handles the main `apply` operation. 309 310 * [`NodeDestroyResourceInstance.Execute`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#EvalWriteState), which handles the main `destroy` operation. 311 312 A vertex must complete successfully before the graph walk will begin evaluation 313 for other vertices that have "happens after" edges. Evaluation can fail with one 314 or more errors, in which case the graph walk is halted and the errors are 315 returned to the user. 316 317 ### Expression Evaluation 318 319 An important part of vertex evaluation for most vertex types is evaluating 320 any expressions in the configuration block associated with the vertex. This 321 completes the processing of the portions of the configuration that were not 322 processed by the configuration loader. 323 324 The high-level process for expression evaluation is: 325 326 1. Analyze the configuration expressions to see which other objects they refer 327 to. For example, the expression `aws_instance.example[1]` refers to one of 328 the instances created by a `resource "aws_instance" "example"` block in 329 configuration. This analysis is performed by 330 [`lang.References`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/lang#References), 331 or more often one of the helper wrappers around it: 332 [`lang.ReferencesInBlock`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/lang#ReferencesInBlock) 333 or 334 [`lang.ReferencesInExpr`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/lang#ReferencesInExpr) 335 336 1. Retrieve from the state the data for the objects that are referred to and 337 create a lookup table of the values from these objects that the 338 HCL evaluation code can refer to. 339 340 1. Prepare the table of built-in functions so that HCL evaluation can refer to 341 them. 342 343 1. Ask HCL to evaluate each attribute's expression (a 344 [`hcl.Expression`](https://pkg.go.dev/github.com/hashicorp/hcl/v2/#Expression) 345 object) against the data and function lookup tables. 346 347 In practice, steps 2 through 4 are usually run all together using one 348 of the methods on [`lang.Scope`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/lang#Scope); 349 most commonly, 350 [`lang.EvalBlock`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/lang#Scope.EvalBlock) 351 or 352 [`lang.EvalExpr`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/lang#Scope.EvalExpr). 353 354 Expression evaluation produces a dynamic value represented as a 355 [`cty.Value`](https://pkg.go.dev/github.com/zclconf/go-cty/cty#Value). 356 This Go type represents values from the Terraform language and such values 357 are eventually passed to provider plugins. 358 359 ### Sub-graphs 360 361 Some vertices have a special additional behavior that happens after their 362 evaluation steps are complete, where the vertex implementation is given 363 the opportunity to build another separate graph which will be walked as part 364 of the evaluation of the vertex. 365 366 The main example of this is when a `resource` block has the `count` argument 367 set. In that case, the plan graph initially contains one vertex for each 368 `resource` block, but that graph then _dynamically expands_ to have a sub-graph 369 containing one vertex for each instance requested by the count. That is, the 370 sub-graph of `aws_instance.example` might contain vertices for 371 `aws_instance.example[0]`, `aws_instance.example[1]`, etc. This is necessary 372 because the `count` argument may refer to other objects whose values are not 373 known when the main graph is constructed, but become known while evaluating 374 other vertices in the main graph. 375 376 This special behavior applies to vertex objects that implement 377 [`terraform.GraphNodeDynamicExpandable`](https://pkg.go.dev/github.com/hashicorp/terraform/internal/terraform#GraphNodeDynamicExpandable). 378 Such vertices have their own nested _graph builder_, _graph walk_, 379 and _vertex evaluation_ steps, with the same behaviors as described in these 380 sections for the main graph. The difference is in which graph transforms 381 are used to construct the graph and in which evaluation steps apply to the 382 nodes in that sub-graph.