Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: adds root module example + more docs #1

Merged
merged 4 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .trunk/configs/.markdownlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ whitespace: false
# Ignore MD041/first-line-heading/first-line-h1
# Error: First line in a file should be a top-level heading
MD041: false

# Ignore markdownlint/MD029
# Error: Ordered list item prefix
MD029: false
176 changes: 142 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# terraform-child-module-template
# client-tf-templates

This repository serves as a template for creating Terraform [child modules](https://developer.hashicorp.com/terraform/language/modules#child-modules), providing a standardized structure and essential files for efficient module development. It's designed to ensure consistency and best practices across Terraform projects.
This repository serves as a template for creating Terraform [child](https://opentofu.org/docs/language/modules/#child-modules) and [root](https://opentofu.org/docs/language/modules/#the-root-module) modules, providing a standardized structure and essential files for efficient module development. It's designed to ensure consistency and best practices across Terraform projects.

This README.md serves as the module’s primary documentation and entry point.
Child module example is provided in [terraform-random-pet](./terraform-random-pet/) directory.

Root module example is provided in [root-module](./root-module/) directory.

This README.md serves as the module's primary documentation and entry point.

## Recommenations

Expand All @@ -14,60 +18,164 @@ We recommend to include:
- Prerequisites and Dependencies: Mention any dependencies, required providers, or external resources.
- Example Configurations: If applicable, include or link to example code snippets or a separate examples/ directory.

## Repository Structure
## Structure

This template includes a recommended layout for both root modules and child TF modules. Each file has a specific purpose and set of best practices. While many principles apply to both root and child modules, any differences are noted below.

Below is the recommended structure for a Terraform child module. You'll fine the best practices for what to include inside each file. This layout helps maintain clarity, and consistency across your infrastructure code:
For root modules:

```sh
.
├── README.md
├── main.tf
├── data.tf (optional)
├── outputs.tf
├── outputs.tf (optional)
├── providers.tf
├── variables.tf
└── versions.tf
```

## Additional Tips
For child modules:

- Testing and Examples: Consider adding an examples/ directory with sample configurations and a test/ directory (if using tools like terratest or native Terraform testing) to ensure the module works as intended
- Continuous Improvement: Update documentation and constraints (versions.tf) as Terraform and providers evolve, and as you refine the module’s functionality.
```sh
.
├── README.md
├── main.tf
├── data.tf (optional)
├── outputs.tf (optional)
├── variables.tf
└── versions.tf
```

### File-by-File Guidance

The principles below apply to both root and child modules, unless otherwise specified.

1. `main.tf`

- Purpose: Defines core resources and the module’s primary logic. In root modules, this may also include calls to child modules.
- Best Practices:
- Resource definitions: Declare here all the primary resources that this module is responsible for managing.
- Locals and expressions: Use locals blocks to simplify expressions and keep the code DRY (Don’t Repeat Yourself).
- Comments and structure: Organize resources logically and use comments to explain complex or non-obvious configurations.
- Minimal hard-coding: Use variables extensively to avoid embedding environment-specific values directly in the code.
- Child module calls:
- Use Terraform Registry Modules with Version Pinning:
```hcl
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "1.0.0"
}
```
- Use Git Sources with a Specific Tag or Commit
```hcl
module "vpc" {
source = "git::https://github.com/org/terraform-aws-vpc.git?ref=v1.0.0"
}
```

2. `data.tf` (Optional)

- Purpose: Contains data sources that retrieve external information.
- Best Practices:
- Data source declarations: Place all data blocks here, for example, `data "aws_ami" "linux" { ... }`.
- Clear naming and purpose: Use descriptive names for data sources to indicate their role (e.g., `data "aws_ami" "ubuntu_latest"`).
- Commenting and filtering: Document why each data source is used and ensure filters or queries are well explained.
- Minimize external dependencies: Only query the minimum necessary information. Overly complicated data sources can slow down Terraform runs and confuse future maintainers.

<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
3. `outputs.tf` (Optional)

## Requirements
- Purpose: Defines values exported from the module for use by its caller.
- Best Practices:
- Descriptive output names: Use meaningful names (e.g., `instance_id`, `db_connection_string`).
- Descriptions: Include description attributes to clarify the purpose of each output.
- Minimal outputs: Only output what consumers need. For sensitive outputs, mark as `sensitive = true`.

| Name | Version |
| ------------------------------------------------------------------------ | ------- |
| <a name="requirement_terraform"></a> [terraform](#requirement_terraform) | ~> 1.0 |
| <a name="requirement_random"></a> [random](#requirement_random) | ~> 3.0 |
4. `providers.tf` (Root Module Only)

## Providers
- Purpose: Configures providers for the root module, such as authentication or default regions.
- Best Practices:
- Provider configuration: Define providers (e.g., `aws {}`, `google {}`) and set their region, credentials, or other parameters.
- Multiple provider configurations: If you need multiple configurations for the same provider (e.g., two AWS regions), define them here with explicit aliases.
- Avoid hard-coded and static credentials: Instead of embedding static credentials directly in your code, consider:
- AWS Assume Role: For the AWS provider, configure an assume role to obtain temporary credentials dynamically.
- Encrypted Configuration Files: For providers requiring API tokens, use a tool like [SOPS](https://getsops.io/) to encrypt sensitive variables.

| Name | Version |
| --------------------------------------------------------- | ------- |
| <a name="provider_random"></a> [random](#provider_random) | 3.6.3 |
5. `variables.tf`

## Modules
- Purpose: Defines input variables controlling the module’s configuration.
- Best Practices:
- Descriptive variables: Use meaningful names and description attributes.
- Default values: Provide reasonable defaults when possible. For mandatory inputs, omit defaults to enforce explicit user input.
- Type vonstraints and validation: Use type constraints and validation blocks to catch incorrect inputs early.
- Group related variables: Organize variables logically, adding comments to separate sections if many variables exist.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Type vonstraints and validation: Use type constraints and validation blocks to catch incorrect inputs early.
- Type constraints and validation: Use type constraints and validation blocks to catch incorrect inputs early.


No modules.
6. `versions.tf`

## Resources
- Purpose: Sets Terraform and provider version requirements for consistency and compatibility.
- Best Practices:
- See the detailed version constraints explanation in [Versioning TF and Providers].(#versioning-tf-and-providers)
- Regular Review: Update constraints as Terraform and providers evolve.
gberenice marked this conversation as resolved.
Show resolved Hide resolved

| Name | Type |
| --------------------------------------------------------------------------------------------------------- | -------- |
| [random_pet.template](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource |
## Versioning TF and Providers

## Inputs
We’re particular about how we version providers and Terraform/OpenTofu in child and root modules. We recommend the following:

| Name | Description | Type | Default | Required |
| --------------------------------------------------- | ----------------------------- | -------- | ------- | :------: |
| <a name="input_length"></a> [length](#input_length) | The length of the random name | `number` | `2` | no |
### Child Modules

## Outputs
Since child-modules are intended to be used many times throughout your code, it’s important to make it so that they create as little restrictions on the consuming consuming root module as possible.

| Name | Description |
| -------------------------------------------------------------------------------- | ----------------------------- |
| <a name="output_random_pet_name"></a> [random_pet_name](#output_random_pet_name) | The generated random pet name |
This means you should:

<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
- Identify the earliest Terraform/OpenTofu and provider versions your child module supports.
- Use the `>=` operator to ensure that consumers run at least these versions.

By setting a lower bound (e.g., `>= 1.3`) rather than pinning exact versions, you allow root modules to choose their own Terraform and provider versions. This means a root module can upgrade Terraform or providers without requiring updates to all child modules.

Example:

```hcl
terraform {
required_version = ">= 1.3"

required_providers {
random = {
source = "hashicorp/random"
version = ">= 3.0"
}
}
}
```

In this example, the child module only demands a minimum version (Terraform 1.3, Random provider 3.0), letting the root module run newer versions as they become available.

### Root Modules

Root modules are intended to be planned and applied and therefore they should be more prescriptive so that they’re called consistently in each case that you instantiate a new root module instance (i.e. a state file).

To accomplish that, you should do the following:

- Explicitly pin the latest version of Terraform/OpenTofu that your root module supports. You’ll need to upgrade this version each time you want to use a new TF version across your code base.
- Identify the highest stable provider versions your root module supports, then use the [pessimistic operator](https://developer.hashicorp.com/terraform/language/expressions/version-constraints#operators) `~>` to allow only patch-level updates. This gives you automatic bug fixes and minor improvements without risking major breaking changes.

Example:

```hcl
terraform {
required_version = "1.3.7"

required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.81.0"
}
}
}
```

In this example Terraform is pinned exactly at 1.3.7, the AWS provider is pinned with `~> 5.81.0`, which means it can update to 5.81.1, 5.81.2, etc., but not jump to 5.82.0.

## Additional Tips

- Testing and Examples: Consider adding an examples/ directory with sample configurations and a test/ directory (if using tools like terratest or native Terraform testing) to ensure the module works as intended
- Continuous Improvement: Update documentation and constraints (versions.tf) as Terraform and providers evolve, and as you refine the module’s functionality.
gberenice marked this conversation as resolved.
Show resolved Hide resolved
8 changes: 0 additions & 8 deletions data.tf

This file was deleted.

12 changes: 0 additions & 12 deletions main.tf

This file was deleted.

12 changes: 0 additions & 12 deletions outputs.tf

This file was deleted.

48 changes: 48 additions & 0 deletions root-module/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# root-module

This is a template root module.
Comment on lines +1 to +3
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance the module description

The current description "This is a template root module" is too generic. Consider providing a more specific description that explains the purpose and functionality of this root module example.

-This is a template root module.
+# Example Root Module
+
+This root module demonstrates how to compose child modules together to create a complete infrastructure. It showcases best practices for:
+- Module composition
+- Variable management
+- Resource organization
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# root-module
This is a template root module.
# root-module
# Example Root Module
This root module demonstrates how to compose child modules together to create a complete infrastructure. It showcases best practices for:
- Module composition
- Variable management
- Resource organization


<!-- README TEMPLATE: AFTER READING THE BELOW SECTION, DELETE THE BELOW SECTION AND REPLACE WITH YOUR OWN CONTENT -->

## Documentation Recommendations (DO NOT INCLUDE THIS INTO THE REAL README)

- Module description: Briefly explain what the root module sets up (e.g., infrastructure for RDS Postgres instances).
- Use [terraform-docs](https://github.com/terraform-docs/terraform-docs) to ensure that variables, outputs, child module, and resource documentation is included.
- Maintain current information: Keep the README updated as the infrastructure evolves.
Comment on lines +7 to +11
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove documentation recommendations from template

The "Documentation Recommendations" section with the note "(DO NOT INCLUDE THIS INTO THE REAL README)" should not be part of the template itself. Consider moving these recommendations to a separate documentation guide or contributing guidelines document.

Comment on lines +5 to +11
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Move documentation recommendations to a separate guide

The "Documentation Recommendations" section marked with "(DO NOT INCLUDE THIS INTO THE REAL README)" should be moved to a separate documentation guide or contributing guidelines document.

Consider creating a new file CONTRIBUTING.md in the repository root to house these guidelines.


<!-- README TEMPLATE: ENDING DELETE MARKER -->

<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

## Requirements

| Name | Version |
| ------------------------------------------------------------------------ | ------- |
| <a name="requirement_terraform"></a> [terraform](#requirement_terraform) | ~> 1.0 |
| <a name="requirement_random"></a> [random](#requirement_random) | ~> 3.0 |

## Providers

No providers.

## Modules

| Name | Source | Version |
| ----------------------------------------------------------------- | ------------------------ | ------- |
| <a name="module_random_pet"></a> [random_pet](#module_random_pet) | masterpointio/random/pet | 0.1.0 |

## Resources

No resources.

## Inputs

| Name | Description | Type | Default | Required |
| --------------------------------------------------- | ----------------------------- | -------- | ------- | :------: |
| <a name="input_length"></a> [length](#input_length) | The length of the random name | `number` | `2` | no |

## Outputs

No outputs.

<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
5 changes: 5 additions & 0 deletions root-module/example.auto.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This example.auto.tfvars file provides a sample set of input variable values for the root module.
# Terraform automatically loads any `.auto.tfvars` files, applying these values without requiring additional command-line flags.
# Rename or remove this file to fit your needs.

length = 1
12 changes: 12 additions & 0 deletions root-module/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
locals {
# Get the current timestamp and format it as YYYYMMDD
prefix = formatdate("YYYYMMDD", timestamp())
}
gberenice marked this conversation as resolved.
Show resolved Hide resolved

module "random_pet" {
source = "masterpointio/random/pet"
version = "0.1.0"

length = var.length
prefix = local.prefix
}
gberenice marked this conversation as resolved.
Show resolved Hide resolved
3 changes: 3 additions & 0 deletions root-module/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
provider "random" {
# Configuration options
}
9 changes: 9 additions & 0 deletions root-module/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
variable "length" {
description = "The length of the random name"
type = number
default = 2
validation {
condition = var.length > 0
error_message = "The length must be a positive number."
}
}
10 changes: 10 additions & 0 deletions root-module/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
terraform {
required_version = "1.8.7"

required_providers {
random = {
source = "hashicorp/random"
version = "~> 3.0.0"
}
}
}
Loading
Loading