Skip to main content

Terraform Cloud vs Self-Managed — Remote Execution and Governance

· 6 min read
Goel Academy
DevOps & Cloud Learning Hub

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:

FeatureSelf-Managed EquivalentTFC Advantage
Remote stateS3 + DynamoDBBuilt-in, zero setup
Remote executionGitHub Actions / GitLab CIConsistent environment, no runner management
Sentinel policiesOPA + ConftestIntegrated into plan/apply lifecycle
Private module registryGit tags + internal docsVersioned, searchable, with docs
Cost estimationInfracost (third-party)Built into plan output
Team managementCloud IAM + CI permissionsWorkspace-level access control
VCS integrationWebhooks + CI configOne-click setup
Run triggersCI pipeline dependenciesCross-workspace orchestration

Terraform Cloud vs Enterprise vs Self-Managed

CapabilitySelf-Managed (OSS)TF Cloud FreeTF Cloud TeamTF Cloud BusinessTF Enterprise
Remote stateManual (S3/GCS)Yes (unlimited)YesYesYes
Remote executionCI/CD pipelineYes (limited)YesYesYes
Sentinel policiesOPA/ConftestNoNoYesYes
Cost estimationInfracostNoYesYesYes
Private registryGit reposYesYesYesYes
Team managementIAM/RBACNoYesYesYes
SSO/SAMLManualNoNoYesYes
Audit loggingCI logsNoNoYesYes
Self-hosted agentsN/ANoNoYesYes
Air-gapped installN/ANoNoNoYes
Concurrent runsUnlimited (your infra)13+UnlimitedUnlimited
PriceYour time + infraFree~$20/user/monthCustomCustom

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.