This is page 3 of 3. Use http://codebase.md/severity1/terraform-cloud-mcp?page={x} to view the full context.
# Directory Structure
```
├── .gitignore
├── .python-version
├── CLAUDE.md
├── docs
│ ├── API_REFERENCES.md
│ ├── CLAUDE.md
│ ├── CONTRIBUTING.md
│ ├── conversations
│ │ ├── account.md
│ │ ├── apply-management-conversation.md
│ │ ├── assessment-results-conversation.md
│ │ ├── cost-estimate-conversation.md
│ │ ├── organization-entitlements-conversation.md
│ │ ├── organizations-management-conversation.md
│ │ ├── plan-management-conversation.md
│ │ ├── project-management-conversation.md
│ │ ├── runs-management-conversation.md
│ │ ├── state_management.md
│ │ ├── variables-conversation.md
│ │ └── workspace-management-conversation.md
│ ├── DEVELOPMENT.md
│ ├── FILTERING_SYSTEM.md
│ ├── models
│ │ ├── account.md
│ │ ├── apply.md
│ │ ├── assessment_result.md
│ │ ├── cost_estimate.md
│ │ ├── organization.md
│ │ ├── plan.md
│ │ ├── project.md
│ │ ├── run.md
│ │ ├── state_version_outputs.md
│ │ ├── state_versions.md
│ │ ├── variables.md
│ │ └── workspace.md
│ ├── README.md
│ └── tools
│ ├── account.md
│ ├── apply.md
│ ├── assessment_results.md
│ ├── cost_estimate.md
│ ├── organization.md
│ ├── plan.md
│ ├── project.md
│ ├── run.md
│ ├── state_version_outputs.md
│ ├── state_versions.md
│ ├── variables.md
│ └── workspace.md
├── env.example
├── LICENSE
├── mypy.ini
├── pyproject.toml
├── README.md
├── terraform_cloud_mcp
│ ├── __init__.py
│ ├── api
│ │ ├── __init__.py
│ │ ├── CLAUDE.md
│ │ └── client.py
│ ├── configs
│ │ ├── __init__.py
│ │ ├── CLAUDE.md
│ │ └── filter_configs.py
│ ├── models
│ │ ├── __init__.py
│ │ ├── account.py
│ │ ├── applies.py
│ │ ├── assessment_results.py
│ │ ├── base.py
│ │ ├── CLAUDE.md
│ │ ├── cost_estimates.py
│ │ ├── filters.py
│ │ ├── organizations.py
│ │ ├── plans.py
│ │ ├── projects.py
│ │ ├── runs.py
│ │ ├── state_version_outputs.py
│ │ ├── state_versions.py
│ │ ├── variables.py
│ │ └── workspaces.py
│ ├── server.py
│ ├── tools
│ │ ├── __init__.py
│ │ ├── account.py
│ │ ├── applies.py
│ │ ├── assessment_results.py
│ │ ├── CLAUDE.md
│ │ ├── cost_estimates.py
│ │ ├── organizations.py
│ │ ├── plans.py
│ │ ├── projects.py
│ │ ├── runs.py
│ │ ├── state_version_outputs.py
│ │ ├── state_versions.py
│ │ ├── variables.py
│ │ └── workspaces.py
│ └── utils
│ ├── __init__.py
│ ├── CLAUDE.md
│ ├── decorators.py
│ ├── env.py
│ ├── filters.py
│ ├── payload.py
│ └── request.py
└── uv.lock
```
# Files
--------------------------------------------------------------------------------
/terraform_cloud_mcp/models/variables.py:
--------------------------------------------------------------------------------
```python
"""Variable models for Terraform Cloud API
This module contains models for Terraform Cloud variable-related requests,
including workspace variables and variable sets.
Reference:
- https://developer.hashicorp.com/terraform/cloud-docs/api-docs/workspace-variables
- https://developer.hashicorp.com/terraform/cloud-docs/api-docs/variable-sets
"""
from enum import Enum
from typing import List, Optional
from pydantic import Field
from .base import APIRequest
class VariableCategory(str, Enum):
"""Variable category options for workspace variables.
Defines the type of variable:
- TERRAFORM: Terraform input variables available in configuration
- ENV: Environment variables available during plan/apply operations
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/workspace-variables
See:
docs/models/variables.md for reference
"""
TERRAFORM = "terraform"
ENV = "env"
class WorkspaceVariable(APIRequest):
"""Model for workspace variable data.
Represents a variable that can be set on a workspace, including
Terraform input variables and environment variables.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/workspace-variables
See:
docs/models/variables.md for reference
"""
key: str = Field(
...,
description="Variable name/key",
min_length=1,
max_length=255,
)
value: Optional[str] = Field(
None,
description="Variable value",
max_length=256000,
)
description: Optional[str] = Field(
None,
description="Description of the variable",
max_length=512,
)
category: VariableCategory = Field(
...,
description="Variable category (terraform or env)",
)
hcl: Optional[bool] = Field(
False,
description="Whether the value is HCL code (only valid for terraform variables)",
)
sensitive: Optional[bool] = Field(
False,
description="Whether the variable value is sensitive",
)
class WorkspaceVariableParams(APIRequest):
"""Parameters for workspace variable operations without routing fields.
This model provides all optional parameters for creating or updating workspace
variables, separating configuration parameters from routing information like
workspace ID and variable ID.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/workspace-variables
See:
docs/models/variables.md for reference
"""
key: Optional[str] = Field(
None,
description="Variable name/key",
min_length=1,
max_length=255,
)
value: Optional[str] = Field(
None,
description="Variable value",
max_length=256000,
)
description: Optional[str] = Field(
None,
description="Description of the variable",
max_length=512,
)
category: Optional[VariableCategory] = Field(
None,
description="Variable category (terraform or env)",
)
hcl: Optional[bool] = Field(
None,
description="Whether the value is HCL code (only valid for terraform variables)",
)
sensitive: Optional[bool] = Field(
None,
description="Whether the variable value is sensitive",
)
class WorkspaceVariableCreateRequest(APIRequest):
"""Request model for creating workspace variables.
Used for POST /workspaces/:workspace_id/vars endpoint.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/workspace-variables
See:
docs/models/variables.md for reference
"""
workspace_id: str = Field(
...,
description="The workspace ID",
pattern=r"^ws-[a-zA-Z0-9]{16}$",
)
key: str = Field(
...,
description="Variable name/key",
min_length=1,
max_length=255,
)
category: VariableCategory = Field(
...,
description="Variable category (terraform or env)",
)
value: Optional[str] = Field(
None,
description="Variable value",
max_length=256000,
)
description: Optional[str] = Field(
None,
description="Description of the variable",
max_length=512,
)
hcl: Optional[bool] = Field(
None,
description="Whether the value is HCL code (only valid for terraform variables)",
)
sensitive: Optional[bool] = Field(
None,
description="Whether the variable value is sensitive",
)
class WorkspaceVariableUpdateRequest(APIRequest):
"""Request model for updating workspace variables.
Used for PATCH /workspaces/:workspace_id/vars/:variable_id endpoint.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/workspace-variables
See:
docs/models/variables.md for reference
"""
workspace_id: str = Field(
...,
description="The workspace ID",
pattern=r"^ws-[a-zA-Z0-9]{16}$",
)
variable_id: str = Field(
...,
description="The variable ID",
pattern=r"^var-[a-zA-Z0-9]{16}$",
)
key: Optional[str] = Field(
None,
description="Variable name/key",
min_length=1,
max_length=255,
)
value: Optional[str] = Field(
None,
description="Variable value",
max_length=256000,
)
description: Optional[str] = Field(
None,
description="Description of the variable",
max_length=512,
)
category: Optional[VariableCategory] = Field(
None,
description="Variable category (terraform or env)",
)
hcl: Optional[bool] = Field(
None,
description="Whether the value is HCL code (only valid for terraform variables)",
)
sensitive: Optional[bool] = Field(
None,
description="Whether the variable value is sensitive",
)
# Variable Sets Models
class VariableSet(APIRequest):
"""Model for variable set data.
Represents a collection of variables that can be applied to multiple
workspaces or projects.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/variable-sets
See:
docs/models/variables.md for reference
"""
name: str = Field(
...,
description="Variable set name",
min_length=1,
max_length=90,
)
description: Optional[str] = Field(
None,
description="Description of the variable set",
max_length=512,
)
global_: Optional[bool] = Field(
False,
alias="global",
description="Whether this is a global variable set",
)
priority: Optional[bool] = Field(
False,
description="Whether this variable set takes priority over workspace variables",
)
class VariableSetParams(APIRequest):
"""Parameters for variable set operations without routing fields.
This model provides all optional parameters for creating or updating variable
sets, separating configuration parameters from routing information.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/variable-sets
See:
docs/models/variables.md for reference
"""
name: Optional[str] = Field(
None,
description="Variable set name",
min_length=1,
max_length=90,
)
description: Optional[str] = Field(
None,
description="Description of the variable set",
max_length=512,
)
global_: Optional[bool] = Field(
None,
alias="global",
description="Whether this is a global variable set",
)
priority: Optional[bool] = Field(
None,
description="Whether this variable set takes priority over workspace variables",
)
class VariableSetCreateRequest(APIRequest):
"""Request model for creating variable sets.
Used for POST /organizations/:organization/varsets endpoint.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/variable-sets
See:
docs/models/variables.md for reference
"""
organization: str = Field(
...,
description="The organization name",
min_length=1,
)
name: str = Field(
...,
description="Variable set name",
min_length=1,
max_length=90,
)
params: Optional[VariableSetParams] = Field(
None,
description="Additional variable set parameters",
)
class VariableSetUpdateRequest(APIRequest):
"""Request model for updating variable sets.
Used for PATCH /varsets/:varset_id endpoint.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/variable-sets
See:
docs/models/variables.md for reference
"""
varset_id: str = Field(
...,
description="The variable set ID",
pattern=r"^varset-[a-zA-Z0-9]{16}$",
)
params: Optional[VariableSetParams] = Field(
None,
description="Variable set parameters to update",
)
class VariableSetVariable(APIRequest):
"""Model for variables within a variable set.
Represents a variable that belongs to a variable set with the same
structure as workspace variables.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/variable-sets
See:
docs/models/variables.md for reference
"""
key: str = Field(
...,
description="Variable name/key",
min_length=1,
max_length=255,
)
value: Optional[str] = Field(
None,
description="Variable value",
max_length=256000,
)
description: Optional[str] = Field(
None,
description="Description of the variable",
max_length=512,
)
category: VariableCategory = Field(
...,
description="Variable category (terraform or env)",
)
hcl: Optional[bool] = Field(
False,
description="Whether the value is HCL code (only valid for terraform variables)",
)
sensitive: Optional[bool] = Field(
False,
description="Whether the variable value is sensitive",
)
class VariableSetAssignmentRequest(APIRequest):
"""Request model for assigning variable sets to workspaces or projects.
Used for POST /varsets/:varset_id/relationships/workspaces or projects endpoints.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/variable-sets
See:
docs/models/variables.md for reference
"""
varset_id: str = Field(
...,
description="The variable set ID",
pattern=r"^varset-[a-zA-Z0-9]{16}$",
)
workspace_ids: Optional[List[str]] = Field(
None,
description="List of workspace IDs to assign the variable set to",
)
project_ids: Optional[List[str]] = Field(
None,
description="List of project IDs to assign the variable set to",
)
# Variable Set Variables Models
class VariableSetVariableParams(APIRequest):
"""Parameters for variable set variable operations without routing fields.
This model provides all optional parameters for creating or updating variables
within variable sets, separating configuration parameters from routing information
like variable set ID and variable ID.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/variable-sets
See:
docs/models/variables.md for reference
"""
key: Optional[str] = Field(
None,
description="Variable name/key",
min_length=1,
max_length=255,
)
value: Optional[str] = Field(
None,
description="Variable value",
max_length=256000,
)
description: Optional[str] = Field(
None,
description="Description of the variable",
max_length=512,
)
category: Optional[VariableCategory] = Field(
None,
description="Variable category (terraform or env)",
)
hcl: Optional[bool] = Field(
None,
description="Whether the value is HCL code (only valid for terraform variables)",
)
sensitive: Optional[bool] = Field(
None,
description="Whether the variable value is sensitive",
)
# List Request Models
class VariableSetListRequest(APIRequest):
"""Request model for listing variable sets.
Used for GET /organizations/:organization/varsets endpoint.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/variable-sets
See:
docs/models/variables.md for reference
"""
organization: str = Field(
...,
description="The organization name",
min_length=1,
)
page_number: int = Field(
1,
description="The page number to return",
ge=1,
)
page_size: int = Field(
20,
description="The number of items per page",
ge=1,
le=100,
)
```
--------------------------------------------------------------------------------
/docs/conversations/runs-management-conversation.md:
--------------------------------------------------------------------------------
```markdown
# Run Management - Example Conversations
This document shows example conversations with Claude using the Terraform Cloud MCP run management features.
## Creating and Managing Runs
```
**User**: Create a plan for my "production" workspace
**Claude**: I'll create a plan-only run in your "production" workspace.
[Claude uses get_workspace_details tool to find the production workspace]
[Claude uses create_run tool with plan_only=True]
I've created a plan-only run in the "production" workspace. Here are the details:
**Run Details:**
- Run ID: run-J7cW8p2qRmYx3Dkv
- Status: Pending
- Created by: Your account ([email protected])
- Message: "Plan requested via Claude"
- Type: Plan only (will not auto-apply)
The run has been queued and will start shortly. Since this is a plan-only run, you'll need to review the plan output before applying any changes. I'll let you know when the plan is complete.
```
```
**User**: Create a destroy plan for "dev-test" workspace
**Claude**: I'll create a destroy plan for your "dev-test" workspace.
[Claude uses get_workspace_details tool to find the dev-test workspace]
[Claude uses create_run tool with is_destroy=True and plan_only=True]
I've created a destroy plan in the "dev-test" workspace. Here are the details:
**Run Details:**
- Run ID: run-L2tP6k8xZnQv1Bmj
- Status: Pending
- Created by: Your account ([email protected])
- Message: "Destroy plan requested via Claude"
- Type: Destroy plan (will delete all resources)
- Auto-apply: No (plan only)
This run will create a plan to destroy all resources managed by the "dev-test" workspace. Since this is a plan-only run, you'll need to review and explicitly approve the destruction before any resources are deleted.
```
```
**User**: Run a targeted plan in "staging" that only affects the database resources
**Claude**: I'll create a targeted plan in the "staging" workspace that only affects database resources.
[Claude uses get_workspace_details tool to find the staging workspace]
[Claude uses create_run tool with target_addrs for database resources]
I've created a targeted plan in the "staging" workspace that will only affect database resources. Here are the details:
**Run Details:**
- Run ID: run-K3jR7m9zXpYw2Ctn
- Status: Pending
- Created by: Your account ([email protected])
- Message: "Targeted plan for database resources only"
- Type: Plan
- Targets:
- module.database
- aws_rds_cluster.main
- aws_rds_instance.replicas
The run has been queued and will start shortly. This plan will only show changes related to the database resources you specified. Other resources in the workspace will not be affected by this run.
```
```
**User**: Create a plan with custom variables in the "development" workspace
**Claude**: I'll create a run with custom variables in the "development" workspace.
[Claude uses get_workspace_details tool to find the development workspace]
[Claude uses create_run tool with custom variables for environment, instance_type, and enable_monitoring]
I've created a plan in the "development" workspace with the custom variables you specified. Here are the details:
**Run Details:**
- Run ID: run-P8vT5q2nWxYz1Fmb
- Status: Pending
- Created by: Your account ([email protected])
- Message: "Plan with custom variable values"
**Custom Variables for this Run:**
- environment = "dev"
- instance_type = "t3.large"
- enable_monitoring = "true"
These custom variable values will override the workspace's default values for this specific run only. The run has been queued and will start shortly.
```
```
**User**: Create a run in "sandbox" workspace with auto-apply enabled
**Claude**: I'll create a run with auto-apply enabled in the "sandbox" workspace.
[Claude uses get_workspace_details tool to find the sandbox workspace]
[Claude uses create_run tool with auto_apply=True]
I've created a run with auto-apply enabled in the "sandbox" workspace. Here are the details:
**Run Details:**
- Run ID: run-Q9wU6r3oZyXa2Gnd
- Status: Pending
- Created by: Your account ([email protected])
- Message: "Run with auto-apply enabled"
- Auto-apply: Yes (will automatically apply after successful plan)
The run has been queued and will start shortly. Since auto-apply is enabled for this run, it will automatically apply the changes after the plan completes successfully, without requiring manual approval.
```
## Listing and Viewing Runs
```
**User**: List all runs in my "production" workspace
**Claude**: I'll list all runs in your "production" workspace.
[Claude uses get_workspace_details tool to find the production workspace]
[Claude uses list_runs_in_workspace tool to get all runs]
Here are the recent runs in your "production" workspace:
1. **run-J7cW8p2qRmYx3Dkv** - "Plan requested via Claude"
- Created: 10 minutes ago
- Status: Planned (awaiting approval)
- Created by: You ([email protected])
- Type: Plan only
2. **run-K4jR7p9qZnYw2Lmp** - "Weekly infrastructure update"
- Created: Yesterday at 14:32
- Status: Applied
- Created by: CI/CD Pipeline
- Changes: 3 to add, 1 to change, 0 to destroy
3. **run-L5kS8q1rAoZx3Mnq** - "Security patch application"
- Created: 3 days ago
- Status: Applied
- Created by: [email protected]
- Changes: 0 to add, 5 to change, 0 to destroy
4. **run-M6lT9r2sBpAy4Nod** - "Roll back configuration change"
- Created: 5 days ago
- Status: Discarded
- Created by: [email protected]
- Changes: 0 to add, 3 to change, 0 to destroy
5. **run-N7mU0s3tCqBz5Ope** - "Add new monitoring resources"
- Created: 7 days ago
- Status: Applied
- Created by: You ([email protected])
- Changes: 8 to add, 0 to change, 0 to destroy
```
```
**User**: Show me runs in "example-org" that have errored in the last month
**Claude**: I'll find runs in the "example-org" organization that have errored in the last month.
[Claude uses list_runs_in_organization tool with filter_status="errored"]
I found the following runs in "example-org" that have errored in the last month:
1. **run-P8vT5q2nWxYz1Fmb** - "Plan with custom variable values"
- Workspace: development
- Created: Today at 09:45
- Status: Errored (Planning failed)
- Error: "Invalid value for variable 'instance_type'"
2. **run-S9pV6u3rDsEa8Ijf** - "Deploy new database resources"
- Workspace: prod-db-cluster
- Created: Yesterday at 18:22
- Status: Errored (Apply failed)
- Error: "Error creating RDS instance: InsufficientDBInstanceCapacity"
3. **run-T0qW7v4sEtFb9Jkg** - "Update network configuration"
- Workspace: network-infra
- Created: 3 days ago
- Status: Errored (Planning failed)
- Error: "Provider configuration not found for aws"
4. **run-U1rX8w5tFuGc0Klh** - "Scale up application servers"
- Workspace: app-servers
- Created: 5 days ago
- Status: Errored (Apply failed)
- Error: "Error applying Auto Scaling configuration: ValidationError"
5. **run-V2sY9x6uGvHd1Lmi** - "Security patch deployment"
- Workspace: security-baseline
- Created: 2 weeks ago
- Status: Errored (Planning timed out)
- Error: "Terraform operation timed out after 1 hour"
```
```
**User**: Find all destroy plan runs in my organization
**Claude**: I'll find all destroy plan runs in your organization.
[Claude uses list_runs_in_organization tool to search for destroy runs across all workspaces]
Here are the destroy plan runs I found in the "example-org" organization:
1. **run-L2tP6k8xZnQv1Bmj** - "Destroy plan requested via Claude"
- Workspace: dev-test
- Created: Today at 09:32
- Status: Planned (awaiting approval)
- Type: Destroy (will delete all resources)
- Created by: You ([email protected])
2. **run-W3tZ0y7vHwIe2Mnj** - "Decommission test environment"
- Workspace: feature-test
- Created: 3 days ago
- Status: Applied
- Type: Destroy (deleted all resources)
- Created by: [email protected]
- Result: 0 to add, 0 to change, 27 to destroy
3. **run-X4uA1z8wIxJf3Nok** - "Clean up temporary resources"
- Workspace: sandbox
- Created: 1 week ago
- Status: Applied
- Type: Destroy (partial)
- Created by: [email protected]
- Result: 0 to add, 0 to change, 5 to destroy
4. **run-Y5vB2a9xJyKg4Opl** - "Remove deprecated services"
- Workspace: legacy-systems
- Created: 2 weeks ago
- Status: Discarded
- Type: Destroy (partial)
- Created by: CI/CD Pipeline
```
```
**User**: Get details for run ID "run-J7cW8p2qRmYx3Dkv"
**Claude**: I'll get the details for run ID "run-J7cW8p2qRmYx3Dkv".
[Claude uses get_run_details tool with the specified run ID]
Here are the details for run ID "run-J7cW8p2qRmYx3Dkv":
**Basic Information:**
- Run ID: run-J7cW8p2qRmYx3Dkv
- Status: Planned and finished (awaiting approval)
- Message: "Plan requested via Claude"
- Created: Today at 09:30 (10 minutes ago)
- Created by: You ([email protected])
**Workspace:**
- Name: production
- Organization: example-org
- Terraform Version: 1.5.2
**Plan Details:**
- Resources to add: 2
- Resources to change: 1
- Resources to destroy: 0
- Changes summary:
- Adding 2 AWS Lambda functions
- Updating IAM role permissions
**Phases:**
- Plan started: Today at 09:30:15
- Plan completed: Today at 09:31:45
- Plan duration: 90 seconds
- Apply phase: Awaiting approval
**Configuration:**
- VCS commit: 7a8b9c0d... (feat: add new lambda functions)
- Plan only: Yes (requires manual apply)
- Auto-apply: No
- Terraform Version: 1.5.2
- Working directory: /terraform
The run is currently awaiting your approval to apply the plan. You can apply it using the apply_run function with this run ID.
```
## Run Actions
```
**User**: Apply the pending run for "staging" workspace
**Claude**: I'll apply the pending run for the "staging" workspace.
[Claude uses list_runs_in_workspace tool to find pending runs in staging workspace]
[Claude uses apply_run tool with a review comment]
I've applied the pending run (run-K3jR7m9zXpYw2Ctn) in the "staging" workspace. Here's the status:
**Apply Initiated:**
- Run ID: run-K3jR7m9zXpYw2Ctn
- Status: Applying
- Comment: "Applied via Claude after review"
- Action taken: Apply started
The run is now applying the planned changes to your infrastructure. The apply process typically takes a few minutes to complete. The targeted changes to your database resources are now being executed.
```
```
**User**: Cancel the run "run-P8vT5q2nWxYz1Fmb", it's taking too long
**Claude**: I'll cancel the run "run-P8vT5q2nWxYz1Fmb" that's taking too long.
[Claude uses cancel_run tool with the specified run ID and cancellation reason]
I've initiated the cancellation of run "run-P8vT5q2nWxYz1Fmb". Here's the status:
**Cancellation Initiated:**
- Run ID: run-P8vT5q2nWxYz1Fmb
- Status: Cancelling
- Comment: "Cancelled due to long execution time"
The system is now gracefully stopping the run. It may take a moment to completely cancel as Terraform needs to clean up any in-progress operations. Once cancelled, the workspace will be unlocked and available for new runs.
```
```
**User**: Discard the plan for the "dev" workspace, those changes aren't needed
**Claude**: I'll discard the pending plan for the "dev" workspace.
[Claude uses list_runs_in_workspace tool to find pending runs in dev workspace]
[Claude uses discard_run tool with reason for discarding]
I've discarded the pending plan (run-Z6wC3b0yKzLh5Pqm) in the "dev" workspace. Here's the confirmation:
**Plan Discarded:**
- Run ID: run-Z6wC3b0yKzLh5Pqm
- Status: Discarded
- Comment: "Changes no longer needed"
- Created by: You ([email protected])
The plan has been discarded and will not be applied. The workspace is now unlocked and available for new runs. No changes were made to your infrastructure.
```
```
**User**: Force cancel the stuck run in "production"
**Claude**: I'll force cancel the stuck run in the "production" workspace.
[Claude uses list_runs_in_workspace tool to find running runs in production workspace]
[Claude uses cancel_run tool first, then force_cancel_run tool for the stuck run]
I've initiated a force cancel for the stuck run (run-A7xD4c1zLaMe6Rqn) in the "production" workspace. Here's the status:
**Force Cancellation Initiated:**
- Run ID: run-A7xD4c1zLaMe6Rqn
- Status: Force cancelling
- Comment: "Force cancelling stuck run"
I first attempted a normal cancel operation, and then proceeded with the force cancel to immediately terminate the run. This will immediately unlock the workspace, allowing you to queue new runs. Note that force cancelling may leave some Terraform state operations incomplete, but it's necessary when a run is truly stuck.
```
```
**User**: I need to execute this run immediately, force execute it
**Claude**: I'll force execute a run to bypass the queue and start it immediately.
[Claude uses list_runs_in_organization tool to find pending runs in the queue]
[Claude uses force_execute_run tool to prioritize the run]
I've force executed the pending run (run-B8yE5d2aLbNf7Sro). Here's what happened:
**Force Execution Initiated:**
- Run ID: run-B8yE5d2aLbNf7Sro
- Status: Running (Planning)
- Workspace: deployment
- Original position: #3 in queue
The force execute command:
1. Cancelled all prior runs that were blocking this run
2. Unlocked the workspace
3. Started this run immediately
The run is now actively planning and has bypassed the queue. This is useful in urgent situations, but should be used sparingly as it cancels other queued runs.
```
```
--------------------------------------------------------------------------------
/docs/tools/variables.md:
--------------------------------------------------------------------------------
```markdown
# Variable Tools
This module provides tools for managing variables and variable sets in Terraform Cloud.
## Overview
Variables in Terraform Cloud are used to provide inputs to your Terraform configurations and set environment variables during runs. These tools allow you to manage variables at the workspace level and create reusable variable sets that can be shared across multiple workspaces and projects.
## API Reference
These tools interact with the Terraform Cloud Variables API:
- [Workspace Variables API Documentation](https://developer.hashicorp.com/terraform/cloud-docs/api-docs/workspace-variables)
- [Variable Sets API Documentation](https://developer.hashicorp.com/terraform/cloud-docs/api-docs/variable-sets)
- [Variables in Terraform Cloud](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/variables)
## Tools Reference
### list_workspace_variables
**Function:** `list_workspace_variables(workspace_id: str) -> Dict[str, Any]`
**Description:** Retrieves all variables (both Terraform and environment) configured for a specific workspace.
**Parameters:**
- `workspace_id` (str): The ID of the workspace (format: "ws-xxxxxxxx")
**Returns:** JSON response containing list of workspace variables with their configuration and values.
**Notes:**
- Requires "read variables" permission on the workspace
- Sensitive variable values are not returned in the response
- Both Terraform and environment variables are included
### create_workspace_variable
**Function:** `create_workspace_variable(workspace_id: str, key: str, category: str, params: Optional[WorkspaceVariableParams] = None) -> Dict[str, Any]`
**Description:** Creates a new Terraform or environment variable within a workspace.
**Parameters:**
- `workspace_id` (str): The ID of the workspace (format: "ws-xxxxxxxx")
- `key` (str): The variable name/key
- `category` (str): Variable category ("terraform" or "env")
- `params` (WorkspaceVariableParams, optional): Additional variable parameters including value, description, HCL format, and sensitivity settings
**Returns:** JSON response with the created variable including its configuration and metadata.
**Notes:**
- Requires "write variables" permission on the workspace
- HCL format is only valid for terraform category variables
- Sensitive variables will have their values hidden in responses
### update_workspace_variable
**Function:** `update_workspace_variable(workspace_id: str, variable_id: str, params: Optional[WorkspaceVariableParams] = None) -> Dict[str, Any]`
**Description:** Modifies the configuration of an existing workspace variable. Only specified attributes will be updated.
**Parameters:**
- `workspace_id` (str): The ID of the workspace (format: "ws-xxxxxxxx")
- `variable_id` (str): The ID of the variable (format: "var-xxxxxxxx")
- `params` (WorkspaceVariableParams, optional): Variable parameters to update including key, value, description, category, HCL format, and sensitivity
**Returns:** JSON response with the updated variable including all current settings.
**Notes:**
- Requires "write variables" permission on the workspace
- Only specified attributes are updated; others remain unchanged
- Cannot change a sensitive variable to non-sensitive
### delete_workspace_variable
**Function:** `delete_workspace_variable(workspace_id: str, variable_id: str) -> Dict[str, Any]`
**Description:** Permanently removes a variable from a workspace. This action cannot be undone.
**Parameters:**
- `workspace_id` (str): The ID of the workspace (format: "ws-xxxxxxxx")
- `variable_id` (str): The ID of the variable (format: "var-xxxxxxxx")
**Returns:** Empty response with HTTP 204 status code if successful.
**Notes:**
- DESTRUCTIVE OPERATION: Requires `ENABLE_DELETE_TOOLS=true` environment variable
- Requires "write variables" permission on the workspace
- This action cannot be undone
### list_variable_sets
**Function:** `list_variable_sets(organization: str, page_number: int = 1, page_size: int = 20) -> Dict[str, Any]`
**Description:** Retrieves a paginated list of all variable sets in a Terraform Cloud organization.
**Parameters:**
- `organization` (str): The name of the organization
- `page_number` (int, optional): The page number to return (default: 1)
- `page_size` (int, optional): Number of items per page (default: 20, max: 100)
**Returns:** JSON response containing paginated list of variable sets with their configuration and metadata.
**Notes:**
- Requires "read variable sets" permission on the organization
- Variable sets allow you to reuse variables across multiple workspaces
- Results are paginated with metadata indicating total count
### get_variable_set
**Function:** `get_variable_set(varset_id: str) -> Dict[str, Any]`
**Description:** Retrieves comprehensive information about a variable set including its variables, workspace assignments, and configuration.
**Parameters:**
- `varset_id` (str): The ID of the variable set (format: "varset-xxxxxxxx")
**Returns:** JSON response with variable set details including configuration and relationships.
**Notes:**
- Requires "read variable sets" permission
- Includes information about workspace and project assignments
- Shows global status and priority settings
### create_variable_set
**Function:** `create_variable_set(organization: str, name: str, params: Optional[VariableSetParams] = None) -> Dict[str, Any]`
**Description:** Creates a new variable set which can be used to manage variables across multiple workspaces and projects.
**Parameters:**
- `organization` (str): The name of the organization
- `name` (str): The name to give the variable set
- `params` (VariableSetParams, optional): Additional variable set parameters including description, global status, and priority settings
**Returns:** JSON response with the created variable set including its configuration and metadata.
**Notes:**
- Requires "write variable sets" permission on the organization
- Global variable sets are automatically applied to all workspaces
- Priority variable sets override workspace-level variables
### update_variable_set
**Function:** `update_variable_set(varset_id: str, params: Optional[VariableSetParams] = None) -> Dict[str, Any]`
**Description:** Modifies the settings of a variable set. Only specified attributes will be updated.
**Parameters:**
- `varset_id` (str): The ID of the variable set (format: "varset-xxxxxxxx")
- `params` (VariableSetParams, optional): Variable set parameters to update including name, description, global status, and priority
**Returns:** JSON response with the updated variable set including all current settings.
**Notes:**
- Requires "write variable sets" permission
- Only specified attributes are updated; others remain unchanged
- Changing global status affects workspace assignments
### delete_variable_set
**Function:** `delete_variable_set(varset_id: str) -> Dict[str, Any]`
**Description:** Permanently removes a variable set and all its variables. The variable set will be unassigned from all workspaces and projects.
**Parameters:**
- `varset_id` (str): The ID of the variable set (format: "varset-xxxxxxxx")
**Returns:** Empty response with HTTP 204 status code if successful.
**Notes:**
- DESTRUCTIVE OPERATION: Requires `ENABLE_DELETE_TOOLS=true` environment variable
- Requires "write variable sets" permission
- This action cannot be undone and removes all variables in the set
### assign_variable_set_to_workspaces
**Function:** `assign_variable_set_to_workspaces(varset_id: str, workspace_ids: List[str]) -> Dict[str, Any]`
**Description:** Makes the variables in a variable set available to the specified workspaces.
**Parameters:**
- `varset_id` (str): The ID of the variable set (format: "varset-xxxxxxxx")
- `workspace_ids` (List[str]): List of workspace IDs (format: ["ws-xxxxxxxx", ...])
**Returns:** Empty response with HTTP 204 status code if successful.
**Notes:**
- Variables from variable sets take precedence over workspace variables if the variable set has priority enabled
- Does not affect global variable sets (they're automatically applied)
### unassign_variable_set_from_workspaces
**Function:** `unassign_variable_set_from_workspaces(varset_id: str, workspace_ids: List[str]) -> Dict[str, Any]`
**Description:** Removes the variable set assignment from the specified workspaces. The variables will no longer be available in those workspaces.
**Parameters:**
- `varset_id` (str): The ID of the variable set (format: "varset-xxxxxxxx")
- `workspace_ids` (List[str]): List of workspace IDs (format: ["ws-xxxxxxxx", ...])
**Returns:** Empty response with HTTP 204 status code if successful.
**Notes:**
- Cannot unassign global variable sets from workspaces
- Variables become unavailable immediately in the affected workspaces
### assign_variable_set_to_projects
**Function:** `assign_variable_set_to_projects(varset_id: str, project_ids: List[str]) -> Dict[str, Any]`
**Description:** Makes the variables in a variable set available to all workspaces within the specified projects.
**Parameters:**
- `varset_id` (str): The ID of the variable set (format: "varset-xxxxxxxx")
- `project_ids` (List[str]): List of project IDs (format: ["prj-xxxxxxxx", ...])
**Returns:** Empty response with HTTP 204 status code if successful.
**Notes:**
- All workspaces in the project receive the variable set
- New workspaces added to the project will automatically inherit the variable set
### unassign_variable_set_from_projects
**Function:** `unassign_variable_set_from_projects(varset_id: str, project_ids: List[str]) -> Dict[str, Any]`
**Description:** Removes the variable set assignment from the specified projects. The variables will no longer be available in workspaces within those projects.
**Parameters:**
- `varset_id` (str): The ID of the variable set (format: "varset-xxxxxxxx")
- `project_ids` (List[str]): List of project IDs (format: ["prj-xxxxxxxx", ...])
**Returns:** Empty response with HTTP 204 status code if successful.
**Notes:**
- Affects all workspaces within the specified projects
- Cannot unassign global variable sets from projects
### list_variables_in_variable_set
**Function:** `list_variables_in_variable_set(varset_id: str) -> Dict[str, Any]`
**Description:** Retrieves all variables that belong to a specific variable set, including their configuration and values.
**Parameters:**
- `varset_id` (str): The ID of the variable set (format: "varset-xxxxxxxx")
**Returns:** JSON response containing list of variables in the variable set with their configuration.
**Notes:**
- Requires "read variable sets" permission
- Sensitive variable values are not returned in the response
- Both Terraform and environment variables are included
### create_variable_in_variable_set
**Function:** `create_variable_in_variable_set(varset_id: str, key: str, category: str, params: Optional[VariableSetVariableParams] = None) -> Dict[str, Any]`
**Description:** Creates a new Terraform or environment variable within a variable set.
**Parameters:**
- `varset_id` (str): The ID of the variable set (format: "varset-xxxxxxxx")
- `key` (str): The variable name/key
- `category` (str): Variable category ("terraform" or "env")
- `params` (VariableSetVariableParams, optional): Additional variable parameters including value, description, HCL format, and sensitivity settings
**Returns:** JSON response with the created variable including its configuration and metadata.
**Notes:**
- Requires "write variable sets" permission
- HCL format is only valid for terraform category variables
- Variable becomes available in all workspaces/projects assigned to the variable set
### update_variable_in_variable_set
**Function:** `update_variable_in_variable_set(varset_id: str, var_id: str, params: Optional[VariableSetVariableParams] = None) -> Dict[str, Any]`
**Description:** Modifies the configuration of an existing variable within a variable set. Only specified attributes will be updated.
**Parameters:**
- `varset_id` (str): The ID of the variable set (format: "varset-xxxxxxxx")
- `var_id` (str): The ID of the variable (format: "var-xxxxxxxx")
- `params` (VariableSetVariableParams, optional): Variable parameters to update including key, value, description, category, HCL format, and sensitivity
**Returns:** JSON response with the updated variable including all current settings.
**Notes:**
- Requires "write variable sets" permission
- Only specified attributes are updated; others remain unchanged
- Changes affect all workspaces using this variable set
### delete_variable_from_variable_set
**Function:** `delete_variable_from_variable_set(varset_id: str, var_id: str) -> Dict[str, Any]`
**Description:** Permanently removes a variable from a variable set. This action cannot be undone.
**Parameters:**
- `varset_id` (str): The ID of the variable set (format: "varset-xxxxxxxx")
- `var_id` (str): The ID of the variable (format: "var-xxxxxxxx")
**Returns:** Empty response with HTTP 204 status code if successful.
**Notes:**
- DESTRUCTIVE OPERATION: Requires `ENABLE_DELETE_TOOLS=true` environment variable
- Requires "write variable sets" permission
- This action cannot be undone
- Variable becomes unavailable in all workspaces using this variable set
## Common Error Scenarios
| Error | Description | Resolution |
|-------|-------------|------------|
| 401 Unauthorized | Invalid or missing authentication token | Verify TFC_TOKEN environment variable |
| 403 Forbidden | Insufficient permissions for the operation | Check user/team permissions on workspace/organization |
| 404 Not Found | Workspace, variable set, or variable not found | Verify the ID format and existence |
| 422 Unprocessable Entity | Invalid variable configuration (e.g., HCL on env variable) | Review variable parameters and constraints |
| 409 Conflict | Variable key already exists in workspace/variable set | Use a different key or update the existing variable |
```
--------------------------------------------------------------------------------
/terraform_cloud_mcp/models/runs.py:
--------------------------------------------------------------------------------
```python
"""Run models for Terraform Cloud API
This module contains models for Terraform Cloud run-related requests.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/run
"""
from enum import Enum
from typing import List, Optional
from pydantic import Field
from .base import APIRequest
class RunOperation(str, Enum):
"""Operation options for runs in Terraform Cloud.
Defines the different types of operations a run can perform:
- PLAN_ONLY: Create a plan without applying changes
- PLAN_AND_APPLY: Create a plan and apply if approved
- SAVE_PLAN: Save the plan for later use
- REFRESH_ONLY: Only refresh state without planning changes
- DESTROY: Destroy all resources
- EMPTY_APPLY: Apply even with no changes detected
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/run#list-runs-in-a-workspace
See:
docs/models/run.md for reference
"""
PLAN_ONLY = "plan_only"
PLAN_AND_APPLY = "plan_and_apply"
SAVE_PLAN = "save_plan"
REFRESH_ONLY = "refresh_only"
DESTROY = "destroy"
EMPTY_APPLY = "empty_apply"
class RunStatus(str, Enum):
"""Status options for runs in Terraform Cloud.
Defines the various states a run can be in during its lifecycle,
from initial creation through planning, policy checks, application,
and completion or cancellation.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/run#list-runs-in-a-workspace
See:
docs/models/run.md for reference
"""
PENDING = "pending"
FETCHING = "fetching"
FETCHING_COMPLETED = "fetching_completed"
PRE_PLAN_RUNNING = "pre_plan_running"
PRE_PLAN_COMPLETED = "pre_plan_completed"
QUEUING = "queuing"
PLAN_QUEUED = "plan_queued"
PLANNING = "planning"
PLANNED = "planned"
COST_ESTIMATING = "cost_estimating"
COST_ESTIMATED = "cost_estimated"
POLICY_CHECKING = "policy_checking"
POLICY_OVERRIDE = "policy_override"
POLICY_SOFT_FAILED = "policy_soft_failed"
POLICY_CHECKED = "policy_checked"
CONFIRMED = "confirmed"
POST_PLAN_RUNNING = "post_plan_running"
POST_PLAN_COMPLETED = "post_plan_completed"
PLANNED_AND_FINISHED = "planned_and_finished"
PLANNED_AND_SAVED = "planned_and_saved"
APPLY_QUEUED = "apply_queued"
APPLYING = "applying"
APPLIED = "applied"
DISCARDED = "discarded"
ERRORED = "errored"
CANCELED = "canceled"
FORCE_CANCELED = "force_canceled"
class RunSource(str, Enum):
"""Source options for runs in Terraform Cloud.
Identifies the origin of a run:
- TFE_UI: Created through the Terraform Cloud web interface
- TFE_API: Created through the API
- TFE_CONFIGURATION_VERSION: Created by uploading a configuration version
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/run#list-runs-in-a-workspace
See:
docs/models/run.md for reference
"""
TFE_UI = "tfe-ui"
TFE_API = "tfe-api"
TFE_CONFIGURATION_VERSION = "tfe-configuration-version"
class RunStatusGroup(str, Enum):
"""Status group options for categorizing runs.
Groups run statuses into categories for filtering:
- NON_FINAL: Runs that are still in progress
- FINAL: Runs that have reached a terminal state
- DISCARDABLE: Runs that can be discarded
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/run#list-runs-in-a-workspace
See:
docs/models/run.md for reference
"""
NON_FINAL = "non_final"
FINAL = "final"
DISCARDABLE = "discardable"
class RunVariable(APIRequest):
"""Model for run-specific variables.
Run variables are used to provide input values for a specific run,
which override any workspace variables for that run only.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/run#create-a-run
See:
docs/models/run.md for reference
"""
key: str = Field(
...,
# No alias needed as field name matches API field name
description="Variable key",
min_length=1,
max_length=128,
)
value: str = Field(
...,
# No alias needed as field name matches API field name
description="Variable value",
max_length=256,
)
class RunListInWorkspaceRequest(APIRequest):
"""Request parameters for listing runs in a workspace.
Used with the GET /workspaces/{workspace_id}/runs endpoint to retrieve
and filter run data for a specific workspace.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/run#list-runs-in-a-workspace
See:
docs/models/run.md for reference
"""
workspace_id: str = Field(
...,
description="The workspace ID to list runs for",
pattern=r"^ws-[a-zA-Z0-9]{16}$", # Standardized workspace ID pattern
)
page_number: Optional[int] = Field(1, ge=1, description="Page number to fetch")
page_size: Optional[int] = Field(
20, ge=1, le=100, description="Number of results per page"
)
filter_operation: Optional[str] = Field(
None,
description="Filter runs by operation type, comma-separated",
max_length=100,
)
filter_status: Optional[str] = Field(
None, description="Filter runs by status, comma-separated", max_length=100
)
filter_source: Optional[str] = Field(
None, description="Filter runs by source, comma-separated", max_length=100
)
filter_status_group: Optional[str] = Field(
None, description="Filter runs by status group", max_length=50
)
filter_timeframe: Optional[str] = Field(
None, description="Filter runs by timeframe", max_length=50
)
filter_agent_pool_names: Optional[str] = Field(
None,
description="Filter runs by agent pool names, comma-separated",
max_length=100,
)
search_user: Optional[str] = Field(
None, description="Search for runs by VCS username", max_length=100
)
search_commit: Optional[str] = Field(
None, description="Search for runs by commit SHA", max_length=40
)
search_basic: Optional[str] = Field(
None,
description="Basic search across run ID, message, commit SHA, and username",
max_length=100,
)
class RunListInOrganizationRequest(APIRequest):
"""Request parameters for listing runs in an organization.
These parameters map to the query parameters in the runs API.
The endpoint returns a paginated list of runs across all workspaces in an organization,
with options for filtering by workspace name, status, and other criteria.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/run#list-runs-in-an-organization
See:
docs/models/run.md for reference
"""
organization: str = Field(
...,
description="The organization name",
min_length=3,
pattern=r"^[a-z0-9][-a-z0-9_]*[a-z0-9]$",
)
page_number: Optional[int] = Field(1, ge=1, description="Page number to fetch")
page_size: Optional[int] = Field(
20, ge=1, le=100, description="Number of results per page"
)
filter_operation: Optional[str] = Field(
None,
description="Filter runs by operation type, comma-separated",
max_length=100,
)
filter_status: Optional[str] = Field(
None, description="Filter runs by status, comma-separated", max_length=100
)
filter_source: Optional[str] = Field(
None, description="Filter runs by source, comma-separated", max_length=100
)
filter_status_group: Optional[str] = Field(
None, description="Filter runs by status group", max_length=50
)
filter_timeframe: Optional[str] = Field(
None, description="Filter runs by timeframe", max_length=50
)
filter_agent_pool_names: Optional[str] = Field(
None,
description="Filter runs by agent pool names, comma-separated",
max_length=100,
)
filter_workspace_names: Optional[str] = Field(
None,
description="Filter runs by workspace names, comma-separated",
max_length=250,
)
search_user: Optional[str] = Field(
None, description="Search for runs by VCS username", max_length=100
)
search_commit: Optional[str] = Field(
None, description="Search for runs by commit SHA", max_length=40
)
search_basic: Optional[str] = Field(
None,
description="Basic search across run ID, message, commit SHA, and username",
max_length=100,
)
class BaseRunRequest(APIRequest):
"""Base class for run requests with common fields.
Common fields shared across run creation and management APIs.
Provides field definitions and validation rules for run operations.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/run
Note:
Consolidates common parameters for consistency across endpoints
See:
docs/models/run.md for reference
"""
# Inherits model_config from APIRequest -> BaseModelConfig
# Optional fields with their defaults
message: Optional[str] = Field(None, description="Message to include with the run")
auto_apply: Optional[bool] = Field(
None,
alias="auto-apply",
description="Whether to auto-apply the run when planned (defaults to workspace setting)",
)
is_destroy: Optional[bool] = Field(
False,
alias="is-destroy",
description="Whether this run should destroy all resources",
)
refresh: Optional[bool] = Field(
True, description="Whether to refresh state before plan"
)
refresh_only: Optional[bool] = Field(
False, alias="refresh-only", description="Whether this is a refresh-only run"
)
plan_only: Optional[bool] = Field(
False,
alias="plan-only",
description="Whether this is a speculative, plan-only run",
)
allow_empty_apply: Optional[bool] = Field(
False,
alias="allow-empty-apply",
description="Whether to allow apply when there are no changes",
)
allow_config_generation: Optional[bool] = Field(
False,
alias="allow-config-generation",
description="Whether to allow generating config for imports",
)
target_addrs: Optional[List[str]] = Field(
None, alias="target-addrs", description="Resource addresses to target"
)
replace_addrs: Optional[List[str]] = Field(
None, alias="replace-addrs", description="Resource addresses to replace"
)
variables: Optional[List[RunVariable]] = Field(
None, description="Run-specific variables"
)
terraform_version: Optional[str] = Field(
None,
alias="terraform-version",
description="Specific Terraform version (only valid for plan-only runs)",
)
save_plan: Optional[bool] = Field(
False,
alias="save-plan",
description="Whether to save the plan without becoming the current run",
)
debugging_mode: Optional[bool] = Field(
False, alias="debugging-mode", description="Enable debug logging for this run"
)
class RunCreateRequest(BaseRunRequest):
"""Request model for creating a Terraform Cloud run.
Validates and structures the request according to the Terraform Cloud API
requirements for creating runs. The model inherits common run attributes from
BaseRunRequest and adds workspace_id as a required parameter.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/run#create-a-run
Note:
This inherits all configuration fields from BaseRunRequest
and adds workspace_id as a required parameter.
This model is typically used internally by the create_run tool function,
which accepts parameters directly and constructs the request object.
See:
docs/models/run.md for reference
"""
# Required fields
workspace_id: str = Field(
...,
# No alias needed as field name matches API field name
description="The workspace ID to execute the run in (required)",
pattern=r"^ws-[a-zA-Z0-9]{16}$", # Standardized workspace ID pattern
)
# Optional fields specific to run creation
configuration_version_id: Optional[str] = Field(
None,
alias="configuration-version-id",
description="The configuration version ID to use",
pattern=r"^cv-[a-zA-Z0-9]{16}$",
)
class RunActionRequest(APIRequest):
"""Base request model for run actions like apply, discard, cancel, etc.
This model provides common fields used in run action requests such as
applying, discarding, or canceling runs. It includes the run ID and
an optional comment field that can be included with the action.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/run#apply-a-run
Note:
This model is used for multiple run action endpoints that share the
same basic structure but perform different operations on the run.
See:
docs/models/run.md for reference
"""
run_id: str = Field(
...,
# No alias needed as field name matches API field name
description="The ID of the run to perform an action on",
pattern=r"^run-[a-zA-Z0-9]{16}$",
)
comment: Optional[str] = Field(
None,
# No alias needed as field name matches API field name
description="An optional comment about the run",
)
class RunParams(BaseRunRequest):
"""Parameters for run operations without routing fields.
This model provides all optional parameters that can be used when creating runs,
reusing the field definitions from BaseRunRequest.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/run#create-a-run
Note:
All fields are inherited from BaseRunRequest.
See:
docs/models/run.md for reference
"""
# Inherits model_config and all fields from BaseRunRequest
# Response handling is implemented through raw dictionaries
```
--------------------------------------------------------------------------------
/terraform_cloud_mcp/tools/runs.py:
--------------------------------------------------------------------------------
```python
"""Run management tools for Terraform Cloud API.
This module provides tools for managing runs in Terraform Cloud.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/run
"""
from typing import Optional
from ..api.client import api_request
from ..utils.decorators import handle_api_errors
from ..utils.payload import create_api_payload, add_relationship
from ..utils.request import query_params
from ..models.base import APIResponse
from ..models.runs import (
RunListInWorkspaceRequest,
RunListInOrganizationRequest,
RunCreateRequest,
RunActionRequest,
RunParams,
)
@handle_api_errors
async def create_run(
workspace_id: str,
params: Optional[RunParams] = None,
) -> APIResponse:
"""Create a run in a workspace
Creates a new Terraform run to trigger infrastructure changes through Terraform Cloud,
representing a single execution of plan and apply operations. The run queues in the
workspace and executes based on the workspace's execution mode and settings. Use this
to deploy new infrastructure, apply configuration changes, or destroy resources.
API endpoint: POST /runs
Args:
workspace_id: The workspace ID to execute the run in (format: "ws-xxxxxxxx")
params: Optional run configuration with:
- message: Description of the run's purpose
- is_destroy: Whether to destroy all resources managed by the workspace
- auto_apply: Whether to auto-apply after a successful plan
- refresh: Whether to refresh Terraform state before planning
- refresh_only: Only refresh the state without planning changes
- plan_only: Create a speculative plan without applying
- allow_empty_apply: Allow applying when there are no changes
- target_addrs: List of resource addresses to specifically target
- replace_addrs: List of resource addresses to force replacement
- variables: Run-specific variables that override workspace variables
- terraform_version: Specific Terraform version to use for this run
- save_plan: Save the plan for later execution
- debugging_mode: Enable extended debug logging
Returns:
The created run details with ID, status, configuration information,
workspace relationship, and links to associated resources
See:
docs/tools/run.md for reference documentation
"""
# Convert optional params to dictionary
param_dict = params.model_dump(exclude_none=True) if params else {}
# Create validated request object
request = RunCreateRequest(workspace_id=workspace_id, **param_dict)
# Extract variables for special handling
variables = request.variables
# Create API payload using utility function
payload = create_api_payload(
resource_type="runs",
model=request,
exclude_fields={"workspace_id", "variables"}, # Fields handled separately
)
# Add workspace relationship
add_relationship(
payload=payload,
relation_name="workspace",
resource_type="workspaces",
resource_id=workspace_id,
)
# Add optional configuration version relationship
if request.configuration_version_id:
add_relationship(
payload=payload,
relation_name="configuration-version",
resource_type="configuration-versions",
resource_id=request.configuration_version_id,
)
# Transform variables to key-value format required by API
if variables:
payload["data"]["attributes"]["variables"] = [
{"key": var.key, "value": var.value} for var in variables
]
return await api_request("runs", method="POST", data=payload)
@handle_api_errors
async def list_runs_in_workspace(
workspace_id: str,
page_number: int = 1,
page_size: int = 20,
filter_operation: Optional[str] = None,
filter_status: Optional[str] = None,
filter_source: Optional[str] = None,
filter_status_group: Optional[str] = None,
filter_timeframe: Optional[str] = None,
filter_agent_pool_names: Optional[str] = None,
search_user: Optional[str] = None,
search_commit: Optional[str] = None,
search_basic: Optional[str] = None,
) -> APIResponse:
"""List runs in a workspace with filtering and pagination
Retrieves run history for a specific workspace with options to filter by status,
operation type, source, and other criteria. Useful for auditing changes, troubleshooting,
or monitoring deployment history.
API endpoint: GET /workspaces/{workspace_id}/runs
Args:
workspace_id: The workspace ID to list runs for (format: "ws-xxxxxxxx")
page_number: Page number to fetch (default: 1)
page_size: Number of results per page (default: 20)
filter_operation: Filter by operation type
filter_status: Filter by status
filter_source: Filter by source
filter_status_group: Filter by status group
filter_timeframe: Filter by timeframe
filter_agent_pool_names: Filter by agent pool names
search_user: Search by VCS username
search_commit: Search by commit SHA
search_basic: Search across run ID, message, commit SHA, and username
Returns:
List of runs with metadata, status info, and pagination details
See:
docs/tools/run.md for reference documentation
"""
# Create request using Pydantic model for validation
request = RunListInWorkspaceRequest(
workspace_id=workspace_id,
page_number=page_number,
page_size=page_size,
filter_operation=filter_operation,
filter_status=filter_status,
filter_source=filter_source,
filter_status_group=filter_status_group,
filter_timeframe=filter_timeframe,
filter_agent_pool_names=filter_agent_pool_names,
search_user=search_user,
search_commit=search_commit,
search_basic=search_basic,
)
# Use the unified query params utility function
params = query_params(request)
# Make API request
return await api_request(
f"workspaces/{workspace_id}/runs", method="GET", params=params
)
@handle_api_errors
async def list_runs_in_organization(
organization: str,
page_number: int = 1,
page_size: int = 20,
filter_operation: Optional[str] = None,
filter_status: Optional[str] = None,
filter_source: Optional[str] = None,
filter_status_group: Optional[str] = None,
filter_timeframe: Optional[str] = None,
filter_agent_pool_names: Optional[str] = None,
filter_workspace_names: Optional[str] = None,
search_user: Optional[str] = None,
search_commit: Optional[str] = None,
search_basic: Optional[str] = None,
) -> APIResponse:
"""List runs across all workspaces in an organization
Retrieves run history across all workspaces in an organization with powerful filtering.
Useful for organization-wide auditing, monitoring deployments across teams, or finding
specific runs by commit or author.
API endpoint: GET /organizations/{organization}/runs
Args:
organization: The organization name
page_number: Page number to fetch (default: 1)
page_size: Number of results per page (default: 20)
filter_operation: Filter by operation type
filter_status: Filter by status
filter_source: Filter by source
filter_status_group: Filter by status group
filter_timeframe: Filter by timeframe
filter_agent_pool_names: Filter by agent pool names
filter_workspace_names: Filter by workspace names
search_user: Search by VCS username
search_commit: Search by commit SHA
search_basic: Basic search across run attributes
Returns:
List of runs across workspaces with metadata and pagination details
See:
docs/tools/run.md for reference documentation
"""
# Create request using Pydantic model for validation
request = RunListInOrganizationRequest(
organization=organization,
page_number=page_number,
page_size=page_size,
filter_operation=filter_operation,
filter_status=filter_status,
filter_source=filter_source,
filter_status_group=filter_status_group,
filter_timeframe=filter_timeframe,
filter_agent_pool_names=filter_agent_pool_names,
filter_workspace_names=filter_workspace_names,
search_user=search_user,
search_commit=search_commit,
search_basic=search_basic,
)
# Use the unified query params utility function
params = query_params(request)
# Make API request
return await api_request(
f"organizations/{organization}/runs", method="GET", params=params
)
@handle_api_errors
async def get_run_details(run_id: str) -> APIResponse:
"""Get detailed information about a specific run
Retrieves comprehensive information about a run including its current status,
plan output, and relationship to other resources. Use to check run progress or results.
API endpoint: GET /runs/{run_id}
Args:
run_id: The ID of the run to retrieve details for (format: "run-xxxxxxxx")
Returns:
Complete run details including status, plan, and relationships
See:
docs/tools/run.md for reference documentation
"""
# Make API request
return await api_request(f"runs/{run_id}", method="GET")
@handle_api_errors
async def apply_run(run_id: str, comment: str = "") -> APIResponse:
"""Apply a run that is paused waiting for confirmation after a plan
Confirms and executes the apply phase for a run that has completed planning and is
waiting for approval. Use this when you've reviewed the plan output and want to
apply the proposed changes to your infrastructure.
API endpoint: POST /runs/{run_id}/actions/apply
Args:
run_id: The ID of the run to apply (format: "run-xxxxxxxx")
comment: An optional comment explaining the reason for applying the run
Returns:
Run details with updated status information and confirmation of the apply action
including timestamp information and any comment provided
See:
docs/tools/run.md for reference documentation
"""
request = RunActionRequest(run_id=run_id, comment=comment)
# Create payload if comment is provided
payload = {}
if request.comment:
payload = {"comment": request.comment}
# Make API request
return await api_request(
f"runs/{run_id}/actions/apply", method="POST", data=payload
)
@handle_api_errors
async def discard_run(run_id: str, comment: str = "") -> APIResponse:
"""Discard a run that is paused waiting for confirmation
Cancels a run without applying its changes, typically used when the plan
shows undesired changes or after reviewing and rejecting a plan. This action
removes the run from the queue and unlocks the workspace for new runs.
API endpoint: POST /runs/{run_id}/actions/discard
Args:
run_id: The ID of the run to discard (format: "run-xxxxxxxx")
comment: An optional explanation for why the run was discarded
Returns:
Run status update with discarded state information, timestamp of the
discard action, and user information
See:
docs/tools/run.md for reference documentation
"""
request = RunActionRequest(run_id=run_id, comment=comment)
# Create payload if comment is provided
payload = {}
if request.comment:
payload = {"comment": request.comment}
# Make API request
return await api_request(
f"runs/{run_id}/actions/discard", method="POST", data=payload
)
@handle_api_errors
async def cancel_run(run_id: str, comment: str = "") -> APIResponse:
"""Cancel a run that is currently planning or applying
Gracefully stops an in-progress run during planning or applying phases. Use this
when you need to stop a run that's taking too long, consuming too many resources,
or needs to be stopped for any reason. The operation attempts to cleanly terminate
the run by sending an interrupt signal.
API endpoint: POST /runs/{run_id}/actions/cancel
Args:
run_id: The ID of the run to cancel (format: "run-xxxxxxxx")
comment: An optional explanation for why the run was canceled
Returns:
Run status update with canceled state, timestamp of cancellation,
and any provided comment in the response metadata
See:
docs/tools/run.md for reference documentation
"""
request = RunActionRequest(run_id=run_id, comment=comment)
# Create payload if comment is provided
payload = {}
if request.comment:
payload = {"comment": request.comment}
# Make API request
return await api_request(
f"runs/{run_id}/actions/cancel", method="POST", data=payload
)
@handle_api_errors
async def force_cancel_run(run_id: str, comment: str = "") -> APIResponse:
"""Forcefully cancel a run immediately
Immediately terminates a run that hasn't responded to a normal cancel request.
Use this as a last resort when a run is stuck and not responding to regular
cancellation. This action bypasses the graceful shutdown process and forces
the workspace to be unlocked.
API endpoint: POST /runs/{run_id}/actions/force-cancel
Args:
run_id: The ID of the run to force cancel (format: "run-xxxxxxxx")
comment: An optional explanation for why the run was force canceled
Returns:
Run status update confirming forced cancellation with timestamp,
user information, and workspace unlock status
See:
docs/tools/run.md for reference documentation
"""
request = RunActionRequest(run_id=run_id, comment=comment)
# Create payload if comment is provided
payload = {}
if request.comment:
payload = {"comment": request.comment}
# Make API request
return await api_request(
f"runs/{run_id}/actions/force-cancel", method="POST", data=payload
)
@handle_api_errors
async def force_execute_run(run_id: str) -> APIResponse:
"""Forcefully execute a run by canceling all prior runs
Prioritizes a specific run by canceling other queued runs to unlock the workspace,
equivalent to clicking "Run this plan now" in the UI. Use this when a run is
stuck in the pending queue but needs immediate execution due to urgency or
priority over other queued runs.
API endpoint: POST /runs/{run_id}/actions/force-execute
Args:
run_id: The ID of the run to execute (format: "run-xxxxxxxx")
Returns:
Status update confirming the run has been promoted to active status,
with information about which runs were canceled to allow execution
See:
docs/tools/run.md for reference documentation
"""
# Make API request
return await api_request(f"runs/{run_id}/actions/force-execute", method="POST")
```
--------------------------------------------------------------------------------
/terraform_cloud_mcp/tools/workspaces.py:
--------------------------------------------------------------------------------
```python
"""Workspace management tools for Terraform Cloud MCP
This module implements the workspace-related endpoints of the Terraform Cloud API.
Reference: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/workspaces
"""
import logging
from typing import Optional
from ..api.client import api_request
from ..utils.decorators import handle_api_errors
from ..utils.payload import create_api_payload
from ..utils.request import query_params
from ..models.base import APIResponse
from ..models.workspaces import (
WorkspaceCreateRequest,
WorkspaceUpdateRequest,
WorkspaceListRequest,
WorkspaceParams,
DataRetentionPolicyRequest,
)
@handle_api_errors
async def create_workspace(
organization: str, name: str, params: Optional[WorkspaceParams] = None
) -> APIResponse:
"""Create a new workspace in an organization.
Creates a new Terraform Cloud workspace which serves as an isolated environment
for managing infrastructure. Workspaces contain variables, state files, and run
histories for a specific infrastructure configuration.
API endpoint: POST /organizations/{organization}/workspaces
Args:
organization: The name of the organization
name: The name to give the workspace
params: Additional workspace parameters (optional):
- description: Human-readable description of the workspace
- execution_mode: How Terraform runs are executed (remote, local, agent)
- terraform_version: Version of Terraform to use (default: latest)
- working_directory: Subdirectory to use when running Terraform
- vcs_repo: Version control repository configuration
- auto_apply: Whether to automatically apply successful plans
- file_triggers_enabled: Whether file changes trigger runs
- trigger_prefixes: Directories that trigger runs when changed
- trigger_patterns: Glob patterns that trigger runs when files match
- allow_destroy_plan: Whether to allow destruction plans
- auto_apply_run_trigger: Whether to auto-apply changes from run triggers
Returns:
The created workspace data including configuration, settings and metadata
See:
docs/tools/workspace.md for reference documentation
"""
param_dict = params.model_dump(exclude_none=True) if params else {}
request = WorkspaceCreateRequest(organization=organization, name=name, **param_dict)
payload = create_api_payload(
resource_type="workspaces", model=request, exclude_fields={"organization"}
)
return await api_request(
f"organizations/{organization}/workspaces", method="POST", data=payload
)
@handle_api_errors
async def update_workspace(
organization: str, workspace_name: str, params: Optional[WorkspaceParams] = None
) -> APIResponse:
"""Update an existing workspace.
Modifies the settings of a Terraform Cloud workspace. This can be used to change
attributes like execution mode, VCS repository settings, description, or any other
workspace configuration options. Only specified attributes will be updated;
unspecified attributes remain unchanged.
API endpoint: PATCH /organizations/{organization}/workspaces/{workspace_name}
Args:
organization: The name of the organization that owns the workspace
workspace_name: The name of the workspace to update
params: Workspace parameters to update (optional):
- name: New name for the workspace (if renaming)
- description: Human-readable description of the workspace
- execution_mode: How Terraform runs are executed (remote, local, agent)
- terraform_version: Version of Terraform to use
- working_directory: Subdirectory to use when running Terraform
- vcs_repo: Version control repository configuration (oauth-token-id, identifier)
- auto_apply: Whether to automatically apply successful plans
- file_triggers_enabled: Whether file changes trigger runs
- trigger_prefixes: Directories that trigger runs when changed
- trigger_patterns: Glob patterns that trigger runs when files match
- allow_destroy_plan: Whether to allow destruction plans
- auto_apply_run_trigger: Whether to auto-apply changes from run triggers
Returns:
The updated workspace with all current settings and configuration
See:
docs/tools/workspace.md for reference documentation
"""
# Extract parameters from the params object if provided
param_dict = params.model_dump(exclude_none=True) if params else {}
# Create request using Pydantic model
request = WorkspaceUpdateRequest(
organization=organization, workspace_name=workspace_name, **param_dict
)
# Create API payload using utility function
payload = create_api_payload(
resource_type="workspaces",
model=request,
exclude_fields={"organization", "workspace_name"},
)
# Log payload for debugging
logger = logging.getLogger(__name__)
logger.debug(f"Update workspace payload: {payload}")
# Make API request
response = await api_request(
f"organizations/{organization}/workspaces/{workspace_name}",
method="PATCH",
data=payload,
)
# Log response for debugging
logger.debug(f"Update workspace response: {response}")
return response
@handle_api_errors
async def list_workspaces(
organization: str,
page_number: int = 1,
page_size: int = 20,
search: Optional[str] = None,
) -> APIResponse:
"""List workspaces in an organization.
Retrieves a paginated list of all workspaces in a Terraform Cloud organization.
Results can be filtered using a search string to find specific workspaces by name.
Use this tool to discover existing workspaces, check workspace configurations,
or find specific workspaces by partial name match.
API endpoint: GET /organizations/{organization}/workspaces
Args:
organization: The name of the organization to list workspaces from
page_number: The page number to return (default: 1)
page_size: The number of items per page (default: 20, max: 100)
search: Optional search string to filter workspaces by name
Returns:
Paginated list of workspaces with their configuration settings and metadata
See:
docs/tools/workspace.md for reference documentation
"""
# Create request using Pydantic model for validation
request = WorkspaceListRequest(
organization=organization,
page_number=page_number,
page_size=page_size,
search=search,
)
params = query_params(request)
return await api_request(
f"organizations/{organization}/workspaces", method="GET", params=params
)
@handle_api_errors
async def delete_workspace(organization: str, workspace_name: str) -> APIResponse:
"""Delete a workspace.
Permanently deletes a Terraform Cloud workspace and all its resources including
state versions, run history, and configuration versions. This action cannot be undone.
WARNING: This is a destructive operation. For workspaces that have active resources,
consider running a destroy plan first or use safe_delete_workspace instead.
API endpoint: DELETE /organizations/{organization}/workspaces/{workspace_name}
Args:
organization: The name of the organization that owns the workspace
workspace_name: The name of the workspace to delete
Returns:
Success message with no content (HTTP 204) if successful
Error response with explanation if the workspace cannot be deleted
See:
docs/tools/workspace.md for reference documentation
"""
# Make API request
return await api_request(
f"organizations/{organization}/workspaces/{workspace_name}",
method="DELETE",
)
@handle_api_errors
async def safe_delete_workspace(organization: str, workspace_name: str) -> APIResponse:
"""Safely delete a workspace by first checking if it can be deleted.
Initiates a safe delete operation which checks if the workspace has resources
before deleting it. This is a safer alternative to delete_workspace as it prevents
accidental deletion of workspaces with active infrastructure.
The operation follows these steps:
1. Checks if the workspace has any resources
2. If no resources exist, deletes the workspace
3. If resources exist, returns an error indicating the workspace cannot be safely deleted
API endpoint: POST /organizations/{organization}/workspaces/{workspace_name}/actions/safe-delete
Args:
organization: The name of the organization that owns the workspace
workspace_name: The name of the workspace to delete
Returns:
Status of the safe delete operation including:
- Success response if deletion was completed
- Error with details if workspace has resources and cannot be safely deleted
- List of resources that would be affected by deletion (if applicable)
See:
docs/tools/workspace.md for reference documentation
"""
# Make API request
return await api_request(
f"organizations/{organization}/workspaces/{workspace_name}/actions/safe-delete",
method="POST",
)
@handle_api_errors
async def lock_workspace(workspace_id: str, reason: str = "") -> APIResponse:
"""Lock a workspace.
Locks a workspace to prevent runs from being queued. This is useful when you want
to prevent changes to infrastructure while performing maintenance or making manual
adjustments. Locking a workspace does not affect currently running plans or applies.
API endpoint: POST /workspaces/{workspace_id}/actions/lock
Args:
workspace_id: The ID of the workspace to lock (format: "ws-xxxxxxxx")
reason: Optional reason for locking
Returns:
The workspace with updated lock status and related metadata
See:
docs/tools/workspace.md for reference documentation
"""
payload = {}
if reason:
payload = {"reason": reason}
return await api_request(
f"workspaces/{workspace_id}/actions/lock", method="POST", data=payload
)
@handle_api_errors
async def unlock_workspace(workspace_id: str) -> APIResponse:
"""Unlock a workspace.
Removes the lock from a workspace, allowing runs to be queued. This enables
normal operation of the workspace after it was previously locked.
API endpoint: POST /workspaces/{workspace_id}/actions/unlock
Args:
workspace_id: The ID of the workspace to unlock (format: "ws-xxxxxxxx")
Returns:
The workspace with updated lock status and related metadata
See:
docs/tools/workspace.md for reference documentation
"""
return await api_request(f"workspaces/{workspace_id}/actions/unlock", method="POST")
@handle_api_errors
async def force_unlock_workspace(workspace_id: str) -> APIResponse:
"""Force unlock a workspace. This should be used with caution.
Forces a workspace to unlock even when the normal unlock process isn't possible.
This is typically needed when a run has orphaned a lock or when the user who locked
the workspace is unavailable. This operation requires admin privileges on the workspace.
WARNING: Forcing an unlock can be dangerous if the workspace is legitimately locked
for active operations. Only use this when you are certain it's safe to unlock.
API endpoint: POST /workspaces/{workspace_id}/actions/force-unlock
Args:
workspace_id: The ID of the workspace to force unlock (format: "ws-xxxxxxxx")
Returns:
The workspace with updated lock status and related metadata
See:
docs/tools/workspace.md for reference documentation
"""
# Make API request
return await api_request(
f"workspaces/{workspace_id}/actions/force-unlock", method="POST"
)
@handle_api_errors
async def set_data_retention_policy(workspace_id: str, days: int) -> APIResponse:
"""Set a data retention policy for a workspace.
Creates or updates a data retention policy that determines how long Terraform Cloud
keeps run history and state files for a workspace. This can be used to comply with
data retention requirements or to reduce resource usage.
API endpoint: POST /workspaces/{workspace_id}/relationships/data-retention-policy
Args:
workspace_id: The ID of the workspace (format: "ws-xxxxxxxx")
days: Number of days to retain data
Returns:
The created data retention policy with configuration details and timestamps
See:
docs/tools/workspace.md for reference documentation
"""
# Create request using Pydantic model
request = DataRetentionPolicyRequest(workspace_id=workspace_id, days=days)
# Create API payload using utility function
payload = create_api_payload(
resource_type="data-retention-policy",
model=request,
exclude_fields={"workspace_id"},
)
# Make API request
return await api_request(
f"workspaces/{workspace_id}/relationships/data-retention-policy",
method="POST",
data=payload,
)
@handle_api_errors
async def get_data_retention_policy(workspace_id: str) -> APIResponse:
"""Get the data retention policy for a workspace.
Retrieves the current data retention policy for a workspace, which defines how long
Terraform Cloud keeps run history and state files before automatic removal.
API endpoint: GET /workspaces/{workspace_id}/relationships/data-retention-policy
Args:
workspace_id: The ID of the workspace (format: "ws-xxxxxxxx")
Returns:
The data retention policy with configuration details and timestamps
See:
docs/tools/workspace.md for reference documentation
"""
# Make API request
return await api_request(
f"workspaces/{workspace_id}/relationships/data-retention-policy", method="GET"
)
@handle_api_errors
async def delete_data_retention_policy(workspace_id: str) -> APIResponse:
"""Delete the data retention policy for a workspace.
Removes the data retention policy from a workspace, reverting to the default behavior
of retaining all data indefinitely. This is useful when you no longer want to automatically
remove historical data after a certain period.
API endpoint: DELETE /workspaces/{workspace_id}/relationships/data-retention-policy
Args:
workspace_id: The ID of the workspace (format: "ws-xxxxxxxx")
Returns:
Empty response with HTTP 204 status code indicating successful deletion
See:
docs/tools/workspace.md for reference documentation
"""
# Make API request
return await api_request(
f"workspaces/{workspace_id}/relationships/data-retention-policy",
method="DELETE",
)
@handle_api_errors
async def get_workspace_details(
workspace_id: str = "", organization: str = "", workspace_name: str = ""
) -> APIResponse:
"""Get details for a specific workspace, identified either by ID or by org name and workspace name.
Retrieves comprehensive information about a workspace including its configuration,
VCS settings, execution mode, and other attributes. This is useful for checking
workspace settings before operations or determining the current state of a workspace.
The workspace can be identified either by its ID directly, or by the combination
of organization name and workspace name.
API endpoint:
- GET /workspaces/{workspace_id} (when using workspace_id)
- GET /organizations/{organization}/workspaces/{workspace_name} (when using org+name)
Args:
workspace_id: The ID of the workspace (format: "ws-xxxxxxxx")
organization: The name of the organization (required if workspace_id not provided)
workspace_name: The name of the workspace (required if workspace_id not provided)
Returns:
Comprehensive workspace details including settings, configuration and status
See:
docs/tools/workspace.md for reference documentation
"""
# Ensure we have either workspace_id OR both organization and workspace_name
if not workspace_id and not (organization and workspace_name):
raise ValueError(
"Either workspace_id OR both organization and workspace_name must be provided"
)
# Determine API path based on provided parameters
if workspace_id:
path = f"workspaces/{workspace_id}"
else:
path = f"organizations/{organization}/workspaces/{workspace_name}"
# Make API request
return await api_request(path, method="GET")
```
--------------------------------------------------------------------------------
/docs/DEVELOPMENT.md:
--------------------------------------------------------------------------------
```markdown
# Terraform Cloud MCP Development Guide
This document outlines the development guidelines, code standards, and best practices for the Terraform Cloud MCP project.
## Getting Started
### Development Setup
```bash
# Clone the repository
git clone https://github.com/severity1/terraform-cloud-mcp.git
cd terraform-cloud-mcp
# Create virtual environment and activate it
uv venv
source .venv/bin/activate
# Install in development mode (editable)
uv pip install -e .
# Install development dependencies
uv pip install black mypy pydantic ruff
```
### Build & Run Commands
- Setup: `uv pip install -e .` (install with latest changes)
- Install dev deps: `uv pip install black mypy pydantic ruff`
- Format: `uv pip install black && uv run -m black .`
- Type check: `uv pip install mypy && uv run -m mypy .`
- Lint: `uv pip install ruff && uv run -m ruff check .`
- Fix lint issues: `uv pip install ruff && uv run -m ruff check --fix .`
### Development With Claude Integrations
#### Adding to Claude Code (Development Mode)
```bash
# Add to Claude Code with your development path and token
claude mcp add -e TFC_TOKEN=YOUR_TF_TOKEN -s user terraform-cloud-mcp -- "$(pwd)/terraform_cloud_mcp/server.py"
# To use a self-hosted Terraform Enterprise instance:
# claude mcp add -e TFC_TOKEN=YOUR_TF_TOKEN -e TFC_ADDRESS=https://terraform.example.com -s user terraform-cloud-mcp -- "$(pwd)/terraform_cloud_mcp/server.py"
```
#### Adding to Claude Desktop (Development Mode)
Create a `claude_desktop_config.json` configuration file:
- mac: ~/Library/Application Support/Claude/claude_desktop_config.json
- win: %APPDATA%\Claude\claude_desktop_config.json
```json
{
"mcpServers": {
"terraform-cloud-mcp": {
"command": "/path/to/uv", # Get this by running: `which uv`
"args": [
"--directory",
"/path/to/your/terraform-cloud-mcp", # Full path to this project
"run",
"terraform_cloud_mcp/server.py"
],
"env": {
"TFC_TOKEN": "your_terraform_cloud_token", # Your actual TF Cloud token
"TFC_ADDRESS": "https://app.terraform.io" # Optional, change for self-hosted TFE
}
}
}
}
```
## Core Principles
- **KISS (Keep It Simple, Stupid)**: Favor simple, maintainable solutions over complex ones
- **DRY (Don't Repeat Yourself)**: Use utility functions for common patterns
- **Consistency**: Follow established patterns throughout the codebase
- **Type Safety**: Use proper typing and validation everywhere
- **Documentation**: All code should be well-documented with standardized formats
- **Testability**: Write code that can be easily tested
- **Modularity**: Keep components focused and decoupled
## Code Organization
```
terraform_cloud_mcp/
├── api/ # API client and core request handling
│ ├── __init__.py
│ └── client.py # Core API client with error handling
├── models/ # Pydantic data models for validation
│ ├── __init__.py
│ ├── account.py # Account-related models
│ ├── applies.py # Apply-related models
│ ├── assessment_results.py # Assessment results models
│ ├── base.py # Base model classes and shared types
│ ├── cost_estimates.py # Cost estimation models
│ ├── organizations.py # Organization models
│ ├── plans.py # Plan-related models
│ ├── projects.py # Project management models
│ ├── runs.py # Run management models
│ └── workspaces.py # Workspace management models
├── tools/ # Tool implementations exposed via MCP
│ ├── __init__.py
│ ├── account.py # Account management tools
│ ├── applies.py # Apply management tools
│ ├── assessment_results.py # Assessment results tools
│ ├── cost_estimates.py # Cost estimation tools
│ ├── organizations.py # Organization management tools
│ ├── plans.py # Plan management tools
│ ├── projects.py # Project management tools
│ ├── runs.py # Run management tools
│ └── workspaces.py # Workspace management tools
├── utils/ # Shared utilities
│ ├── __init__.py
│ ├── decorators.py # Error handling decorators
│ ├── filters.py # Response filtering for token optimization
│ ├── payload.py # JSON:API payload utilities
│ └── request.py # Request parameter utilities
└── server.py # MCP server entry point
```
## Code Style Guidelines
### General Guidelines
- **Imports**: stdlib → third-party → local, alphabetically within groups
- **Formatting**: Black, 100 char line limit
- **Types**: Type hints everywhere, Pydantic models for validation
- **Naming**: snake_case (functions/vars), PascalCase (classes), UPPER_CASE (constants)
- **Error handling**: Use `handle_api_errors` decorator from `terraform_cloud_mcp/utils/decorators.py`
- **Audit-safe filtering**: Automatic filtering system uses conservative 5-15% token reduction while preserving 100% audit compliance - all user accountability, security, and change tracking data preserved
- **Async pattern**: All API functions should be async, using httpx
- **Security**: Never log tokens, validate all inputs, redact sensitive data
- **MCP Tool Registration**: Follow minimal pattern in `server.py`:
- Use simple `mcp.tool()(function_name)` for standard operations
- Use `mcp.tool(enabled=False)(function_name)` for dangerous delete operations
## Pydantic Model Standards
### Pattern Principles
- **Validation First**: Use Pydantic to validate all input parameters
- **Explicit Aliases**: Use field aliases for API compatibility (`kebab-case` to `snake_case`)
- **Base Models**: Extend from common base classes
- **No Response Models**: Use `APIResponse` type alias (`Dict[str, Any]`) for responses
### Model Structure
1. **Base Classes**:
See the base classes defined in `terraform_cloud_mcp/models/base.py`:
- `BaseModelConfig` - Core configuration for all models
- `APIRequest` - Base class for all API requests
- `APIResponse` type alias
2. **Request Models**:
For examples of request models with field aliases, see:
- `VcsRepoConfig` in `terraform_cloud_mcp/models/workspaces.py`
- `WorkspaceCreateRequest` in `terraform_cloud_mcp/models/workspaces.py`
- `WorkspaceListRequest` in `terraform_cloud_mcp/models/workspaces.py`
3. **Enum Classes**:
For enum implementation examples, see:
- `ExecutionMode` in `terraform_cloud_mcp/models/base.py`
- Status enums in `terraform_cloud_mcp/models/runs.py`
### Implementation Pattern
1. **Tool Implementation**:
See the implementation pattern in `terraform_cloud_mcp/tools/workspaces.py`:
- `create_workspace` function for a complete implementation example
- `update_workspace` function for updating existing resources
- `list_workspaces` function for listing resources with pagination
- Other CRUD operations in workspace management tools
### Pydantic Best Practices
1. **Use Field Aliases** for API compatibility:
See the `execution_mode` field in `terraform_cloud_mcp/models/workspaces.py` (class `BaseWorkspaceRequest`)
2. **Use Field Validators** for constraints:
See the `page_size` field in `terraform_cloud_mcp/models/workspaces.py` (class `WorkspaceListRequest`)
3. **Use Description** for clarity:
See the `description` field in `terraform_cloud_mcp/models/workspaces.py` (class `BaseWorkspaceRequest`)
4. **Use Enums** for constrained choices:
See `ExecutionMode` enum in `terraform_cloud_mcp/models/base.py`
5. **Parameter Inheritance** for common parameters:
See the class hierarchy in `terraform_cloud_mcp/models/workspaces.py`:
- `BaseWorkspaceRequest` defines common fields
- `WorkspaceCreateRequest` extends it with required fields
- `WorkspaceUpdateRequest` adds routing fields
- `WorkspaceParams` provides a parameter object without routing fields
## Utility Functions
### JSON:API Payload Utilities
To ensure consistent handling of API payloads, use the utility functions from `terraform_cloud_mcp/utils/payload.py`:
1. **create_api_payload**:
See implementation in `terraform_cloud_mcp/utils/payload.py` and example usage in `create_workspace` function in `terraform_cloud_mcp/tools/workspaces.py`
2. **add_relationship**:
See implementation in `terraform_cloud_mcp/utils/payload.py` and usage examples in `terraform_cloud_mcp/tools/runs.py`
### Request Parameter Utilities
For handling pagination and request parameters, see `terraform_cloud_mcp/utils/request.py` and its usage in list operations like `list_workspaces` in `terraform_cloud_mcp/tools/workspaces.py`
## Documentation Standards
### Documentation Principles
- **Essential Information**: Focus on what's needed without verbosity
- **Completeness**: Provide enough context to understand usage
- **External References**: Move examples to dedicated documentation files
- **Agent-Friendly**: Include sufficient context for AI agents/LLMs
### Model Classes
See `VcsRepoConfig` class in `terraform_cloud_mcp/models/workspaces.py` for proper model documentation
For derived classes that inherit from a base class, see `WorkspaceCreateRequest` in `terraform_cloud_mcp/models/workspaces.py` as an example of how to document inheritance
### Tool Functions
See `create_workspace` function in `terraform_cloud_mcp/tools/workspaces.py` for proper tool function documentation including:
- Purpose description
- API endpoint reference
- Parameter documentation
- Return value description
- External documentation references
### Utility Functions
See utility functions in `terraform_cloud_mcp/utils/payload.py` and `terraform_cloud_mcp/utils/request.py` for examples of properly documented helper functions
### Documentation Structure
Documentation is organized in dedicated markdown files:
```
docs/
models/ # Documentation for Pydantic models
account.md
apply.md
assessment_result.md
cost_estimate.md
organization.md
plan.md
project.md
run.md
workspace.md
tools/ # Reference documentation for MCP tools
account.md
apply.md
assessment_results.md
cost_estimate.md
organization.md
plan.md
project.md
run.md
workspace.md
conversations/ # Example conversations using the tools
account.md
apply-management-conversation.md
assessment-results-conversation.md
cost-estimate-conversation.md
organization-entitlements-conversation.md
organizations-management-conversation.md
plan-management-conversation.md
project-management-conversation.md
runs-management-conversation.md
workspace-management-conversation.md
```
#### Tool Documentation Format
Each tool documentation file should follow this structure:
```markdown
# Module Name Tools
Brief introduction about the module's purpose.
## Overview
Detailed explanation of the functionality and concepts.
## API Reference
Links to relevant Terraform Cloud API documentation:
- [API Section 1](https://developer.hashicorp.com/terraform/cloud-docs/api-docs/section)
- [API Section 2](https://developer.hashicorp.com/terraform/cloud-docs/api-docs/section)
## Tools Reference
### function_name
**Function:** `function_name(param1: type, param2: type) -> ReturnType`
**Description:** Explanation of what the function does.
**Parameters:**
- `param1` (type): Parameter description
- `param2` (type): Parameter description
**Returns:** Description of return value structure
**Notes:**
- Important usage information
- Related functionality
- Permissions required
**Common Error Scenarios:**
| Error | Cause | Solution |
|-------|-------|----------|
| 404 | Resource not found | Verify ID and permissions |
| 422 | Invalid parameters | Ensure values match required format |
```
## Code Commenting Standards
The KISS (Keep It Simple, Stupid) principle applies to comments as much as code. Comments should be minimal, precise, and focused only on what's not obvious from the code itself.
### Comment Only When Necessary
Add comments only in these essential situations:
1. **Non-obvious "Why"**: Explain reasoning that isn't evident from reading the code
2. **Complex Logic**: Brief explanation of multi-step transformations or algorithms
3. **Edge Cases**: Why special handling is needed for boundary conditions
4. **Security Considerations**: Rationale for security measures (without exposing vulnerabilities)
5. **API Requirements**: Explanations of why code conforms to specific API requirements
### Avoid Unnecessary Comments
1. **No "What" Comments**: Don't describe what the code does when it's self-evident
2. **No Redundant Information**: Don't repeat documentation that exists in docstrings
3. **No Commented-Out Code**: Delete unused code rather than commenting it out
4. **No Obvious Comments**: Don't state the obvious (e.g., "Increment counter")
### Effective Comment Examples
#### For Complex Transformations
```python
# Transform variables array to required API format with key-value pairs
variables_array = [{"key": var.key, "value": var.value} for var in variables]
```
#### For Error Handling
```python
# Return standardized success response for 204 No Content to ensure consistent interface
if response.status_code == 204:
return {"status": "success"}
```
#### For Security Measures
```python
# Redact token from error message to prevent credential exposure
error_message = error_message.replace(token, "[REDACTED]")
```
#### For Performance Considerations
```python
# Use exclude_unset to prevent default values from overriding server defaults
request_data = data.model_dump(exclude_unset=True)
```
## Quality Assurance Protocol
### Mandatory Quality Check Sequence
After ANY code changes, run these commands in exact order:
1. **`uv run -m ruff check --fix .`** - Fix linting issues automatically
2. **`uv run -m black .`** - Format code consistently
3. **`uv run -m mypy .`** - Verify type safety
4. **Manual Testing** - Test basic tool functionality (see methodology below)
5. **Documentation Completeness** - Verify using checklist below
**IMPORTANT**: Fix all issues at each step before proceeding to the next step.
### Manual Testing Methodology
For each new tool, test these scenarios in order:
1. **Happy Path**: Test with valid, typical parameters
2. **Edge Cases**: Test with boundary values, empty strings, None values
3. **Error Cases**: Test with invalid IDs, missing permissions, malformed data
4. **Integration**: Test with related tools in realistic workflows
### Documentation Completeness Checklist
- [ ] Function docstring includes API endpoint reference
- [ ] Parameter descriptions include formats and constraints
- [ ] Return value description explains structure and key fields
- [ ] docs/tools/ entry created with function signature and examples
- [ ] docs/models/ entry created if new models added
- [ ] docs/conversations/ updated with realistic usage scenario
- [ ] Cross-references between all documentation layers verified
- [ ] All links in documentation are valid and accessible
### Quality Standards
- **Code Coverage**: All new functions must have comprehensive docstrings
- **Type Safety**: All parameters and return values must have type hints
- **Error Handling**: All tools must use @handle_api_errors decorator
- **Security**: No tokens or sensitive data in logs or error messages
- **Consistency**: New code must follow established patterns in existing codebase
## Enhanced Code Style Guidelines
### Core Principles
- **KISS Principle**: Keep It Simple, Stupid. Favor simple, maintainable solutions over complex ones.
- **DRY Principle**: Don't Repeat Yourself. Use utility functions for common patterns.
- **Imports**: stdlib → third-party → local, alphabetically within groups
- **Formatting**: Black, 100 char line limit
- **Types**: Type hints everywhere, Pydantic models for validation
- **Naming**: snake_case (functions/vars), PascalCase (classes), UPPER_CASE (constants)
- **Error handling**: Use `handle_api_errors` decorator from `terraform_cloud_mcp/utils/decorators.py`
- **Async pattern**: All API functions should be async, using httpx
- **Security**: Never log tokens, validate all inputs, redact sensitive data
### Pydantic Patterns
See `terraform_cloud_mcp/models/workspaces.py` for reference implementation:
- Use `BaseModelConfig` base class for common configuration
- Use `APIRequest` for request validation
- Define explicit model classes for parameter objects (e.g., `WorkspaceParams`)
- Use `params` parameter instead of `**kwargs` in tool functions
- Use explicit field aliases (e.g., `alias="kebab-case-name"`) for API field mapping
- Type API responses as `APIResponse` (alias for `Dict[str, Any]`)
### Utility Functions
Use common utilities for repetitive patterns:
- `create_api_payload()` from `utils/payload.py` for JSON:API payload creation
- `add_relationship()` from `utils/payload.py` for relationship management
- `query_params()` from `utils/request.py` for converting model to API parameters
### API Response Handling
- Handle 204 No Content responses properly, returning `{"status": "success", "status_code": 204}`
- Implement custom redirect handling for pre-signed URLs
- Use proper error handling for JSON parsing failures
### Documentation Standards
- Docstrings with Args/Returns for all functions
- Reference specific code implementations in docs rather than code snippets
- See cost_estimates.py for latest documentation patterns
### Comments Guidelines
Follow KISS principles for comments:
- Only explain the non-obvious "why" behind code choices, not the "what"
- Add comments for complex logic, edge cases, security measures, or API-specific requirements
- Avoid redundant, unnecessary, or self-explanatory comments
- Keep comments concise and directly relevant
## Contributing
For details on how to extend the server, contribute code, and the release process, see our [Contributing Guide](CONTRIBUTING.md).
```
--------------------------------------------------------------------------------
/terraform_cloud_mcp/tools/variables.py:
--------------------------------------------------------------------------------
```python
"""Variable management tools for Terraform Cloud MCP
This module implements workspace variables and variable sets endpoints
of the Terraform Cloud API.
Reference:
- https://developer.hashicorp.com/terraform/cloud-docs/api-docs/workspace-variables
- https://developer.hashicorp.com/terraform/cloud-docs/api-docs/variable-sets
"""
from typing import List, Optional
from ..api.client import api_request
from ..utils.decorators import handle_api_errors
from ..utils.payload import create_api_payload
from ..utils.request import query_params
from ..models.base import APIResponse
from ..models.variables import (
WorkspaceVariableCreateRequest,
WorkspaceVariableUpdateRequest,
WorkspaceVariableParams,
VariableSetCreateRequest,
VariableSetUpdateRequest,
VariableSetParams,
VariableSetVariableParams,
VariableSetListRequest,
VariableCategory,
)
# Workspace Variables Tools
@handle_api_errors
async def list_workspace_variables(workspace_id: str) -> APIResponse:
"""List all variables for a workspace.
Retrieves all variables (both Terraform and environment) configured
for a specific workspace.
API endpoint: GET /workspaces/{workspace_id}/vars
Args:
workspace_id: The ID of the workspace (format: "ws-xxxxxxxx")
Returns:
List of workspace variables with their configuration and values
See:
docs/tools/variables.md#list-workspace-variables for reference documentation
"""
endpoint = f"workspaces/{workspace_id}/vars"
return await api_request(endpoint, method="GET")
@handle_api_errors
async def create_workspace_variable(
workspace_id: str,
key: str,
category: str,
params: Optional[WorkspaceVariableParams] = None,
) -> APIResponse:
"""Create a new variable in a workspace.
Creates a new Terraform or environment variable within a workspace.
Variables can be marked as sensitive to hide their values.
API endpoint: POST /workspaces/{workspace_id}/vars
Args:
workspace_id: The ID of the workspace (format: "ws-xxxxxxxx")
key: The variable name/key
category: Variable category ("terraform" or "env")
params: Additional variable parameters (optional):
- value: Variable value
- description: Description of the variable
- hcl: Whether the value is HCL code (terraform variables only)
- sensitive: Whether the variable value is sensitive
Returns:
The created variable with its configuration and metadata
See:
docs/tools/variables.md#create-workspace-variable for reference documentation
"""
param_dict = params.model_dump(exclude_none=True) if params else {}
request = WorkspaceVariableCreateRequest(
workspace_id=workspace_id,
key=key,
category=VariableCategory(category),
**param_dict,
)
payload = create_api_payload(
resource_type="vars", model=request, exclude_fields={"workspace_id"}
)
return await api_request(
f"workspaces/{workspace_id}/vars", method="POST", data=payload
)
@handle_api_errors
async def update_workspace_variable(
workspace_id: str,
variable_id: str,
params: Optional[WorkspaceVariableParams] = None,
) -> APIResponse:
"""Update an existing workspace variable.
Modifies the configuration of an existing workspace variable. Only
specified attributes will be updated; unspecified attributes remain unchanged.
API endpoint: PATCH /workspaces/{workspace_id}/vars/{variable_id}
Args:
workspace_id: The ID of the workspace (format: "ws-xxxxxxxx")
variable_id: The ID of the variable (format: "var-xxxxxxxx")
params: Variable parameters to update (optional):
- key: New variable name/key
- value: New variable value
- description: New description of the variable
- category: New variable category ("terraform" or "env")
- hcl: Whether the value is HCL code (terraform variables only)
- sensitive: Whether the variable value is sensitive
Returns:
The updated variable with all current settings and configuration
See:
docs/tools/variables.md#update-workspace-variable for reference documentation
"""
param_dict = params.model_dump(exclude_none=True) if params else {}
request = WorkspaceVariableUpdateRequest(
workspace_id=workspace_id, variable_id=variable_id, **param_dict
)
payload = create_api_payload(
resource_type="vars",
model=request,
exclude_fields={"workspace_id", "variable_id"},
)
return await api_request(
f"workspaces/{workspace_id}/vars/{variable_id}", method="PATCH", data=payload
)
@handle_api_errors
async def delete_workspace_variable(workspace_id: str, variable_id: str) -> APIResponse:
"""Delete a workspace variable.
Permanently removes a variable from a workspace. This action cannot be undone.
API endpoint: DELETE /workspaces/{workspace_id}/vars/{variable_id}
Args:
workspace_id: The ID of the workspace (format: "ws-xxxxxxxx")
variable_id: The ID of the variable (format: "var-xxxxxxxx")
Returns:
Empty response with HTTP 204 status code if successful
See:
docs/tools/variables.md#delete-workspace-variable for reference documentation
"""
endpoint = f"workspaces/{workspace_id}/vars/{variable_id}"
return await api_request(endpoint, method="DELETE")
# Variable Sets Tools
@handle_api_errors
async def list_variable_sets(
organization: str,
page_number: int = 1,
page_size: int = 20,
) -> APIResponse:
"""List variable sets in an organization.
Retrieves a paginated list of all variable sets in a Terraform Cloud organization.
Variable sets allow you to reuse variables across multiple workspaces.
API endpoint: GET /organizations/{organization}/varsets
Args:
organization: The name of the organization
page_number: The page number to return (default: 1)
page_size: The number of items per page (default: 20, max: 100)
Returns:
Paginated list of variable sets with their configuration and metadata
See:
docs/tools/variables.md#list-variable-sets for reference documentation
"""
request = VariableSetListRequest(
organization=organization,
page_number=page_number,
page_size=page_size,
)
params = query_params(request)
return await api_request(
f"organizations/{organization}/varsets", method="GET", params=params
)
@handle_api_errors
async def get_variable_set(varset_id: str) -> APIResponse:
"""Get details for a specific variable set.
Retrieves comprehensive information about a variable set including its
variables, workspace assignments, and configuration.
API endpoint: GET /varsets/{varset_id}
Args:
varset_id: The ID of the variable set (format: "varset-xxxxxxxx")
Returns:
Variable set details including configuration and relationships
See:
docs/tools/variables.md#get-variable-set for reference documentation
"""
endpoint = f"varsets/{varset_id}"
return await api_request(endpoint, method="GET")
@handle_api_errors
async def create_variable_set(
organization: str,
name: str,
params: Optional[VariableSetParams] = None,
) -> APIResponse:
"""Create a new variable set in an organization.
Creates a new variable set which can be used to manage variables across
multiple workspaces and projects.
API endpoint: POST /organizations/{organization}/varsets
Args:
organization: The name of the organization
name: The name to give the variable set
params: Additional variable set parameters (optional):
- description: Description of the variable set
- global: Whether this is a global variable set
- priority: Whether this variable set takes priority over workspace variables
Returns:
The created variable set with its configuration and metadata
See:
docs/tools/variables.md#create-variable-set for reference documentation
"""
param_dict = params.model_dump(exclude_none=True) if params else {}
request = VariableSetCreateRequest(
organization=organization, name=name, **param_dict
)
payload = create_api_payload(
resource_type="varsets", model=request, exclude_fields={"organization"}
)
return await api_request(
f"organizations/{organization}/varsets", method="POST", data=payload
)
@handle_api_errors
async def update_variable_set(
varset_id: str,
params: Optional[VariableSetParams] = None,
) -> APIResponse:
"""Update an existing variable set.
Modifies the settings of a variable set. Only specified attributes will be
updated; unspecified attributes remain unchanged.
API endpoint: PATCH /varsets/{varset_id}
Args:
varset_id: The ID of the variable set (format: "varset-xxxxxxxx")
params: Variable set parameters to update (optional):
- name: New name for the variable set
- description: New description of the variable set
- global: Whether this is a global variable set
- priority: Whether this variable set takes priority over workspace variables
Returns:
The updated variable set with all current settings and configuration
See:
docs/tools/variables.md#update-variable-set for reference documentation
"""
param_dict = params.model_dump(exclude_none=True) if params else {}
request = VariableSetUpdateRequest(varset_id=varset_id, **param_dict)
payload = create_api_payload(
resource_type="varsets", model=request, exclude_fields={"varset_id"}
)
return await api_request(f"varsets/{varset_id}", method="PATCH", data=payload)
@handle_api_errors
async def delete_variable_set(varset_id: str) -> APIResponse:
"""Delete a variable set.
Permanently removes a variable set and all its variables. This action cannot be undone.
The variable set will be unassigned from all workspaces and projects.
API endpoint: DELETE /varsets/{varset_id}
Args:
varset_id: The ID of the variable set (format: "varset-xxxxxxxx")
Returns:
Empty response with HTTP 204 status code if successful
See:
docs/tools/variables.md#delete-variable-set for reference documentation
"""
endpoint = f"varsets/{varset_id}"
return await api_request(endpoint, method="DELETE")
@handle_api_errors
async def assign_variable_set_to_workspaces(
varset_id: str, workspace_ids: List[str]
) -> APIResponse:
"""Assign a variable set to one or more workspaces.
Makes the variables in a variable set available to the specified workspaces.
Variables from variable sets take precedence over workspace variables if
the variable set has priority enabled.
API endpoint: POST /varsets/{varset_id}/relationships/workspaces
Args:
varset_id: The ID of the variable set (format: "varset-xxxxxxxx")
workspace_ids: List of workspace IDs (format: ["ws-xxxxxxxx", ...])
Returns:
Empty response with HTTP 204 status code if successful
See:
docs/tools/variables.md#assign-variable-set-to-workspaces for reference documentation
"""
# Build relationships payload
relationships_data = []
for workspace_id in workspace_ids:
relationships_data.append({"id": workspace_id, "type": "workspaces"})
payload = {"data": relationships_data}
endpoint = f"varsets/{varset_id}/relationships/workspaces"
return await api_request(endpoint, method="POST", data=payload)
@handle_api_errors
async def unassign_variable_set_from_workspaces(
varset_id: str, workspace_ids: List[str]
) -> APIResponse:
"""Remove a variable set from one or more workspaces.
Removes the variable set assignment from the specified workspaces. The variables
will no longer be available in those workspaces.
API endpoint: DELETE /varsets/{varset_id}/relationships/workspaces
Args:
varset_id: The ID of the variable set (format: "varset-xxxxxxxx")
workspace_ids: List of workspace IDs (format: ["ws-xxxxxxxx", ...])
Returns:
Empty response with HTTP 204 status code if successful
See:
docs/tools/variables.md#unassign-variable-set-from-workspaces for reference documentation
"""
# Build relationships payload
relationships_data = []
for workspace_id in workspace_ids:
relationships_data.append({"type": "workspaces", "id": workspace_id})
payload = {"data": relationships_data}
endpoint = f"varsets/{varset_id}/relationships/workspaces"
return await api_request(endpoint, method="DELETE", data=payload)
@handle_api_errors
async def assign_variable_set_to_projects(
varset_id: str, project_ids: List[str]
) -> APIResponse:
"""Assign a variable set to one or more projects.
Makes the variables in a variable set available to all workspaces within
the specified projects.
API endpoint: POST /varsets/{varset_id}/relationships/projects
Args:
varset_id: The ID of the variable set (format: "varset-xxxxxxxx")
project_ids: List of project IDs (format: ["prj-xxxxxxxx", ...])
Returns:
Empty response with HTTP 204 status code if successful
See:
docs/tools/variables.md#assign-variable-set-to-projects for reference documentation
"""
# Build relationships payload
relationships_data = []
for project_id in project_ids:
relationships_data.append({"id": project_id, "type": "projects"})
payload = {"data": relationships_data}
endpoint = f"varsets/{varset_id}/relationships/projects"
return await api_request(endpoint, method="POST", data=payload)
@handle_api_errors
async def unassign_variable_set_from_projects(
varset_id: str, project_ids: List[str]
) -> APIResponse:
"""Remove a variable set from one or more projects.
Removes the variable set assignment from the specified projects. The variables
will no longer be available in workspaces within those projects.
API endpoint: DELETE /varsets/{varset_id}/relationships/projects
Args:
varset_id: The ID of the variable set (format: "varset-xxxxxxxx")
project_ids: List of project IDs (format: ["prj-xxxxxxxx", ...])
Returns:
Empty response with HTTP 204 status code if successful
See:
docs/tools/variables.md#unassign-variable-set-from-projects for reference documentation
"""
# Build relationships payload
relationships_data = []
for project_id in project_ids:
relationships_data.append({"type": "projects", "id": project_id})
payload = {"data": relationships_data}
endpoint = f"varsets/{varset_id}/relationships/projects"
return await api_request(endpoint, method="DELETE", data=payload)
# Variable Set Variables Tools
@handle_api_errors
async def list_variables_in_variable_set(varset_id: str) -> APIResponse:
"""List all variables in a variable set.
Retrieves all variables that belong to a specific variable set,
including their configuration and values.
API endpoint: GET /varsets/{varset_id}/relationships/vars
Args:
varset_id: The ID of the variable set (format: "varset-xxxxxxxx")
Returns:
List of variables in the variable set with their configuration
See:
docs/tools/variables.md#list-variables-in-variable-set for reference documentation
"""
endpoint = f"varsets/{varset_id}/relationships/vars"
return await api_request(endpoint, method="GET")
@handle_api_errors
async def create_variable_in_variable_set(
varset_id: str,
key: str,
category: str,
params: Optional[VariableSetVariableParams] = None,
) -> APIResponse:
"""Create a new variable in a variable set.
Creates a new Terraform or environment variable within a variable set.
Variables can be marked as sensitive to hide their values.
API endpoint: POST /varsets/{varset_id}/relationships/vars
Args:
varset_id: The ID of the variable set (format: "varset-xxxxxxxx")
key: The variable name/key
category: Variable category ("terraform" or "env")
params: Additional variable parameters (optional):
- value: Variable value
- description: Description of the variable
- hcl: Whether the value is HCL code (terraform variables only)
- sensitive: Whether the variable value is sensitive
Returns:
The created variable with its configuration and metadata
See:
docs/tools/variables.md#create-variable-in-variable-set for reference documentation
"""
# Create a temporary request-like structure for the variable
# Note: We don't have specific models for variable set variables yet
var_data = {
"key": key,
"category": VariableCategory(category).value,
}
if params:
param_dict = params.model_dump(exclude_none=True)
var_data.update(param_dict)
payload = {"data": {"type": "vars", "attributes": var_data}}
return await api_request(
f"varsets/{varset_id}/relationships/vars", method="POST", data=payload
)
@handle_api_errors
async def update_variable_in_variable_set(
varset_id: str,
var_id: str,
params: Optional[VariableSetVariableParams] = None,
) -> APIResponse:
"""Update an existing variable in a variable set.
Modifies the configuration of an existing variable within a variable set. Only
specified attributes will be updated; unspecified attributes remain unchanged.
API endpoint: PATCH /varsets/{varset_id}/relationships/vars/{var_id}
Args:
varset_id: The ID of the variable set (format: "varset-xxxxxxxx")
var_id: The ID of the variable (format: "var-xxxxxxxx")
params: Variable parameters to update (optional):
- key: New variable name/key
- value: New variable value
- description: New description of the variable
- category: New variable category ("terraform" or "env")
- hcl: Whether the value is HCL code (terraform variables only)
- sensitive: Whether the variable value is sensitive
Returns:
The updated variable with all current settings and configuration
See:
docs/tools/variables.md#update-variable-in-variable-set for reference documentation
"""
param_dict = params.model_dump(exclude_none=True) if params else {}
payload = {"data": {"type": "vars", "attributes": param_dict}}
return await api_request(
f"varsets/{varset_id}/relationships/vars/{var_id}", method="PATCH", data=payload
)
@handle_api_errors
async def delete_variable_from_variable_set(varset_id: str, var_id: str) -> APIResponse:
"""Delete a variable from a variable set.
Permanently removes a variable from a variable set. This action cannot be undone.
API endpoint: DELETE /varsets/{varset_id}/relationships/vars/{var_id}
Args:
varset_id: The ID of the variable set (format: "varset-xxxxxxxx")
var_id: The ID of the variable (format: "var-xxxxxxxx")
Returns:
Empty response with HTTP 204 status code if successful
See:
docs/tools/variables.md#delete-variable-from-variable-set for reference documentation
"""
endpoint = f"varsets/{varset_id}/relationships/vars/{var_id}"
return await api_request(endpoint, method="DELETE")
```