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

What are Terraform’s use cases?
- Single cloud deployment: provisioning resources on one provider
- Multicloud deployment: managing AWS and Azure from one configuration
- Kubernetes: provisioning clusters and deploying into them
- High availability and disaster recovery across regionss
- Consistent environments: reusing one module across dev, stage, and prod
- Generic CI/CD pipelines
- Policy as code: blocking noncompliant resources before apply
- Multi-infrastructure tools integrations
- 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"]
}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=falseThe 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=false7. 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.jsonNow, 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.ymlYou 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.

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.
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.

