Skip to main content

Command Palette

Search for a command to run...

A Comprehensive Guide to Terraform

Updated
21 min read
A Comprehensive Guide to Terraform

What is Terraform?

Terraform is an open-source infrastructure as code (IaC) tool developed by HashiCorp. It allows developers and system administrators to define and provision data center infrastructure using a declarative configuration language. With Terraform, you can manage a wide variety of cloud providers, including AWS, Azure, Google Cloud, and many more, all from a single configuration file.

In contrast, Ansible is an imperative language. In imperative languages like Ansible, you define how to achieve the desired outcome step-by-step (e.g., "Install Nginx, then configure it"). This approach focuses more on the sequence of tasks or operations required to reach the desired state. Terraform, on the other hand, is declarative, meaning you define what the final state should look like, and Terraform handles the process of getting there.

Benefits of Infrastructure as Code (IaC)

Infrastructure as Code provides several key benefits:

  • Consistency: IaC enables you to manage and provision your infrastructure using code, reducing the risk of human error and ensuring that your environments are consistent.

  • Version Control: Just like application code, infrastructure code can be versioned, allowing for easy rollbacks and tracking of changes over time.

  • Automation: Terraform automates the process of infrastructure provisioning and management, saving time and reducing manual tasks.

  • Collaboration: Teams can collaborate more effectively by sharing and reusing infrastructure code, improving overall productivity.

Installing Terraform

Step-by-Step Installation Guide for Various Platforms

Windows:

  1. Download the Terraform ZIP file from the Terraform website.

  2. Extract the ZIP file and move the terraform.exe file to a directory included in your system's PATH (e.g., C:\Windows).

  3. Open a command prompt and run terraform -version to verify the installation.

macOS:

  1. Install Terraform using Homebrew by running the following command:

     brew install terraform
    
  2. After installation, verify by running:

     terraform -version
    

Linux:

  1. Download the Terraform package for Linux from the Terraform website.

  2. Unzip the package and move the terraform binary to /usr/local/bin:

     unzip terraform_<version>_linux_amd64.zip
     sudo mv terraform /usr/local/bin/
    
  3. Verify the installation:

     terraform -version
    

Verifying the Installation

After installation, you can verify that Terraform is correctly installed by running the following command in your terminal or command prompt:

terraform -version

This command should return the installed version of Terraform.

HCL Basics

Introduction to HashiCorp Configuration Language (HCL)

HashiCorp Configuration Language (HCL) is a domain-specific language designed for defining infrastructure resources. HCL is both human-readable and machine-friendly, making it easy to understand and write configuration files.

Basic Structure of HCL Files

HCL files are structured in blocks, with each block representing a resource, provider, or module. The general syntax is as follows:

resource "<resource_type>" "<resource_name>" {
  # configuration arguments
}

Example of a Simple Configuration

Here’s a simple example of an HCL configuration that defines an AWS EC2 instance:

provider "aws" {
  region = "us-west-2"
}

resource "aws_instance" "example" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
}

Creating, Updating, and Destroying Infrastructure

Basic Terraform Commands

  • terraform init: Initializes a new or existing Terraform working directory, downloading required providers.

  • terraform plan: Creates an execution plan, showing what actions Terraform will take to reach the desired state.

  • terraform apply: Applies the changes required to reach the desired state of the configuration.

  • terraform destroy: Destroys all resources defined in the configuration.

Example Workflow Demonstrating These Commands

  1. Initialize your Terraform working directory:

     terraform init
    
  2. Create a plan to review changes:

     terraform plan
    
  3. Apply the changes:

     terraform apply
    
  4. If you need to remove the infrastructure, run:

     terraform destroy
    

Managing State Files

Terraform uses state files to keep track of resources it manages. By default, the state is stored locally in a file named terraform.tfstate. It’s crucial to manage state files properly, especially in team environments, to prevent conflicts and ensure consistency.

Using Multiple Providers

Introduction to Terraform Providers

Providers are responsible for managing the lifecycle of resources. Terraform supports multiple providers, allowing users to manage resources across various cloud platforms from a single configuration.

How to Configure and Use Multiple Providers in a Single Project

To use multiple providers, define each provider in your configuration. For example:

provider "aws" {
  region = "us-west-2"
}

provider "azurerm" {
  features {}
}

Example of Using AWS and Azure Together

Here’s an example of using both AWS and Azure providers:

provider "aws" {
  region = "us-west-2"
}

resource "aws_instance" "aws_example" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
}

provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "azure_example" {
  name     = "example-resources"
  location = "West US"
}

Version Constraints

Explanation of Version Constraints and Their Importance

Version constraints allow you to specify which versions of a provider you want to use in your Terraform configuration. This is important for maintaining compatibility and ensuring stability as providers are updated.

How to Define Version Constraints in Your Configuration

You can define version constraints in your provider block. For example:

provider "aws" {
  version = "~> 3.0"
}

Examples of Various Constraints

  • >= 2.0: Accepts any version greater than or equal to 2.0.

  • <= 3.0: Accepts any version less than or equal to 3.0.

  • ~> 2.0: Accepts any version that starts with 2 (e.g., 2.1, 2.2), but not 3.0.

Using Aliases

Explanation of Aliases and Their Use Cases

Aliases allow you to define multiple instances of the same provider within a single configuration. This is useful when you need to connect to different accounts or regions with the same provider.

How to Define and Use Aliases in Your Terraform Configuration

You can define an alias in the provider block, then reference it when declaring resources. For example:

provider "aws" {
  alias  = "secondary"
  region = "us-east-1"
}

resource "aws_instance" "example" {
  provider      = aws.secondary
  ami           = "ami-12345678"
  instance_type = "t2.micro"
}

Example of Configuring Multiple Instances of the Same Provider with Different Settings

Here’s an example of using aliases to manage resources in different regions:

provider "aws" {
  region = "us-west-2"
}

provider "aws" {
  alias  = "secondary"
  region = "us-east-1"
}

resource "aws_instance" "west_example" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
}

resource "aws_instance" "east_example" {
  provider      = aws.secondary
  ami           = "ami-12345678"
  instance_type = "t2.micro"
}

Working with Variables in Terraform

Variables in Terraform help make configurations more flexible and reusable. Instead of hardcoding values directly into your Terraform configuration, you can define variables and pass values at runtime or from external files.

Defining Variables in variables.tf

A common way to define variables is by creating a variables.tf file. This file holds all the variable definitions, including default values, descriptions, and types.

Example of a variables.tf file:

variable "instance_type" {
  description = "The type of instance to create"
  type        = string
  default     = "t2.micro"
}

To apply a configuration that includes variables from variables.tf, you must specify the values using the -var option or pass a separate .tfvars file. If no value is provided, Terraform uses the default value if defined.

Using .tfvars Files

Another method to assign variable values is by using a .tfvars file. Terraform automatically loads .tfvars files, such as terraform.tfvars, without the need for additional flags. These files can contain values for the variables defined in the variables.tf file.

Example of a terraform.tfvars file:

instance_type = "t3.medium"

Key Differences Between variables.tf and .tfvars

  • variables.tf: This file contains the variable definitions, including defaults, types, and descriptions. When using this file, you need to provide variable values manually through the command line using the -var flag, or by specifying a separate .tfvars file.

    • * Example of applying a configuration with a variables.tf file:

      plaintext terraform apply -var="instance_type=t3.large"

  • .tfvars: This file contains the values for variables and is automatically loaded by Terraform during the execution of terraform plan or terraform apply. You don’t need to specify it explicitly, making it more convenient for storing variable values.

    • Example of auto-loaded .tfvars:

        terraform apply
      

Using variables is an essential part of writing scalable, reusable Terraform configurations. Whether you use variables.tf for definitions or .tfvars for auto-loaded values, both methods offer flexibility depending on your project needs.

String

The string type represents a single line of text. It's useful for single values like instance types, region names, or identifiers.

Example:

variable "instance_type" {
  type        = string
  description = "Type of instance to create"
  default     = "t2.micro"
}
  • Usage: Terraform expects instance_type to be a text string, such as "t2.micro".

2. Number

The number type represents an integer or float. This is useful for setting quantities, limits, or numeric configurations.

Example:

variable "instance_count" {
  type        = number
  description = "Number of instances to create"
  default     = 3
}
  • Usage: Terraform expects instance_count to be a numeric value like 1, 2, or 3.5.

3. Bool

The bool type is a Boolean, representing true or false. This is often used to control on/off or enabled/disabled settings.

Example:

variable "enable_logging" {
  type        = bool
  description = "Enable or disable logging"
  default     = true
}
  • Usage: Terraform expects enable_logging to be either true or false.

4. Any

The any type is a flexible type that accepts any value type. Use any when you want the variable to be adaptable and accept various data formats (string, number, list, map, etc.).

Example:

variable "custom_value" {
  type        = any
  description = "Accepts any type of value"
}
  • Usage: You can assign a string, number, list, or map to custom_value.

5. List

The list type is an ordered collection of values. All items in the list must be of the same type, which is defined as list(<type>).

Example:

variable "availability_zones" {
  type        = list(string)
  description = "List of availability zones"
  default     = ["us-west-1a", "us-west-1b", "us-west-1c"]
}
  • Usage: Terraform expects availability_zones to be a list of strings (e.g., ["us-west-1a", "us-west-1b"]).


6. Map

The map type is a collection of key-value pairs, where each key is unique and typically a string. It’s useful for providing lookup tables or setting multiple values.

Example:

variable "instance_tags" {
  type        = map(string)
  description = "Tags for the instance"
  default     = {
    Name    = "my-instance"
    Env     = "production"
    Project = "terraform-guide"
  }
}
  • Usage: Terraform expects instance_tags to be a map with string keys and string values, allowing you to reference individual keys as needed (e.g., instance_tags["Env"]).


7. Object

The object type allows you to define a complex structure with named attributes, each having a specified type. It’s useful for structured data, like grouping multiple configurations into one variable.

Example:

variable "database_config" {
  type = object({
    engine   = string
    version  = string
    instances = number
  })
  default = {
    engine   = "postgres"
    version  = "12"
    instances = 1
  }
}
  • Usage: The database_config variable must contain values for engine, version, and instances, with the specified types.


8. Tuple

The tuple type is a fixed-length, ordered collection where each element can be a different type. It’s defined as tuple([type1, type2, ...]).

Example:

variable "instance_info" {
  type = tuple([string, number, bool])
  default = ["t2.micro", 3, true]
}
  • Usage: The instance_info variable must be a tuple with a string (e.g., instance type), a number (e.g., instance count), and a boolean (e.g., enabled).

9. Set

Summary Table

TypeExampleDescription
string"us-west-1"Single line of text
number3Integer or float
booltrue or falseBoolean
anyAny data typeAccepts any data type
listlist(string)Ordered collection of a single type
mapmap(string)Key-value pairs with unique keys
objectobject({ key1 = string, key2 = number})Collection with named attributes
tupletuple([string, number, bool])Ordered collection of mixed types

Using these types in Terraform can help you design clear, structured, and flexible configurations. Let me know if you'd like examples for specific scenarios!

Output Variables in Terraform (Short Summary)

Output variables in Terraform are used to extract and display key information from your infrastructure after deployment. They help you access important details, such as instance IDs, IP addresses, or any other values you want to track.

Key Features:

  • Display values: Show information after running terraform apply.

  • Pass between modules: Share data from one module to another.

  • Debugging: Help inspect resource states.

Example:

output "instance_id" {
  value = aws_instance.example.id
}

Sensitive Data:

Mark outputs as sensitive = true to hide sensitive information (e.g., passwords).

Best Practices:

  • Use clear, descriptive names.

  • Limit exposure of sensitive data.

Handling Secrets in Terraform

In Terraform, sensitive data such as passwords, API keys, and other credentials can be handled securely by using the sensitive = true attribute. This prevents Terraform from displaying the sensitive information in the console or output, but note that it does not encrypt the data in the state file.

Using Sensitive Attribute

When you mark a value as sensitive, Terraform hides it from the terminal output, but it still appears in the state file.

Example:

hclCopy codeoutput "db_password" {
  value     = aws_secretsmanager_secret.example.secret_string
  sensitive = true
}

In this case:

  • Console: The value won’t be displayed if you run terraform apply or terraform output.

  • State File: The secret is still present in plain text within the state file, which can be a security risk if not properly secured.

Limitations

  • Errors when accessed: If you try to access or reference a sensitive value in your Terraform output, you might encounter errors or blocked access. Sensitive outputs can’t be easily read from the command line.

  • State file: Even with sensitive = true, the data will still be in the state file in clear text, so ensure the state file is stored securely (e.g., in a remote backend like AWS S3 with proper encryption).

Best Practices:

  • Use remote state backends with encryption to protect the state file.

  • Mark outputs and variables that handle sensitive information as sensitive = true to prevent console leaks.

  • Use secret management tools (e.g., AWS Secrets Manager, HashiCorp Vault) to handle sensitive data securely.

Resource Attributes in Terraform

In Terraform, resource attributes define the configuration of infrastructure resources. These attributes allow you to specify details like instance type, AMI, tags, and more. Resource attributes can also refer to other resources, helping Terraform manage dependencies between them.

Example: Creating an AWS Key Pair and Using It in an EC2 Instance

  1. Creating a Key Pair: You can create an AWS EC2 Key Pair using the aws_key_pair resource. This key can later be used for SSH access to an EC2 instance.
resource "aws_key_pair" "example" {
  key_name   = "my-key"
  public_key = file("~/.ssh/id_rsa.pub")
}
  • key_name: The name of the key pair in AWS.

  • public_key: The path to the local public key file to be uploaded to AWS.

  1. Using the Key Pair in an EC2 Instance: Once the key pair is created, you can refer to it when creating an EC2 instance to enable SSH access.
resource "aws_instance" "example" {
  ami           = "ami-08eb150f611ca277f"
  instance_type = "t2.micro"
  key_name      = aws_key_pair.example.key_name

  tags = {
    Name = "MyEC2Instance"
  }
}
  • key_name: This references the key pair created earlier. The instance will use this key pair for SSH authentication.

Using depends_on Attribute

The depends_on attribute explicitly defines dependencies between resources. Terraform usually handles dependencies automatically, but when you want to ensure one resource is created before another (especially for non-obvious dependencies), depends_on is useful.

Example: Enforcing a Dependency

In this example, we ensure that the EC2 instance only gets created after the key pair is successfully created:

resource "aws_instance" "example" {
  ami           = "ami-08eb150f611ca277f"
  instance_type = "t2.micro"
  key_name      = aws_key_pair.example.key_name

  depends_on = [
    aws_key_pair.example
  ]
}
  • depends_on: Ensures that Terraform creates the key pair before provisioning the EC2 instance, even if Terraform might otherwise try to parallelize the operations.

Resource Targeting in Terraform

Resource targeting is a feature in Terraform that allows users to focus on specific resources during a plan or apply operation. This is useful when you want to create, update, or destroy only certain parts of your infrastructure without affecting the rest. By using resource targeting, you can test changes in a specific area, troubleshoot issues, or apply configurations selectively.


Why Use Resource Targeting?

In large infrastructures managed by Terraform, it may be inefficient or risky to apply changes to all resources every time, especially when you only need to update one or a few. For instance:

  • Debugging: If a specific resource is causing issues, you can target it to apply a fix without modifying the entire infrastructure.

  • Incremental Changes: When testing incremental changes, applying only to targeted resources can prevent unintended changes to other components.

  • Speed: Targeting resources can speed up the process of applying changes in large environments by focusing only on necessary components.


How to Target Resources

In Terraform, you can use the -target option with both the plan and apply commands to specify which resources to focus on. Here's how:

1. Targeting During terraform plan

The -target flag can be used with terraform plan to preview changes for specific resources without considering other parts of the infrastructure.

terraform plan -target=aws_instance.my_instance

This command tells Terraform to generate a plan that only includes changes for the aws_instance.my_instance resource, ignoring other resources in the configuration.

2. Targeting During terraform apply

Similarly, you can target specific resources during the terraform apply command to actually apply the changes only to those resources.

terraform apply -target=aws_instance.my_instance

This ensures that Terraform applies changes only to the aws_instance.my_instance resource and leaves the rest of the infrastructure untouched.


Multiple Resources Targeting

You can target multiple resources by passing multiple -target flags in both the plan and apply commands.

terraform apply -target=aws_instance.my_instance -target=aws_s3_bucket.my_bucket

In this example, Terraform will only apply changes to the aws_instance.my_instance and aws_s3_bucket.my_bucket resources.


When Not to Use Resource Targeting

Although resource targeting is a powerful feature, it should be used cautiously. Some reasons to avoid over-reliance on targeting:

  • State Inconsistency: When used too frequently, targeting may lead to inconsistencies between Terraform's state and the actual deployed infrastructure.

  • Dependencies: Targeting specific resources may break dependencies or cause resources that rely on each other to be applied out of order, leading to failures or unpredictable behavior.

  • Partial Updates: Updating only some resources can leave the infrastructure in a partially updated state, which might not match the overall desired configuration.


Best Practices for Resource Targeting

  1. Use Sparingly: Use resource targeting only for specific cases like troubleshooting or minor incremental updates.

  2. Review the Plan: Always run terraform plan before applying changes to ensure that the changes align with expectations.

  3. Be Aware of Dependencies: Understand the dependencies between resources before targeting specific ones to avoid breaking the infrastructure.


Example

Suppose you have the following Terraform configuration:

resource "aws_instance" "my_instance" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
}

resource "aws_s3_bucket" "my_bucket" {
  bucket = "my-unique-bucket-name"
  acl    = "private"
}

If you only want to update the EC2 instance (aws_instance.my_instance) without affecting the S3 bucket (aws_s3_bucket.my_bucket), you can use resource targeting:

terraform apply -target=aws_instance.my_instance

Example: Using a Data Source to Retrieve an Existing AWS AMI

In this example, we use a data source to retrieve the ID of the most recent Amazon Machine Image (AMI) for Ubuntu.

data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"] # Canonical, the Ubuntu AMI owner ID

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
  }
}

resource "aws_instance" "example" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t2.micro"

  tags = {
    Name = "MyUbuntuInstance"
  }
}
  • data "aws_ami" "ubuntu": This data block queries AWS for the most recent Ubuntu AMI.

  • ami = data.aws_ami.ubuntu.id: The EC2 instance references the AMI ID retrieved by the data source.


Example: Using a Data Source to Query an Existing VPC

In this example, we retrieve information about an existing AWS VPC by querying it based on its name.

data "aws_vpc" "example" {
  filter {
    name   = "tag:Name"
    values = ["my-existing-vpc"]
  }
}

resource "aws_subnet" "example" {
  vpc_id     = data.aws_vpc.example.id
  cidr_block = "10.0.1.0/24"
}
  • data "aws_vpc" "example": This data source queries AWS for a VPC with the tag Name=my-existing-vpc.

  • vpc_id = data.aws_vpc.example.id: The aws_subnet resource uses the VPC ID retrieved from the data source.


Common Use Cases for Data Sources

  1. Using existing resources: Retrieve existing VPCs, AMIs, subnets, or other cloud resources without creating them.

  2. Cross-module data: Query information from resources in another module or workspace.

  3. Dynamic configuration: Dynamically set resource attributes based on external data, like fetching the latest AMI.

Managing State in Terraform

Terraform keeps track of the current state of your infrastructure in a state file. This file is essential for Terraform to know what resources exist, their configurations, and any changes required. Understanding Terraform state is crucial for maintaining accurate and reliable infrastructure.


Remote State

Remote state allows Terraform to store the state file in a remote, shared location (e.g., AWS S3, Azure Blob Storage) rather than locally. This is particularly useful for teams working collaboratively, as it enables a consistent and accessible state file.

  • Benefits: Centralized access, security controls, and automatic backups.

  • Setup Example (for AWS S3):

      terraform {
        backend "s3" {
          bucket = "my-terraform-state-bucket"
          key    = "path/to/my/state"
          region = "us-west-2"
        }
      }
    

State Locking

State locking is a mechanism to prevent multiple Terraform operations from running on the same state file simultaneously, avoiding conflicts and potential errors. Most remote backends (e.g., S3 with DynamoDB, Azure Blob Storage) support state locking automatically.

  • Benefit: Prevents race conditions, ensuring only one Terraform execution can modify the state file at any time.


Key Resource Lifecycle Management Options

Create Before Destroy

Terraform usually destroys old resources before creating new ones when updating. Create Before Destroy is a lifecycle configuration that tells Terraform to create a new resource before destroying the old one, ensuring uninterrupted availability.

  • Example:

      resource "aws_instance" "example" {
        ami           = "ami-12345678"
        instance_type = "t2.micro"
    
        lifecycle {
          create_before_destroy = true
        }
      }
    
  • Use Case: Maintaining uptime when updating or replacing critical resources.

Prevent Destroy

The prevent_destroy option in Terraform prevents accidental deletion of critical resources.

  • Example:

      resource "aws_instance" "example" {
        ami           = "ami-12345678"
        instance_type = "t2.micro"
    
        lifecycle {
          prevent_destroy = true
        }
      }
    
  • Use Case: Protects essential resources from accidental destruction, especially in production environments.


Ignore Changes

The ignore_changes lifecycle setting prevents Terraform from modifying specific attributes in response to configuration changes. This is useful if a field is managed outside Terraform but still needed in the configuration.

  • Example:

      resource "aws_instance" "example" {
        ami           = "ami-12345678"
        instance_type = "t2.micro"
        tags = {
          Name = "MyInstance"
        }
    
        lifecycle {
          ignore_changes = [tags]
        }
      }
    
  • Use Case: Useful for attributes managed manually or by other processes, where you don’t want Terraform to overwrite changes.


Tainting Resources

Tainting marks a resource for recreation on the next terraform apply. This is useful if a resource has become corrupt or needs a refresh without changing the configuration.

  • Example:

      terraform taint aws_instance.example
    
  • Use Case: Refreshing instances or resources that require replacement due to issues without modifying configurations.


Terraform Workspaces

Terraform workspaces allow you to manage multiple environments (e.g., dev, staging, production) within a single configuration, each with its own isolated state file.

  • Default Workspace: Terraform has a default workspace called “default.”

  • Creating and Switching Workspaces:

  •   terraform workspace new dev
      terraform workspace select dev
    
  • Use Case: Useful for managing infrastructure across multiple environments, as each workspace has its isolated state.

Terraform Import

The terraform import command allows you to import existing infrastructure (not originally created by Terraform) into your Terraform state file. This makes it easier to manage resources created outside of Terraform by bringing them under Terraform's control without recreating them.

  • Usage: You specify the resource type and identifier for the resource in your Terraform configuration, and then use the terraform import command to pull its current state.

  • Example:

      resource "aws_instance" "example" {
        # Configuration here
      }
    
    • To import an existing AWS EC2 instance with ID i-0abcd1234ef56789:

        bashCopy codeterraform import aws_instance.example i-0abcd1234ef56789
      
  • Use Case: Great for existing resources you want to start managing with Terraform without creating new ones.


Count Parameter

The count parameter in Terraform is used to create multiple instances of a resource or module with a single configuration block, providing an easy way to scale resources.

  • Example:

      resource "aws_instance" "example" {
        count         = 3
        ami           = "ami-12345678"
        instance_type = "t2.micro"
    
        tags = {
          Name = "Instance-${count.index}"
        }
      }
    
    • This configuration will create 3 instances of the EC2 instance, each with a unique Name tag like Instance-0, Instance-1, etc.
  • Use Case: Simplifies configuration when you need multiple identical resources, allowing easy scaling and a more compact configuration.


Terraform Modules

Modules in Terraform are reusable configurations that help organize and simplify code. By grouping related resources into modules, you can avoid redundancy, improve readability, and make your infrastructure code more modular.

Why Use Modules?

  1. Reusability: Once created, modules can be used across multiple projects.

  2. Consistency: Standardizes configurations and enforces best practices.

  3. Easier Management: Simplifies complex configurations by breaking them into smaller, manageable parts.

Creating a Basic Module

Let’s create a simple module for setting up an EC2 instance with a security group.

  1. Define the module: Create a folder for the module, e.g., modules/ec2_instance.

  2. Write the configuration:

    • modules/ec2_instance/main.tf

        variable "instance_type" {
          default = "t2.micro"
        }
      
        variable "ami_id" {
          description = "AMI ID for the instance"
        }
      
        resource "aws_instance" "web" {
          ami           = var.ami_id
          instance_type = var.instance_type
      
          tags = {
            Name = "ModuleInstance"
          }
        }
      
    • modules/ec2_instance/variables.tf

        variable "instance_type" {
          description = "Instance type"
          type        = string
          default     = "t2.micro"
        }
      
        variable "ami_id" {
          description = "AMI ID for the instance"
          type        = string
        }
      
  3. Using the Module in Your Configuration:

    • In your main configuration, use the module block to call the EC2 module.
    module "ec2_instance" {
      source        = "./modules/ec2_instance"
      ami_id        = "ami-12345678"
      instance_type = "t2.small"
    }

Complex Module Example

Modules can include multiple resources and use advanced logic, like conditional expressions and dynamic blocks, to create flexible infrastructure. For instance, a networking module could create a VPC, subnets, route tables, and security groups—all in one.

Terraform Registry Modules

The Terraform Registry hosts many pre-built modules that you can use directly. You simply reference the module from the registry in your configuration, and Terraform downloads it automatically.

  • Example:

      module "vpc" {
        source  = "terraform-aws-modules/vpc/aws"
        version = "3.0.0"
    
        name    = "my-vpc"
        cidr    = "10.0.0.0/16"
        azs     = ["us-west-1a", "us-west-1b"]
        public_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
      }
    

Summary of Terraform Modules

  • Modules are reusable groups of resources.

  • They improve code maintainability, consistency, and reduce duplication.

  • Modules can be custom-built or used from the Terraform Registry, enabling faster development and better infrastructure standards.


More from this blog

DevOps Journey with M Hassan

174 posts

I am writing these blogs because I recently completed a comprehensive DevOps course where I gained in-depth knowledge of the topics mentioned. As I progressed through the course, I realized the importance of having a concise and accessible resource to revise and reinforce my understanding of each topic. Therefore, I decided to create cheat sheets in the form of blog posts. These cheat sheets will not only serve as a handy reference for myself but also benefit others who are also interested in mastering DevOps concepts. By documenting each topic and providing concise explanations, I aim to create a valuable resource that simplifies complex concepts and facilitates hands-on practice. This way, I can solidify my own understanding while helping others on their DevOps journey.

A Comprehensive Guide to Terraform