Skip to content

Commit d44c442

Browse files
feat: add workspace resources functionality
- Add WorkspaceResource model and WorkspaceResourceListOptions - Add WorkspaceResourcesService for listing workspace resources - Add workspace_resources.py example CLI tool with flag-based interface - Add comprehensive unit tests for workspace resources - Update client.py to include workspace_resources service - Update models/__init__.py with WorkspaceResource exports
1 parent 2a6d5bc commit d44c442

6 files changed

Lines changed: 536 additions & 0 deletions

File tree

examples/workspace_resources.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
"""Example script for working with workspace resources in Terraform Enterprise.
2+
3+
This script demonstrates how to list resources within a workspace.
4+
"""
5+
6+
import argparse
7+
import sys
8+
9+
from pytfe import TFEClient
10+
from pytfe.models import WorkspaceResourceListOptions
11+
12+
13+
def list_workspace_resources(
14+
client: TFEClient,
15+
workspace_id: str,
16+
page_number: int | None = None,
17+
page_size: int | None = None,
18+
) -> None:
19+
"""List all resources in a workspace."""
20+
try:
21+
print(f"Listing resources for workspace: {workspace_id}")
22+
23+
# Prepare list options
24+
options = None
25+
if page_number or page_size:
26+
options = WorkspaceResourceListOptions()
27+
if page_number:
28+
options.page_number = page_number
29+
if page_size:
30+
options.page_size = page_size
31+
32+
# List workspace resources (returns an iterator)
33+
resources = list(client.workspace_resources.list(workspace_id, options))
34+
35+
if not resources:
36+
print("No resources found in this workspace.")
37+
return
38+
39+
print(f"\nFound {len(resources)} resource(s):")
40+
print("-" * 80)
41+
42+
for resource in resources:
43+
print(f"ID: {resource.id}")
44+
print(f"Address: {resource.address}")
45+
print(f"Name: {resource.name}")
46+
print(f"Module: {resource.module}")
47+
print(f"Provider: {resource.provider}")
48+
print(f"Provider Type: {resource.provider_type}")
49+
print(f"Created At: {resource.created_at}")
50+
print(f"Updated At: {resource.updated_at}")
51+
print(f"Modified By State Version: {resource.modified_by_state_version_id}")
52+
if resource.name_index:
53+
print(f"Name Index: {resource.name_index}")
54+
print("-" * 80)
55+
56+
except Exception as e:
57+
print(f"Error listing workspace resources: {e}", file=sys.stderr)
58+
sys.exit(1)
59+
60+
61+
def main():
62+
"""Main function to handle command line arguments and execute operations."""
63+
parser = argparse.ArgumentParser(
64+
description="Manage workspace resources in Terraform Enterprise",
65+
formatter_class=argparse.RawDescriptionHelpFormatter,
66+
epilog="""
67+
Examples:
68+
# List all resources in a workspace
69+
python workspace_resources.py --list --workspace-id ws-abc123
70+
71+
# List with pagination
72+
python workspace_resources.py --list --workspace-id ws-abc123 --page-number 2 --page-size 50
73+
74+
Environment variables:
75+
TFE_TOKEN: Your Terraform Enterprise API token
76+
TFE_URL: Your Terraform Enterprise URL (default: https://app.terraform.io)
77+
TFE_ORG: Your Terraform Enterprise organization name
78+
""",
79+
)
80+
81+
# Add command flags
82+
parser.add_argument(
83+
"--list",
84+
action="store_true",
85+
help="List workspace resources"
86+
)
87+
parser.add_argument(
88+
"--workspace-id",
89+
required=True,
90+
help="ID of the workspace (required, e.g., ws-abc123)"
91+
)
92+
parser.add_argument(
93+
"--page-number",
94+
type=int,
95+
help="Page number for pagination"
96+
)
97+
parser.add_argument(
98+
"--page-size",
99+
type=int,
100+
help="Page size for pagination"
101+
)
102+
103+
args = parser.parse_args()
104+
105+
if not args.list:
106+
parser.print_help()
107+
sys.exit(1)
108+
109+
# Initialize TFE client
110+
try:
111+
client = TFEClient()
112+
except Exception as e:
113+
print(f"Error initializing TFE client: {e}", file=sys.stderr)
114+
print(
115+
"Make sure TFE_TOKEN and TFE_URL environment variables are set.",
116+
file=sys.stderr,
117+
)
118+
sys.exit(1)
119+
120+
# Execute the list command
121+
list_workspace_resources(
122+
client,
123+
args.workspace_id,
124+
args.page_number,
125+
args.page_size,
126+
)
127+
128+
129+
if __name__ == "__main__":
130+
main()

src/pytfe/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from .resources.state_versions import StateVersions
3434
from .resources.variable import Variables
3535
from .resources.variable_sets import VariableSets, VariableSetVariables
36+
from .resources.workspace_resources import WorkspaceResourcesService
3637
from .resources.workspaces import Workspaces
3738

3839

@@ -72,6 +73,7 @@ def __init__(self, config: TFEConfig | None = None):
7273
self.variable_sets = VariableSets(self._transport)
7374
self.variable_set_variables = VariableSetVariables(self._transport)
7475
self.workspaces = Workspaces(self._transport)
76+
self.workspace_resources = WorkspaceResourcesService(self._transport)
7577
self.registry_modules = RegistryModules(self._transport)
7678
self.registry_providers = RegistryProviders(self._transport)
7779

src/pytfe/models/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,12 @@
353353
WorkspaceUpdateRemoteStateConsumersOptions,
354354
)
355355

356+
# ── Workspace Resources ───────────────────────────────────────────────────────
357+
from .workspace_resource import (
358+
WorkspaceResource,
359+
WorkspaceResourceListOptions,
360+
)
361+
356362
# ── Public surface ────────────────────────────────────────────────────────────
357363
__all__ = [
358364
# OAuth
@@ -524,6 +530,9 @@
524530
"WorkspaceTagListOptions",
525531
"WorkspaceUpdateOptions",
526532
"WorkspaceUpdateRemoteStateConsumersOptions",
533+
# Workspace Resources
534+
"WorkspaceResource",
535+
"WorkspaceResourceListOptions",
527536
"RunQueue",
528537
"ReadRunQueueOptions",
529538
# Runs
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""Workspace resources models for Terraform Enterprise."""
2+
3+
from pydantic import BaseModel
4+
5+
6+
class WorkspaceResource(BaseModel):
7+
"""Represents a Terraform Enterprise workspace resource.
8+
9+
These are resources managed by Terraform in a workspace's state.
10+
"""
11+
12+
id: str
13+
address: str
14+
name: str
15+
created_at: str
16+
updated_at: str
17+
module: str
18+
provider: str
19+
provider_type: str
20+
modified_by_state_version_id: str
21+
name_index: str | None = None
22+
23+
24+
class WorkspaceResourceListOptions(BaseModel):
25+
"""Options for listing workspace resources."""
26+
27+
# Pagination
28+
page_number: int | None = None
29+
page_size: int | None = None
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""Workspace resources service for Terraform Enterprise."""
2+
3+
import urllib.parse
4+
from collections.abc import Iterator
5+
from typing import Any
6+
7+
from pytfe.models import (
8+
WorkspaceResource,
9+
WorkspaceResourceListOptions,
10+
)
11+
12+
from ._base import _Service
13+
14+
15+
def _workspace_resource_from(data: dict[str, Any]) -> WorkspaceResource:
16+
"""Convert API response data to WorkspaceResource model."""
17+
attributes = data.get("attributes", {})
18+
19+
return WorkspaceResource(
20+
id=data.get("id", ""),
21+
address=attributes.get("address", ""),
22+
name=attributes.get("name", ""),
23+
created_at=attributes.get("created-at", ""),
24+
updated_at=attributes.get("updated-at", ""),
25+
module=attributes.get("module", ""),
26+
provider=attributes.get("provider", ""),
27+
provider_type=attributes.get("provider-type", ""),
28+
modified_by_state_version_id=attributes.get("modified-by-state-version-id", ""),
29+
name_index=attributes.get("name-index"),
30+
)
31+
32+
33+
class WorkspaceResourcesService(_Service):
34+
"""Service for managing workspace resources in Terraform Enterprise.
35+
36+
Workspace resources represent the infrastructure resources
37+
managed by Terraform in a workspace's state file.
38+
"""
39+
40+
def list(
41+
self, workspace_id: str, options: WorkspaceResourceListOptions | None = None
42+
) -> Iterator[WorkspaceResource]:
43+
"""List workspace resources for a given workspace.
44+
45+
Args:
46+
workspace_id: The ID of the workspace to list resources for
47+
options: Optional query parameters for filtering and pagination
48+
49+
Yields:
50+
WorkspaceResource objects
51+
"""
52+
if not workspace_id or not workspace_id.strip():
53+
raise ValueError("workspace_id is required")
54+
55+
# URL encode the workspace ID and construct URL
56+
encoded_workspace_id = urllib.parse.quote(workspace_id, safe="")
57+
url = f"/api/v2/workspaces/{encoded_workspace_id}/resources"
58+
59+
# Handle parameters
60+
params: dict[str, int] = {}
61+
if options:
62+
if options.page_number is not None:
63+
params["page[number]"] = options.page_number
64+
if options.page_size is not None:
65+
params["page[size]"] = options.page_size
66+
67+
# Use the _list method from base service to handle pagination
68+
for item in self._list(url, params=params):
69+
yield _workspace_resource_from(item)

0 commit comments

Comments
 (0)