Skip to main content

Command Palette

Search for a command to run...

Terraform Master Documentation

Published
10 min read

Terraform is a product by Hashicorp that uses Infrastructure as Code (IaC) to provision cloud infrastructure. With the right approach and resources, learning Terraform can be an exciting and rewarding experience, regardless of your background or familiarity with related concepts. In this getting started Terraform tutorial, we will cover all the basics (and more) of the Terraform workflow and show how to use Terraform step-by-step, enabling you to manage cloud infrastructure with IaC.

  1. Definition:

    Terraform is an IaC tool that allows users to provision and manage infrastructure resources across various cloud platforms and on-premises environments. It uses declarative configuration files, written in HashiCorp Configuration Language (HCL) or JSON, to define and automate the lifecycle of resources, ensuring predictability and consistency. Terraform’s extensible plugin-based architecture supports a wide range of providers, enabling seamless integration and management of diverse infrastructure environments.

  2. Purpose:

    Manage infrastructure with code to enable reproducibility and automation.

  3. Features:

    Infrastructure as Code: Write infrastructure in a declarative configuration language.

    Execution Plans: Preview changes before applying them.

    Resource Graph: Creates a dependency graph to determine resource creation order.

    Change Automation: Applies complex changes with minimal human interaction.

  4. Benefits:

    Consistency: Ensures the same infrastructure is provisioned every time.

    Version Control: Infrastructure configurations can be stored in version control systems.

    Scalability: Easily scale infrastructure up or down.

    Collaboration: Teams can collaborate on infrastructure as code.

    Automation: Reduces manual provisioning errors and effort.

  5. Integration:

    Terraform integrates with numerous providers and services including:

    Cloud Providers: AWS, Azure, Google Cloud, Oracle Cloud.

    Configuration Management Tools: Chef, Puppet, Ansible.

    Version Control Systems: GitHub, GitLab.

    CI/CD Tools: Jenkins, CircleCI, GitLab CI.

  6. Configuration Files:

    Terraform configuration files are the cornerstone of defining and managing infrastructure as code. These files are written in HashiCorp Configuration Language (HCL) or optionally JSON, and they describe the desired state of your infrastructure.

    File Extensions: .tf for HCL files, .tf.json for JSON files.

    Key Elements of Configuration Files

    1. Providers

    2. Resources

    3. Variables

    4. Outputs

    5. Modules

    6. Data Sources

    7. Locals

    8. Provisioners

Lets deep dive into each of them.

  1. Providers

    Definition: Providers are plugins that interact with various APIs and services. Each provider requires configuration to establish connections and define settings.

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

    Example: Configuring AWS as a provider.

     provider "aws" {
       region = "us-west-2"
       access_key = "your_access_key"
       secret_key = "your_secret_key"
     }
    
  2. Resources

    Definition: Resources define the components of your infrastructure, such as virtual machines, storage accounts, and networking components

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

    Example: Creating an AWS EC2 instance.

     resource "aws_instance" "example" {
       ami           = "ami-0c55b159cbfafe1f0"
       instance_type = "t2.micro"
    
       tags = {
         Name = "example-instance"
       }
     }
    
  3. Variables

    Definition: Variables allow parameterization of Terraform configurations to make them flexible and reusable.

    Types: String, Number, Boolean, List, Map, Object, Tuple

     variable "instance_type" {
       type    = string
       default = "t2.micro"
     }
    

    Example: Defining and using variables.

     variable "region" {
       type    = string
       default = "us-west-2"
     }
    
     provider "aws" {
       region = var.region
     }
    
  4. Outputs

    Definition: Outputs export values from your Terraform configuration. These can be used for referencing in other configurations or for display purposes.

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

    Example: Defining an output value.

     output "instance_id" {
     value = aws_instance.example.id
     }
    
     output "instance_public_ip" {
       value = aws_instance.example.public_ip
     }
    
  5. Modules

    Definition: Modules encapsulate multiple resources into reusable and manageable pieces. They allow you to create complex configurations by combining smaller components.

     module "vpc" {
       source = "./modules/vpc"
     cidr = "10.0.0.0/16"
     }
    

    Example: Using a module for VPC creation.

     module "vpc" {
       source = "./modules/vpc"
     cidr = "10.0.0.0/16"
     }
    
     module "web_server" {
       source = "./modules/web_server"
       instance_type = var.instance_type
       vpc_id = module.vpc.vpc_id
     }
    
  6. Data Sources

    Definition: Data sources allow Terraform to use information defined outside of Terraform, such as values from other resources or services.

     data "aws_ami" "example" {
       most_recent = true
    
       filter {
         name   = "name"
         values = ["amzn-ami-hvm-*-x86_64-gp2"]
       }
    
       owners = ["self"]
     }
    

    Example: Using a data source to get the latest AMI.

     data "aws_ami" "example" {
       most_recent = true
    
       filter {
         name   = "name"
         values = ["amzn-ami-hvm-*-x86_64-gp2"]
       }
    
       owners = ["amazon"]
     }
    
     resource "aws_instance" "example" {
     ami = data.aws_ami.example.id
       instance_type = "t2.micro"
     }
    
  7. Locals

    Definition: Local values assign names to expressions, allowing you to use them multiple times in your configuration without repeating the expression.

     locals {
       instance_type = "t2.micro"
     }
    

    Example: Defining local values.

     locals {
       instance_type = "t2.micro"
       ami_id = "ami-0c55b159cbfafe1f0"
     }
    
     resource "aws_instance" "example" {
       ami           = local.ami_id
       instance_type = local.instance_type
     }
    
  8. Provisioners

    Definition: Provisioners allow you to execute scripts or commands on a local machine or on a remote resource after it's created or destroyed.

    Types: local-exec, remote-exec.

     resource "aws_instance" "example" {
       provisioner "local-exec" {
         command = "echo ${self.public_ip} >> ip_addresses.txt"
       }
     }
    

    Example: Using a provisioner to run a local script.

     resource "aws_instance" "example" {
       ami           = "ami-0c55b159cbfafe1f0"
       instance_type = "t2.micro"
    
       provisioner "local-exec" {
         command = "echo ${self.public_ip} >> ip_addresses.txt"
       }
     }
    

    Best Practices for Configuration Files

    Organize Files: Use a logical file structure. Separate providers, resources, variables, outputs, and modules into different files.

    DRY Principle: Avoid repetition. Use variables, locals, and modules to keep configurations DRY (Don't Repeat Yourself).

    Use Version Control: Always version control your Terraform configurations to track changes and enable collaboration.

    Document Configurations: Clearly document what each file, module, and variable does.

    Use Remote State: For team collaboration and consistency, store Terraform state in a remote backend.

  9. Data Types:

    Terraform supports various data types to define and manage infrastructure more effectively. Understanding these data types is essential for writing robust and flexible Terraform configurations.

    1. String

      Definition: Represents textual data.

      Usage: Commonly used for names, IDs, paths, etc.

       variable "example_string" {
         type    = string
         default = "Hello, Terraform!"
       }
      
    2. Number

      Definition: Represents numeric values, either integers or floating-point.

      Usage: Used for quantities, counts, sizes, etc.

       variable "example_number" {
         type    = number
         default = 42
       }
      
    3. Boolean

      Definition: Represents true or false values.

      Usage: Used for enabling/disabling features, toggles, etc.

       variable "example_boolean" {
         type    = bool
         default = true
       }
      
    4. List

      Definition: An ordered sequence of values, all of the same data type.

      Usage: Used for multiple values of the same type, such as IP addresses, instance IDs, etc.

       variable "example_list" {
         type    = list(string)
         default = ["value1", "value2", "value3"]
       }
      
    5. Map

      Definition: A collection of key-value pairs, where keys are strings.

      Usage: Used for associative arrays, such as configurations, tags, etc.

       variable "example_map" {
         type = map(string)
         default = {
           key1 = "value1"
           key2 = "value2"
         }
       }
      
    6. Set

      Definition: An unordered collection of unique values, all of the same data type.

      Usage: Used for collections where order does not matter, but uniqueness does, such as security group rules.

       variable "example_set" {
         type    = set(string)
         default = ["value1", "value2", "value3"]
       }
      
    7. Object

      Definition: A collection of named attributes that each have their own type.

      Usage: Used for complex data structures with named attributes, such as resource configurations.

       variable "example_object" {
         type = object({
           id     = string
           count  = number
           active = bool
         })
         default = {
           id     = "example"
           count  = 10
           active = true
         }
       }
      
    8. Tuple

      Definition: An ordered sequence of values, each of which can have a different type.

      Usage: Used for fixed-length sequences where each element has a specific type.

       variable "example_tuple" {
         type    = tuple([string, number, bool])
         default = ["example", 42, true]
       }
      

      Terraform configurations often use complex structures involving multiple data types. Here's an example that combines several types:

       variable "app_config" {
       type = object({
           name     = string
           version  = string
           instances = number
           zones    = list(string)
           tags     = map(string)
         })
         default = {
           name     = "my-app"
           version  = "1.0"
           instances = 3
           zones    = ["us-west-2a", "us-west-2b"]
          tags     = {
            Name    = "my-app"
             Version = "1.0"
           }
         }
       }
       resource "aws_instance" "example" {
         count         = var.app_config.instances
         ami           = "ami-0c55b159cbfafe1f0"
         instance_type = "t2.micro"
         availability_zone = element(var.app_config.zones, count.index)
        tags = var.app_config.tags
       }
      
  10. State File

    The Terraform state file is a fundamental component of how Terraform operates. It tracks the current state of your infrastructure, mapping Terraform configurations to real-world resources.

    Definition:

    The state file is a JSON file that stores information about the infrastructure resources managed by Terraform.

    Purpose:

    It keeps track of resource metadata, helps with mapping resources to configuration files, and enables efficient updates by storing the current state of your infrastructure.

    Features and Benefits

    1. Resource Mapping

Feature: Maps Terraform resource names to actual resource IDs in the infrastructure.

Benefit: Ensures Terraform knows which resources it is managing, facilitating operations like apply and destroy.

  1. Plan and Apply Efficiency

Feature: Stores the state of resources to efficiently create plans and apply changes.

Benefit: Increases speed and accuracy by comparing the current state with the desired state.

  1. Dependency Management

Feature: Tracks dependencies between resources.

Benefit: Ensures resources are created or destroyed in the correct order.

  1. Drift Detection

Feature: Detects changes outside of Terraform.

Benefit: Alerts users to manual changes or configuration drift.

  1. Collaboration

Feature: When stored remotely, state files can be shared among team members.

Benefit: Facilitates collaboration and consistency across teams.

  1. Best Practices

Use Remote Backends

Store state files remotely to facilitate team collaboration and state locking.

Enable State Locking

Prevents concurrent operations that could corrupt the state file.

Encrypt State Files

Protect sensitive information by encrypting state files.

Version Control State Management Code

Use version control for Terraform configurations, but do not store state files in version control.

Regular Backups

Keep regular backups of state files to prevent data loss.

Use Workspaces for Multiple Environments

Use Terraform workspaces to manage different environments (e.g., dev, staging, production) within a single configuration.

  1. Remote Backend

    Remote backends allow Terraform to store the state file in a remote location, providing features such as collaboration, locking, and state management. Some commonly used remote backends include AWS S3, Azure Blob Storage, Google Cloud Storage, and Terraform Cloud.

    Benefits of Remote Backends

    1. Collaboration

      Teams can work together on the same Terraform configuration and state file.

    2. State Locking

      Prevents simultaneous operations that can lead to state corruption.

      Ensures only one user can modify the state at a time.

    3. Persistence and Availability

      State files are stored in a reliable and highly available storage service.

    4. Security

      State files can be encrypted and access-controlled.

    5. Centralized Management

      Easier to manage and track state files from a central location.

Configuring Remote Backends

AWS S3 Example

  1. Create an S3 Bucket

    Ensure the bucket is configured for versioning and server-side encryption.

  2. Configure Backend in Terraform

    Add backend configuration to the Terraform configuration file.

     terraform {
       backend "s3" {
         bucket         = "my-terraform-state-bucket"
         key            = "path/to/my/statefile"
         region         = "us-west-2"
         dynamodb_table = "my-lock-table"
         encrypt        = true
       }
     }
    
  3. Initialize Terraform

    Run terraform init to initialize the backend.

Detailed Configuration of Remote Backends

AWS S3 with DynamoDB for State Locking

  1. Create an S3 Bucket and DynamoDB Table

    Ensure the S3 bucket has versioning and server-side encryption enabled.

    Create a DynamoDB table with a primary key named LockID for state locking.

  2. Configure Backend

    Add the backend configuration to your Terraform configuration file.

     terraform {
       backend "s3" {
         bucket         = "my-terraform-state-bucket"
         key            = "path/to/my/statefile"
         region         = "us-west-2"
         dynamodb_table = "my-lock-table"
         encrypt        = true
       }
     }
    
  3. IAM Policies

    Ensure the IAM role or user Terraform uses has the appropriate permissions for S3 and DynamoDB.

    S3 Policy:

     {
       "Version": "2012-10-17",
       "Statement": [
         {
           "Effect": "Allow",
           "Action": [
             "s3:ListBucket",
             "s3:GetObject",
             "s3:PutObject"
           ],
           "Resource": [
             "arn:aws:s3:::my-terraform-state-bucket",
             "arn:aws:s3:::my-terraform-state-bucket/*"
           ]
         }
       ]
     }
    

    DynamoDB Policy:

     {
       "Version": "2012-10-17",
       "Statement": [
         {
           "Effect": "Allow",
           "Action": [
             "dynamodb:PutItem",
             "dynamodb:GetItem",
             "dynamodb:DeleteItem",
             "dynamodb:Scan"
           ],
           "Resource": "arn:aws:dynamodb:us-west-2:123456789012:table/my-lock-table"
         }
       ]
     }
    
  4. Initialize Terraform

    Run terraform init to initialize the backend.

Advanced State Management

  1. State Versioning

    Enable versioning in the remote storage backend to track state file changes over time.

  2. State Environments

    Use Terraform workspaces to manage different environments within the same configuration.

  3. State File Splitting

    Split state files by using multiple Terraform configurations or workspaces to manage large infrastructures.

  4. State Management Commands

    terraform state list: List resources in the state.

    terraform state show <resource>: Show detailed state for a resource.

    terraform state mv <source> <destination>: Move resources in the state.

    terraform state rm <resource>: Remove resources from the state.

    terraform state pull: Pull the current state.

    terraform state push: Push a state file to a remote backend.

    Example of Managing State

    Listing Resources

    terraform state list

    Showing Resource Details

    terraform state show aws_instance.example

    Moving Resources

    terraform state mv aws_instance.example aws_instance.new_example

    Removing Resources

    terraform state rm aws_instance.example

    By understanding and implementing these best practices and features of Terraform state files and remote backends, you can efficiently manage your infrastructure as code, enhance collaboration, and maintain the integrity and security of your state files.

    1. Modules

    Definition: Modules encapsulate multiple resources into reusable and manageable pieces. They allow you to create complex configurations by combining smaller components.

    Usuage:

    module "vpc" {
    source = "./modules/vpc"
    cidr = "10.0.0.0/16"
    }
    

    Example: Using a module for VPC creation.

    module "web_server" {
    source = "./modules/web_server"
    instance_type = var.instance_type
    vpc_id = module.vpc.vpc_id
    }
    

More from this blog

Mastering Terraform

19 posts