awslabs.terraform-mcp-server 1.0.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. awslabs/__init__.py +17 -0
  2. awslabs/terraform_mcp_server/__init__.py +17 -0
  3. awslabs/terraform_mcp_server/impl/resources/__init__.py +25 -0
  4. awslabs/terraform_mcp_server/impl/resources/terraform_aws_provider_resources_listing.py +66 -0
  5. awslabs/terraform_mcp_server/impl/resources/terraform_awscc_provider_resources_listing.py +69 -0
  6. awslabs/terraform_mcp_server/impl/tools/__init__.py +33 -0
  7. awslabs/terraform_mcp_server/impl/tools/execute_terraform_command.py +223 -0
  8. awslabs/terraform_mcp_server/impl/tools/execute_terragrunt_command.py +320 -0
  9. awslabs/terraform_mcp_server/impl/tools/run_checkov_scan.py +376 -0
  10. awslabs/terraform_mcp_server/impl/tools/search_aws_provider_docs.py +691 -0
  11. awslabs/terraform_mcp_server/impl/tools/search_awscc_provider_docs.py +641 -0
  12. awslabs/terraform_mcp_server/impl/tools/search_specific_aws_ia_modules.py +458 -0
  13. awslabs/terraform_mcp_server/impl/tools/search_user_provided_module.py +349 -0
  14. awslabs/terraform_mcp_server/impl/tools/utils.py +572 -0
  15. awslabs/terraform_mcp_server/models/__init__.py +49 -0
  16. awslabs/terraform_mcp_server/models/models.py +381 -0
  17. awslabs/terraform_mcp_server/scripts/generate_aws_provider_resources.py +1240 -0
  18. awslabs/terraform_mcp_server/scripts/generate_awscc_provider_resources.py +1039 -0
  19. awslabs/terraform_mcp_server/scripts/scrape_aws_terraform_best_practices.py +143 -0
  20. awslabs/terraform_mcp_server/server.py +440 -0
  21. awslabs/terraform_mcp_server/static/AWSCC_PROVIDER_RESOURCES.md +3125 -0
  22. awslabs/terraform_mcp_server/static/AWS_PROVIDER_RESOURCES.md +3833 -0
  23. awslabs/terraform_mcp_server/static/AWS_TERRAFORM_BEST_PRACTICES.md +2523 -0
  24. awslabs/terraform_mcp_server/static/MCP_INSTRUCTIONS.md +142 -0
  25. awslabs/terraform_mcp_server/static/TERRAFORM_WORKFLOW_GUIDE.md +330 -0
  26. awslabs/terraform_mcp_server/static/__init__.py +38 -0
  27. awslabs_terraform_mcp_server-1.0.14.dist-info/METADATA +166 -0
  28. awslabs_terraform_mcp_server-1.0.14.dist-info/RECORD +30 -0
  29. awslabs_terraform_mcp_server-1.0.14.dist-info/WHEEL +4 -0
  30. awslabs_terraform_mcp_server-1.0.14.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,572 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Utility functions for Terraform MCP server tools."""
16
+
17
+ import asyncio
18
+ import re
19
+ import requests
20
+ import time
21
+ import traceback
22
+ from awslabs.terraform_mcp_server.models import SubmoduleInfo, TerraformVariable
23
+ from loguru import logger
24
+ from typing import Any, Dict, List, Optional, Tuple
25
+
26
+
27
+ def clean_description(description: str) -> str:
28
+ """Remove emoji characters from description strings.
29
+
30
+ Args:
31
+ description: The module description text
32
+
33
+ Returns:
34
+ Cleaned description without emojis
35
+ """
36
+ # This regex pattern targets common emoji Unicode ranges
37
+ emoji_pattern = re.compile(
38
+ '['
39
+ '\U0001f1e0-\U0001f1ff' # flags (iOS)
40
+ '\U0001f300-\U0001f5ff' # symbols & pictographs
41
+ '\U0001f600-\U0001f64f' # emoticons
42
+ '\U0001f680-\U0001f6ff' # transport & map symbols
43
+ '\U0001f700-\U0001f77f' # alchemical symbols
44
+ '\U0001f780-\U0001f7ff' # Geometric Shapes
45
+ '\U0001f800-\U0001f8ff' # Supplemental Arrows-C
46
+ '\U0001f900-\U0001f9ff' # Supplemental Symbols and Pictographs
47
+ '\U0001fa00-\U0001fa6f' # Chess Symbols
48
+ '\U0001fa70-\U0001faff' # Symbols and Pictographs Extended-A
49
+ '\U00002702-\U000027b0' # Dingbats
50
+ ']+',
51
+ flags=re.UNICODE,
52
+ )
53
+
54
+ # Clean the description
55
+ return emoji_pattern.sub(r'', description).strip()
56
+
57
+
58
+ async def get_github_release_details(owner: str, repo: str) -> Dict[str, Any]:
59
+ """Fetch detailed release information from GitHub API.
60
+
61
+ Args:
62
+ owner: The GitHub repository owner
63
+ repo: The GitHub repository name
64
+
65
+ Returns:
66
+ Dictionary containing version details and cleaned version string
67
+ """
68
+ logger.info(f'Fetching GitHub release details for {owner}/{repo}')
69
+
70
+ # Try to get the latest release first
71
+ release_url = f'https://api.github.com/repos/{owner}/{repo}/releases/latest'
72
+ logger.debug(f'Making request to GitHub releases API: {release_url}')
73
+
74
+ try:
75
+ response = requests.get(release_url)
76
+ logger.debug(f'GitHub releases API response code: {response.status_code}')
77
+
78
+ if response.status_code == 200:
79
+ release_data = response.json()
80
+ logger.info(f'Found latest GitHub release: {release_data.get("tag_name")}')
81
+
82
+ # Extract just the requested fields (tag name and publish date)
83
+ version_details = {
84
+ 'tag_name': release_data.get('tag_name'),
85
+ 'published_at': release_data.get('published_at'),
86
+ }
87
+
88
+ # Use clean version for the module result
89
+ clean_version = release_data.get('tag_name', '')
90
+ if clean_version.startswith('v'):
91
+ clean_version = clean_version[1:]
92
+
93
+ logger.debug(f'Extracted version: {clean_version}')
94
+
95
+ return {'details': version_details, 'version': clean_version}
96
+ except Exception as ex:
97
+ logger.error(f'Error fetching GitHub release details: {ex}')
98
+ logger.debug(f'Stack trace: {traceback.format_exc()}')
99
+
100
+ # Fallback to tags if no releases found
101
+ tags_url = f'https://api.github.com/repos/{owner}/{repo}/tags'
102
+ logger.debug(f'No releases found, trying tags: {tags_url}')
103
+
104
+ try:
105
+ response = requests.get(tags_url)
106
+ logger.debug(f'GitHub tags API response code: {response.status_code}')
107
+
108
+ if response.status_code == 200 and response.json():
109
+ tags_data = response.json()
110
+ if tags_data:
111
+ latest_tag = tags_data[0] # Tags are typically sorted newest first
112
+ logger.info(f'Found latest GitHub tag: {latest_tag.get("name")}')
113
+
114
+ version_details = {
115
+ 'tag_name': latest_tag.get('name'),
116
+ 'published_at': None, # Tags don't have publish dates in GitHub API
117
+ }
118
+
119
+ # Use clean version for the module result
120
+ clean_version = latest_tag.get('name', '')
121
+ if clean_version.startswith('v'):
122
+ clean_version = clean_version[1:]
123
+
124
+ logger.debug(f'Extracted version from tag: {clean_version}')
125
+
126
+ return {'details': version_details, 'version': clean_version}
127
+ except Exception as ex:
128
+ logger.error(f'Error fetching GitHub tags: {ex}')
129
+ logger.debug(f'Stack trace: {traceback.format_exc()}')
130
+
131
+ # Return empty details if nothing was found
132
+ logger.warning('No GitHub release or tag information found')
133
+ return {'details': {}, 'version': ''}
134
+
135
+
136
+ async def get_submodules(owner: str, repo: str, branch: str = 'master') -> List[SubmoduleInfo]:
137
+ """Fetch submodules from a module's GitHub repository.
138
+
139
+ Args:
140
+ owner: GitHub repository owner
141
+ repo: GitHub repository name
142
+ branch: Branch name (default: master)
143
+
144
+ Returns:
145
+ List of SubmoduleInfo objects
146
+ """
147
+ logger.info(f'Checking for submodules in {owner}/{repo} ({branch} branch)')
148
+ submodules = []
149
+
150
+ # Check if modules directory exists
151
+ modules_url = f'https://api.github.com/repos/{owner}/{repo}/contents/modules?ref={branch}'
152
+ logger.debug(f'Checking for modules directory: {modules_url}')
153
+
154
+ try:
155
+ # Get list of directories in /modules
156
+ start_time = time.time()
157
+ response = requests.get(
158
+ modules_url,
159
+ headers={'Accept': 'application/vnd.github.v3+json'},
160
+ timeout=3.0, # Add timeout
161
+ )
162
+ logger.debug(f'GitHub API request took {time.time() - start_time:.2f} seconds')
163
+
164
+ if response.status_code == 404:
165
+ logger.debug(f'No modules directory found in {branch} branch')
166
+ return []
167
+
168
+ if response.status_code == 403:
169
+ logger.warning(f'GitHub API rate limit reached, status: {response.status_code}')
170
+ # Return empty list but don't fail completely
171
+ return []
172
+
173
+ if response.status_code != 200:
174
+ logger.warning(f'Failed to get modules directory: status {response.status_code}')
175
+ return []
176
+
177
+ modules_list = response.json()
178
+ if not isinstance(modules_list, list):
179
+ logger.warning('Unexpected API response format for modules listing')
180
+ return []
181
+
182
+ # Filter for directories only
183
+ submodule_dirs = [item for item in modules_list if item.get('type') == 'dir']
184
+ logger.info(f'Found {len(submodule_dirs)} potential submodules')
185
+
186
+ # Process submodules with concurrency limits
187
+ # Only process up to 5 submodules to avoid timeouts
188
+ max_submodules = min(len(submodule_dirs), 5)
189
+ logger.info(f'Processing {max_submodules} out of {len(submodule_dirs)} submodules')
190
+
191
+ # Process each submodule
192
+ for i, submodule in enumerate(submodule_dirs[:max_submodules]):
193
+ name = submodule.get('name')
194
+ path = submodule.get('path', f'modules/{name}')
195
+
196
+ # Create basic submodule info
197
+ submodule_info = SubmoduleInfo(
198
+ name=name,
199
+ path=path,
200
+ )
201
+
202
+ # Add a slight delay between API requests to avoid rate limiting
203
+ if i > 0:
204
+ await asyncio.sleep(0.2) # 200ms delay between requests
205
+
206
+ # Try to get README content
207
+ readme_url = (
208
+ f'https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{path}/README.md'
209
+ )
210
+ logger.debug(f'Fetching README for submodule {name}: {readme_url}')
211
+
212
+ try:
213
+ start_time = time.time()
214
+ readme_response = requests.get(readme_url, timeout=2.0) # Add timeout
215
+ logger.debug(f'README fetch took {time.time() - start_time:.2f} seconds')
216
+
217
+ if readme_response.status_code == 200:
218
+ readme_content = readme_response.text
219
+ # Truncate if too long
220
+ if len(readme_content) > 8000:
221
+ readme_content = (
222
+ readme_content[:8000] + '...\n[README truncated due to length]'
223
+ )
224
+
225
+ # Extract description from first paragraph if available
226
+ description = extract_description_from_readme(readme_content)
227
+ if description:
228
+ submodule_info.description = description
229
+
230
+ submodule_info.readme_content = readme_content
231
+ logger.debug(
232
+ f'Found README for submodule {name} ({len(readme_content)} chars)'
233
+ )
234
+ else:
235
+ logger.debug(
236
+ f'No README found for submodule {name}, status: {readme_response.status_code}'
237
+ )
238
+ # Try lowercase readme.md as fallback
239
+ lowercase_readme_url = f'https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{path}/readme.md'
240
+ logger.debug(f'Trying lowercase readme.md: {lowercase_readme_url}')
241
+
242
+ lowercase_response = requests.get(lowercase_readme_url, timeout=2.0)
243
+ if lowercase_response.status_code == 200:
244
+ readme_content = lowercase_response.text
245
+ if len(readme_content) > 8000:
246
+ readme_content = (
247
+ readme_content[:8000] + '...\n[README truncated due to length]'
248
+ )
249
+
250
+ description = extract_description_from_readme(readme_content)
251
+ if description:
252
+ submodule_info.description = description
253
+
254
+ submodule_info.readme_content = readme_content
255
+ logger.debug(
256
+ f'Found lowercase readme.md for {name} ({len(readme_content)} chars)'
257
+ )
258
+ except Exception as ex:
259
+ logger.error(f'Error fetching README for submodule {name}: {ex}')
260
+
261
+ # Add the submodule to our result list
262
+ submodules.append(submodule_info)
263
+
264
+ if len(submodule_dirs) > max_submodules:
265
+ logger.warning(
266
+ f'Only processed {max_submodules} out of {len(submodule_dirs)} submodules to avoid timeouts'
267
+ )
268
+
269
+ return submodules
270
+
271
+ except Exception as e:
272
+ logger.error(f'Error fetching submodules: {e}')
273
+ logger.debug(f'Stack trace: {traceback.format_exc()}')
274
+ return []
275
+
276
+
277
+ def extract_description_from_readme(readme_content: str) -> Optional[str]:
278
+ """Extract a short description from the README content.
279
+
280
+ Args:
281
+ readme_content: The README markdown content
282
+
283
+ Returns:
284
+ Short description or None if not found
285
+ """
286
+ if not readme_content:
287
+ return None
288
+
289
+ # Try to find the first paragraph after any headings
290
+ lines = readme_content.split('\n')
291
+ paragraph_text = []
292
+
293
+ for line in lines:
294
+ # Skip headings, horizontal rules and blank lines
295
+ if line.startswith('#') or line.startswith('---') or not line.strip():
296
+ # If we already found a paragraph, return it
297
+ if paragraph_text:
298
+ break
299
+ continue
300
+
301
+ # Found text content, add to paragraph
302
+ paragraph_text.append(line)
303
+
304
+ # If this line ends a paragraph, break
305
+ if not line.endswith('\\') and len(paragraph_text) > 0:
306
+ break
307
+
308
+ if paragraph_text:
309
+ description = ' '.join(paragraph_text).strip()
310
+ # Limit to 200 chars max
311
+ if len(description) > 200:
312
+ description = description[:197] + '...'
313
+ return description
314
+
315
+ return None
316
+
317
+
318
+ def extract_outputs_from_readme(readme_content: str) -> List[Dict[str, str]]:
319
+ """Extract module outputs from the README content.
320
+
321
+ Looks for the Outputs section in the README, which is typically at the bottom
322
+ of the file and contains a table of outputs with descriptions.
323
+
324
+ Args:
325
+ readme_content: The README markdown content
326
+
327
+ Returns:
328
+ List of dictionaries containing output name and description
329
+ """
330
+ if not readme_content:
331
+ return []
332
+
333
+ outputs = []
334
+
335
+ # Find the Outputs section
336
+ lines = readme_content.split('\n')
337
+ in_outputs_section = False
338
+ in_outputs_table = False
339
+
340
+ for i, line in enumerate(lines):
341
+ # Look for Outputs heading
342
+ if re.match(r'^#+\s+Outputs?$', line, re.IGNORECASE):
343
+ in_outputs_section = True
344
+ continue
345
+
346
+ # If we're in the outputs section, look for the table header
347
+ if in_outputs_section and not in_outputs_table:
348
+ if '|' in line and ('Name' in line or 'Output' in line) and 'Description' in line:
349
+ in_outputs_table = True
350
+ continue
351
+
352
+ # If we're in the outputs table, parse each row
353
+ if in_outputs_section and in_outputs_table:
354
+ # Skip the table header separator line
355
+ if line.strip().startswith('|') and all(c in '|-: ' for c in line):
356
+ continue
357
+
358
+ # If we hit another heading or the table ends, stop parsing
359
+ if line.strip().startswith('#') or not line.strip() or '|' not in line:
360
+ break
361
+
362
+ # Parse the table row
363
+ if '|' in line:
364
+ parts = [part.strip() for part in line.split('|')]
365
+ if len(parts) >= 3: # Should have at least empty, name, description columns
366
+ name_part = parts[1].strip()
367
+ desc_part = parts[2].strip()
368
+
369
+ # Clean up any markdown formatting
370
+ name = re.sub(r'`(.*?)`', r'\1', name_part).strip()
371
+ description = re.sub(r'`(.*?)`', r'\1', desc_part).strip()
372
+
373
+ if name:
374
+ outputs.append({'name': name, 'description': description})
375
+
376
+ # If we didn't find a table, try looking for a list format
377
+ if not outputs and in_outputs_section:
378
+ for line in lines:
379
+ # If we hit another heading, stop parsing
380
+ if line.strip().startswith('#'):
381
+ break
382
+
383
+ # Look for list items that might be outputs
384
+ list_match = re.match(r'^[-*]\s+`([^`]+)`\s*[-:]\s*(.+)$', line)
385
+ if list_match:
386
+ name = list_match.group(1).strip()
387
+ description = list_match.group(2).strip()
388
+
389
+ outputs.append({'name': name, 'description': description})
390
+
391
+ logger.debug(f'Extracted {len(outputs)} outputs from README')
392
+ return outputs
393
+
394
+
395
+ async def get_variables_tf(
396
+ owner: str, repo: str, branch: str = 'main'
397
+ ) -> Tuple[Optional[str], Optional[List[TerraformVariable]]]:
398
+ """Fetch and parse the variables.tf file from a GitHub repository.
399
+
400
+ Args:
401
+ owner: GitHub repository owner
402
+ repo: GitHub repository name
403
+ branch: Branch name (default: main)
404
+
405
+ Returns:
406
+ Tuple containing the raw variables.tf content and a list of parsed TerraformVariable objects
407
+ """
408
+ logger.info(f'Fetching variables.tf from {owner}/{repo} ({branch} branch)')
409
+
410
+ # Try to get the variables.tf file
411
+ variables_url = f'https://raw.githubusercontent.com/{owner}/{repo}/{branch}/variables.tf'
412
+ logger.debug(f'Fetching variables.tf: {variables_url}')
413
+
414
+ try:
415
+ start_time = time.time()
416
+ response = requests.get(variables_url, timeout=3.0)
417
+ logger.debug(f'variables.tf fetch took {time.time() - start_time:.2f} seconds')
418
+
419
+ if response.status_code == 200:
420
+ variables_content = response.text
421
+ logger.info(f'Found variables.tf ({len(variables_content)} chars)')
422
+
423
+ # Parse the variables.tf file
424
+ variables = parse_variables_tf(variables_content)
425
+ logger.info(f'Parsed {len(variables)} variables from variables.tf')
426
+
427
+ return variables_content, variables
428
+ else:
429
+ logger.debug(
430
+ f'No variables.tf found at {branch} branch, status: {response.status_code}'
431
+ )
432
+
433
+ # Try master branch as fallback
434
+ if branch != 'master':
435
+ logger.debug('Trying master branch for variables.tf')
436
+ master_variables_url = (
437
+ f'https://raw.githubusercontent.com/{owner}/{repo}/master/variables.tf'
438
+ )
439
+ master_response = requests.get(master_variables_url, timeout=3.0)
440
+
441
+ if master_response.status_code == 200:
442
+ variables_content = master_response.text
443
+ logger.info(
444
+ f'Found variables.tf in master branch ({len(variables_content)} chars)'
445
+ )
446
+
447
+ # Parse the variables.tf file
448
+ variables = parse_variables_tf(variables_content)
449
+ logger.info(f'Parsed {len(variables)} variables from variables.tf')
450
+
451
+ return variables_content, variables
452
+ except Exception as ex:
453
+ logger.error(f'Error fetching variables.tf: {ex}')
454
+ logger.debug(f'Stack trace: {traceback.format_exc()}')
455
+
456
+ return None, None
457
+
458
+
459
+ def parse_variables_tf(content: str) -> List[TerraformVariable]:
460
+ """Parse variables.tf content to extract variable definitions.
461
+
462
+ Args:
463
+ content: The content of the variables.tf file
464
+
465
+ Returns:
466
+ List of TerraformVariable objects
467
+ """
468
+ if not content:
469
+ return []
470
+
471
+ variables = []
472
+
473
+ # Simple regex pattern to match variable blocks
474
+ # This is a simplified approach and may not handle all complex HCL syntax
475
+ variable_blocks = re.finditer(r'variable\s+"([^"]+)"\s*{([^}]+)}', content, re.DOTALL)
476
+
477
+ for match in variable_blocks:
478
+ var_name = match.group(1)
479
+ var_block = match.group(2)
480
+
481
+ # Initialize variable with name
482
+ variable = TerraformVariable(name=var_name)
483
+
484
+ # Extract type
485
+ type_match = re.search(r'type\s*=\s*(.+?)($|\n)', var_block)
486
+ if type_match:
487
+ variable.type = type_match.group(1).strip()
488
+
489
+ # Extract description
490
+ desc_match = re.search(r'description\s*=\s*"([^"]+)"', var_block)
491
+ if desc_match:
492
+ variable.description = desc_match.group(1).strip()
493
+
494
+ # Check for default value
495
+ default_match = re.search(r'default\s*=\s*(.+?)($|\n)', var_block)
496
+ if default_match:
497
+ default_value = default_match.group(1).strip()
498
+ variable.default = default_value
499
+ variable.required = False
500
+
501
+ variables.append(variable)
502
+
503
+ return variables
504
+
505
+
506
+ # Security-related constants and utilities
507
+ # These are used to prevent command injection and other security issues
508
+
509
+
510
+ def get_dangerous_patterns() -> List[str]:
511
+ """Get a list of dangerous patterns for command injection detection.
512
+
513
+ Returns:
514
+ List of dangerous patterns to check for
515
+ """
516
+ # Dangerous patterns that could indicate command injection attempts
517
+ # Separated by platform for better organization and maintainability
518
+ patterns = [
519
+ '|',
520
+ ';',
521
+ '&',
522
+ '&&',
523
+ '||', # Command chaining
524
+ '>',
525
+ '>>',
526
+ '<', # Redirection
527
+ '`',
528
+ '$(', # Command substitution
529
+ '--', # Double dash options
530
+ 'rm',
531
+ 'mv',
532
+ 'cp', # Potentially dangerous commands
533
+ '/bin/',
534
+ '/usr/bin/', # Path references
535
+ '../',
536
+ './', # Directory traversal
537
+ # Unix/Linux specific dangerous patterns
538
+ 'sudo', # Privilege escalation
539
+ 'chmod',
540
+ 'chown', # File permission changes
541
+ 'su', # Switch user
542
+ 'bash',
543
+ 'sh',
544
+ 'zsh', # Shell execution
545
+ 'curl',
546
+ 'wget', # Network access
547
+ 'ssh',
548
+ 'scp', # Remote access
549
+ 'eval', # Command evaluation
550
+ 'exec', # Command execution
551
+ 'source', # Script sourcing
552
+ # Windows specific dangerous patterns
553
+ 'cmd',
554
+ 'powershell',
555
+ 'pwsh', # Command shells
556
+ 'net', # Network commands
557
+ 'reg', # Registry access
558
+ 'runas', # Privilege escalation
559
+ 'del',
560
+ 'rmdir', # File deletion
561
+ 'start', # Process execution
562
+ 'taskkill', # Process termination
563
+ 'sc', # Service control
564
+ 'schtasks', # Scheduled tasks
565
+ 'wmic', # WMI commands
566
+ '%SYSTEMROOT%',
567
+ '%WINDIR%', # System directories
568
+ '.bat',
569
+ '.cmd',
570
+ '.ps1', # Script files
571
+ ]
572
+ return patterns
@@ -0,0 +1,49 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from .models import (
16
+ ModuleSearchResult,
17
+ TerraformAWSProviderDocsResult,
18
+ TerraformAWSCCProviderDocsResult,
19
+ SubmoduleInfo,
20
+ TerraformExecutionRequest,
21
+ TerraformExecutionResult,
22
+ TerragruntExecutionRequest,
23
+ TerragruntExecutionResult,
24
+ CheckovVulnerability,
25
+ CheckovScanRequest,
26
+ CheckovScanResult,
27
+ TerraformVariable,
28
+ TerraformOutput,
29
+ SearchUserProvidedModuleRequest,
30
+ SearchUserProvidedModuleResult,
31
+ )
32
+
33
+ __all__ = [
34
+ 'ModuleSearchResult',
35
+ 'TerraformAWSProviderDocsResult',
36
+ 'TerraformAWSCCProviderDocsResult',
37
+ 'SubmoduleInfo',
38
+ 'TerraformExecutionRequest',
39
+ 'TerraformExecutionResult',
40
+ 'TerragruntExecutionRequest',
41
+ 'TerragruntExecutionResult',
42
+ 'CheckovVulnerability',
43
+ 'CheckovScanRequest',
44
+ 'CheckovScanResult',
45
+ 'TerraformVariable',
46
+ 'TerraformOutput',
47
+ 'SearchUserProvidedModuleRequest',
48
+ 'SearchUserProvidedModuleResult',
49
+ ]