[Virtual Event] Unifying infra and app promotions with Spacelift, OpenTofu, and Kargo

Sign up ➡️

Terraform

9 Terraform Use Cases (With Code Examples)

terraform use cases

Terraform provisions and manages infrastructure through declarative code. The harder question is where it earns its place, and where another tool does the job better.

This article covers nine Terraform use cases with a working example for each: single cloud, multicloud, Kubernetes, high availability and disaster recovery, consistent environments, CI/CD, policy as code, tool integrations, and infrastructure orchestration.

All examples were tested against Terraform 1.15.x, AWS provider 6.x, azurerm provider 4.x, and Helm provider 3.x.

What is Terraform?

Terraform is one of the most used infrastructure as code (IaC) tools. It allows IT professionals to define their infrastructure as code and automates the deployment and management of infrastructure across multiple cloud providers and services.  If you are looking for a reliable and efficient way to manage your infrastructure’s lifecycle, from provisioning and compliance to resource management, Terraform does that job.

Terraform shipped under the Mozilla Public License 2.0 until August 10, 2023, when HashiCorp moved it to the Business Source License 1.1. Terraform is source-available today, not open source. IBM completed its acquisition of HashiCorp on February 27, 2025.

If you need an OSI-licensed tool, OpenTofu forked from the last MPL-licensed Terraform release and is maintained under the Linux Foundation. The current Terraform release line is 1.15.

Terraform use cases

infographic showing terraform use cases

What are Terraform’s use cases?

  1. Single cloud deployment: provisioning resources on one provider
  2. Multicloud deployment: managing AWS and Azure from one configuration
  3. Kubernetes: provisioning clusters and deploying into them
  4. High availability and disaster recovery across regionss
  5. Consistent environments: reusing one module across dev, stage, and prod
  6. Generic CI/CD pipelines
  7. Policy as code: blocking noncompliant resources before apply
  8. Multi-infrastructure tools integrations
  9. Infrastructure orchestration with Spacelift

1. Single cloud deployment

With Terraform, it is very easy to deploy resources inside your cloud provider. You just need to navigate to the documentation related to your cloud provider and declare a provider configuration for it to handle authentication. Then, you can easily create your resources.

Multiple modules are available to get you started. You can browse them in the registry if you don’t want to write much code yourself.

Here is a very basic example that handles the creation of a VPC in AWS:

provider "aws" {
 region = "eu-west-1"
}

resource "aws_vpc" "my_vpc" {
 cidr_block = "10.0.0.0/16"
}

2. Multicloud deployment

You can use Terraform to make multicloud deployments in the same state file. You simply repeat the same steps for the second cloud provider.  Multicloud reduces vendor lock-in, improves redundancy, and gives you cost leverage across providers.

However, a single state file spanning two clouds couples their blast radius. A failed apply can leave resources in both providers mid-change. Split state per provider once the configuration grows past a handful of resources.

Here is an example that handles the creation of a VPC in AWS and a virtual network in Azure:

terraform {
  required_version = ">= 1.15"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.0"
    }
  }
}


provider "aws" {
 region = "eu-west-1"
}

resource "aws_vpc" "my_vpc" {
 cidr_block = "10.0.0.0/16"
}

provider "azurerm" {
 features {}
}

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

resource "azurerm_virtual_network" "example" {
 name                = "example-network"
 location            = azurerm_resource_group.example.location
 resource_group_name = azurerm_resource_group.example.name
 address_space       = ["10.0.0.0/16"]
}

Read more: Multi-Cloud Provisioning and Management with Terraform

3. Handling Kubernetes deployments

Terraform has dedicated providers for both Kubernetes and Helm. This means you can spawn a Kubernetes cluster with Terraform, and you also have various ways to configure resources on that cluster. 

Let’s take a look at an example that spawns an EKS cluster and then install ArgoCD in it:

terraform {
  required_version = ">= 1.15"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
    helm = {
      source  = "hashicorp/helm"
      version = "~> 3.2"
    }
  }
}

resource "aws_eks_cluster" "main" {
  name     = "main-eks-cluster"
  role_arn = aws_iam_role.eks_cluster.arn

  vpc_config {
    subnet_ids = var.subnet_ids
  }

  tags = {
    Name = "main-eks-cluster"
  }
}

data "aws_eks_cluster_auth" "main" {
  name = aws_eks_cluster.main.name
}


provider "helm" {
  kubernetes = {
    host                   = aws_eks_cluster.main.endpoint
    token                  = data.aws_eks_cluster_auth.main.token
    cluster_ca_certificate = base64decode(aws_eks_cluster.main.certificate_authority[0].data)
  }
}

resource "helm_release" "argocd" {
  name             = "argocd"
  repository       = "https://argoproj.github.io/argo-helm"
  chart            = "argo-cd"
  version          = "10.2.2"
  namespace        = "argocd"
  create_namespace = true

  set = [
    {
      name  = "server.service.type"
      value = "LoadBalancer"
    },
    {
      name  = "server.service.annotations.service\\.beta\\.kubernetes\\.io/aws-load-balancer-type"
      value = "nlb"
    }
  ]
}

The Helm provider takes its connection details from the EKS cluster resource, so ArgoCD deploys into that cluster once it exists. Note the syntax: Helm provider 3.0 moved kubernetesfrom a block to a nested object andset from repeated blocks to a list. Configurations written for 2.x fail on 3.x.

Read also: Terraform vs. Helm

4. Implementing high-availability (HA) and disaster-recovery (DR) configurations

Terraform builds HA and DR architectures by placing resources across availability zones and regions, so services stay up when a zone or a whole region fails.

Here is a DR example that shows how to create instances in different regions:

terraform {
  required_version = ">= 1.15"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
  }
}

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

resource "aws_instance" "primary" {
  ami           = var.primary_ami_id
  instance_type = "t2.micro"

  tags = {
    Name = "primary-instance"
  }
}

resource "aws_instance" "dr" {
  region        = "us-east-1"
  ami           = var.dr_ami_id
  instance_type = "t2.micro"

  tags = {
    Name = "dr-instance"
  }
}

AWS provider 6.0 added a region argument to most resources, so cross-region topologies no longer need aliased provider blocks. If you are pinned below 6.0, use provider "aws" { alias = "dr" } and reference it with provider = aws.dr instead.

Learn more: How to Implement Terraform Disaster Recovery

5. Consistent environments

Terraform modules let you replicate one configuration across environments and drive the differences through inputs.

I have built a very simple module that provisions one EC2 instance and has configurable parameters for the ami_id and instance_type:

resource "aws_instance" "this" {
 ami           = var.ami_id
 instance_type = var.instance_type
}

variable "ami_id" {
 type    = string
 default = "ami"
}

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

Now, I can leverage this module in different configurations:

  • Dev environment:
# dev/main.tf
module "instance_dev" {
 source = "../"
}
  • Stage environment:
# stage/main.tf
module "instance_stage" {
 source        = "../"
 instance_type = "t3.micro"
}
  • Prod environment
# prod/main.tf
module "instance_prod" {
  source        = "../"
  instance_type = "t3.medium"
}

The instance types differ per environment, but the code path and the machine image are identical, which is what keeps the environments consistent.

6. Generic CI/CD pipelines

Terraform integrates with many tools to create a better workflow. Natively, its format and validate commands help you do the continuous integration part. With these options, you can check if the code respects the linting standards and is valid.

For the CI part, add a misconfiguration scanner before code reaches the main branch. Trivy and Checkov are the two actively maintained options. tfsec was folded into Trivy and receives no new checks, and Terrascan was archived in November 2025, so treat both as legacy.

Example GitHub Actions pipeline that checks formatting, initializes, validates, scans with Trivy, and runs a plan:

name: Terraform CI Pipeline

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  terraform:
    name: Terraform Checks
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v6

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v4
        with:
          terraform_version: 1.15.8
          terraform_wrapper: false

      - name: Terraform format check
        run: terraform fmt -check -recursive

      - name: Terraform init
        run: terraform init -backend=false

      - name: Terraform validate
        run: terraform validate

      - name: Scan for misconfigurations
        uses: aquasecurity/trivy-action@v0.36.0
        with:
          scan-type: config
          scan-ref: .
          severity: HIGH,CRITICAL
          exit-code: '1'

      - name: Terraform plan
        run: terraform plan -no-color -input=false

The pipeline can be accommodated to do the CD part as well. In my example, I will run apply only when there is a merge to the main branch:

     - name: Terraform apply
        if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        run: terraform apply -auto-approve -input=false

7. Policy as code

Policy as code should go hand in hand with CI/CD pipelines. Use OPA (Rego) or HashiCorp Sentinel to define policies and evaluate them before apply.Policies help with governance and compliance so you can restrict resources or resource parameters to ensure your organization’s requirements are respected.

Here is a Rego policy that denies any instance type other than t3.micro. Save it as restrict_instance_type.rego:

package terraform

import rego.v1

deny contains msg if {
	some resource in input.resource_changes
	resource.type == "aws_instance"
	resource.change.after.instance_type != "t3.micro"
	msg := sprintf("EC2 instance %s has an invalid instance type: %s", [resource.address, resource.change.after.instance_type])
}

OPA 1.0 made if and contains mandatory, so the older deny[msg] { ... } form no longer parses. The import rego.v1 line is a no-op on OPA 1.0 and later, but keeping it means the policy also runs on pre-1.0 runtimes, which matters if you have policy sets pinned to an older OPA version.

Now, we will need to evaluate this against an input, so we will need to build a terraform plan in a JSON format. I will use the above example with the primary and dr instances.

terraform plan -out=tfplan.binary                                      
terraform show -json tfplan.binary > tfplan.json

Now, let’s run the OPA policy against our plan and see what happens:

opa eval -i tfplan.json -d restrict_instance_type.rego "data.terraform.deny"{
 "result": [
   {
     "expressions": [
       {
         "value": [
           "EC2 instance aws_instance.dr has an invalid instance type: t2.micro",
           "EC2 instance aws_instance.primary has an invalid instance type: t2.micro"
         ],
         "text": "data.terraform.deny",
         "location": {
           "row": 1,
           "col": 1
         }
       }
     ]
   }
 ]
}

Our instances have t2.micro types, so you can see they have an invalid instance type because we only allow t3.micro instances.

To learn more, check out How to Use Open Policy Agent (OPA) with Terraform.

8. Multi-infrastructure tools integrations

Terraform provisions the infrastructure; Ansible configures what runs on it; Kubernetes and Helm handle the workloads. Wiring them together means one pipeline delivers infrastructure and application in a single pass.

The usual handoff is a Terraform output feeding the next tool. Export the instance IPs Terraform created, then hand them to an Ansible inventory:

output "web_server_ips" {
  value = aws_instance.web[*].public_ip
}
terraform output -json web_server_ips | jq -r '.[]' > inventory.ini
ansible-playbook -i inventory.ini configure-web.yml

You can see examples of this in the following posts:

9. Infrastructure management integration

Terraform handles every use case above. What it does not handle is the workflow around them: who approves an apply, what runs when a pull request opens, where state lives, how credentials are issued, and how policy gets enforced without a hand-built pipeline. That is the gap Spacelift fills.

With Spacelift you can:

  • Run Terraform through a purpose-built CI/CD workflow instead of a generic pipeline you maintain yourself.
  • Combine Terraform with Ansible, Kubernetes, CloudFormation, and Pulumi in one stack, passing outputs between them.
  • Enforce policy as code with OPA: restrict resource types and parameters, set how many approvals an apply requires, decide what happens when a pull request opens or merges, and control where notifications go.
  • Issue dynamic, short-lived cloud credentials instead of storing static keys.
  • Detect drift on a schedule and reconcile it before production surfaces it.
  • Give developers self-service infrastructure through Golden Paths, with your guardrails attached.
  • Use Spacelift Intelligence and Intent to provision from a plain-language request while your policies still apply.

Every example in this article runs the same way inside Spacelift. The difference is that the approvals, state, credentials, and policy checks come with the platform instead of being scripted into YAML you own.

terraform use cases examples

Key points

Terraform covers all nine use cases in this article. The workflow around them is where teams spend their time: approvals, state, credentials, drift, and policy. Spacelift handles that layer so you are not maintaining a generic CI/CD pipeline to do infrastructure work it was never designed for.

If you want to learn more about Spacelift, create a free account today, or book a demo with one of our engineers.

Automate Terraform deployments with Spacelift

Automate your infrastructure provisioning and build more complex workflows based on Terraform using policy as code, programmatic configuration, context sharing, drift detection, resource visualization, and many more.

Learn more

Frequently asked questions

  • What is Terraform mainly used for?

    Terraform is mainly used to provision and manage cloud infrastructure using code. It lets teams define infrastructure like servers, networks, and storage in declarative configuration files, which can be versioned and automated. Terraform supports multiple cloud providers and enables consistent, repeatable deployments across environments.

  • Is Terraform scripting or coding?

    Terraform uses a declarative configuration language called HCL. It is a configuration, not a general-purpose scripting language.

  • Is Terraform a frontend or a backend tool?

    Terraform is a CLI for infrastructure automation. It is not a frontend or backend application.

  • Can Terraform manage more than one cloud at once?

    Yes. Declare a provider per cloud in the same configuration and Terraform manages resources across all of them in one plan and apply. Keeping multiple clouds in a single state file couples their blast radius, so split state per provider as the configuration grows.

Terraform State at Scale

Get the three-stage maturity model
and a quick-reference checklist
for your platform team.

terraform state at scale bottom overlay
Share your data and download the guide