Skip to content

Terraform Module and Workspaces

The Dry Principle

The Dry Principle is a software design principle that stands for "Don't Repeat Yourself." This principle aims to reduce repetition in code and promote code reuse. It states that every piece of knowledge or logic in a system should have a single, unambiguous representation in the code.

Benefits of the Dry Principle

  • Reduces the amount of code in a system
  • Promotes code reuse and simplifies maintenance
  • Improves readability and clarity of code

Example

Suppose we have a web application that requires a user to log in. We could implement the login functionality in two different ways:

  1. We could write the login code directly into the user interface of each page that requires authentication.
  2. We could write a separate login module that can be included in any page that requires authentication.

The second option follows the Dry Principle by reducing repetition in the code and promoting code reuse. It allows us to maintain a single, unambiguous representation of the login functionality in the code.

Best Practices

  1. Keep Modules DRY (Don't Repeat Yourself): Modularize common sets of resources to create reusable components. Avoid duplicating code, which can lead to inconsistencies and maintenance difficulties.
  2. Organize Modules Logically: Group related resources together in a module. This helps in code organization and understanding the structure of your infrastructure.
  3. Use Descriptive Names: Use clear, descriptive names for modules, variables, and outputs to improve readability and ease of use.
  4. Document Your Modules: Always document what your module does, its inputs (variables), and its outputs. This makes it easier for others to use and maintain your modules.
  5. Manage Module Versions: When sharing or reusing modules, utilize a versioning system. This will help prevent unexpected changes from impacting your infrastructure.
  6. Test Your Modules: Like any other code, test your modules thoroughly. Ensure they work as expected when used independently or in combination with other modules.
  7. Limit Module Scope: Each module should do one thing well. Avoid creating overly broad modules that manage too many different types of resources.
  8. Use Inputs and Outputs: Leverage module inputs and outputs to create flexible and interoperable modules.
  9. Consider Security: When designing your modules, keep security principles in mind. Limit permissions to only those necessary for the module to function.

Example

Consider a module that creates an AWS S3 bucket:

modules/aws_s3/main.tf

resource "aws_s3_bucket" "bucket" {
  bucket = var.bucket_name
  acl    = var.acl

  tags = var.tags
}

variable "bucket_name" {
  description = "The name of the bucket"
  type        = string
}

variable "acl" {
  description = "The access control list setting for the bucket"
  type        = string
  default     = "private"
}

variable "tags" {
  description = "Tags to apply to the bucket"
  type        = map(string)
  default     = {}
}

You can use this module in your main configuration like so:

main.tf

module "my_bucket" {
  source      = "./modules/aws_s3"
  bucket_name = "my_bucket"
  acl         = "private"
  tags        = { Environment = "Dev" }
}

In this example, the my_bucket module will create an S3 bucket with the specified settings. The module is reusable, easy to read, and flexible thanks to the use of variables.

Updating Terraform Modules

  • After adding, removing, or modifying module blocks, you should run terraform init again in order for Terraform to make the necessary adjustments that have been defined in the configuration.
  • Re-running the terraform init command will not amend any modules that have already been installed if the configuration of those modules has not been changed.
  • You can also use the init command to upgrade the providers and modules for your project.
  • To upgrade your providers and modules, you can run terraform init -upgrade.
  • To upgrade your modules, you can run terraform get -update.

Using External Modules

  • If the module is not within your project, you can still use it by specifying its source in the module block of your root module.
  • The source can be a local path or a remote repository.
  • Terraform can load modules from a variety of sources, including the local filesystem, Terraform Registry, and version control repositories.

Versioning Modules

  • Terraform recommends using semantic versioning for modules.
  • You can specify the version of a module by adding a version argument to the module block in your root module.
  • For example, to use version 1.0.4 of a module, you can specify version = "~> 1.0.4" in the module block.
  • This allows you to use any version from 1.0.4 to 1.0.xxxx, where only the rightmost component can increment.
  • You can also use other version constraints such as >=, <=, and = to specify the range of acceptable versions.
  • When depending on third-party modules, it is recommended to require specific versions to ensure that updates only happen when convenient for you.
  • For modules maintained within your organization, specifying version ranges may be appropriate if semantic versioning is used consistently or if there is a well-defined release process that avoids unwanted updates.

Storing Modules

  • There are several options for storing Terraform modules and controlling access to them.
    • One option is to use the public Terraform Registry, which is a centralized repository for Terraform providers and modules.
    • Another option is to use a private registry, such as the one provided by Terraform Cloud or Terraform Enterprise.
    • A private registry allows you to host your own modules within your organization and control access to them using Terraform Cloud API tokens.
    • You can also use other services that implement the registry API, such as GitLab’s Terraform Module Registry, or create your own private registry by following the published protocol.
    • Additionally, you can load private modules directly from version control and other sources, but those methods do not support version constraints or a browsable marketplace.