Skip to content

Commit

Permalink
rework a few slightly clunky descriptions, add some clarification and…
Browse files Browse the repository at this point in the history
… expand a few ideas
  • Loading branch information
rsalmond committed Jan 9, 2019
1 parent 14a5da4 commit b8f3b5c
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 18 deletions.
24 changes: 16 additions & 8 deletions code-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,28 +43,36 @@ Please make sure that you understand key concepts - [resource module](key-concep
### Common recommendations for structuring code

* It is easier and faster to work with smaller number of resources
* Radius blast is smaller with fewer resources
* Use remote state not later than when infrastructure layers starts to grow in any direction \(number of dependencies or resources\)
* It is possible to move resources in Terraform state file but it may be harder to do if you have inconsistent structure and [naming](naming.md)
* `terraform plan` and `terraform apply` both make cloud API calls to verify the status of resources
* If you have your entire infrastructure in a single composition this can take many minutes
* Blast radius is smaller with fewer resources
* Insulating unrelated resources from each other by placing them in separate compositions reduces the risk if something goes wrong
* Start your project using remote state
* Your laptop is no place for your infrastructure source of truth
* Managing a tfstate file in git is a nightmare
* Later when infrastructure layers starts to grow in any direction \(number of dependencies or resources\)
* Try to practise a consistent structure and [naming](naming.md) convention
* Like procedural code, Terraform code should be written for people to read first, consistency will help when changes happen six months from now
* It is possible to move resources in Terraform state file but it may be harder to do if you have inconsistent structure and naming
* Keep resource modules as plain as possible
* Don't hardcode values which can be customised or discovered using data sources
* Don't hardcode values which can be passed as variables or discovered using data sources
* Use data sources and `terraform_remote_state` specifically as a glue between infrastructure modules within composition
* \(add links to other blog posts\)

We will group imaginary projects by the _complexity_ - from small to very-large infrastructures. This separation is not strict, so check other structures also.
We will group example projects by the _complexity_ - from small to very-large infrastructures. This separation is not strict, so check other structures also.

### Orchestration of infrastructure modules and compositions

Having small infrastructure means that there are small amount of dependencies and few resources. As project grows the need in chaining execution of Terraform configurations, connecting different infrastructure modules, and passing values within composition becomes visible.
Having small infrastructure means that there are a small number of dependencies and few resources. As the project grows the need to chain the execution of Terraform configurations, connecting different infrastructure modules, and passing values within a composition becomes visible.

There are at least 4 distinct group of orchestration solutions which developers use:
There are at least 4 distinct groups of orchestration solutions which developers use:

1. Terraform only. Very straightforward, developers have to know only Terraform to get job done.
2. Terragrunt. Pure orchestration tool which can be used to orchestrate the entire infrastructure as well as handle dependencies. Terragrunt operates with infrastructure modules and compositions natively, so it reduces duplication of code.
3. In-house scripts. Often this happens as a starting point towards orchestration and before discovering Terragrunt.
4. Ansible or similar general purpose automation tool. Usually used when Terraform is adopted after Ansible, or when Ansible UI is actively used.

So, we have another way for reviewing project structures - whether Terraform or Terragrunt is used.
With that in mind we will reviewing the first two of these project structures, Terraform only and Terragrunt.

See examples of code structures for [Terraform](examples/terraform-1/terraform.md) or [Terragrunt](examples/terragrunt.md) in the next chapter.

23 changes: 13 additions & 10 deletions naming.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
There should be no reason to not follow at least these :\)
{% endhint %}

1. Use `_` \(underscore\) instead of `-` \(dash\) in all names: resource names, data source names, variables, outputs.
1. Use `_` \(underscore\) instead of `-` \(dash\) in all: resource names, data source names, variable names, outputs.
* Beware that actual cloud resources have many hidden restrictions in their naming conventions. Some cannot contain dashes, some must be camel cased. These conventions refer to Terraform names themselves.
2. Only use lowercase letters and numbers.

## Resource and data source arguments
Expand All @@ -32,7 +33,7 @@ There should be no reason to not follow at least these :\)
{% code-tabs-item title="main.tf" %}
```text
resource "aws_route_table" "public" {
count = "2"
count = "2"
vpc_id = "vpc-12345678"
# ... remaining arguments omited
Expand All @@ -57,19 +58,21 @@ resource "aws_route_table" "public" {
{% endcode-tabs %}
{% endhint %}

### Usage of `tags`
### Placement of `tags`

{% hint style="success" %}
{% code-tabs %}
{% code-tabs-item title="main.tf" %}
```text
resource "aws_nat_gateway" "this" {
count = "1"
count = "1"
allocation_id = "..."
subnet_id = "..."
tags = "..."
tags = {
Name = "..."
}
depends_on = ["aws_internet_gateway.this"]
Expand Down Expand Up @@ -127,23 +130,23 @@ resource "aws_nat_gateway" "this" {
## Variables

1. Don't reinvent the wheel in resource modules - use the same variable names, description and default as defined in "Argument Reference" section for the resource you are working on.
2. Allow skipping of `type = "list"` declaration if there is `default = []` also.
3. Allow skipping of `type = "map"` declaration if there is `default = {}` also.
2. Omit `type = "list"` declaration if there is `default = []` also.
3. Omit `type = "map"` declaration if there is `default = {}` also.
4. Use plural form in name of variables of type `list` and `map`.
5. Order of keys: `description` , `type`, `default` .
5. When defining variables order the keys: `description` , `type`, `default` .
6. Always include `decription` for all variables even if you think it is obvious.

## Outputs

Name for the outputs is important to make consistent and understandable outside of its scope \(when user is using a module it should be obvious what type and attribute of the value is returned\).
Name for the outputs is important to make them consistent and understandable outside of its scope \(when user is using a module it should be obvious what type and attribute of the value is returned\).

1. The general recommendation for the names of outputs is that it should be descriptive for the value it contains and be less free-form than you would normally want.
2. Good structure for names of output looks like `{name}_{type}_{attribute}` , where:
1. `{name}` is a resource or data source name without provider prefix. `{name}` for `aws_subnet` is `subnet`, for`aws_vpc` it is `vpc`.
2. `{type}` is a type of a resource sources
3. `{attribute}` is an attribute returned by the output
4. [See examples](naming.md#code-examples-of-output).
3. If output is returning a value with interpolation functions and multiple resources, the `{name}` and `{type}` there should be as generic as possible \(`this` is often the most generic and should be preferred\). [See example](naming.md#code-examples-of-output).
3. If output is returning a value with interpolation functions and multiple resources, the `{name}` and `{type}` there should be as generic as possible \(`this` is often the most generic and should be preferred\). [See example](naming.md#code-examples-of-output).
4. If the returned value is a list it should have plural name. [See example](naming.md#use-plural-name-if-returning-value-is-type-of-list).
5. Always include `decription` for all outputs even if you think it is obvious.

Expand Down

0 comments on commit b8f3b5c

Please sign in to comment.