Sign In
Sign In

Terraform for DevOps: Best Practices

Terraform for DevOps: Best Practices
Hostman Team
Technical writer
Terraform
27.03.2025
Reading time: 10 min

Terraform is one of the most popular Infrastructure as Code (IaC) tools that allows you to manage infrastructure using various cloud providers. It follows a declarative approach, meaning you describe the desired infrastructure rather than writing step-by-step instructions for its creation—Terraform then automatically provisions it.

This tutorial outlines best practices for efficient Terraform development.

File and Folder Structure

Maintaining a clear and organized file structure is crucial when working on a large project with complex infrastructure. A consistent folder and file structure improves project maintainability.

A project example:

-- PROJECT-DIRECTORY/
-- modules/
      -- <service1-name>/
         -- main.tf
         -- variables.tf
         -- outputs.tf
         -- provider.tf
         -- README
      -- <service2-name>/
         -- main.tf
         -- variables.tf
         -- outputs.tf
         -- provider.tf
         -- README
      -- ...other…
 -- environments/
      -- dev/
         -- backend.tf
         -- main.tf
         -- outputs.tf
         -- variables.tf
         -- terraform.tfvars
 -- qa/
         -- backend.tf
         -- main.tf
         -- outputs.tf
         -- variables.tf
         -- terraform.tfvars
-- stage/
         -- backend.tf
         -- main.tf
         -- outputs.tf
         -- variables.tf
         -- terraform.tfvars
-- prod/
         -- backend.tf
         -- main.tf
         -- outputs.tf
         -- variables.tf
         -- terraform.tfvars

Separating Configuration Files

Keeping all the code in a single main.tf file is a bad idea.  Instead, split it into separate files based on their purpose:

  • main.tf – Calls modules and data sources to create resources.

  • variables.tf – Defines variables used in main.tf.

  • outputs.tf – Specifies the outputs of resources created in main.tf.

  • versions.tf – Defines Terraform and provider version requirements.

  • terraform.tfvars – Contains variable values.

Module Structure

The standard module structure is described in the Terraform documentation.

Follow these best practices:

  • Group resources based on their purpose, e.g., vps.tf, s3.tf, load_balancer.tf.

  • Avoid creating a separate file for each resource unless necessary.

  • Include a README.md file for each module with a clear description of its purpose.

Directory Separation for Applications and Environments

  • To independently manage infrastructure for different applications, place resources for each application in its own directory.

  • Store shared resources (e.g., networks) in a dedicated common resources directory.

  • Use separate directories for each environment (dev, qa, stage, production).

  • Use modules to share common code across all environments.

  • Each environment directory should contain:

    • backend.tf – Defines the Terraform backend state configuration.

    • main.tf – Contains the infrastructure description.

Static Files and Templates

  • Static files should be stored in the files/ directory.

  • Template files should be placed in the templates/ directory.

General Code Structuring Guidelines

  • Place count or for_each as the first argument within a resource or data source block, followed by a new line. Always list tags, depends_on, and lifecycle as the last arguments in a consistent order, separated by a single blank line.

  • Keep resource modules simple—avoid unnecessary complexity.

  • Avoid hardcoding values—use variables or data sources instead.

Naming, Code Style, and File Formatting

Terraform code should be written in a way that other developers can easily understand. Consistent naming conventions improve code readability and maintainability.

Key Naming Rules

  • Use underscore (_) instead of hyphen (-) to separate multiple words in names.

  • Use only lowercase letters and numbers.

  • Use singular nouns for resource names.

  • Do not repeat the resource type in its name:

    • Good example: resource "aws_route_table" "public" {}

    • Bad example: resource "aws_route_table" "public_route_table" {}

  • Differentiate resources of the same type using descriptive names (e.g., primary, secondary, public, private).

Variable Management Rules

  • Declare all variables in the variables.tf file.

  • Use descriptive names that reflect the variable's purpose.

  • Provide meaningful descriptions for all variables—this helps generate module documentation and provides context for new developers.

  • Organize variable keys in the following order:

    • description

    • type

    • default

    • validation

  • Provide default values whenever possible:

    • Specify a default if a variable has environment-independent values (e.g., disk size).

    • If a variable has environment-specific values, do not set a default.

  • Use plural names for variables of type list(...) or map(...).

  • Prefer simple types (number, string, list(...), map(...), any) over object(), unless strict key constraints are needed.

  • For boolean variables, use positive names (e.g., enable_external_access).

  • For numeric input/local/output variables, include measurement units in the name (e.g., ram_size_gb).

  • Use binary prefixes for storage units (kilo, mega, giga).

Output Value Rules

  • Organize all outputs in the outputs.tf file.

  • Output all useful values that other modules may need.

  • Provide meaningful descriptions for all output values.

  • Name outputs based on their contents, following this structure: {name}_{type}_{attribute}

  • Document outputs in README.md.

  • Use tools like terraform-docs to auto-generate documentation when committing code.

Using Built-in Formatting Tools

  • Use the terraform fmt command to format Terraform files according to its canonical style.

  • All Terraform files must comply with terraform fmt standards to ensure consistency.

Best Security Practices in Terraform

Terraform interacts with cloud infrastructure using sensitive data such as API keys. To protect your infrastructure, follow these security best practices:

Secure the Terraform State File

  • Never store the state file locally or in version control (e.g., Git). Instead, use Terraform Remote State (e.g., Hostman S3 Storage, AWS S3, Azure Storage). The state file contains sensitive values in plain text, posing a security risk.

  • Add Terraform state files to .gitignore to prevent accidental commits.

  • Encrypt state files as an extra security measure.

  • Regularly back up state files in case of corruption or accidental loss.

  • Use one state file per environment (e.g., dev, staging, production).

Enable State Locking

Multiple developers running Terraform commands simultaneously can lead to state corruption and data loss.

To prevent conflicts, enable state locking, which ensures only one user modifies the state at a time.

Note that not all backends support built-in locking. Azure Blob Storage supports locking natively, while AWS S3 supports locking when used with DynamoDB.

Keep Secrets Out of the State File

Terraform stores secrets in plain text within the state file. Avoid storing secrets directly in Terraform configuration.

Instead, use secret management tools (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault) and reference secrets using data sources rather than hardcoding them.

Minimize the Blast Radius

The blast radius refers to the potential impact of failure in your infrastructure. To reduce risk:

  • Deploy smaller, isolated infrastructure components.

  • Manage critical resources separately from less essential ones.

  • Implement least privilege access for Terraform execution roles.

Perform Security Audits

Run security checks after every terraform apply. Use security auditing tools like InSpec or Serverspec. These tools ensure that infrastructure remains in a secure state.

Use the sensitive Flag for Variables

Terraform configurations often contain sensitive inputs like passwords, API tokens, and private keys.

To prevent accidental exposure, mark variables as sensitive:

variable "db_password" {
  description = "Database admin password"
  type        = string
  sensitive   = true
}

Terraform will hide these values in the console and logs.

However, sensitive does not encrypt the data—other precautions are still necessary.

Use .tfvars Files for Variable Definitions

Instead of defining many variables manually, store them in .tfvars files:

db_password = "super_secret_password"

Pass the file during execution:

terraform apply -var-file="secrets.tfvars"

Terraform automatically loads .tfvars files if they exist.

Always store secrets locally using -var-file rather than committing them to version control.

Using Modules in Terraform

Modules are designed for code reuse and help organize infrastructure as code effectively.

Use Common Modules

Use official Terraform modules whenever possible. There is no need to reinvent a module that already exists.

Each module should focus on a single aspect of infrastructure, such as creating database instances.

Tag Module Versions

Sometimes, breaking changes are required in modules. Use version tags so that users can lock their configurations to a specific version and avoid unexpected issues.

Avoid Declaring Providers and Remote State in Shared Modules

Shared modules should not declare providers or remote state. Instead, define providers and remote state configuration in the root modules.

Expose Outputs for All Resources

Variables and outputs help define dependencies between modules and resources. Users cannot properly integrate your module into their Terraform configurations without output values.

Include at least one output that references each resource in a shared module.

Use Submodules for Complex Logic

Submodules help break down complex Terraform logic into smaller, reusable units. This approach reduces code duplication for shared resources.

  • Store submodules in modules/$modulename.

  • Consider modules as private unless the module documentation states otherwise.

Minimize Resources in Each Root Module

Avoid large root configurations that contain too many resources in a single directory and state file.

Smaller modules make infrastructure easier to manage and faster to deploy.

Additional Recommendations

Version Control

  • Store infrastructure code in version control just like application code to maintain history and allow easy rollbacks.

  • Follow a branching strategy such as GitFlow.

  • Use separate branches for environment-specific root configurations, if necessary.

Best Practices for Testing

Static Analysis

  • Validate configuration syntax and structure before deploying resources using linters and dry-run tools.

  • Use terraform validate and tools like tflint, config-lint, Checkov, Terrascan, tfsec, Deepsource.

Integration Testing

  • Test modules in isolation to ensure correctness.

  • Use testing frameworks like Terratest, Kitchen-Terraform, InSpec

Plan Before Applying

  • Always check the output of terraform validate and terraform plan before applying changes to an environment.

Keep Terraform Up to Date

Stay on the latest Terraform version whenever a major release occurs.

Run terraform -v to check for updates.

Protect Stateful Resources

Enable deletion protection for stateful resources like databases to prevent accidental removal.

Use the self Variable

The self variable helps when values are unknown before deployment.

Example: If you need the IP address of an instance, but it’s only available after deployment, you can use self to reference it dynamically.

Use Terraform Workspaces

Workspaces allow managing multiple instances of the same Terraform configuration, each with its own state.

Useful for handling multiple environments (e.g., dev, staging, production) using the same Terraform codebase.

For example, here is how you could use workspaces to manage the dev and prod environments:

terraform workspace new dev
terraform apply
terraform workspace new prod
terraform apply
Terraform
27.03.2025
Reading time: 10 min

Similar

Terraform

How to Use Terraform Variables

Terraform is a popular software tool for DevOps engineers and system administrators, with its primary goal being creating and managing cloud infrastructure. Its main feature is the ability to automate all processes related to infrastructure deployment. Terraform has a set of core elements used to describe infrastructure. These include providers, resources, data sources, modules, expressions, and variables. Variables in Terraform are special elements that allow users to store and pass values to various parts of modules without modifying the main configuration file's code. They enable flexible management of infrastructure settings and parameters, making configuration and maintenance easier. In this guide, we’ll discuss Terraform variables and explain how to use them in your configuration. Declaring Variables One can describe Terraform variables as containers where users store information (deployment region, instance types, passwords, or access keys). Their values are defined once through CLI parameters or environment variables and can be used in different parts of the configuration. This process is usually done using the variable block in the variable definition file (variables.tf). The syntax for declaring variables is as follows: variable "variable_name" { list_of_arguments } Each variable must have a unique name. This name is used to assign values externally and to reference the value within the module. The name can be anything but should not conflict with meta-arguments like version, providers, locals, etc. Variable arguments are optional, but it’s important not to avoid them, as they allow users to set additional parameters. Some of the main arguments include: type — Specifies the data type allowed for the variable. We’ll cover types in detail in the next chapter. description — This argument allows you to add a description to explain the purpose and usage of the variable. default — Defines a default value for the variable. validation — Specifies custom validation rules. sensitive — Controls the confidentiality of the variable in outputs. nullable — Accepts two values (true and false) and indicates whether the variable can accept a null value. Variable Type Constraints As mentioned earlier, for each variable in Terraform, it’s possible to specify a data type that restricts the values it can accept. This is done using the type argument. Terraform supports the following data types: number — Numeric data format (integers, floating-point numbers, etc.). string — A sequence of Unicode characters for storing textual information. bool — Boolean data type (True or False). map or object — A collection of key-value pairs enclosed in curly braces. list or tuple — A sequence of values of the same or different data types enclosed in square brackets. Example of specifying a variable type: variable "region" { type = string } Variable Description Since the input variables of a module are part of its user interface, you can briefly describe the purpose of each variable using the optional description argument. Here's an example of how to use it: variable "region" { type = string description = "Specifies the server region" } The description helps developers and other users better understand the role of the variable and what values it expects. Custom Validation Rules In Terraform, you can specify custom validation rules for a selected variable using the validation argument. Each validation must include two required arguments: condition and error_message. The condition defines an expression that checks the variable’s value, returning True if it's valid and False if it’s not. The error_message specifies the message to be displayed to the user if the condition returns False. Example of using the validation argument: variable "email" { type = string description = "Email address" validation { condition = can(regex("^\\S+@\\S+\\.\\S+$", var.email)) error_message = "Invalid email format" } } In the above example, we validate the email variable to ensure it matches a proper email address format using a regular expression in the condition. If the email address does not pass the validation, the user will see the error message "Invalid email format." Sensitive Data in Variables When using the sensitive argument, Terraform handles the variable in a way that prevents accidental exposure of sensitive data in the plan or apply outputs. Example of using the sensitive argument: variable "user" { type = object({ name = string role = string }) sensitive = true } resource "example_resource" "example1" { name = var.user.name role = var.user.role } Any resources and other Terraform elements associated with a sensitive variable also become sensitive. This is why sensitive data will not be shown in the output, as represented in the image below. Assigning Values to Root Module Variables After declaring variables in the root module, you can assign values to each of them. There are several ways to do this, which we'll discuss below. Command Line The first method for passing values to variables is using the command line. You can use the -var parameter when running the terraform plan or terraform apply commands. The syntax for this method is as follows: terraform apply|plan -var="variable1=value1" -var="variable2=value2" You can use the -var parameter multiple times in a single command. Variable Definition Files You can also specify values using a special variable definition file, which must end with .tfvars or .tfvars.json. The syntax for this file is: variable1 = "value1" variable2 = "value2" To use the values specified in the file, you must provide the file when running the Terraform command: terraform apply -var-file="file_name.tfvars" Environment Variables The last method we will discuss is using Terraform environment variables. The environment variable names must be in the format TF_VAR_variable_name. The syntax for this method is as follows: export TF_VAR_variable1=value1 export TF_VAR_variable2=value2 terraform apply|plan Conclusion In this guide, we explored variables in Terraform, learned how to declare them, reviewed the main arguments, and discussed methods for assigning values to them. Proper use of variables helps you create a more flexible and secure infrastructure with Terraform.
24 March 2025 · 5 min to read
Terraform

Installing Terraform: Step-by-Step Guide

Terraform is an infrastructure management tool from HashiCorp that enables the deployment and management of infrastructure across various cloud platforms. Its main advantage is its ability to automate the creation and management of infrastructure, making it an essential tool for DevOps engineers and system administrators. This guide covers the steps to install Terraform on both Windows and Ubuntu. What Terraform Is and How It Works Terraform automates infrastructure deployment and management in cloud environments. It manages a variety of resources—such as virtual machines, networks, and data storage—through a single tool. Terraform uses configuration files written in HashiCorp Configuration Language (HCL) to describe the infrastructure to be created. These HCL files let users specify the infrastructure setup, including all resources required. For example, in a configuration file, you can describe a virtual machine with specific attributes, a database, or any other service you want to provision. When you run Terraform, it reads these configuration files and creates the resources described within them. Terraform considers dependencies between resources and creates them in the correct order. If the user changes the Terraform configuration—for instance, by adding a new resource or modifying an existing one—Terraform identifies the necessary adjustments to bring the infrastructure to the desired state as described in the configuration files. One of Terraform’s main benefits is its ability to manage infrastructure across different cloud environments with a single tool. This flexibility simplifies moving infrastructure between cloud platforms and streamlines infrastructure management overall. Installing Terraform on Windows There are several ways to install Terraform on Windows, including: Using a package manager like Chocolatey Manual installation Here’s a guide for both methods. Installation Using Chocolatey Chocolatey is a package manager for Windows that allows software installation, updates, and management from the command line. If Chocolatey is not yet installed, follow the guide on its official website. Once installed, you can install software from the command line using choco install. Here’s the syntax: choco install <package_name> To install Terraform with Chocolatey: Open the command prompt as an administrator from the Start menu. Run the installation command: choco install terraform After the installation, you can verify it with terraform -v: C:\Windows\system32>terraform -vTerraform v1.3.6on windows_amd64 Manual Installation One drawback of using a package manager is the possibility of downloading an outdated version. For the latest version, consider installing Terraform manually. To do this: Visit the HashiCorp website and download the appropriate version for your system. Extract the contents to a preferred folder, such as C:\Terraform. Note that the Terraform command won’t work directly from the command line without the full path unless added to your system path. C:\Windows\system32>terraform -v 'terraform' is not recognized as an internal or external command, operable program or batch file. C:\Windows\system32>C:\Terraform\terraform -v Terraform v1.3.6 on windows_amd64 To make Terraform accessible directly from the command line, add it to the system PATH: Open Control Panel and go to System and Security. In the System section, click on Advanced System Settings. In the System Properties window, go to the Advanced tab. Click Environment Variables. In the System Variables section, find the PATH variable and click Edit. Click New in the Edit Environment Variable window and add the path to the folder where you extracted Terraform (e.g., C:\Terraform). Click OK. Verify the installation in the command prompt: terraform -v Terraform v1.3.6on windows_amd64 Installing Terraform on Ubuntu To install Terraform on Ubuntu, follow these steps: Step 1: Open the terminal and update the package list: sudo apt update Step 2: Install necessary packages for downloading and installation: sudo apt install wget unzip Step 3: Navigate to the directory where you want to install Terraform, for example: cd ~ Step 4: Download the latest version from HashiCorp’s website: wget https://releases.hashicorp.com/terraform/0.x.x/terraform_0.x.x_linux_amd64.zip Replace 0.x.x with the desired version. A list of available versions can be found on the Terraform release page. Step 5: Unzip the downloaded archive: unzip terraform_0.x.x_linux_amd64.zip Step 6: Move the extracted file to /usr/local/bin to make it accessible system-wide: sudo mv terraform /usr/local/bin/ Step 7: Verify that Terraform is installed and accessible by checking its version: terraform -v You should see the Terraform version you installed. Terraform is now ready for use. Advantages of Terraform Terraform offers several advantages for infrastructure management: Automation: Infrastructure is described in configuration files, simplifying deployment and management. Unified Configuration Language: Terraform uses a single configuration language, making it possible to manage infrastructure across different cloud environments with one tool. Dependency Management: Terraform allows you to define dependencies between resources, helping manage the order in which resources are created or destroyed. Rollback Capability: Terraform tracks infrastructure changes, enabling you to roll back updates if needed. Conclusion Terraform is a powerful cloud infrastructure management tool. It lets you create, modify, and delete resources across multiple cloud providers using a single configuration language, which offers convenience, speed, and consistency in infrastructure management. Additionally, you can back up configurations and manage changes through version control tools.
02 November 2024 · 5 min to read

Do you have questions,
comments, or concerns?

Our professionals are available to assist you at any moment,
whether you need help or are just unsure of where to start.
Email us
Hostman's Support