Skip to main content

Terraform Cloud Backend Plugin

The Terraform Cloud Backend provides an API proxy to Terraform Cloud with automatic token resolution from Vault.

Overview

PropertyValue
Package@internal/plugin-terraform-cloud-backend
TypeBackend
Plugin IDterraform-cloud
IntegrationTerraform Cloud, Vault

Architecture

API Endpoints

MethodEndpointDescription
POST/tokensGet TFC token for org
GET/available-tokensList available tokens
GET/healthHealth check
ALL/proxy/*Proxy to Terraform Cloud

Token Resolution

Resolution Strategy

Token Lookup Paths

The backend searches for tokens in these Vault paths:

  1. User-specific org token: users/{username}/tfc-{org-name}
  2. User generic token: users/{username}/tfc-token
  3. Group-specific org token: groups/{group}/tfc-{org-name}
  4. Group generic token: groups/{group}/tfc-token

Token Format

{
"token": "xxx.atlasv1.yyy",
"organization": "acme-corp",
"created": "2024-01-15T10:30:00Z"
}

Or multi-organization format:

{
"acme-corp": "xxx.atlasv1.yyy",
"other-org": "xxx.atlasv1.zzz"
}

API Proxy

All requests to /proxy/* are forwarded to Terraform Cloud:

Rate Limit Headers

The proxy forwards TFC rate limit headers:

HeaderDescription
X-RateLimit-LimitRequest limit per period
X-RateLimit-RemainingRemaining requests
X-RateLimit-ResetReset timestamp

User Info Extraction

User information is extracted from JWT claims:

interface UserInfo {
userId: string; // From sub claim
username: string; // Extracted from userId
groups: string[]; // From ent claims
}

JWT Claims Processing

// Extract from sub: "user:default/john.doe"
const userId = claims.sub;
const username = userId.split("/").pop(); // "john.doe"

// Extract from ent: ["group:default/platform-team"]
const groups = claims.ent
.filter((e) => e.startsWith("group:"))
.map((e) => e.split("/").pop()); // ["platform-team"]

Configuration

# app-config.yaml
terraformCloud:
apiUrl: https://app.terraform.io/api/v2

TypeScript Config Schema

// config.d.ts
export interface Config {
terraformCloud?: {
/**
* Terraform Cloud API base URL
* @default https://app.terraform.io/api/v2
*/
apiUrl?: string;
};
}

Example: Token Setup

Organization-Specific Token

Store in Vault at groups/platform-team/tfc-acme-corp:

{
"token": "xxx.atlasv1.yyy"
}

Multi-Organization Token

Store in Vault at groups/platform-team/tfc-token:

{
"acme-corp": "xxx.atlasv1.yyy",
"staging-org": "xxx.atlasv1.zzz",
"default": "xxx.atlasv1.www"
}

Error Handling

Token Not Found

{
"error": "Token not found",
"message": "No TFC token found for organization 'acme-corp'",
"checkedPaths": [
"users/john.doe/tfc-acme-corp",
"users/john.doe/tfc-token",
"groups/platform-team/tfc-acme-corp",
"groups/platform-team/tfc-token"
]
}

TFC API Error

{
"error": "TFC API Error",
"status": 403,
"message": "Forbidden - token may be expired or revoked"
}