website: Break up main Modules and Module Development pages

This one is a lot like the previous two commits, but slightly more complex:

- Only adding one new meta-argument page, for `providers`; otherwise, it just
  re-uses the dual-purpose pages I made in the resources commit.

- About that `providers` argument: The stuff that was relevant to consumers of a
  module went in that meta-argument page, but there was also a huge deep dive on
  how the _author_ of a re-usable module should handle provider configurations
  in cases where inheriting the default providers isn't sufficient. THAT, I
  moved into a new page in the module development section. (For the consumer of
  a module, this should all be an implementation detail; the module README
  should tell you which aliased providers you need to configure and pass, and
  then you just do it, without worrying about proxy configuration blocks etc.)

- The "standard module structure" recommendations in the main module development
  page gets a page of its own, to make it more prominent and discoverable.

- Same deal with using the old URL as a landing page, at least for the main
  module calls page. It didn't seem necessary for the module development page.
This commit is contained in:
Nick Fagerlund 2020-11-12 18:21:35 -08:00
parent 6e2f5eb0be
commit 209541aaf0
8 changed files with 892 additions and 745 deletions

View File

@ -48,12 +48,20 @@ module registry for sharing modules internally within your organization.
## Using Modules
- [Module Blocks](/docs/configuration/modules.html) documents the syntax for
- [Module Blocks](/docs/configuration/blocks/modules/syntax.html) documents the syntax for
calling a child module from a parent module, including meta-arguments like
`for_each`.
- [Module Sources](/docs/modules/sources.html) documents what kinds of paths,
addresses, and URIs can be used in the `source` argument of a module block.
- The Meta-Arguments section documents special arguments that can be used with
every module, including
[`providers`](/docs/configuration/meta-arguments/module-providers.html),
[`depends_on`](/docs/configuration/meta-arguments/depends_on.html),
[`count`](/docs/configuration/meta-arguments/count.html),
and [`for_each`](/docs/configuration/meta-arguments/for_each.html).
## Developing Modules
For information about developing reusable modules, see

View File

@ -0,0 +1,201 @@
---
layout: "language"
page_title: "Modules - Configuration Language"
sidebar_current: "docs-config-modules"
description: |-
Modules allow multiple resources to be grouped together and encapsulated.
---
# Module Blocks
-> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and
earlier, see
[0.11 Configuration Language: Modules](../configuration-0-11/modules.html).
> **Hands-on:** Try the [Reuse Configuration with Modules](https://learn.hashicorp.com/collections/terraform/modules?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn.
A _module_ is a container for multiple resources that are used together.
Every Terraform configuration has at least one module, known as its
_root module_, which consists of the resources defined in the `.tf` files in
the main working directory.
A module can call other modules, which lets you include the child module's
resources into the configuration in a concise way. Modules
can also be called multiple times, either within the same configuration or
in separate configurations, allowing resource configurations to be packaged
and re-used.
This page describes how to call one module from another. For more information
about creating re-usable child modules, see [Module Development](/docs/modules/index.html).
## Calling a Child Module
To _call_ a module means to include the contents of that module into the
configuration with specific values for its
[input variables](/docs/configuration/variables.html). Modules are called
from within other modules using `module` blocks:
```hcl
module "servers" {
source = "./app-cluster"
servers = 5
}
```
A module that includes a `module` block like this is the _calling module_ of the
child module.
The label immediately after the `module` keyword is a local name, which the
calling module can use to refer to this instance of the module.
Within the block body (between `{` and `}`) are the arguments for the module.
Module calls use the following kinds of arguments:
- The `source` argument is mandatory for all modules.
- The `version` argument is recommended for modules from a registry.
- Most other arguments correspond to [input variables](/docs/configuration/variables.html)
defined by the module. (The `servers` argument in the example above is one of
these.)
- Terraform defines a few other meta-arguments that can be used with all
modules, including `for_each` and `depends_on`.
### Source
All modules **require** a `source` argument, which is a meta-argument defined by
Terraform. Its value is either the path to a local directory containing the
module's configuration files, or a remote module source that Terraform should
download and use. This value must be a literal string with no template
sequences; arbitrary expressions are not allowed. For more information on
possible values for this argument, see [Module Sources](/docs/modules/sources.html).
The same source address can be specified in multiple `module` blocks to create
multiple copies of the resources defined within, possibly with different
variable values.
After adding, removing, or modifying `module` blocks, you must re-run
`terraform init` to allow Terraform the opportunity to adjust the installed
modules. By default this command will not upgrade an already-installed module;
use the `-upgrade` option to instead upgrade to the newest available version.
### Version
When using modules installed from a module registry, we recommend explicitly
constraining the acceptable version numbers to avoid unexpected or unwanted
changes.
Use the `version` argument in the `module` block to specify versions:
```shell
module "consul" {
source = "hashicorp/consul/aws"
version = "0.0.5"
servers = 3
}
```
The `version` argument accepts a [version constraint string](/docs/configuration/version-constraints.html).
Terraform will use the newest installed version of the module that meets the
constraint; if no acceptable versions are installed, it will download the newest
version that meets the constraint.
Version constraints are supported only for modules installed from a module
registry, such as the public [Terraform Registry](https://registry.terraform.io/)
or [Terraform Cloud's private module registry](/docs/cloud/registry/index.html).
Other module sources can provide their own versioning mechanisms within the
source string itself, or might not support versions at all. In particular,
modules sourced from local file paths do not support `version`; since
they're loaded from the same source repository, they always share the same
version as their caller.
### Meta-arguments
Along with `source` and `version`, Terraform defines a few more
optional meta-arguments that have special meaning across all modules,
described in more detail in the following pages:
- `count` - Creates multiple instances of a module from a single `module` block.
See [the `count` page](/docs/configuration/meta-arguments/count.html)
for details.
- `for_each` - Creates multiple instances of a module from a single `module`
block. See
[the `for_each` page](/docs/configuration/meta-arguments/for_each.html)
for details.
- `providers` - Passes provider configurations to a child module. See
[the `providers` page](/docs/configuration/meta-arguments/module-providers.html)
for details. If not specified, the child module inherits all of the default
(un-aliased) provider configurations from the calling module.
- `depends_on` - Creates explicit dependencies between the entire
module and the listed targets. See
[the `depends_on` page](/docs/configuration/meta-arguments/depends_on.html)
for details.
In addition to the above, the `lifecycle` argument is not currently used by
Terraform but is reserved for planned future features.
## Accessing Module Output Values
The resources defined in a module are encapsulated, so the calling module
cannot access their attributes directly. However, the child module can
declare [output values](/docs/configuration/outputs.html) to selectively
export certain values to be accessed by the calling module.
For example, if the `./app-cluster` module referenced in the example above
exported an output value named `instance_ids` then the calling module
can reference that result using the expression `module.servers.instance_ids`:
```hcl
resource "aws_elb" "example" {
# ...
instances = module.servers.instance_ids
}
```
For more information about referring to named values, see
[Expressions](/docs/configuration/expressions/index.html).
## Transferring Resource State Into Modules
When refactoring an existing configuration to split code into child modules,
moving resource blocks between modules causes Terraform to see the new location
as an entirely different resource from the old. Always check the execution plan
after moving code across modules to ensure that no resources are deleted by
surprise.
If you want to make sure an existing resource is preserved, use
[the `terraform state mv` command](/docs/commands/state/mv.html) to inform
Terraform that it has moved to a different module.
When passing resource addresses to `terraform state mv`, resources within child
modules must be prefixed with `module.<MODULE NAME>.`. If a module was called with
[`count`](/docs/configuration/meta-arguments/count.html) or
[`for_each`](/docs/configuration/meta-arguments/for_each.html),
its resource addresses must be prefixed with `module.<MODULE NAME>[<INDEX>].`
instead, where `<INDEX>` matches the `count.index` or `each.key` value of a
particular module instance.
Full resource addresses for module contents are used within the UI and on the
command line, but cannot be used within a Terraform configuration. Only
[outputs](/docs/configuration/outputs.html) from a module can be referenced from
elsewhere in your configuration.
## Tainting 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 not possible to taint an entire module. Instead, each resource within
the module must be tainted separately.

View File

@ -0,0 +1,123 @@
---
layout: "language"
page_title: "The Module providers Meta-Argument - Configuration Language"
---
# The Module `providers` Meta-Argument
In a [module call](/docs/configuration/blocks/modules/syntax.html) block, the
optional `providers` meta-argument specifies which
[provider configurations](/docs/configuration/providers.html) from the parent
module will be available inside the 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"
}
# An alternate configuration is also defined for a different
# region, using the alias "usw2".
provider "aws" {
alias = "usw2"
region = "us-west-2"
}
# An example child module is instantiated with the alternate configuration,
# so any AWS resources it defines will use the us-west-2 region.
module "example" {
source = "./example"
providers = {
aws = aws.usw2
}
}
```
## Default Behavior: Inherit Default Providers
The `providers` argument is optional. If you omit it, a child module inherits
all of the _default_ provider configurations from its parent module. (Default
provider configurations are ones that don't use the `alias` argument.)
If you specify a `providers` argument, it cancels this default behavior, and the
child module will _only_ have access to the provider configurations you specify.
## Usage and Behavior
The value of `providers` is a map, where:
- The keys are the provider configuration names used inside the child module.
- The values are provider configuration names from the parent module.
Both keys and values should be unquoted references to provider configurations.
For default configurations, this is the local name of the provider; for
alternate configurations, this is a `<PROVIDER>.<ALIAS>` reference.
Within a child module, resources are assigned to provider configurations as
normal — either Terraform chooses a default based on the name of the resource
type, or the resource specifies an alternate configuration with the `provider`
argument. If the module receives a `providers` map when it's called, the
provider configuration names used within the module are effectively remapped to
refer the specified configurations from the parent module.
## When to Specify Providers
There are two main reasons to use the `providers` argument:
- Using different default provider configurations for a child module.
- Configuring a module that requires multiple configurations of the same provider.
### Changing Default Provider Configurations
Most re-usable modules only use default provider configurations, which they can
automatically inherit from their caller when `providers` is omitted.
However, in Terraform configurations that use multiple configurations of the
same provider, you might want some child modules to use the default provider
configuration and other ones to use an alternate. (This usually happens when
using one configuration to manage resources in multiple different regions of the
same cloud provider.)
By using the `providers` argument (like in the code example above), you can
accommodate this without needing to edit the child module. Although the code
within the child module always refers to the default provider configuration, the
actual configuration of that default can be different for each instance.
### Modules With Alternate Provider Configurations
In rare cases, a single re-usable module might require multiple configurations
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
}
}
```
Non-default provider configurations are never automatically inherited, so any
module that works like this will always need a `providers` argument. The
documentation for the module should specify all of the provider configuration
names it needs.
## More Information for Module Developers
For more details and guidance about working with providers inside a re-usable
child module, see
[Module Development: Providers Within Modules](/docs/modules/providers.html).

View File

@ -1,632 +1,44 @@
---
layout: "language"
page_title: "Modules - Configuration Language"
sidebar_current: "docs-config-modules"
description: |-
Modules allow multiple resources to be grouped together and encapsulated.
page_title: "Modules Landing Page - Configuration Language"
---
# Modules
# Modules Landing Page
-> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and
earlier, see
[0.11 Configuration Language: Modules](../configuration-0-11/modules.html).
To improve navigation, we've split the old Modules page into several smaller
pages.
> **Hands-on:** Try the [Reuse Configuration with Modules](https://learn.hashicorp.com/collections/terraform/modules?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn.
<a id="calling-a-child-module"></a>
<a id="accessing-module-output-values"></a>
<a id="transferring-resource-state-into-modules"></a>
<a id="tainting-resources-within-a-module"></a>
<a id="module-versions"></a>
<a id="other-meta-arguments"></a>
A _module_ is a container for multiple resources that are used together.
## Syntax and Elements of Module Blocks
Every Terraform configuration has at least one module, known as its
_root module_, which consists of the resources defined in the `.tf` files in
the main working directory.
This information has moved to
[Module Blocks](/docs/configuration/blocks/modules/syntax.html).
A module can call other modules, which lets you include the child module's
resources into the configuration in a concise way. Modules
can also be called multiple times, either within the same configuration or
in separate configurations, allowing resource configurations to be packaged
and re-used.
<a id="multiple-instances-of-a-module"></a>
This page describes how to call one module from another. Other pages in this
section of the documentation describe the different elements that make up
modules, and there is further information about how modules can be used,
created, and published in [the dedicated _Modules_
section](/docs/modules/index.html).
## Multiple Instances with `count` and `for_each`
## Calling a Child Module
This information has moved to
[`count`](/docs/configuration/meta-arguments/count.html) and
[`for_each`](/docs/configuration/meta-arguments/for_each.html).
To _call_ a module means to include the contents of that module into the
configuration with specific values for its
[input variables](./variables.html). Modules are called
from within other modules using `module` blocks:
<a id="providers-within-modules"></a>
<a id="provider-version-constraints-in-modules"></a>
<a id="implicit-provider-inheritance"></a>
<a id="passing-providers-explicitly"></a>
<a id="proxy-configuration-blocks"></a>
<a id="legacy-shared-modules-with-provider-configurations"></a>
```hcl
module "servers" {
source = "./app-cluster"
## Handling Provider Configurations in Re-usable Modules
servers = 5
}
```
A module that includes a `module` block like this is the _calling module_ of the
child module.
The label immediately after the `module` keyword is a local name, which the
calling module can use to refer to this instance of the module.
Within the block body (between `{` and `}`) are the arguments for the module.
Most of the arguments correspond to [input variables](./variables.html)
defined by the module, including the `servers` argument in the above example.
Terraform also defines a few meta-arguments that are reserved by Terraform
and used for its own purposes; we will discuss those throughout the rest of
this section.
All modules require a `source` argument, which is a meta-argument defined by
Terraform. Its value is either the path to a local directory containing the
module's configuration files, or a remote module source that Terraform should
download and use. This value must be a literal string with no template
sequences; arbitrary expressions are not allowed. For more information on
possible values for this argument, see [Module Sources](/docs/modules/sources.html).
The same source address can be specified in multiple `module` blocks to create
multiple copies of the resources defined within, possibly with different
variable values.
After adding, removing, or modifying `module` blocks, you must re-run
`terraform init` to allow Terraform the opportunity to adjust the installed
modules. By default this command will not upgrade an already-installed module;
use the `-upgrade` option to instead upgrade to the newest available version.
## Accessing Module Output Values
The resources defined in a module are encapsulated, so the calling module
cannot access their attributes directly. However, the child module can
declare [output values](./outputs.html) to selectively
export certain values to be accessed by the calling module.
For example, if the `./app-cluster` module referenced in the example above
exported an output value named `instance_ids` then the calling module
can reference that result using the expression `module.servers.instance_ids`:
```hcl
resource "aws_elb" "example" {
# ...
instances = module.servers.instance_ids
}
```
For more information about referring to named values, see
[Expressions](./expressions.html).
## Transferring Resource State Into Modules
When refactoring an existing configuration to split code into child modules,
moving resource blocks between modules causes Terraform to see the new location
as an entirely different resource from the old. Always check the execution plan
after moving code across modules to ensure that no resources are deleted by
surprise.
If you want to make sure an existing resource is preserved, use
[the `terraform state mv` command](/docs/commands/state/mv.html) to inform
Terraform that it has moved to a different module.
When passing resource addresses to `terraform state mv`, resources within child
modules must be prefixed with `module.<MODULE NAME>.`. If a module was called
with `count` or `for_each` ([see below][inpage-multiple]), its resource
addresses must be prefixed with `module.<MODULE NAME>[<INDEX>].` instead, where
`<INDEX>` matches the `count.index` or `each.key` value of a particular module
instance.
Full resource addresses for module contents are used within the UI and on the
command line, but cannot be used within a Terraform configuration. Only
[outputs](./outputs.html) from a module can be referenced from
elsewhere in your configuration.
## Other Meta-arguments
Along with the `source` meta-argument described above, module blocks have
some optional meta-arguments that have special meaning across all modules,
described in more detail below:
- `version` - A [version constraint string](./version-constraints.html)
that specifies acceptable versions of the module. Described in detail under
[Module Versions][inpage-versions] below.
- `count` and `for_each` - Both of these arguments create multiple instances of a
module from a single `module` block. Described in detail under
[Multiple Instances of a Module][inpage-multiple] below.
- `providers` - A map whose keys are provider configuration names
that are expected by child module and whose values are the corresponding
provider configurations in the calling module. This allows
[provider configurations to be passed explicitly to child modules](#passing-providers-explicitly).
If not specified, the child module inherits all of the default (un-aliased)
provider configurations from the calling module. Described in detail under
[Providers Within Modules][inpage-providers]
- `depends_on` - Creates explicit dependencies between the entire
module and the listed targets. This will delay the final evaluation of the
module, and any sub-modules, until after the dependencies have been applied.
Modules have the same dependency resolution behavior
[as defined for managed resources](./resources.html#resource-dependencies).
In addition to the above, the `lifecycle` argument is not currently used by
Terraform but is reserved for planned future features.
Since modules are a complex feature in their own right, further detail
about how modules can be used, created, and published is included in
[the dedicated section on modules](/docs/modules/index.html).
## Module Versions
[inpage-versions]: #module-versions
When using modules installed from a module registry, we recommend explicitly
constraining the acceptable version numbers to avoid unexpected or unwanted
changes.
Use the `version` attribute in the `module` block to specify versions:
```shell
module "consul" {
source = "hashicorp/consul/aws"
version = "0.0.5"
servers = 3
}
```
The `version` attribute accepts a [version constraint string](./version-constraints.html).
Terraform will use the newest installed version of the module that meets the
constraint; if no acceptable versions are installed, it will download the newest
version that meets the constraint.
Version constraints are supported only for modules installed from a module
registry, such as the public [Terraform Registry](https://registry.terraform.io/)
or [Terraform Cloud's private module registry](/docs/cloud/registry/index.html).
Other module sources can provide their own versioning mechanisms within the
source string itself, or might not support versions at all. In particular,
modules sourced from local file paths do not support `version`; since
they're loaded from the same source repository, they always share the same
version as their caller.
## Multiple Instances of a Module
[inpage-multiple]: #multiple-instances-of-a-module
-> **Note:** Module support for the `for_each` and `count` meta-arguments was
added in Terraform 0.13. Previous versions can only use these arguments with
individual resources.
Use the `for_each` or the `count` argument to create multiple instances of a
module from a single `module` block. These arguments have the same syntax and
type constraints as
[`for_each`](./resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings)
and
[`count`](./resources.html#count-multiple-resource-instances-by-count)
when used with resources.
```hcl
# my_buckets.tf
module "bucket" {
for_each = toset(["assets", "media"])
source = "./publish_bucket"
name = "${each.key}_bucket"
}
```
```hcl
# publish_bucket/bucket-and-cloudfront.tf
variable "name" {} # this is the input parameter of the module
resource "aws_s3_bucket" "example" {
# Because var.name includes each.key in the calling
# module block, its value will be different for
# each instance of this module.
bucket = var.name
# ...
}
resource "aws_iam_user" "deploy_user" {
# ...
}
```
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 declare multiple module instances by using the `for_each` attribute,
which accepts a map (with string keys) or a set of strings as its value. Additionally,
we use the special `each.key` value in our module block, because the
[`each`](/docs/configuration/resources.html#the-each-object) object is available when
we have declared `for_each` on the module block. When using the `count` argument, the
[`count`](/docs/configuration/resources.html#the-count-object) object is available.
Resources from child modules are prefixed with `module.module_name[module index]`
when displayed in plan output and elsewhere in the UI. For a module without
`count` or `for_each`, the address will not contain the module index as the module's
name suffices to reference the module.
In our 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) of `module.bucket["assets"].aws_s3_bucket.example`
and `module.bucket["media"].aws_s3_bucket.example` respectively.
## Providers Within Modules
[inpage-providers]: #providers-within-modules
In a configuration with multiple modules, there are some special considerations
for how resources are associated with provider configurations.
Each resource in the configuration must be associated with one provider
configuration. Provider configurations, unlike most other concepts in
Terraform, are global to an entire Terraform configuration and can be shared
across module boundaries. Provider configurations can be defined only in a
root Terraform module.
Providers can be passed down to descendent modules in two ways: either
_implicitly_ through inheritance, or _explicitly_ via the `providers` argument
within a `module` block. These two options are discussed in more detail in the
following sections.
A module intended to be called by one or more other modules must not contain
any `provider` blocks, with the exception of the special
"proxy provider blocks" discussed under
_[Passing Providers Explicitly](#passing-providers-explicitly)_
below.
For backward compatibility with configurations targeting Terraform v0.10 and
earlier Terraform does not produce an error for a `provider` block in a shared
module if the `module` block only uses features available in Terraform v0.10,
but that is a legacy usage pattern that is no longer recommended. A legacy
module containing its own provider configurations is not compatible with the
`for_each`, `count`, and `depends_on` arguments that were introduced in
Terraform v0.13. For more information, see
[Legacy Shared Modules with Provider Configurations](#legacy-shared-modules-with-provider-configurations).
Provider configurations are used for all operations on associated resources,
including destroying remote objects and refreshing state. Terraform retains, as
part of its state, a reference to the provider configuration that was most
recently used to apply changes to each resource. When a `resource` block is
removed from the configuration, this record in the state will be used to locate
the appropriate configuration because the resource's `provider` argument
(if any) will no longer be present in the configuration.
As a consequence, you must ensure that all resources that belong to a
particular provider configuration are destroyed before you can remove that
provider configuration's block from your configuration. If Terraform finds
a resource instance tracked in the state whose provider configuration block is
no longer available then it will return an error during planning, prompting you
to reintroduce the provider configuration.
### Provider Version Constraints in Modules
Although provider _configurations_ are shared between modules, each module must
declare its own [provider requirements](provider-requirements.html), so that
Terraform can ensure that there is a single version of the provider that is
compatible with all modules in the configuration and to specify the
[source address](provider-requirements.html#source-addresses) that serves as
the global (module-agnostic) identifier for a provider.
To declare that a module requires particular versions of a specific provider,
use a `required_providers` block inside a `terraform` block:
```hcl
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 2.7.0"
}
}
}
```
A provider requirement says, for example, "This module requires version v2.7.0
of the provider `hashicorp/aws` and will refer to it as `aws`." It doesn't,
however, specify any of the configuration settings that determine what remote
endpoints the provider will access, such as an AWS region; configuration
settings come from provider _configurations_, and a particular overall Terraform
configuration can potentially have
[several different configurations for the same provider](providers.html#alias-multiple-provider-instances).
If you are writing a shared Terraform module, constrain only the minimum
required provider version using a `>=` constraint. This should specify the
minimum version containing the features your module relies on, and thus allow a
user of your module to potentially select a newer provider version if other
features are needed by other parts of their overall configuration.
### Implicit Provider Inheritance
For convenience in simple configurations, a child module automatically inherits
default (un-aliased) provider configurations from its parent. This means that
explicit `provider` blocks appear only in the root module, and downstream
modules can simply declare resources for that provider and have them
automatically associated with the root provider configurations.
For example, the root module might contain only a `provider` block and a
`module` block to instantiate a child module:
```hcl
provider "aws" {
region = "us-west-1"
}
module "child" {
source = "./child"
}
```
The child module can then use any resource from this provider with no further
provider configuration required:
```hcl
resource "aws_s3_bucket" "example" {
bucket = "provider-inherit-example"
}
```
We recommend using this approach when a single configuration for each provider
is sufficient for an entire configuration.
~> **Note:** Only provider configurations are inherited by child modules, not provider source or version requirements. Each module must [declare its own provider requirements](provider-requirements.html). This is especially important for non-HashiCorp providers.
In more complex situations there may be
[multiple provider configurations](/docs/configuration/providers.html#alias-multiple-provider-configurations),
or a child module may need to use different provider settings than
its parent. For such situations, you must pass providers explicitly.
### Passing Providers Explicitly
When child modules each need a different configuration of a particular
provider, or where the child module requires a different provider configuration
than its parent, you can use the `providers` argument within a `module` block
to explicitly define which provider configurations are available to the
child module. For example:
```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"
}
# An alternate configuration is also defined for a different
# region, using the alias "usw2".
provider "aws" {
alias = "usw2"
region = "us-west-2"
}
# An example child module is instantiated with the alternate 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 is similar to
[the `provider` argument](resources.html#provider-selecting-a-non-default-provider-configuration)
within a resource, but is a map rather than a single string because a module may
contain resources from many different providers.
The keys of the `providers` map are provider configuration names as expected by
the child module, and the values are the names of corresponding configurations
in the _current_ module.
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
that may result when mixing both implicit and explicit provider passing.
Additional provider configurations (those with the `alias` argument set) are
_never_ inherited automatically by child modules, and so must always be passed
explicitly using the `providers` map. 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` must then contain _proxy configuration blocks_ like
the following, to declare that it requires its calling module to pass
configurations with these names in its `providers` argument:
```hcl
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 two provider configurations to
use.
### Proxy Configuration Blocks
A proxy configuration block is one that contains only the `alias` argument. It
serves as a placeholder for provider configurations passed between modules, and
declares that a module expects to be explicitly passed an additional (aliased)
provider configuration.
-> **Note:** Although a completely empty proxy configuration block is also
valid, it is not necessary: proxy configuration blocks are needed only to
establish which _aliased_ provider configurations a child module expects.
Don't use a proxy configuration block if a module only needs a single default
provider configuration, and don't use proxy configuration blocks only to imply
[provider requirements](./provider-requirements.html).
## Legacy Shared Modules with Provider Configurations
In Terraform v0.10 and earlier there was no explicit way to use different
configurations of a provider in different modules in the same configuration,
and so module authors commonly worked around this by writing `provider` blocks
directly inside their modules, making the module have its own separate
provider configurations separate from those declared in the root module.
However, that pattern had a significant drawback: because a provider
configuration is required to destroy the remote object associated with a
resource instance as well as to create or update it, a provider configuration
must always stay present in the overall Terraform configuration for longer
than all of the resources it manages. If a particular module includes
both resources and the provider configurations for those resources then
removing the module from its caller would violate that constraint: both the
resources and their associated providers would, in effect, be removed
simultaneously.
Terraform v0.11 introduced the mechanisms described in earlier sections to
allow passing provider configurations between modules in a structured way, and
thus we explicitly recommended against writing a child module with its own
provider configuration blocks. However, that legacy pattern continued to work
for compatibility purposes -- though with the same drawback -- until Terraform
v0.13.
Terraform v0.13 introduced the possibility for a module itself to use the
`for_each`, `count`, and `depends_on` arguments, but the implementation of
those unfortunately conflicted with the support for the legacy pattern.
To retain the backward compatibility as much as possible, Terraform v0.13
continues to support the legacy pattern for module blocks that do not use these
new features, but a module with its own provider configurations is not
compatible with `for_each`, `count`, or `depends_on`. Terraform will produce an
error if you attempt to combine these features. For example:
```
Error: Module does not support count
on main.tf line 15, in module "child":
15: count = 2
Module "child" cannot be used with count because it contains a nested provider
configuration for "aws", at child/main.tf:2,10-15.
This module can be made compatible with count by changing it to receive all of
its provider configurations from the calling module, by using the "providers"
argument in the calling module block.
```
To make a module compatible with the new features, you must either remove all
of the `provider` blocks from its definition or, if you need multiple
configurations for the same provider, replace them with
_proxy configuration blocks_ as described in
[Passing Providers Explicitly](#passing-providers-explicitly).
If the new version of the module uses proxy configuration blocks, or if the
calling module needs the child module to use different provider configurations
than its own default provider configurations, the calling module must then
include an explicit `providers` argument to describe which provider
configurations the child module will use:
```hcl
provider "aws" {
region = "us-west-1"
}
provider "aws" {
region = "us-east-1"
alias = "east"
}
module "child" {
count = 2
providers = {
# By default, the child module would use the
# default (unaliased) AWS provider configuration
# using us-west-1, but this will override it
# to use the additional "east" configuration
# for its resources instead.
aws = aws.east
}
}
```
Since the association between resources and provider configurations is
static, module calls using `for_each` or `count` cannot pass different
provider configurations to different instances. If you need different
instances of your module to use different provider configurations then you
must use a separate `module` block for each distinct set of provider
configurations:
```hcl
provider "aws" {
alias = "usw1"
region = "us-west-1"
}
provider "aws" {
alias = "usw2"
region = "us-west-2"
}
provider "google" {
alias = "usw1"
credentials = "${file("account.json")}"
project = "my-project-id"
region = "us-west1"
zone = "us-west1-a"
}
provider "google" {
alias = "usw2"
credentials = "${file("account.json")}"
project = "my-project-id"
region = "us-west2"
zone = "us-west2-a"
}
module "bucket_w1" {
source = "./publish_bucket"
providers = {
aws.src = aws.usw1
google.src = google.usw2
}
}
module "bucket_w2" {
source = "./publish_bucket"
providers = {
aws.src = aws.usw2
google.src = google.usw2
}
}
```
## Tainting 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 not possible to taint an entire module. Instead, each resource within
the module must be tainted separately.
This information has moved to
[The `providers` Meta-Argument](/docs/configuration/meta-arguments/module-providers.html)
(for users of re-usable modules) and
[Providers Within Modules](/docs/modules/providers.html)
(for module developers).

View File

@ -17,11 +17,11 @@ directly in terms of physical objects.
The `.tf` files in your working directory when you run [`terraform plan`](/docs/commands/plan.html)
or [`terraform apply`](/docs/commands/apply.html) together form the _root_
module. That module may [call other modules](/docs/configuration/modules.html#calling-a-child-module)
module. That module may [call other modules](/docs/configuration/blocks/modules/syntax.html#calling-a-child-module)
and connect them together by passing output values from one to input values
of another.
To learn how to _use_ modules, see [the Modules configuration section](/docs/configuration/modules.html).
To learn how to _use_ modules, see [the Modules configuration section](/docs/configuration/blocks/modules/index.html).
This section is about _creating_ re-usable modules that other configurations
can include using `module` blocks.
@ -71,126 +71,3 @@ your module is not creating any new abstraction and so the module is
adding unnecessary complexity. Just use the resource type directly in the
calling module instead.
## Standard Module Structure
The standard module structure is a file and directory layout we recommend for
reusable modules distributed in separate repositories. Terraform tooling is
built to understand the standard module structure and use that structure to
generate documentation, index modules for the module registry, and more.
The standard module structure expects the layout documented below. The list may
appear long, but everything is optional except for the root module. Most modules
don't need to do any extra work to follow the standard structure.
* **Root module**. This is the **only required element** for the standard
module structure. Terraform files must exist in the root directory of
the repository. This should be the primary entrypoint for the module and is
expected to be opinionated. For the
[Consul module](https://registry.terraform.io/modules/hashicorp/consul)
the root module sets up a complete Consul cluster. It makes a lot of assumptions
however, and we expect that advanced users will use specific _nested modules_
to more carefully control what they want.
* **README**. The root module and any nested modules should have README
files. This file should be named `README` or `README.md`. The latter will
be treated as markdown. There should be a description of the module and
what it should be used for. If you want to include an example for how this
module can be used in combination with other resources, put it in an [examples
directory like this](https://github.com/hashicorp/terraform-aws-consul/tree/master/examples).
Consider including a visual diagram depicting the infrastructure resources
the module may create and their relationship.
The README doesn't need to document inputs or outputs of the module because
tooling will automatically generate this. If you are linking to a file or
embedding an image contained in the repository itself, use a commit-specific
absolute URL so the link won't point to the wrong version of a resource in the
future.
* **LICENSE**. The license under which this module is available. If you are
publishing a module publicly, many organizations will not adopt a module
unless a clear license is present. We recommend always having a license
file, even if it is not an open source license.
* **`main.tf`, `variables.tf`, `outputs.tf`**. These are the recommended filenames for
a minimal module, even if they're empty. `main.tf` should be the primary
entrypoint. For a simple module, this may be where all the resources are
created. For a complex module, resource creation may be split into multiple
files but any nested module calls should be in the main file. `variables.tf`
and `outputs.tf` should contain the declarations for variables and outputs,
respectively.
* **Variables and outputs should have descriptions.** All variables and
outputs should have one or two sentence descriptions that explain their
purpose. This is used for documentation. See the documentation for
[variable configuration](/docs/configuration/variables.html) and
[output configuration](/docs/configuration/outputs.html) for more details.
* **Nested modules**. Nested modules should exist under the `modules/`
subdirectory. Any nested module with a `README.md` is considered usable
by an external user. If a README doesn't exist, it is considered for internal
use only. These are purely advisory; Terraform will not actively deny usage
of internal modules. Nested modules should be used to split complex behavior
into multiple small modules that advanced users can carefully pick and
choose. For example, the
[Consul module](https://registry.terraform.io/modules/hashicorp/consul)
has a nested module for creating the Cluster that is separate from the
module to setup necessary IAM policies. This allows a user to bring in their
own IAM policy choices.
If the root module includes calls to nested modules, they should use relative
paths like `./modules/consul-cluster` so that Terraform will consider them
to be part of the same repository or package, rather than downloading them
again separately.
If a repository or package contains multiple nested modules, they should
ideally be [composable](./composition.html) by the caller, rather than
calling directly to each other and creating a deeply-nested tree of modules.
* **Examples**. Examples of using the module should exist under the
`examples/` subdirectory at the root of the repository. Each example may have
a README to explain the goal and usage of the example. Examples for
submodules should also be placed in the root `examples/` directory.
Because examples will often be copied into other repositories for
customization, any `module` blocks should have their `source` set to the
address an external caller would use, not to a relative path.
A minimal recommended module following the standard structure is shown below.
While the root module is the only required element, we recommend the structure
below as the minimum:
```sh
$ tree minimal-module/
.
├── README.md
├── main.tf
├── variables.tf
├── outputs.tf
```
A complete example of a module following the standard structure is shown below.
This example includes all optional elements and is therefore the most
complex a module can become:
```sh
$ tree complete-module/
.
├── README.md
├── main.tf
├── variables.tf
├── outputs.tf
├── ...
├── modules/
│   ├── nestedA/
│   │   ├── README.md
│   │   ├── variables.tf
│   │   ├── main.tf
│   │   ├── outputs.tf
│   ├── nestedB/
│   ├── .../
├── examples/
│   ├── exampleA/
│   │   ├── main.tf
│   ├── exampleB/
│   ├── .../
```

View File

@ -0,0 +1,369 @@
---
layout: "language"
page_title: "Providers Within Modules - Configuration Language"
---
# Providers Within Modules
[inpage-providers]: #providers-within-modules
In a configuration with multiple modules, there are some special considerations
for how resources are associated with provider configurations.
Each resource in the configuration must be associated with one provider
configuration. Provider configurations, unlike most other concepts in
Terraform, are global to an entire Terraform configuration and can be shared
across module boundaries. Provider configurations can be defined only in a
root Terraform module.
Providers can be passed down to descendent modules in two ways: either
_implicitly_ through inheritance, or _explicitly_ via the `providers` argument
within a `module` block. These two options are discussed in more detail in the
following sections.
A module intended to be called by one or more other modules must not contain
any `provider` blocks, with the exception of the special
"proxy provider blocks" discussed under
_[Passing Providers Explicitly](#passing-providers-explicitly)_
below.
For backward compatibility with configurations targeting Terraform v0.10 and
earlier Terraform does not produce an error for a `provider` block in a shared
module if the `module` block only uses features available in Terraform v0.10,
but that is a legacy usage pattern that is no longer recommended. A legacy
module containing its own provider configurations is not compatible with the
`for_each`, `count`, and `depends_on` arguments that were introduced in
Terraform v0.13. For more information, see
[Legacy Shared Modules with Provider Configurations](#legacy-shared-modules-with-provider-configurations).
Provider configurations are used for all operations on associated resources,
including destroying remote objects and refreshing state. Terraform retains, as
part of its state, a reference to the provider configuration that was most
recently used to apply changes to each resource. When a `resource` block is
removed from the configuration, this record in the state will be used to locate
the appropriate configuration because the resource's `provider` argument
(if any) will no longer be present in the configuration.
As a consequence, you must ensure that all resources that belong to a
particular provider configuration are destroyed before you can remove that
provider configuration's block from your configuration. If Terraform finds
a resource instance tracked in the state whose provider configuration block is
no longer available then it will return an error during planning, prompting you
to reintroduce the provider configuration.
## Provider Version Constraints in Modules
Although provider _configurations_ are shared between modules, each module must
declare its own [provider requirements](/docs/configuration/provider-requirements.html), so that
Terraform can ensure that there is a single version of the provider that is
compatible with all modules in the configuration and to specify the
[source address](/docs/configuration/provider-requirements.html#source-addresses) that serves as
the global (module-agnostic) identifier for a provider.
To declare that a module requires particular versions of a specific provider,
use a `required_providers` block inside a `terraform` block:
```hcl
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 2.7.0"
}
}
}
```
A provider requirement says, for example, "This module requires version v2.7.0
of the provider `hashicorp/aws` and will refer to it as `aws`." It doesn't,
however, specify any of the configuration settings that determine what remote
endpoints the provider will access, such as an AWS region; configuration
settings come from provider _configurations_, and a particular overall Terraform
configuration can potentially have
[several different configurations for the same provider](/docs/configuration/providers.html#alias-multiple-provider-instances).
If you are writing a shared Terraform module, constrain only the minimum
required provider version using a `>=` constraint. This should specify the
minimum version containing the features your module relies on, and thus allow a
user of your module to potentially select a newer provider version if other
features are needed by other parts of their overall configuration.
## Implicit Provider Inheritance
For convenience in simple configurations, a child module automatically inherits
default (un-aliased) provider configurations from its parent. This means that
explicit `provider` blocks appear only in the root module, and downstream
modules can simply declare resources for that provider and have them
automatically associated with the root provider configurations.
For example, the root module might contain only a `provider` block and a
`module` block to instantiate a child module:
```hcl
provider "aws" {
region = "us-west-1"
}
module "child" {
source = "./child"
}
```
The child module can then use any resource from this provider with no further
provider configuration required:
```hcl
resource "aws_s3_bucket" "example" {
bucket = "provider-inherit-example"
}
```
We recommend using this approach when a single configuration for each provider
is sufficient for an entire configuration.
~> **Note:** Only provider configurations are inherited by child modules, not provider source or version requirements. Each module must [declare its own provider requirements](/docs/configuration/provider-requirements.html). This is especially important for non-HashiCorp providers.
In more complex situations there may be
[multiple provider configurations](/docs/configuration/providers.html#alias-multiple-provider-configurations),
or a child module may need to use different provider settings than
its parent. For such situations, you must pass providers explicitly.
## Passing Providers Explicitly
When child modules each need a different configuration of a particular
provider, or where the child module requires a different provider configuration
than its parent, you can use the `providers` argument within a `module` block
to explicitly define which provider configurations are available to the
child module. For example:
```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"
}
# An alternate configuration is also defined for a different
# region, using the alias "usw2".
provider "aws" {
alias = "usw2"
region = "us-west-2"
}
# An example child module is instantiated with the alternate 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 is similar to
[the `provider` argument](/docs/configuration/meta-arguments/resource-provider.html)
within a resource, but is a map rather than a single string because a module may
contain resources from many different providers.
The keys of the `providers` map are provider configuration names as expected by
the child module, and the values are the names of corresponding configurations
in the _current_ module.
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
that may result when mixing both implicit and explicit provider passing.
Additional provider configurations (those with the `alias` argument set) are
_never_ inherited automatically by child modules, and so must always be passed
explicitly using the `providers` map. 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` must then contain _proxy configuration blocks_ like
the following, to declare that it requires its calling module to pass
configurations with these names in its `providers` argument:
```hcl
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 two provider configurations to
use.
## Proxy Configuration Blocks
A proxy configuration block is one that contains only the `alias` argument. It
serves as a placeholder for provider configurations passed between modules, and
declares that a module expects to be explicitly passed an additional (aliased)
provider configuration.
-> **Note:** Although a completely empty proxy configuration block is also
valid, it is not necessary: proxy configuration blocks are needed only to
establish which _aliased_ provider configurations a child module expects.
Don't use a proxy configuration block if a module only needs a single default
provider configuration, and don't use proxy configuration blocks only to imply
[provider requirements](/docs/configuration/provider-requirements.html).
## Legacy Shared Modules with Provider Configurations
In Terraform v0.10 and earlier there was no explicit way to use different
configurations of a provider in different modules in the same configuration,
and so module authors commonly worked around this by writing `provider` blocks
directly inside their modules, making the module have its own separate
provider configurations separate from those declared in the root module.
However, that pattern had a significant drawback: because a provider
configuration is required to destroy the remote object associated with a
resource instance as well as to create or update it, a provider configuration
must always stay present in the overall Terraform configuration for longer
than all of the resources it manages. If a particular module includes
both resources and the provider configurations for those resources then
removing the module from its caller would violate that constraint: both the
resources and their associated providers would, in effect, be removed
simultaneously.
Terraform v0.11 introduced the mechanisms described in earlier sections to
allow passing provider configurations between modules in a structured way, and
thus we explicitly recommended against writing a child module with its own
provider configuration blocks. However, that legacy pattern continued to work
for compatibility purposes -- though with the same drawback -- until Terraform
v0.13.
Terraform v0.13 introduced the possibility for a module itself to use the
`for_each`, `count`, and `depends_on` arguments, but the implementation of
those unfortunately conflicted with the support for the legacy pattern.
To retain the backward compatibility as much as possible, Terraform v0.13
continues to support the legacy pattern for module blocks that do not use these
new features, but a module with its own provider configurations is not
compatible with `for_each`, `count`, or `depends_on`. Terraform will produce an
error if you attempt to combine these features. For example:
```
Error: Module does not support count
on main.tf line 15, in module "child":
15: count = 2
Module "child" cannot be used with count because it contains a nested provider
configuration for "aws", at child/main.tf:2,10-15.
This module can be made compatible with count by changing it to receive all of
its provider configurations from the calling module, by using the "providers"
argument in the calling module block.
```
To make a module compatible with the new features, you must either remove all
of the `provider` blocks from its definition or, if you need multiple
configurations for the same provider, replace them with
_proxy configuration blocks_ as described in
[Passing Providers Explicitly](#passing-providers-explicitly).
If the new version of the module uses proxy configuration blocks, or if the
calling module needs the child module to use different provider configurations
than its own default provider configurations, the calling module must then
include an explicit `providers` argument to describe which provider
configurations the child module will use:
```hcl
provider "aws" {
region = "us-west-1"
}
provider "aws" {
region = "us-east-1"
alias = "east"
}
module "child" {
count = 2
providers = {
# By default, the child module would use the
# default (unaliased) AWS provider configuration
# using us-west-1, but this will override it
# to use the additional "east" configuration
# for its resources instead.
aws = aws.east
}
}
```
Since the association between resources and provider configurations is
static, module calls using `for_each` or `count` cannot pass different
provider configurations to different instances. If you need different
instances of your module to use different provider configurations then you
must use a separate `module` block for each distinct set of provider
configurations:
```hcl
provider "aws" {
alias = "usw1"
region = "us-west-1"
}
provider "aws" {
alias = "usw2"
region = "us-west-2"
}
provider "google" {
alias = "usw1"
credentials = "${file("account.json")}"
project = "my-project-id"
region = "us-west1"
zone = "us-west1-a"
}
provider "google" {
alias = "usw2"
credentials = "${file("account.json")}"
project = "my-project-id"
region = "us-west2"
zone = "us-west2-a"
}
module "bucket_w1" {
source = "./publish_bucket"
providers = {
aws.src = aws.usw1
google.src = google.usw2
}
}
module "bucket_w2" {
source = "./publish_bucket"
providers = {
aws.src = aws.usw2
google.src = google.usw2
}
}
```

View File

@ -0,0 +1,128 @@
---
layout: "language"
page_title: "Standard Module Structure"
---
# Standard Module Structure
The standard module structure is a file and directory layout we recommend for
reusable modules distributed in separate repositories. Terraform tooling is
built to understand the standard module structure and use that structure to
generate documentation, index modules for the module registry, and more.
The standard module structure expects the layout documented below. The list may
appear long, but everything is optional except for the root module. Most modules
don't need to do any extra work to follow the standard structure.
* **Root module**. This is the **only required element** for the standard
module structure. Terraform files must exist in the root directory of
the repository. This should be the primary entrypoint for the module and is
expected to be opinionated. For the
[Consul module](https://registry.terraform.io/modules/hashicorp/consul)
the root module sets up a complete Consul cluster. It makes a lot of assumptions
however, and we expect that advanced users will use specific _nested modules_
to more carefully control what they want.
* **README**. The root module and any nested modules should have README
files. This file should be named `README` or `README.md`. The latter will
be treated as markdown. There should be a description of the module and
what it should be used for. If you want to include an example for how this
module can be used in combination with other resources, put it in an [examples
directory like this](https://github.com/hashicorp/terraform-aws-consul/tree/master/examples).
Consider including a visual diagram depicting the infrastructure resources
the module may create and their relationship.
The README doesn't need to document inputs or outputs of the module because
tooling will automatically generate this. If you are linking to a file or
embedding an image contained in the repository itself, use a commit-specific
absolute URL so the link won't point to the wrong version of a resource in the
future.
* **LICENSE**. The license under which this module is available. If you are
publishing a module publicly, many organizations will not adopt a module
unless a clear license is present. We recommend always having a license
file, even if it is not an open source license.
* **`main.tf`, `variables.tf`, `outputs.tf`**. These are the recommended filenames for
a minimal module, even if they're empty. `main.tf` should be the primary
entrypoint. For a simple module, this may be where all the resources are
created. For a complex module, resource creation may be split into multiple
files but any nested module calls should be in the main file. `variables.tf`
and `outputs.tf` should contain the declarations for variables and outputs,
respectively.
* **Variables and outputs should have descriptions.** All variables and
outputs should have one or two sentence descriptions that explain their
purpose. This is used for documentation. See the documentation for
[variable configuration](/docs/configuration/variables.html) and
[output configuration](/docs/configuration/outputs.html) for more details.
* **Nested modules**. Nested modules should exist under the `modules/`
subdirectory. Any nested module with a `README.md` is considered usable
by an external user. If a README doesn't exist, it is considered for internal
use only. These are purely advisory; Terraform will not actively deny usage
of internal modules. Nested modules should be used to split complex behavior
into multiple small modules that advanced users can carefully pick and
choose. For example, the
[Consul module](https://registry.terraform.io/modules/hashicorp/consul)
has a nested module for creating the Cluster that is separate from the
module to setup necessary IAM policies. This allows a user to bring in their
own IAM policy choices.
If the root module includes calls to nested modules, they should use relative
paths like `./modules/consul-cluster` so that Terraform will consider them
to be part of the same repository or package, rather than downloading them
again separately.
If a repository or package contains multiple nested modules, they should
ideally be [composable](./composition.html) by the caller, rather than
calling directly to each other and creating a deeply-nested tree of modules.
* **Examples**. Examples of using the module should exist under the
`examples/` subdirectory at the root of the repository. Each example may have
a README to explain the goal and usage of the example. Examples for
submodules should also be placed in the root `examples/` directory.
Because examples will often be copied into other repositories for
customization, any `module` blocks should have their `source` set to the
address an external caller would use, not to a relative path.
A minimal recommended module following the standard structure is shown below.
While the root module is the only required element, we recommend the structure
below as the minimum:
```sh
$ tree minimal-module/
.
├── README.md
├── main.tf
├── variables.tf
├── outputs.tf
```
A complete example of a module following the standard structure is shown below.
This example includes all optional elements and is therefore the most
complex a module can become:
```sh
$ tree complete-module/
.
├── README.md
├── main.tf
├── variables.tf
├── outputs.tf
├── ...
├── modules/
│   ├── nestedA/
│   │   ├── README.md
│   │   ├── variables.tf
│   │   ├── main.tf
│   │   ├── outputs.tf
│   ├── nestedB/
│   ├── .../
├── examples/
│   ├── exampleA/
│   │   ├── main.tf
│   ├── exampleB/
│   ├── .../
```

View File

@ -245,13 +245,34 @@
</li>
<li>
<a href="/docs/configuration/modules.html">Module Blocks</a>
<a href="/docs/configuration/blocks/modules/syntax.html">Module Blocks</a>
</li>
<li>
<a href="/docs/modules/sources.html">Module Sources</a>
</li>
<li>
<a href="#">Meta-Arguments</a>
<ul class="nav nav-auto-expand">
<li>
<a href="/docs/configuration/meta-arguments/module-providers.html"><code>providers</code></a>
</li>
<li>
<a href="/docs/configuration/meta-arguments/depends_on.html"><code>depends_on</code></a>
</li>
<li>
<a href="/docs/configuration/meta-arguments/count.html"><code>count</code></a>
</li>
<li>
<a href="/docs/configuration/meta-arguments/for_each.html"><code>for_each</code></a>
</li>
</ul>
</li>
<li>
<a href="#">Module Development</a>
<ul class="nav">
@ -259,6 +280,14 @@
<a href="/docs/modules/index.html">Overview</a>
</li>
<li>
<a href="/docs/modules/structure.html">Standard Module Structure</a>
</li>
<li>
<a href="/docs/modules/providers.html">Providers Within Modules</a>
</li>
<li>
<a href="/docs/modules/composition.html">Best Practices: Module Composition</a>
</li>