diff --git a/website/docs/configuration/modules.html.md b/website/docs/configuration/modules.html.md index 7c6353b07..15c1b37e1 100644 --- a/website/docs/configuration/modules.html.md +++ b/website/docs/configuration/modules.html.md @@ -19,8 +19,6 @@ already. ## Example -Module configuration looks like the following: - ```hcl module "consul" { source = "hashicorp/consul/aws" @@ -30,40 +28,30 @@ module "consul" { ## Description -The `module` block configures a module and tells Terraform to build -its resources. Multiple module blocks may be used to configure and use -multiple modules. +A `module` block instructs Terraform to create an instance of a module, +and in turn to instantiate any resources defined within it. -The NAME of the module is logical: it is used only to reference the -module in other places in the configuration. It has no effect on the -source of the module. Therefore, you may name modules whatever you'd like. +The name given in the block header is used to reference the particular module +instance from expressions within the calling module, and to refer to the +module on the command line. It has no meaning outside of a particular +Terraform configuration. -Within the block (the `{ }`) is configuration for the module. -The only required key is `source`, which tells Terraform where this module -can be downloaded from. Valid source values are covered in more detail -in the -[module section](/docs/modules/index.html). +Within the block body is the configuration for the module. All attributes +within the block must correspond to [variables](/docs/configuration/variables.html) +within the module, with the exception of the following which Terraform +treats as special: -Other configuration within the module are dependent on the module itself. -Module configuration maps directly to -[variables](/docs/configuration/variables.html) within the module, so -parameters can have any of the data types that variables support, including -lists and maps. +* `source` - (Required) A [module source](/docs/modules/sources.html) string + specifying the location of the child module source code. -## Syntax +* `version` - (Optional) A [version constraint](/docs/modules/usage.html#module-versions) + string that specifies which versions of the referenced module are acceptable. + The newest version matching the constraint will be used. `version` is supported + only for modules retrieved from module registries. -The full syntax is: - -```text -module NAME { - source = SOURCE_URL - - CONFIG ... -} -``` - -where `CONFIG` is: - -```text -KEY = VALUE -``` +* `providers` - (Optional) A map whose keys are provider configuration names + that are expected by child module and whose values are corresponding + provider names in the calling module. This allows + [provider configurations to be passed explicitly to child modules](/docs/modules/usage.html#providers-within-modules). + If not specified, the child module inherits all of the default (un-aliased) + provider configurations from the calling module. diff --git a/website/docs/configuration/variables.html.md b/website/docs/configuration/variables.html.md index 63e64c4b9..719723ab2 100644 --- a/website/docs/configuration/variables.html.md +++ b/website/docs/configuration/variables.html.md @@ -69,6 +69,12 @@ These are the parameters that can be set: future version of Terraform will expose these descriptions as part of some Terraform CLI command. +The name of a variable can be any valid identifier. However, due to the +interpretation of [module configuration blocks](/docs/configuration/modules.html), +the names `source`, `version` and `providers` are reserved for Terraform's own +use and are thus not recommended for any module intended to be used as a +child module. + -> **Note**: Default values can be strings, lists, or maps. If a default is specified, it must match the declared type of the variable. diff --git a/website/docs/modules/usage.html.markdown b/website/docs/modules/usage.html.markdown index 3ba349e57..da9f87bbb 100644 --- a/website/docs/modules/usage.html.markdown +++ b/website/docs/modules/usage.html.markdown @@ -7,7 +7,7 @@ description: Using modules in Terraform is very similar to defining resources. # Module Usage -Using modules in Terraform is very similar to defining resources: +Using child modules in Terraform is very similar to defining resources: ```shell module "consul" { @@ -18,18 +18,237 @@ module "consul" { You can view the full documentation for configuring modules in the [Module Configuration](/docs/configuration/modules.html) section. -In modules we only specify a name rather than a name and a type (as in resources). This name can be used elsewhere in the configuration to reference the module and its outputs. +In modules we only specify a name, rather than a name and a type as for resources. +This name is used elsewhere in the configuration to reference the module and +its outputs. -The source tells Terraform what to create. In this example, we create +The source tells Terraform what to create. In this example, we instantiate the [Consul module for AWS](https://registry.terraform.io/modules/hashicorp/consul/aws) -from the [Terraform Registry](https://registry.terraform.io). You can learn -more about the [source configuration below](#source). +from the [Terraform Registry](https://registry.terraform.io). Other source +types are supported, as described in the following section. -Just like a resource, the module configuration can be deleted to remove the module. +Just like a resource, the a module's configuration can be deleted to destroy the +resources belonging to the module. -## Multiple instances of a module +## Source -You can instantiate a module multiple times. +The only required configuration key for a module is the `source` parameter. The +value of this tells Terraform where to download the module's source code. +Terraform comes with support for a variety of module sources. + +The recommended source for external modules is a +[Terraform Registry](/docs/registry/index.html), which provides the full +capabilities of modules such as version constraints. +Registry modules are specified using a simple slash-separated path like the +`hashicorp/consul/aws` path used in the above example. The full source string +for each registry module can be found from the registry website. + +Terraform also supports modules in local directories, identified by a relative +path starting with either `./` or `../`. Such local modules are useful to +organize code more complex repositories, and are described in more detail +in [_Creating Modules_](/docs/modules/create.html). + +Finally, Terraform can download modules directly from various storage providers +and version control systems. These sources do not support versioning and other +registry benefits, but can be convenient for getting started when already +available within an organization. The full list of available sources +are documented in [the module sources documentation](/docs/modules/sources.html). + +When a configuration uses modules, they must first be installed by running +[`terraform init`](/docs/commands/init.html): + +```shell +$ terraform init +``` + +This command will download any modules that haven't been updated already, +as well as performing other Terraform working directory initialization such +as installing providers. + +By default the command will not check for available updates to already-installed +modules, but you can use the `-update` option to check for available upgrades. +When version constraints are specified (as described in the following section) +a newer version will be used only if it is within the given constraint. + +## Module Versions + +It is recommended to explicitly constrain the acceptable version numbers for +each external module so that upstream changes aren't automatically adopted, +since this may result in unexpected or unwanted changes changes. + +The `version` attribute within the `module` block is used for this purpose: + +```shell +module "consul" { + source = "hashicorp/consul/aws" + version = "0.0.5" + + servers = 3 +} +``` + +The `version` attribute value may either be a single explicit version or +a version constraint expression. Constraint expressions use the following +syntax to specify a _range_ of versions that are acceptable: + +* `>= 1.2.0`: version 1.2.0 or newer +* `<= 1.2.0`: version 1.2.0 or older +* `~> 1.2`: any non-beta patch release within the `1.2` range +* `>= 1.0.0, <= 2.0.0`: any version between 1.0.0 and 2.0.0 inclusive + +When depending on third-party modules, references to specific versions are +recommended since this ensures that updates only happen when convenient to you. + +For modules maintained within your organization, a version range strategy +may be appropriate if a semantic versioning methodology is used consistently +or if there is a well-defined release process that avoids unwanted updates. + +Version constraints are supported only for modules installed from a module +registry, such as the [Terraform Registry](https://registry.terraform.io/). +Other module sources may provide their own versioning mechanisms within the +source string itself, or they may not support versions at all. In particular, +modules whose sources are local file paths do not support `version` because +they are constrained to share the same version as their caller by being +obtained by the same source repository. + +## Configuration + +The arguments used in a `module` block, such as the `servers` parameter above, +correspond to [variables](/docs/configuration/variables.html) within the module +itself. You can therefore discover all the available variables for a module by +inspecting the source of it. + +The special arguments `source`, `version` and `providers` are exceptions. These +are used for special purposes by Terraform and should therefore not be used +as variable names within a module. + +## Outputs + +Modules encapsulate their resources. A resource in one module cannot directly depend on resources or attributes in other modules, unless those are exported through [outputs](/docs/configuration/outputs.html). These outputs can be referenced in other places in your configuration, for example: + +```hcl +resource "aws_instance" "client" { + ami = "ami-408c7f28" + instance_type = "t1.micro" + availability_zone = "${module.consul.server_availability_zone}" +} +``` + +This is deliberately very similar to accessing resource attributes. Instead of +referencing a resource attribute, however, the expression in this case +references an output of the module. + +Just like with resources, interpolation expressions can create implicit +dependencies on resources and other modules. Since modules encapsulate +other resources, however, the dependency is not on the module as a whole +but rather on the `server_availability_zone` output specifically, which +allows Terraform to work on resources in different modules concurrently rather +than waiting for the entire module to be complete before proceeding. + +## Providers within Modules + +For convenience in simple configurations, child modules by default inherit +provider configurations from their parent. This means that in most cases +only the root module needs explicit `provider` blocks, and then any defined +provider can be freely used with the same settings in child modules. + +In more complex situations it may be necessary for a child module to use +different provider settings than its parent. In this situation it is +possible to define +[multiple provider instances](/docs/configuration/providers.html#multiple-provider-instances) +and pass them explicitly and selectively to a child module: + +```hcl +# The default "aws" configuration is used for AWS resources in the root +# module where no explicit provider instance is selected. +provider "aws" { + region = "us-west-1" +} + +# A non-default, or "aliased" configuration is also defined for a different +# region. +provider "aws" { + alias = "usw2" + region = "us-west-2" +} + +# An example child module is instantiated with the _aliased_ configuration, +# so any AWS resources it defines will use the us-west-2 region. +module "example" { + source = "./example" + providers = { + aws = "aws.usw2" + } +} +``` + +The `providers` argument within a `module` block serves the same purpose as +the `provider` argument within a resource as described for +[multiple provider instances](/docs/configuration/providers.html#multiple-provider-instances), +but is a map rather than a single string because a module may contain resources +from many different providers. + +Once the `providers` argument is used in a `module` block it overrides all of +the default inheritance behavior, so it is necessary to enumerate mappings +for _all_ of the required providers. This is to avoid confusion and surprises +when mixing both implicit and explicit provider passing. + +In more complex situations it may be necessary for a child module _itself_ +to have multiple instances of the same provider. For example, a module +that configures connectivity between networks in two AWS regions is likely +to need both a source and a destination region. In that case, the root module +may look something like this: + +```hcl +provider "aws" { + alias = "usw1" + region = "us-west-1" +} + +provider "aws" { + alias = "usw2" + region = "us-west-2" +} + +module "tunnel" { + source = "./tunnel" + providers = { + "aws.src" = "aws.usw1" + "aws.dst" = "aws.usw2" + } +} +``` + +The subdirectory `./tunnel` should then contain configuration like the +following, to declare the two provider aliases it expects: + +``` +provider "aws" { + alias = "src" +} + +provider "aws" { + alias = "dst" +} +``` + +Each resource should then have its own `provider` attribute set to either +`"aws.src"` or `"aws.dst"` to choose which of the provider instances to use. + +It is recommended to use the default inheritance behavior in most cases where +only a single default instance of each provider is used, and switch to +passing providers explicitly as soon as multiple instances are needed. + +In all cases it is recommended to keep explicit provider declarations only in +the root module and pass them (either implicitly or explicitly) down to +descendent modules. This avoids the provider configurations being "lost" +when descendent providers are removed from the configuration. It also allows +the user of a configuration to determine which providers require credentials +by inspecting only the root module. + +## Multiple Instances of a Module + +A particular module source can be instantiated multiple times: ```hcl # my_buckets.tf @@ -50,7 +269,7 @@ module "media_bucket" { variable "name" {} # this is the input parameter of the module -resource "aws_s3_bucket" "the_bucket" { +resource "aws_s3_bucket" "example" { # ... } @@ -59,82 +278,64 @@ resource "aws_iam_user" "deploy_user" { } ``` -In this example you define a module in the `./publish_bucket` subdirectory. That module has configuration to create a bucket resource, set access and caching rules. The module wraps the bucket and all the other implementation details required to configure a bucket. +This example defines a local child module in the `./publish_bucket` +subdirectory. That module has configuration to create an S3 bucket. The module +wraps the bucket and all the other implementation details required to configure +a bucket. -We can then define the module multiple times in our configuration by naming each instantiation of the module uniquely, here `module "assets_bucket"` and `module "media_bucket"`, whilst specifying the same module `source`. +We can then instantiate the module multiple times in our configuration by +giving each instance a unique name -- here `module "assets_bucket"` and +`module "media_bucket"` -- whilst specifying the same `source` value. -The resource names in your module get prefixed by `module.` when instantiated, for example the `publish_bucket` module creates `aws_s3_bucket.the_bucket` and `aws_iam_access_key.deploy_user`. The full name of the resulting resources will be `module.assets_bucket.aws_s3_bucket.the_bucket` and `module.assets_bucket.aws_iam_access_key.deploy_user`. Be cautious of this when extracting configuration from your files into a module, the name of your resources will change and Terraform will potentially destroy and recreate them. Always check your configuration with `terraform plan` before running `terraform apply`. +Resources from child modules are prefixed with `module.` +when displayed in plan output and elsewhere in the UI. For example, the +`./publish_bucket` module contains `aws_s3_bucket.example`, and so the two +instances of this module produce S3 bucket resources with [_resource addresses_](/docs/internals/resource-addressing.html) +`module.assets_bucket.aws_s3_bucket.example` and `module.media_bucket.aws_s3_bucket.example` +respectively. These full addresses are used within the UI and on the command +line, but are not valid within interpolation expressions due to the +encapsulation behavior described above. -## Source +When refactoring an existing configuration to introduce modules, moving +resource blocks between modules causes Terraform to see the new location +as an entirely separate resource to the old. Always check the execution plan +after performing such actions to ensure that no resources are surprisingly +deleted. -The only required configuration key for a module is the `source` parameter. The value of this tells Terraform where the module can be downloaded, updated, etc. Terraform comes with support for a variety of module sources. The recommended source for modules is a -[Terraform Registry](/docs/registry/index.html) since this enables additional -features for modules such as versioning. +Each instance of a module may optionally have different providers passed to it +using the `providers` argument described above. This can be useful in situations +where, for example, a duplicated set of resources must be created across +several regions or datacenters. -Terraform can also download modules directly from various storage providers -and version control systems. These sources do not support versioning and other -registry benefits. The full list of available sources -are documented in the [module sources documentation](/docs/modules/sources.html). +## Summarizing Modules in the UI -Prior to running any Terraform command with a configuration that uses modules, you'll have to [get](/docs/commands/get.html) the modules. This is done using the [get command](/docs/commands/get.html). +By default, commands such as the [plan command](/docs/commands/plan.html) and +[graph command](/docs/commands/graph.html) will show each resource in a nested +module to represent the full scope of the configuration. For more complex +configurations, the `-module-depth` option may be useful to summarize some or all +of the modules as single objects. -```shell -$ terraform get -``` - -This command will download the modules if they haven't been already. - -By default, the command will not check for updates, so it is safe (and fast) to run multiple times. You can use the `-update` flag to check and download updates. - -## Configuration - -The parameters used to configure modules, such as the `servers` parameter above, map directly to [variables](/docs/configuration/variables.html) within the module itself. Therefore, you can quickly discover all the configuration -for a module by inspecting the source of it. - -Additionally, because these map directly to variables, module configuration can have any data type available for variables, including maps and lists. - -## Outputs - -Modules encapsulate their resources. A resource in one module cannot directly depend on resources or attributes in other modules, unless those are exported through [outputs](/docs/configuration/outputs.html). These outputs can be referenced in other places in your configuration, for example: - -```hcl -resource "aws_instance" "client" { - ami = "ami-408c7f28" - instance_type = "t1.micro" - availability_zone = "${module.consul.server_availability_zone}" -} -``` - -This purposely is very similar to accessing resource attributes. Instead of mapping to a resource, however, the variable in this case maps to an output of a module. - -Just like resources, this will create a dependency from the `aws_instance.client` resource to the module, so the module will be built first. - -To use module outputs via command line you have to specify the module name before the variable, for example: - -```shell -$ terraform output -module=consul server_availability_zone -``` - -## Plans and Graphs - -Commands such as the [plan command](/docs/commands/plan.html) and [graph command](/docs/commands/graph.html) will expand modules by default. You can use the `-module-depth` parameter to limit the graph. - -For example, with a configuration similar to what we've built above, here is what the graph output looks like by default: +For example, with a configuration similar to what we've built above, the default +graph output looks like the following: ![Terraform Expanded Module Graph](docs/module_graph_expand.png) -If instead we set `-module-depth=0`, the graph will look like this: +If we instead set `-module-depth=0`, the graph will look like this: ![Terraform Module Graph](docs/module_graph.png) -Other commands work similarly with modules. Note that the `-module-depth` flag is purely a formatting flag; it doesn't affect what modules are created or not. +Other commands work similarly with modules. Note that `-module-depth` only +affects how modules are presented in the UI; it does not affect how modules +and their contained resources are processed by Terraform operations. ## Tainting resources within a module -The [taint command](/docs/commands/taint.html) can be used to _taint_ specific resources within a module: +The [taint command](/docs/commands/taint.html) can be used to _taint_ specific +resources within a module: ```shell $ terraform taint -module=salt_master aws_instance.salt_master ``` -It is currently not possible to taint an entire module. +It is not possible to taint an entire module. Instead, each resource within +the module must be tainted separately.