Get started with Terraform Providers

Photo by Sigmund on Unsplash

Get started with Terraform Providers

Terraform is a popular Infrastructure as Code (IaC) tool used for building, changing, and versioning infrastructure efficiently. Providers in Terraform are plugins that allow Terraform to interact with cloud providers, SaaS providers, and other APIs. Terraform configurations must declare which providers they require so that Terraform can install and use them. Additionally, some providers require configuration (like endpoint URLs or cloud regions) before they can be used.

To illustrate the role and place of providers in Terraform's architecture, below is a diagram that clearly delineates the different layers and components involved in Terraform's operation.

  1. User's Terraform Configuration Layer:

    • At the left corner of the diagram, you have the user’s Terraform configuration files (*.tf files).

    • This layer includes the Terraform code written by the user, defining the desired state of the infrastructure using resources and data sources.

  2. Terraform Core:

    • Next there is the Terraform configuration layer, depicted by the Terraform Core.

    • This is the heart of Terraform and handles the core functions like parsing configurations, building the resource dependency graph, and maintaining the state.

    • The Terraform Core communicates with the providers to implement the specifics of the resource types defined in the configurations.

  3. Providers Layer:

    • This layer represented as a set of different providers (e.g., AWS, Azure, Google Cloud, etc.).

    • This layer is crucial as it translates the actions from the Terraform Core into API calls to the respective services.

    • Each provider block connected to the Terraform Core, indicating the communication between them.

Overview

Here's a breakdown of key aspects of Terraform providers:

  1. Purpose: Providers in Terraform serve as a bridge between Terraform and the various services or APIs it manages. Each provider offers a series of resources that Terraform can manage.

  2. Variety of Providers: Terraform supports numerous providers for different services like AWS, Azure, Google Cloud, Kubernetes, etc. There are also providers for SaaS products and other services. The Terraform Registry is the main directory of publicly available Terraform providers, and hosts providers for most major infrastructure platforms.

  3. Resources and Data Sources: Providers define resources and data sources. Resources are elements that Terraform can directly manage (like a VM, a network, or a higher-level component). Data sources allow Terraform to fetch data from outside of Terraform to use in your configuration.

  4. Configuration: Each provider has its own configuration settings, including things like endpoint URLs, region settings, and authentication details. This configuration is done within Terraform files. The Provider Requirements page documents how to declare providers so Terraform can install them.

     # Specify the Terraform version
     terraform {
       required_providers {
         aws = {
           source  = "hashicorp/aws"
           version = "~> 3.0"
         }
       }
     }
    
     # Configure the AWS Provider
     provider "aws" {
       region     = "us-west-2"  # Example: Oregon region
       access_key = "my-access-key"  # Replace with your access key
       secret_key = "my-secret-key"  # Replace with your secret key
     }
    
     # Below this, you would define your resources, such as EC2 instances, S3 buckets, etc.
    
    Remember, in a production environment, you should handle sensitive values (like access_key and secret_key) more securely, possibly using environment variables, credentials file or secrets manager app like Vault.
  5. Versioning: Providers are versioned, and their versions can be specified in Terraform configurations. This ensures that your infrastructure does not break due to changes in a provider.

     terraform {
       required_providers {
         mycloud = {
           source  = "mycorp/mycloud"
           version = "~> 1.0"
         }
       }
     }
    
    💡
    In Terraform 0.13 and later, always declare provider version constraints in the required_providers block. The version argument will be removed in a future version of Terraform.
  6. Custom Providers: If a particular service or tool doesn't have an existing provider, Terraform allows for the creation of custom providers using Go programming language.

  7. Initialization: Providers must be initialized before they can be used. This is typically done using the terraform init command, which installs and configures the necessary providers based on your configuration files.

  8. Registry: Terraform Registry is a public directory of Terraform providers and modules. Users can publish their own custom providers here or use ones provided by others.

  9. Dependency Management by using Lock File: This feature was introduced in Terraform 0.14 and is a significant component of how Terraform manages dependencies, particularly provider versions and module versions. The lock file (terraform.lock.hcl) records the exact versions of providers and modules used in a Terraform configuration. This ensures that Terraform consistently uses the same versions every time it runs, across different environments and team members.

  10. State Management: Providers work with Terraform's state management to keep track of the resources they manage. This state allows Terraform to map real-world resources to your configuration, keep track of metadata, and improve performance for large infrastructures.

Terraform providers are essential components that enable Terraform to manage a wide variety of services and resources in a consistent and predictable manner. They encapsulate the APIs of service providers and expose resources and data sources that can be managed by Terraform configurations.

💡
Hands-on: Try the Perform CRUD Operations with Providers tutorial. To install the required version of the Terraform, use the open-source Terraform version manager tool tfenv. For more information, see the following guide: How to install "tfenv" Terraform version manager on Ubuntu OS

How to use Providers?

Using Terraform providers is a fundamental part of defining your infrastructure as code with Terraform. Here's a step-by-step guide on how to use Terraform providers:

  1. Choose the Required Providers:

    • Identify the cloud services or other platforms (like AWS, Azure, Google Cloud, etc.) you plan to use. Each of these services will have a corresponding Terraform provider.
  2. Specify Providers in Terraform Configuration:

    • In your Terraform configuration files (typically .tf files), specify the providers you need by using the provider block. This block configures the necessary provider plugins for your infrastructure.
    provider "aws" {
      region = "us-west-2"
    }
  • In this example, the AWS provider is configured with the region set to "us-west-2".
  1. Define Provider Version:

    • To ensure consistent behavior, it's recommended to specify the version of the provider you want to use. This is done in the required_providers block within the terraform block.
    terraform {
      required_providers {
        aws = {
          source  = "hashicorp/aws"
          version = "~> 3.0"
        }
      }
    }
  • This configuration pins the AWS provider to version 3.x.
  1. Initialize Providers:

    • Run terraform init in your project directory. This command initializes Terraform, downloads and installs the specified providers, and prepares your environment for further actions.
  2. Use Provider Resources and Data Sources:

    • Within your Terraform configuration, use the resources and data sources provided by the provider to define your infrastructure.
    resource "aws_instance" "example" {
      ami           = "ami-0f34c5ae932e6f0e4"
      instance_type = "t2.micro"
    }
  • This example creates an AWS EC2 instance using the AWS provider.
  1. Apply Configuration:

    • Run terraform plan to see an execution plan showing what Terraform will do based on your configuration.

    • Execute terraform apply to apply the changes and create the defined infrastructure.

  2. Manage Provider Authentication:

    • Configure authentication for your provider, typically using API tokens, access keys, or other credentials. This is often done via environment variables or configuration files for security reasons. For example:

        export AWS_ACCESS_KEY_ID="your_access_key"
        export AWS_SECRET_ACCESS_KEY="your_secret_key"
      
  3. Update Providers as Needed:

    • To update to a newer version of a provider, change the version in the required_providers block and run terraform init -upgrade.
  4. Use Multiple Provider Configurations:

    • If your infrastructure spans multiple services, you can define multiple provider blocks with different configurations by using alias.

        # The default provider configuration; resources that begin with `aws_` will use
        # it as the default, and it can be referenced as `aws`.
        provider "aws" {
          region = "us-east-1"
        }
      
        # Additional provider configuration for west coast region; resources can
        # reference this as `aws.west`.
        provider "aws" {
          alias  = "west"
          region = "us-west-2"
        }
      

By following these steps, you can effectively use Terraform providers to manage a wide range of infrastructure services and platforms. Providers are central to Terraform's ability to manage infrastructure resources in a cloud-agnostic way.

Specifying Provider Requirements

Specifying provider requirements in Terraform is an important step to ensure that your infrastructure is managed with the correct versions of the Terraform providers. This is crucial for maintaining consistency, predictability, and compatibility in your infrastructure management.

Here's an overview of how to specify provider requirements in Terraform:

  1. Purpose of Specifying Provider Requirements:

    • It locks the configuration to specific versions of providers, preventing unintended updates that could introduce breaking changes.

    • It ensures that every member of your team, as well as any automation or CI/CD pipelines, uses the same provider versions, avoiding "it works on my machine" problems.

  2. Using the required_providers Block:

    • Provider requirements are specified in the required_providers block within a terraform block in your Terraform configuration files.

    • This block lists each provider your configuration will use, along with version constraints for each provider.

  3. Example of a required_providers Block:

     terraform {
       required_providers {
         aws = {
           source  = "hashicorp/aws"
           version = "~> 3.0"
         }
         azurerm = {
           source  = "hashicorp/azurerm"
           version = "~> 2.0"
         }
       }
     }
    
    • In this example, two providers (AWS and AzureRM) are specified with version constraints.

    • The source attribute specifies the provider's source address, usually from HashiCorp's public Terraform Registry.

    • The version attribute specifies the version constraint for the provider. The ~> syntax is a version constraint operator that allows minor updates. For example, ~> 3.0 means any version in the 3.x range, but less than 4.0.

      Do not use ~> (or other maximum-version constraints) for modules you intend to reuse across many configurations, even if you know the module isn't compatible with certain newer versions. Doing so can sometimes prevent errors, but more often it forces users of the module to update many modules simultaneously when performing routine upgrades. Specify a minimum version, document any known incompatibilities, and let the root module manage the maximum version.
  4. Version Constraints:

    • Terraform supports several version constraint operators:

      • =: Specifies an exact version.

      • !=: Specifies a version to exclude.

      • >, <, >=, <=: Specifies greater than, less than, greater than or equal to, and less than or equal to, respectively.

      • ~>: The pessimistic constraint operator, which allows only the rightmost version component to increment. For example:

        • ~> 1.0.4: Allows Terraform to install 1.0.5 and 1.0.10 but not 1.1.0.

        • ~> 1.1: Allows Terraform to install 1.2 and 1.10 but not 2.0.

    • You can combine multiple constraints to fine-tune your version requirements.

  5. Version Lock File:

    • When you run terraform init, Terraform creates a terraform.lock.hcl file in your working directory. The lock file is always named .terraform.lock.hcl, and this name is intended to signify that it is a lock file for various items that Terraform caches in the .terraform subdirectory of your working directory.

    • This file locks the exact versions of the providers Terraform has installed, ensuring that the same versions are used whenever you or your team members run Terraform. You should include this file in your version control repository so that you can discuss potential changes to your external dependencies via code review, just as you would discuss potential changes to your configuration itself.

Specifying provider requirements is a key part of managing infrastructure as code using Terraform, ensuring that the infrastructure is reliably and consistently managed across different environments and over time.

Dependency Lock File

The Dependency Lock File in Terraform is a key feature for ensuring consistent and reproducible builds by locking the versions of providers and external modules used in a Terraform project. Here's an in-depth explanation:

What is a Dependency Lock File?

  • File Name: The lock file is named terraform.lock.hcl.

  • Purpose: It records the specific versions of each provider (and module, where applicable) that Terraform has installed. This ensures that Terraform will use the same versions every time it runs, providing consistency across different environments and among team members.

    💡
    At present, the dependency lock file tracks only provider dependencies. Terraform does not remember version selections for remote modules, and so Terraform will always select the newest available module version that meets the specified version constraints. You can use an exact version constraint to ensure that Terraform will always select the same module version.

How is it Generated and Updated?

  • Creation: The lock file is automatically generated or updated when you run terraform init. If a particular terraform init call makes changes to the lock file, Terraform will mention that as part of its output:

      Terraform has made some changes to the provider dependency selections recorded
      in the .terraform.lock.hcl file. Review those changes and commit them to your
      version control system if they represent changes you intended to make.
    

    When you see this message, you can use your version control system to review the changes Terraform has proposed in the file, and if they represent changes you made intentionally you can send the change through your team's usual code review process.

  • Version Locking: When you add a new provider or change a version constraint in your configuration and then run terraform init, Terraform updates the lock file to reflect the exact versions of providers that match your specified constraints.

  • Manual Editing: It's not recommended to manually edit this file since it is auto-generated and managed by Terraform.

What's Inside the Lock File?

  • Provider Versions: The file includes a detailed list of the providers used in the configuration, along with their exact versions.

  • Platform Information: For each provider, the file may include hashes for multiple platforms (like Linux, Windows, etc.). This ensures that the same provider versions are used, regardless of the operating system.

  • Checksum verification: Terraform will also verify that each package it installs matches at least one of the checksums it previously recorded in the lock file, if any, returning an error if none of the checksums match:

      Error: Failed to install provider
    
      Error while installing hashicorp/azurerm v2.1.0: the current package for
      registry.terraform.io/hashicorp/azurerm 2.1.0 doesn't match any of the
      checksums previously recorded in the dependency lock file.
    

    This checksum verification is intended to represent a trust on first use approach. When you add a new provider for the first time you can verify it in whatever way you choose or any way you are required to by relevant regulations, and then trust that Terraform will raise an error if a future run of terraform init encounters a non-matching package for the same provider version.

Why is it Important?

  1. Consistency: It guarantees that the same provider versions are used every time Terraform runs, which is crucial for consistent infrastructure management.

  2. Reproducibility: It ensures that when your Terraform configuration is checked out and used in a different environment (like a different developer's machine or a CI/CD pipeline), it uses the same versions, avoiding any surprises or inconsistencies.

  3. Controlled Upgrades: It allows controlled and intentional upgrades of providers. You can update to newer versions of providers by modifying the version constraints and re-running terraform init, which updates the lock file.

  4. Team Collaboration: In a team environment, committing this file to version control means that every team member will use the same provider versions, reducing "works on my machine" problems.

How Does it Work with Version Control?

  • Committing to VCS: The terraform.lock.hcl file should be committed to your version control system. This ensures that anyone who checks out the code will use the same provider versions.

  • Cross-Platform Compatibility: Since it includes hashes for multiple platforms, it is suitable for teams working across different operating systems.

Best Practices

  • Regularly Update: Regularly update the provider versions in your configuration and the lock file to benefit from new features, bug fixes, and security patches.
    If you run terraform init -upgrade to ask Terraform to consider newer provider versions that still match the configured version constraints, Terraform may then select a newer version for a provider and update its existing provider block to reflect that change. The primary effect of selecting a new provider version is to change the value of version in the provider block. If the upgrade came along with a change to the configured version constraints, Terraform will also record that change in the constraints value.

  • Review Changes: When the lock file changes, review these changes before committing them to version control, especially in team environments.

The Dependency Lock File is a critical component of Terraform's infrastructure as code paradigm, ensuring consistent and reproducible management of your cloud resources and services. It helps mitigate issues arising from version discrepancies, thereby aiding in smoother collaboration and more reliable deployment pipelines.

💡
Hands-on: Try the Lock and Upgrade Provider Versions tutorial on HashiCorp Learn. In this tutorial, you will create a S3 bucket from an initialized Terraform configuration. Then, you will update the Terraform dependency lock file to use the latest version of the AWS provider, and edit the Terraform configuration to conform to the new provider version's requirements.

Terraform CLI commands to manage Providers

Terraform's Command Line Interface (CLI) offers several commands that are specifically related to managing providers. Here's a list of the key commands along with their purposes:

  1. terraform init:

    • Purpose: Initializes a Terraform working directory by installing the necessary providers as specified in the configuration.

    • Usage:

        terraform init [options]
      
    • Details: This is usually the first command you run for any new or updated Terraform configuration. It also automatically downloads and installs the providers defined in the required_providers block.

  2. terraform providers:

    • Purpose: Displays information about the providers required for the current configuration.

    • Usage:

        terraform providers
      
    • Details: This command lists all the providers that are required for the current configuration, along with their versions. It's useful for quickly understanding the provider dependencies of your Terraform project.

  3. terraform get:

    • Purpose: Downloads and updates modules for the configuration mentioned in the root module.

    • Usage:

        terraform get [options]
      
    • Details: While this command is more focused on modules than providers, it's relevant because modules can also specify provider requirements.

  4. terraform apply:

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

    • Usage:

        terraform apply [options] [plan file]
      
    • Details: During the apply phase, Terraform will use the configured providers to create, update, or delete resources. While it's not exclusively a provider command, it's where the configurations set for the providers are executed.

  5. Upgrading Providers:

    • Command: terraform init -upgrade

    • Purpose: Upgrades the providers to the latest versions allowed within the version constraints specified in the configuration.

    • Usage:

        terraform init -upgrade
      
    • 💡
      You can also use the -upgrade flag to downgrade the provider versions if the version constraints are modified to specify a lower provider version.
  6. Provider Lock File Commands:

    • While not a specific command, any changes to provider version constraints in the Terraform configuration followed by terraform init will update the dependency lock file (terraform.lock.hcl), locking in the new versions.
  7. Debugging Providers:

    • Command: Setting the TF_LOG and TF_LOG_PROVIDER environment variables.

    • Purpose: Provides detailed logs for debugging, including provider-related operations.

    • Usage:

        # set environment variables
        export TF_LOG=”DEBUG”
        export TF_LOG_PROVIDER=WARN
        # set path to store logs
        export TF_LOG_PATH="/your-path/to-log/file.log"
      
    • Details: This isn't a Terraform command per se, but setting these environment variables can be incredibly helpful for troubleshooting issues with providers.

Remember, the actual effect of these commands and the necessity to run them can depend on the specifics of your Terraform configuration and the providers you are using. Always refer to the official Terraform documentation for the most up-to-date and detailed information.

💡
Hands-on: Try the Manage Terraform versions tutorial on HashiCorp Learn. In this tutorial, you will update an existing configuration to use the latest version of Terraform and learn how to manage different versions of Terraform within a team.

References:

  1. Terraform Providers

  2. Terraform Providers Overview & How To Use Them

  3. Tutorial: Manage Terraform versions

  4. Dependency Lock File

  5. Lock and upgrade provider versions

  6. Terraform Core

  7. Writing Custom Terraform Providers

  8. The Terraform AWS Provider – Authenticate Terraform to AWS

  9. Provider Configuration

  10. Version Constraints

  11. How to Debug & Troubleshoot Terraform Projects: Tutorial

  12. Experimental Language Features