Terraform Cloud vs Self-Managed — Remote Execution and Governance
Running Terraform from laptops and self-hosted CI works until your team hits 10 engineers and 20 state files. At that point, you spend more time managing the Terraform workflow than the infrastructure itself — who has access to which state, where are credentials stored, how do you enforce policies, and who approved that apply? Terraform Cloud (TFC) is HashiCorp's answer: a managed platform that handles state, execution, policies, and collaboration. But it comes with trade-offs in cost, flexibility, and vendor lock-in.
What Terraform Cloud Provides
Terraform Cloud is a SaaS platform that replaces several things you would otherwise build yourself:
| Feature | Self-Managed Equivalent | TFC Advantage |
|---|---|---|
| Remote state | S3 + DynamoDB | Built-in, zero setup |
| Remote execution | GitHub Actions / GitLab CI | Consistent environment, no runner management |
| Sentinel policies | OPA + Conftest | Integrated into plan/apply lifecycle |
| Private module registry | Git tags + internal docs | Versioned, searchable, with docs |
| Cost estimation | Infracost (third-party) | Built into plan output |
| Team management | Cloud IAM + CI permissions | Workspace-level access control |
| VCS integration | Webhooks + CI config | One-click setup |
| Run triggers | CI pipeline dependencies | Cross-workspace orchestration |
Terraform Cloud vs Enterprise vs Self-Managed
| Capability | Self-Managed (OSS) | TF Cloud Free | TF Cloud Team | TF Cloud Business | TF Enterprise |
|---|---|---|---|---|---|
| Remote state | Manual (S3/GCS) | Yes (unlimited) | Yes | Yes | Yes |
| Remote execution | CI/CD pipeline | Yes (limited) | Yes | Yes | Yes |
| Sentinel policies | OPA/Conftest | No | No | Yes | Yes |
| Cost estimation | Infracost | No | Yes | Yes | Yes |
| Private registry | Git repos | Yes | Yes | Yes | Yes |
| Team management | IAM/RBAC | No | Yes | Yes | Yes |
| SSO/SAML | Manual | No | No | Yes | Yes |
| Audit logging | CI logs | No | No | Yes | Yes |
| Self-hosted agents | N/A | No | No | Yes | Yes |
| Air-gapped install | N/A | No | No | No | Yes |
| Concurrent runs | Unlimited (your infra) | 1 | 3+ | Unlimited | Unlimited |
| Price | Your time + infra | Free | ~$20/user/month | Custom | Custom |
Workspace Management
In TFC, a workspace is the equivalent of a state file + configuration + variables. Each workspace has its own state, variable set, and access controls:
# Using the TFC provider to manage workspaces as code
resource "tfe_workspace" "production_api" {
name = "production-api"
organization = "my-company"
terraform_version = "1.7.0"
working_directory = "services/api"
vcs_repo {
identifier = "my-company/infrastructure"
branch = "main"
oauth_token_id = var.github_oauth_token_id
}
tag_names = ["production", "api", "aws"]
}
resource "tfe_variable" "aws_region" {
key = "AWS_DEFAULT_REGION"
value = "us-east-1"
category = "env"
workspace_id = tfe_workspace.production_api.id
}
resource "tfe_variable" "aws_access_key" {
key = "AWS_ACCESS_KEY_ID"
value = var.aws_access_key_id
category = "env"
sensitive = true
workspace_id = tfe_workspace.production_api.id
}
Variable sets let you share common variables across multiple workspaces:
resource "tfe_variable_set" "aws_credentials" {
name = "AWS Production Credentials"
organization = "my-company"
}
resource "tfe_variable_set_variable" "aws_key" {
variable_set_id = tfe_variable_set.aws_credentials.id
key = "AWS_ACCESS_KEY_ID"
value = var.aws_access_key_id
category = "env"
sensitive = true
}
# Attach to multiple workspaces
resource "tfe_workspace_variable_set" "production" {
for_each = toset([
tfe_workspace.production_api.id,
tfe_workspace.production_web.id,
tfe_workspace.production_db.id,
])
variable_set_id = tfe_variable_set.aws_credentials.id
workspace_id = each.value
}
VCS Integration
Connect your Git repository, and TFC automatically triggers runs when you push:
Developer pushes to feature branch
→ TFC detects change in working directory
→ Speculative plan runs (no apply)
→ Plan output appears in PR as a status check
Developer merges PR to main
→ TFC triggers a full run
→ Plan runs and waits for confirmation
→ Engineer clicks "Confirm & Apply" in TFC UI
→ Apply runs in TFC's secure environment
This is similar to what you build with GitHub Actions, but TFC handles credentials, state locking, and concurrent run queuing automatically.
Run Triggers — Cross-Workspace Orchestration
Run triggers let you chain workspaces. When one workspace completes a successful apply, it triggers a run in downstream workspaces:
# When networking changes, trigger compute and database workspaces
resource "tfe_run_trigger" "compute_after_networking" {
workspace_id = tfe_workspace.production_compute.id
sourceable_id = tfe_workspace.production_networking.id
}
resource "tfe_run_trigger" "database_after_networking" {
workspace_id = tfe_workspace.production_database.id
sourceable_id = tfe_workspace.production_networking.id
}
This replaces complex CI pipeline dependency graphs with a simple declaration.
API-Driven Runs
For advanced automation, TFC exposes a full API. You can trigger runs, set variables, and read outputs programmatically:
# Trigger a run via API
curl \
--header "Authorization: Bearer $TFC_TOKEN" \
--header "Content-Type: application/vnd.api+json" \
--request POST \
--data '{
"data": {
"attributes": {
"message": "Triggered via API",
"auto-apply": false
},
"type": "runs",
"relationships": {
"workspace": {
"data": {
"type": "workspaces",
"id": "ws-abc123"
}
}
}
}
}' \
https://app.terraform.io/api/v2/runs
# Read workspace outputs
curl \
--header "Authorization: Bearer $TFC_TOKEN" \
https://app.terraform.io/api/v2/workspaces/ws-abc123/current-state-version-outputs \
| jq '.data[].attributes | {name, value}'
API-driven runs are ideal for integrating Terraform into custom deployment pipelines, ChatOps bots, or self-service portals.
Migrating from Self-Managed to Terraform Cloud
Moving to TFC requires migrating your backend configuration:
# Before: S3 backend
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "production/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-state-locks"
}
}
# After: TFC backend
terraform {
cloud {
organization = "my-company"
workspaces {
name = "production-api"
}
}
}
# Migration steps
# 1. Create the workspace in TFC (UI or API)
# 2. Update backend configuration
# 3. Run terraform init — Terraform detects the change
terraform init
# Terraform will prompt:
# Do you want to copy existing state to the new backend?
# Enter "yes" to copy the existing state to TFC.
The state migration is atomic — it copies the full state including serial number and lineage. After migration, the S3 state file becomes stale and should not be used.
Cost Estimation
TFC Business includes cost estimation that shows the monthly impact of planned changes:
Plan: 2 to add, 0 to change, 0 to destroy.
Cost Estimation:
Resources: +$127.30/mo
Monthly estimate: $4,312.80 → $4,440.10
+ aws_instance.web (x3)
+$109.50/mo ($0.15/hr x 730hrs x 3)
+ aws_ebs_volume.data (100GB gp3)
+$17.80/mo
This catches expensive mistakes before they hit your bill — like accidentally provisioning m5.4xlarge instances instead of t3.medium.
When Is Terraform Cloud Worth It?
Use TFC Free if you are a solo developer or small team who wants remote state without managing S3 + DynamoDB.
Use TFC Team/Business if you have 10+ engineers, need policy enforcement (Sentinel), require audit logging for compliance, or want to eliminate CI/CD pipeline maintenance for Terraform.
Stay self-managed if you have strict data sovereignty requirements (state must stay in your cloud), need air-gapped operation, or your team already has a mature CI/CD pipeline they are happy with.
Use Terraform Enterprise only if you need self-hosted deployment with all TFC features — typically for regulated industries (banking, healthcare, government).
Closing Notes
Terraform Cloud removes the undifferentiated heavy lifting of managing Terraform at scale — state storage, credential management, policy enforcement, and run orchestration. For small teams, the free tier is enough. For larger organizations, the governance features often pay for themselves by preventing a single misconfiguration that would have caused a production incident. In the next post, we will compare the three major approaches to multi-environment Terraform: workspaces, directory structure, and Terragrunt.
