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.
2020-11-13 03:21:35 +01:00
|
|
|
---
|
|
|
|
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
|
2021-01-15 23:13:53 +01:00
|
|
|
[variable configuration](/docs/language/values/variables.html) and
|
|
|
|
[output configuration](/docs/language/values/outputs.html) for more details.
|
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.
2020-11-13 03:21:35 +01:00
|
|
|
|
|
|
|
* **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/
|
|
|
|
│ ├── .../
|
|
|
|
```
|