Skip to content

Commit 162ec84

Browse files
authored
Merge pull request #16 from hashicorp/feature/aayush-workspacevariables
TF-30336 - Feature providing workspace variables related support to sdk
2 parents 938566f + ebfbbe7 commit 162ec84

5 files changed

Lines changed: 519 additions & 0 deletions

File tree

examples/variables.py

Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Comprehensive example testing all variable functions in TFE workspace.
4+
Tests: list, list_all, create, read, update, and delete operations.
5+
"""
6+
7+
import os
8+
import sys
9+
import time
10+
11+
# Add the src directory to the path
12+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
13+
14+
from tfe import TFEClient, TFEConfig
15+
from tfe.types import CategoryType, VariableCreateOptions, VariableUpdateOptions
16+
17+
18+
def main():
19+
"""Test all variable operations in a workspace."""
20+
21+
# Initialize the TFE client
22+
client = TFEClient(TFEConfig.from_env())
23+
24+
# Replace this with your actual workspace ID
25+
workspace_id = "ws-example123456789" # Get this from your TFE workspace
26+
27+
print(f"Testing all variable operations in workspace: {workspace_id}")
28+
print("=" * 60)
29+
30+
# Track created variables for cleanup
31+
created_variables = []
32+
33+
try:
34+
# 1. Test CREATE function - COMMENTED OUT (already have variables from previous run)
35+
print("\n1. Testing CREATE operation:")
36+
print("-" * 30)
37+
38+
# Create a Terraform variable
39+
terraform_var = VariableCreateOptions(
40+
key="test_terraform_var",
41+
value="production",
42+
description="Test Terraform variable",
43+
category=CategoryType.TERRAFORM,
44+
hcl=False,
45+
sensitive=False,
46+
)
47+
48+
try:
49+
variable = client.variables.create(workspace_id, terraform_var)
50+
created_variables.append(variable.id)
51+
print(f"✓ Created Terraform variable: {variable.key} = {variable.value}")
52+
print(f" ID: {variable.id}, Category: {variable.category}")
53+
except Exception as e:
54+
print(f"✗ Error creating Terraform variable: {e}")
55+
56+
# Create an environment variable
57+
env_var = VariableCreateOptions(
58+
key="TEST_LOG_LEVEL",
59+
value="DEBUG",
60+
description="Test environment variable",
61+
category=CategoryType.ENV,
62+
hcl=False,
63+
sensitive=False,
64+
)
65+
66+
try:
67+
variable = client.variables.create(workspace_id, env_var)
68+
created_variables.append(variable.id)
69+
print(f"✓ Created environment variable: {variable.key} = {variable.value}")
70+
print(f" ID: {variable.id}, Category: {variable.category}")
71+
except Exception as e:
72+
print(f"✗ Error creating environment variable: {e}")
73+
74+
# Create a sensitive variable
75+
secret_var = VariableCreateOptions(
76+
key="TEST_API_KEY",
77+
value="super-secret-key-12345",
78+
description="Test sensitive variable",
79+
category=CategoryType.ENV,
80+
hcl=False,
81+
sensitive=True,
82+
)
83+
84+
try:
85+
variable = client.variables.create(workspace_id, secret_var)
86+
created_variables.append(variable.id)
87+
print(f"✓ Created sensitive variable: {variable.key} = ***HIDDEN***")
88+
print(f" ID: {variable.id}, Category: {variable.category}")
89+
except Exception as e:
90+
print(f"✗ Error creating sensitive variable: {e}")
91+
92+
# Small delay to ensure variables are created
93+
time.sleep(1)
94+
95+
# 2. Test LIST function (workspace-only variables) - COMMENTED OUT
96+
print("\n2. Testing LIST operation (workspace variables only):")
97+
print("-" * 50)
98+
99+
try:
100+
variables = list(client.variables.list(workspace_id))
101+
print(f"Found {len(variables)} workspace variables:")
102+
for var in variables:
103+
value_display = "***SENSITIVE***" if var.sensitive else var.value
104+
print(
105+
f" • {var.key} = {value_display} ({var.category}) [ID: {var.id}]"
106+
)
107+
except Exception as e:
108+
print(f"✗ Error listing variables: {e}")
109+
110+
# 3. Test LIST_ALL function (includes inherited variables from variable sets)
111+
print("\n3. Testing LIST_ALL operation (includes variable sets):")
112+
print("-" * 55)
113+
114+
try:
115+
all_variables = list(client.variables.list_all(workspace_id))
116+
print(f"Found {len(all_variables)} total variables (including inherited):")
117+
for var in all_variables:
118+
value_display = "***SENSITIVE***" if var.sensitive else var.value
119+
print(
120+
f" • {var.key} = {value_display} ({var.category}) [ID: {var.id}]"
121+
)
122+
except Exception as e:
123+
print(f"✗ Error listing all variables: {e}")
124+
125+
# Test READ function with specific variable ID - COMMENTED OUT
126+
print("\n4. Testing READ operation with specific variable ID:")
127+
print("-" * 50)
128+
129+
# Replace this with actual variable ID to test reading
130+
test_variable_id = "var-example123456789"
131+
print(f"Testing READ with variable ID: {test_variable_id}")
132+
133+
try:
134+
variable = client.variables.read(workspace_id, test_variable_id)
135+
# For testing, show actual values even for sensitive variables
136+
if variable.sensitive:
137+
print(f"✓ Read variable: {variable.key} = {variable.value} (SENSITIVE)")
138+
else:
139+
print(f"✓ Read variable: {variable.key} = {variable.value}")
140+
print(f" ID: {variable.id}")
141+
print(f" Description: {variable.description}")
142+
print(f" Category: {variable.category}")
143+
print(f" HCL: {variable.hcl}")
144+
print(f" Sensitive: {variable.sensitive}")
145+
if hasattr(variable, "version_id"):
146+
print(f" Version ID: {variable.version_id}")
147+
except Exception as e:
148+
print(f"✗ Error reading variable {test_variable_id}: {e}")
149+
150+
# Test UPDATE function with specific variable ID - COMMENTED OUT
151+
print("\n5. Testing UPDATE operation with specific variable ID:")
152+
print("-" * 55)
153+
154+
# Replace this with actual variable ID to test updating
155+
test_variable_id = "var-example123456789"
156+
print(f"Testing UPDATE with variable ID: {test_variable_id}")
157+
print("Setting value to: 'npe'")
158+
159+
try:
160+
# First read the current variable to get its details
161+
current_var = client.variables.read(workspace_id, test_variable_id)
162+
print(f"Current value: {current_var.value}")
163+
print(f"Current key: {current_var.key}")
164+
165+
# Update the variable value to "npe"
166+
update_options = VariableUpdateOptions(
167+
key=current_var.key,
168+
value="npe",
169+
description=current_var.description,
170+
hcl=current_var.hcl,
171+
sensitive=current_var.sensitive,
172+
)
173+
174+
updated_variable = client.variables.update(
175+
workspace_id, test_variable_id, update_options
176+
)
177+
print(
178+
f"✓ Updated variable: {updated_variable.key} = {updated_variable.value}"
179+
)
180+
print(f" Description: {updated_variable.description}")
181+
print(f" Category: {updated_variable.category}")
182+
print(f" HCL: {updated_variable.hcl}")
183+
print(f" Sensitive: {updated_variable.sensitive}")
184+
print(f" ID: {updated_variable.id}")
185+
except Exception as e:
186+
print(f"✗ Error updating variable {test_variable_id}: {e}")
187+
188+
# Test DELETE function with specific variable ID
189+
print("\n6. Testing DELETE operation with specific variable ID:")
190+
print("-" * 55)
191+
192+
# Replace this with actual variable ID to test deletion
193+
test_variable_id = "var-example123456789"
194+
print(f"Testing DELETE with variable ID: {test_variable_id}")
195+
196+
try:
197+
# First read the variable to confirm it exists before deletion
198+
variable = client.variables.read(workspace_id, test_variable_id)
199+
print(f"Variable to delete: {variable.key} = {variable.value}")
200+
print(f" ID: {variable.id}")
201+
202+
# Delete the variable
203+
client.variables.delete(workspace_id, test_variable_id)
204+
print(f"✓ Successfully deleted variable with ID: {test_variable_id}")
205+
206+
# Try to read it again to verify deletion
207+
print("Verifying deletion...")
208+
try:
209+
client.variables.read(workspace_id, test_variable_id)
210+
print("✗ Warning: Variable still exists after deletion!")
211+
except Exception as read_error:
212+
if "not found" in str(read_error).lower() or "404" in str(read_error):
213+
print("✓ Confirmed: Variable no longer exists")
214+
else:
215+
print(f"✗ Unexpected error verifying deletion: {read_error}")
216+
217+
except Exception as e:
218+
print(f"✗ Error deleting variable {test_variable_id}: {e}")
219+
220+
# 4. Test READ function
221+
print("\n4. Testing READ operation:")
222+
print("-" * 25)
223+
224+
if created_variables:
225+
test_var_id = created_variables[0] # Use the first created variable
226+
try:
227+
variable = client.variables.read(workspace_id, test_var_id)
228+
value_display = (
229+
"***SENSITIVE***" if variable.sensitive else variable.value
230+
)
231+
print(f"✓ Read variable: {variable.key} = {value_display}")
232+
print(f" ID: {variable.id}")
233+
print(f" Description: {variable.description}")
234+
print(f" Category: {variable.category}")
235+
print(f" HCL: {variable.hcl}")
236+
print(f" Sensitive: {variable.sensitive}")
237+
except Exception as e:
238+
print(f"✗ Error reading variable {test_var_id}: {e}")
239+
else:
240+
print("No variables available to read")
241+
242+
# 5. Test UPDATE function
243+
print("\n5. Testing UPDATE operation:")
244+
print("-" * 27)
245+
246+
if created_variables:
247+
test_var_id = created_variables[0] # Use the first created variable
248+
try:
249+
# First read the current variable to get its details
250+
current_var = client.variables.read(workspace_id, test_var_id)
251+
252+
# Update the variable
253+
update_options = VariableUpdateOptions(
254+
key=current_var.key,
255+
value="updated_value_123",
256+
description="Updated test variable description",
257+
hcl=False,
258+
sensitive=False,
259+
)
260+
261+
updated_variable = client.variables.update(
262+
workspace_id, test_var_id, update_options
263+
)
264+
print(
265+
f"✓ Updated variable: {updated_variable.key} = {updated_variable.value}"
266+
)
267+
print(f" New description: {updated_variable.description}")
268+
print(f" ID: {updated_variable.id}")
269+
except Exception as e:
270+
print(f"✗ Error updating variable {test_var_id}: {e}")
271+
else:
272+
print("No variables available to update")
273+
274+
# 6. Test DELETE function
275+
print("\n6. Testing DELETE operation:")
276+
print("-" * 27)
277+
278+
# Delete all created variables
279+
for var_id in created_variables:
280+
try:
281+
client.variables.delete(workspace_id, var_id)
282+
print(f"✓ Deleted variable with ID: {var_id}")
283+
except Exception as e:
284+
print(f"✗ Error deleting variable {var_id}: {e}")
285+
286+
# Verify deletion by listing variables again
287+
print("\nVerifying deletion - listing variables after cleanup:")
288+
try:
289+
remaining_variables = list(client.variables.list(workspace_id))
290+
# Filter out the variables we just deleted
291+
remaining_test_vars = [
292+
v
293+
for v in remaining_variables
294+
if v.key.startswith("test_") or v.key.startswith("TEST_")
295+
]
296+
if remaining_test_vars:
297+
print(
298+
f"Warning: {len(remaining_test_vars)} test variables still exist:"
299+
)
300+
for var in remaining_test_vars:
301+
print(f" • {var.key} [ID: {var.id}]")
302+
else:
303+
print("✓ All test variables successfully deleted")
304+
except Exception as e:
305+
print(f"✗ Error verifying deletion: {e}")
306+
307+
except Exception as e:
308+
print(f"✗ Unexpected error during testing: {e}")
309+
310+
print("\n" + "=" * 60)
311+
print("Variable testing complete!")
312+
313+
314+
if __name__ == "__main__":
315+
main()

src/tfe/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from .config import TFEConfig
55
from .resources.organizations import Organizations
66
from .resources.projects import Projects
7+
from .resources.variable import Variables
78
from .resources.workspaces import Workspaces
89

910

@@ -26,6 +27,7 @@ def __init__(self, config: TFEConfig | None = None):
2627
)
2728
self.organizations = Organizations(self._transport)
2829
self.projects = Projects(self._transport)
30+
self.variables = Variables(self._transport)
2931
self.workspaces = Workspaces(self._transport)
3032

3133
def close(self) -> None:

src/tfe/errors.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ class RequiredFieldMissing(TFEError): ...
5757
ERR_REQUIRED_NAME = "name is required"
5858
ERR_INVALID_ORG = "invalid organization name"
5959
ERR_REQUIRED_EMAIL = "email is required"
60+
# Workspaces
61+
ERR_INVALID_WORKSPACE_ID = "invalid workspace ID"
62+
ERR_INVALID_VARIABLE_ID = "invalid variable ID"
63+
ERR_REQUIRED_KEY = "key is required"
64+
ERR_REQUIRED_CATEGORY = "category is required"
6065

6166

6267
class WorkspaceNotFound(NotFound): ...

0 commit comments

Comments
 (0)