Skip to content

Commit

Permalink
doc: improve docs on authoring components
Browse files Browse the repository at this point in the history
  • Loading branch information
bishopb committed Oct 13, 2022
1 parent 69dfe03 commit f840119
Show file tree
Hide file tree
Showing 2 changed files with 205 additions and 9 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Example, once you have forked and cloned this repository:

```sh
$ ./bin/terraform-component create my_tf_component
$ ${EDITOR:-vi} components/my_tf_component/terraform/main.tf
$ # edit the files in components/my_tf_component/terraform/
$ ./bin/terraform-component deploy my_tf_component plan
```

Expand All @@ -37,6 +37,8 @@ A component is an isolated unit of infrastructure, defined as Terraform HCL. Thi
mono-repo supports multiple components, a suite of tools to help deploy them, and
common functionality to enhance component collaboration.

[Learn more about components with this official guide.](./components/README.md)

## Motivation

In ["What is Terraform?"][tf-whatis], HashiCorp says (emphasis ours):
Expand All @@ -56,7 +58,7 @@ Workflow frameworks are opinionated. They dictate configuration format and
organization, workflow initiation and intervention, error handling, and so on.
This framework adopts the following principles:

1. *Only `terraform`, `docker`, `bash`, and POSIX tools need to be installed.*
1. *Only `docker`, `bash` and POSIX tools need to be installed.*
Fewer dependencies means its easier to get started. Conventional dependencies
means less has to be learned to use the framework.
1. *Configuration is written in POSIX `sh` shell.* Terraform is declarative, but
Expand Down
208 changes: 201 additions & 7 deletions components/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,208 @@
Components are units of infrastructure, configured in bash and expressed in
Terraform HCL.
This framework lets you easily deploy logical units of infrastructure called
"components".

* To learn how to create components, run `terraform-component create` and read
the help documentation.
* To better understand the organization of a component, refer to the `lib/component_skeleton`
directory.
# What you need to know before you can use this framework

To be successful using this framework, you will need to understand how to build
infrastructure using the Terraform language. You will also need to know how to
set variables using POSIX shell syntax. Here are some guides to get you started
on these topics:

* [Terraform tutorials][terra-tut]
* [POSIX shell scripting][posix-sh]

[posix-sh]:https://www.grymoire.com/Unix/Sh.html
[terra-tut]:https://learn.hashicorp.com/terraform

As you gain more confidence in using this framework, you can use advanced POSIX
sh shell scripting to dynamically adjust values passed in to your Terraform, and
you can deploy more advanced Terraform constructs like conditional resources.

# What is a component?

A component is a logical unit of infrastructure. It does something. What it does
is up to you. When creating a component, plan what its function is within the
infrastructure, thoughtfully. Components should not try to do too much and, at
the same time, not be so small as to offer no value on their own.

## Anatomy of a component

Here is what a component looks like, one generated by `terraform-component create`:

```sh
$ tree components/terraform_state_backend_s3
components/terraform_state_backend_s3
├── LICENSE.md
├── README.md
├── build-env.sh
├── deploy-env.sh
├── deploy-hooks.sh
└── terraform
├── main.tf
├── outputs.tf
└── variables.tf
```

All components have this structure, which generally falls into three categories:

1. Informational files that tell you what the component is and how it can be reused.
`LICENSE.md` and `README.md`
2. POSIX shell scripts that pass data from the command line invocation into the
Terraform HCL, at various points throughout the deployment life-cycle.
`build-env.sh`, `deploy-env.sh`, and `deploy-hooks.sh`.
3. The infrastructure implementation, in Terraform HCL within the `terraform/`
sub-directory. You may have one or more files in here. By convention there
are:
* `main.tf`, which holds the implementation of your infrastructure component
* `outputs.tf`, which documents the outputs this component makes available to
other components
* `variables.tf`, which documents what inputs the component needs to implement
the infrastructure.

### Terraform versions

Different versions of Terraform offer different features. Generally, the higher
the version number, the more features. Usually you want to use the latest
possible version, to take advantage of new features and bug and security fixes.

Each component, individually, declares what version of Terraform it wants to
use. This allows you to develop components independently: older components can
continue to use older versions of Terraform, while newer ones use newer versions.

Set the value of `TERRAFORM_VERSION` in your `build-env.sh` file to the version
of Terraform you want to use, or if you do not, your component inherits the
value from this framework.

[Available versions are listed at Docker Hub.][terra-vers]

Tip: always set the Terraform version in your components.

[terra-vers]:https://hub.docker.com/r/hashicorp/terraform/tags

### Variables

Variables specified in the `terraform/variables.tf` file (or any other `*.tf`
file that holds a `variable` terraform declaration) must be given before your
component will deploy. The values are specified in the `deploy-env.sh` file.

Normally, the values are expressed using simple assignment, like:

```sh
TF_VAR_region=us-west-2
```

However, you can use shell scripting to calculate values dynamically, based on
other values in the environment. For example, to use the value of a variable
named `AWS_REGION` if it exists, but otherwise default to `us-west-2`, you could
use:

```sh
TF_VAR_region=${AWS_REGION:-us-west-2}
```

To experiment with the shell, you can run:

```sh
docker run -it --entrypoint /bin/sh hashicorp/terraform
```

# Licensing of components
[Read more about Terraform variables.][terra-vars]

[terra-vars]:https://www.terraform.io/language/values/variables

### Outputs

Your component may output information that other components to depend upon. This
often happens when a component creates a resource then needs to share that
resource's identifier with dependent components.

Outputs specified in the `terraform/outputs.tf` file (or any other `*.tf`
file that holds an `output` terraform declaration) will be available for other
components to use. (See the next section on expression dependencies.)

[Read more about Terraform outputs.][terra-outs]

[terra-outs]:https://www.terraform.io/language/values/outputs

#### Expressing dependencies

As much as possible, components should be independent and not depend upon other
components, because it simplifies replicating environments and reduces the
complexity of understanding relationships between components.

However, there are times when you want or need component dependencies, so this
framework supports them. [Refer to the Terraform documentation for more.][terra-rs]

[terra-rs]:https://www.terraform.io/language/state/remote-state-data

### Licensing

While this framework is licensed under the GNU General Public License, version 3,
components within this sub-directory may have a different license. Components
must specify their license in the form of a `LICENSE.md` file, even if it's the
same as this framework.

Components without a `LICENSE.md` inherit the license of this framework: GPLv3.

# Component deployment life-cycle

When you run `terraform-component deploy`, the framework code runs through a
series of well-defined steps called "the deployment life-cycle" on the component
you've chosen to deploy.

The first step is to build a Docker image that houses the logical combination of
your component's code and the base files provided by the framework. This image
has _everything_ needed to deploy the Terraform. So long as you make no changes
to your component's Terraform or shell configuration, this image will be used
every time you deploy. The configuration values in `build-env.sh` drive various
features available in the built image, and that's the extent of configurability
you have on the build step.

The second step is to deploy the built Docker image. This step is far more
customizable. The `deploy-env.sh` script runs, programatically setting values
for your Terraform to access. Since the terraform process is multi-staged (init,
plan, apply, etc), you can configure behavior before and after each of these
stages with the `deploy-hooks.sh` file.

# Extending terraform-component

You can use this framework standalone via fork and clone, which is the easiest
way to get started. It's meant to be easy, so that you can focus on getting
infrastructure-as-code deployed.

You can also extend this framework with your own custom behavior or private
components. A common way to do this is:

1. At install time, download a copy of this framework (`npm install`, `composer install`,
etc.) using curl (or similar) and store in a directory named `deployer` (or
similar).
1. Write a script that wraps this framework's `terraform-component`. For example,
that script might authenticate the user with the cloud provider.
1. Arrange for your wrapper script to point to your components and use your
environment setup.

Here's a rough outline of these steps:

```
# download the latest release of this framework and put into a directory named `deployer`
curl https://api.github.com/repos/bishopb/terraform-component/tarball \
| tar xzC deployer --strip 1
# invoke the framework deployer, after setting up the environment and pointing it
# to our custom components
env \
default_TF_VAR_providers=aws \
default_TF_VAR_credentials=aws \
default_TF_VAR_backend=s3 \
COMPONENT_PATH=./components:./deployer/components \
./deployer/bin/terraform-component deploy "component-name" "apply"
```

There is no limit to how you can compose this framework into your environment.

# Additional resources

* To learn how to create components, run `terraform-component create` and read
the help documentation.
* To better understand the organization of a component, refer to the `lib/component_skeleton`
directory.

0 comments on commit f840119

Please sign in to comment.