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  ![Terraform Architecture Diagram, described in text below](./images/architecture-overview.png)
    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.