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:
Download the Terraform ZIP file from the Terraform website.
Extract the ZIP file and move the
terraform.exefile to a directory included in your system's PATH (e.g.,C:\Windows).Open a command prompt and run
terraform -versionto verify the installation.
macOS:
Install Terraform using Homebrew by running the following command:
brew install terraformAfter installation, verify by running:
terraform -version
Linux:
Download the Terraform package for Linux from the Terraform website.
Unzip the package and move the
terraformbinary to/usr/local/bin:unzip terraform_<version>_linux_amd64.zip sudo mv terraform /usr/local/bin/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

Initialize your Terraform working directory:
terraform initCreate a plan to review changes:
terraform planApply the changes:
terraform applyIf 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-varflag, or by specifying a separate.tfvarsfile.

* Example of applying a configuration with a
variables.tffile: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 ofterraform planorterraform 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_typeto 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_countto be a numeric value like1,2, or3.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_loggingto be eithertrueorfalse.
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_zonesto 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_tagsto 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_configvariable must contain values forengine,version, andinstances, 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_infovariable 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
| Type | Example | Description |
string | "us-west-1" | Single line of text |
number | 3 | Integer or float |
bool | true or false | Boolean |
any | Any data type | Accepts any data type |
list | list(string) | Ordered collection of a single type |
map | map(string) | Key-value pairs with unique keys |
object | object({ key1 = string, key2 = number}) | Collection with named attributes |
tuple | tuple([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 applyorterraform 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 = trueto 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
- Creating a Key Pair: You can create an AWS EC2 Key Pair using the
aws_key_pairresource. 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.
- 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
Use Sparingly: Use resource targeting only for specific cases like troubleshooting or minor incremental updates.
Review the Plan: Always run
terraform planbefore applying changes to ensure that the changes align with expectations.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_subnetresource uses the VPC ID retrieved from the data source.
Common Use Cases for Data Sources
Using existing resources: Retrieve existing VPCs, AMIs, subnets, or other cloud resources without creating them.
Cross-module data: Query information from resources in another module or workspace.
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.exampleUse 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 devUse 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 importcommand 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
Nametag likeInstance-0,Instance-1, etc.
- This configuration will create 3 instances of the EC2 instance, each with a unique
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?
Reusability: Once created, modules can be used across multiple projects.
Consistency: Standardizes configurations and enforces best practices.
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.
Define the module: Create a folder for the module, e.g.,
modules/ec2_instance.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 }
Using the Module in Your Configuration:
- In your main configuration, use the
moduleblock to call the EC2 module.
- In your main configuration, use the
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.




