ChrisSacrumCor commited on
Commit
6377f49
·
verified ·
1 Parent(s): bc7b1c7

Create app.py

Browse files

The Terraform MCP server

Files changed (1) hide show
  1. app.py +1919 -0
app.py ADDED
@@ -0,0 +1,1919 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "",
2
+ "# 6. Clean up test",
3
+ "terraform destroy",
4
+ "cd ../..",
5
+ "",
6
+ "# 7. Tag version",
7
+ "git tag v1.0.0",
8
+ "git push --tags"
9
+ ]
10
+
11
+ class TerraformModuleConverter:
12
+ """Convert Terraform configurations to reusable modules"""
13
+
14
+ @staticmethod
15
+ def extract_variables(config: str) -> str:
16
+ """Extract hardcoded values and convert to variables"""
17
+ variables = []
18
+
19
+ # Common patterns to extract
20
+ patterns = {
21
+ r'"(t[23]\.[a-z]+)"': ('instance_type', 'string', 'EC2 instance type'),
22
+ r'"(\d+\.\d+\.\d+\.\d+/\d+)"': ('cidr_block', 'string', 'CIDR block'),
23
+ r'"(us-[a-z]+-\d+[a-z]?)"': ('aws_region', 'string', 'AWS region'),
24
+ r'"(eastus|westus|centralus)"': ('location', 'string', 'Azure location'),
25
+ }
26
+
27
+ for pattern, (var_name, var_type, description) in patterns.items():
28
+ if re.search(pattern, config):
29
+ variables.append(f'''variable "{var_name}" {{
30
+ description = "{description}"
31
+ type = {var_type}
32
+ default = # Add appropriate default
33
+ }}''')
34
+
35
+ return '\n\n'.join(variables)
36
+
37
+ @staticmethod
38
+ def generate_outputs(config: str, resource_name: str) -> str:
39
+ """Generate outputs for a module"""
40
+ outputs = []
41
+
42
+ # Extract resource types from config
43
+ resources = re.findall(r'resource\s+"([^"]+)"\s+"([^"]+)"', config)
44
+ modules = re.findall(r'module\s+"([^"]+)"\s*\{', config)
45
+
46
+ for resource_type, name in resources:
47
+ if 'aws_instance' in resource_type:
48
+ outputs.extend([
49
+ f'output "{name}_id" {{\n description = "ID of the EC2 instance"\n value = {resource_type}.{name}.id\n}}',
50
+ f'output "{name}_public_ip" {{\n description = "Public IP of the instance"\n value = {resource_type}.{name}.public_ip\n}}'
51
+ ])
52
+ elif 'aws_s3_bucket' in resource_type:
53
+ outputs.extend([
54
+ f'output "{name}_name" {{\n description = "Name of the S3 bucket"\n value = {resource_type}.{name}.id\n}}',
55
+ f'output "{name}_arn" {{\n description = "ARN of the S3 bucket"\n value = {resource_type}.{name}.arn\n}}'
56
+ ])
57
+
58
+ for module_name in modules:
59
+ outputs.append(f'output "{module_name}_outputs" {{\n description = "All outputs from {module_name} module"\n value = module.{module_name}\n}}')
60
+
61
+ return '\n\n'.join(outputs)
62
+
63
+ # ============================================================================
64
+ # MCP SERVER IMPLEMENTATION
65
+ # ============================================================================
66
+
67
+ class CleanTerraformMCPServer:
68
+ """Clean Terraform MCP Server - focused on code generation and validation"""
69
+
70
+ def __init__(self):
71
+ self.server = Server("terraform-mcp-server")
72
+ self.code_generator = TerraformCodeGenerator()
73
+ self.validator = TerraformValidator()
74
+ self.workflows = TerraformWorkflows()
75
+ self.module_converter = TerraformModuleConverter()
76
+ self._setup_handlers()
77
+
78
+ def _setup_handlers(self):
79
+ """Setup MCP server handlers"""
80
+
81
+ @self.server.list_tools()
82
+ async def handle_list_tools() -> list[types.Tool]:
83
+ """List all available Terraform tools"""
84
+ return [
85
+ types.Tool(
86
+ name="generate_terraform_config",
87
+ description="Generate complete Terraform configuration for infrastructure resources",
88
+ inputSchema={
89
+ "type": "object",
90
+ "properties": {
91
+ "resource_type": {
92
+ "type": "string",
93
+ "description": "Type of infrastructure resource",
94
+ "enum": ["vpc", "ec2", "s3", "eks", "rds", "vnet", "compute", "aks"]
95
+ },
96
+ "provider": {
97
+ "type": "string",
98
+ "description": "Cloud provider",
99
+ "enum": ["aws", "azurerm", "google"]
100
+ },
101
+ "name": {
102
+ "type": "string",
103
+ "description": "Name for the infrastructure resources"
104
+ },
105
+ "options": {
106
+ "type": "object",
107
+ "description": "Resource-specific configuration options",
108
+ "properties": {
109
+ "cidr": {"type": "string", "description": "CIDR block for VPC"},
110
+ "azs": {"type": "array", "items": {"type": "string"}, "description": "Availability zones"},
111
+ "instance_type": {"type": "string", "description": "EC2 instance type"},
112
+ "key_pair_name": {"type": "string", "description": "SSH key pair name"},
113
+ "enable_nat_gateway": {"type": "boolean", "description": "Enable NAT gateway"},
114
+ "versioning": {"type": "boolean", "description": "Enable S3 versioning"},
115
+ "encryption": {"type": "boolean", "description": "Enable encryption"},
116
+ "node_instance_type": {"type": "string", "description": "EKS node instance type"},
117
+ "min_nodes": {"type": "integer", "description": "Minimum nodes"},
118
+ "max_nodes": {"type": "integer", "description": "Maximum nodes"},
119
+ "desired_nodes": {"type": "integer", "description": "Desired nodes"},
120
+ "kubernetes_version": {"type": "string", "description": "Kubernetes version"}
121
+ }
122
+ }
123
+ },
124
+ "required": ["resource_type", "provider", "name"]
125
+ }
126
+ ),
127
+ types.Tool(
128
+ name="validate_terraform_config",
129
+ description="Validate Terraform configuration syntax and best practices",
130
+ inputSchema={
131
+ "type": "object",
132
+ "properties": {
133
+ "config_content": {
134
+ "type": "string",
135
+ "description": "Terraform configuration content to validate"
136
+ },
137
+ "check_best_practices": {
138
+ "type": "boolean",
139
+ "description": "Include best practices validation",
140
+ "default": True
141
+ },
142
+ "strict_mode": {
143
+ "type": "boolean",
144
+ "description": "Enable strict validation rules",
145
+ "default": False
146
+ }
147
+ },
148
+ "required": ["config_content"]
149
+ }
150
+ ),
151
+ types.Tool(
152
+ name="get_deployment_workflow",
153
+ description="Generate step-by-step Terraform deployment workflow",
154
+ inputSchema={
155
+ "type": "object",
156
+ "properties": {
157
+ "workflow_type": {
158
+ "type": "string",
159
+ "description": "Type of deployment workflow",
160
+ "enum": ["basic", "production", "module_development"],
161
+ "default": "basic"
162
+ },
163
+ "backend_type": {
164
+ "type": "string",
165
+ "description": "Backend storage type",
166
+ "enum": ["local", "s3", "azurerm", "gcs"],
167
+ "default": "local"
168
+ },
169
+ "environment": {
170
+ "type": "string",
171
+ "description": "Target environment",
172
+ "enum": ["development", "staging", "production"],
173
+ "default": "development"
174
+ }
175
+ }
176
+ }
177
+ ),
178
+ types.Tool(
179
+ name="convert_to_module",
180
+ description="Convert Terraform configuration to reusable module structure",
181
+ inputSchema={
182
+ "type": "object",
183
+ "properties": {
184
+ "config_content": {
185
+ "type": "string",
186
+ "description": "Terraform configuration to convert"
187
+ },
188
+ "module_name": {
189
+ "type": "string",
190
+ "description": "Name for the module"
191
+ },
192
+ "extract_variables": {
193
+ "type": "boolean",
194
+ "description": "Extract hardcoded values as variables",
195
+ "default": True
196
+ },
197
+ "generate_examples": {
198
+ "type": "boolean",
199
+ "description": "Generate usage examples",
200
+ "default": True
201
+ }
202
+ },
203
+ "required": ["config_content", "module_name"]
204
+ }
205
+ ),
206
+ types.Tool(
207
+ name="format_terraform_code",
208
+ description="Format and standardize Terraform code",
209
+ inputSchema={
210
+ "type": "object",
211
+ "properties": {
212
+ "config_content": {
213
+ "type": "string",
214
+ "description": "Terraform configuration to format"
215
+ },
216
+ "sort_blocks": {
217
+ "type": "boolean",
218
+ "description": "Sort resource blocks alphabetically",
219
+ "default": True
220
+ }
221
+ },
222
+ "required": ["config_content"]
223
+ }
224
+ ),
225
+ types.Tool(
226
+ name="generate_terraform_docs",
227
+ description="Generate documentation for Terraform configuration",
228
+ inputSchema={
229
+ "type": "object",
230
+ "properties": {
231
+ "config_content": {
232
+ "type": "string",
233
+ "description": "Terraform configuration to document"
234
+ },
235
+ "module_name": {
236
+ "type": "string",
237
+ "description": "Name of the module"
238
+ },
239
+ "include_examples": {
240
+ "type": "boolean",
241
+ "description": "Include usage examples",
242
+ "default": True
243
+ }
244
+ },
245
+ "required": ["config_content"]
246
+ }
247
+ )
248
+ ]
249
+
250
+ @self.server.call_tool()
251
+ async def handle_call_tool(name: str, arguments: dict) -> list[types.TextContent]:
252
+ """Handle tool calls"""
253
+ try:
254
+ if name == "generate_terraform_config":
255
+ return await self._handle_generate_config(arguments)
256
+ elif name == "validate_terraform_config":
257
+ return await self._handle_validate_config(arguments)
258
+ elif name == "get_deployment_workflow":
259
+ return await self._handle_deployment_workflow(arguments)
260
+ elif name == "convert_to_module":
261
+ return await self._handle_convert_to_module(arguments)
262
+ elif name == "format_terraform_code":
263
+ return await self._handle_format_code(arguments)
264
+ elif name == "generate_terraform_docs":
265
+ return await self._handle_generate_docs(arguments)
266
+ else:
267
+ return [types.TextContent(type="text", text=f"❌ Unknown tool: {name}")]
268
+
269
+ except Exception as e:
270
+ logger.error(f"Tool execution error: {e}")
271
+ return [types.TextContent(type="text", text=f"❌ Error executing {name}: {str(e)}")]
272
+
273
+ async def _handle_generate_config(self, args: dict) -> list[types.TextContent]:
274
+ """Handle Terraform configuration generation"""
275
+ resource_type = args.get("resource_type")
276
+ provider = args.get("provider")
277
+ name = args.get("name")
278
+ options = args.get("options", {})
279
+
280
+ # Generate configuration based on resource type
281
+ if resource_type == "vpc" and provider == "aws":
282
+ config = self.code_generator.generate_vpc_config(name, options)
283
+ elif resource_type == "ec2" and provider == "aws":
284
+ config = self.code_generator.generate_ec2_config(name, options)
285
+ elif resource_type == "s3" and provider == "aws":
286
+ config = self.code_generator.generate_s3_config(name, options)
287
+ elif resource_type == "eks" and provider == "aws":
288
+ config = self.code_generator.generate_eks_config(name, options)
289
+ elif resource_type == "vnet" and provider == "azurerm":
290
+ config = self.code_generator.generate_azure_vnet_config(name, options)
291
+ else:
292
+ return [types.TextContent(type="text", text=f"❌ Configuration generation for {resource_type} on {provider} not yet implemented")]
293
+
294
+ # Basic validation
295
+ validation_issues = self.validator.validate_syntax(config)
296
+ best_practices = self.validator.validate_best_practices(config)
297
+
298
+ response = f"""# 🚀 Terraform Configuration Generated
299
+
300
+ ## 📋 Summary
301
+ - **Resource Type**: {resource_type.upper()}
302
+ - **Provider**: {provider}
303
+ - **Name**: {name}
304
+ - **Generated**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
305
+
306
+ ## 🔧 Configuration
307
+
308
+ ```hcl
309
+ {config}
310
+ ```
311
+
312
+ ## ✅ Validation Results
313
+ """
314
+
315
+ if validation_issues:
316
+ response += "### Issues Found:\n"
317
+ for issue in validation_issues:
318
+ response += f"- {issue}\n"
319
+ else:
320
+ response += "- ✅ No syntax issues found\n"
321
+
322
+ if best_practices:
323
+ response += "\n### Best Practice Suggestions:\n"
324
+ for suggestion in best_practices:
325
+ response += f"- {suggestion}\n"
326
+
327
+ response += f"""
328
+
329
+ ## 🚀 Next Steps
330
+
331
+ 1. Save configuration to `main.tf`
332
+ 2. Run `terraform init` to initialize
333
+ 3. Run `terraform plan` to review changes
334
+ 4. Run `terraform apply` to create resources
335
+
336
+ ## 📦 Recommended Module Structure
337
+
338
+ ```
339
+ {name}-infrastructure/
340
+ ├── main.tf # Main configuration
341
+ ├── variables.tf # Input variables
342
+ ├── outputs.tf # Output values
343
+ ├── versions.tf # Provider versions
344
+ └── README.md # Documentation
345
+ ```
346
+ """
347
+
348
+ return [types.TextContent(type="text", text=response)]
349
+
350
+ async def _handle_validate_config(self, args: dict) -> list[types.TextContent]:
351
+ """Handle configuration validation"""
352
+ config_content = args.get("config_content")
353
+ check_best_practices = args.get("check_best_practices", True)
354
+ strict_mode = args.get("strict_mode", False)
355
+
356
+ # Syntax validation
357
+ syntax_issues = self.validator.validate_syntax(config_content)
358
+
359
+ # Best practices validation
360
+ best_practice_suggestions = []
361
+ if check_best_practices:
362
+ best_practice_suggestions = self.validator.validate_best_practices(config_content)
363
+
364
+ response = "# 📋 Terraform Configuration Validation\n\n"
365
+
366
+ # Syntax validation results
367
+ response += "## ✅ Syntax Validation\n\n"
368
+ if not syntax_issues:
369
+ response += "✅ **No syntax issues found**\n\n"
370
+ else:
371
+ response += f"Found {len(syntax_issues)} syntax issues:\n\n"
372
+ for issue in syntax_issues:
373
+ response += f"- {issue}\n"
374
+ response += "\n"
375
+
376
+ # Best practices results
377
+ if check_best_practices:
378
+ response += "## 💡 Best Practices Review\n\n"
379
+ if not best_practice_suggestions:
380
+ response += "✅ **Configuration follows best practices**\n\n"
381
+ else:
382
+ response += f"Found {len(best_practice_suggestions)} suggestions:\n\n"
383
+ for suggestion in best_practice_suggestions:
384
+ response += f"- {suggestion}\n"
385
+ response += "\n"
386
+
387
+ # Overall score
388
+ total_issues = len(syntax_issues)
389
+ total_suggestions = len(best_practice_suggestions) if check_best_practices else 0
390
+
391
+ if total_issues == 0 and total_suggestions == 0:
392
+ response += "## 🏆 Overall Assessment\n\n"
393
+ response += "**Excellent!** Your configuration is clean and follows best practices.\n"
394
+ elif total_issues == 0:
395
+ response += "## ✅ Overall Assessment\n\n"
396
+ response += "**Good!** No syntax errors, but consider the suggestions above.\n"
397
+ else:
398
+ response += "## ⚠️ Overall Assessment\n\n"
399
+ response += "**Needs attention.** Please address the syntax issues before deployment.\n"
400
+
401
+ response += "\n## 🔧 Recommended Actions\n\n"
402
+ if total_issues > 0:
403
+ response += "1. **Fix syntax issues** - Address all syntax problems first\n"
404
+ response += "2. **Run terraform validate** - Validate with Terraform CLI\n"
405
+ response += "3. **Run terraform fmt** - Format the code consistently\n"
406
+ if check_best_practices and total_suggestions > 0:
407
+ response += "4. **Review best practices** - Consider implementing suggestions\n"
408
+ response += "5. **Test thoroughly** - Always test in development environment first\n"
409
+
410
+ return [types.TextContent(type="text", text=response)]
411
+
412
+ async def _handle_deployment_workflow(self, args: dict) -> list[types.TextContent]:
413
+ """Handle deployment workflow generation"""
414
+ workflow_type = args.get("workflow_type", "basic")
415
+ backend_type = args.get("backend_type", "local")
416
+ environment = args.get("environment", "development")
417
+
418
+ if workflow_type == "basic":
419
+ commands = self.workflows.get_basic_workflow()
420
+ elif workflow_type == "production":
421
+ commands = self.workflows.get_production_workflow(backend_type)
422
+ elif workflow_type == "module_development":
423
+ commands = self.workflows.get_module_workflow()
424
+ else:
425
+ return [types.TextContent(type="text", text=f"❌ Unknown workflow type: {workflow_type}")]
426
+
427
+ response = f"""# 🚀 Terraform Deployment Workflow
428
+
429
+ ## ��� Configuration
430
+ - **Workflow Type**: {workflow_type.title()}
431
+ - **Backend**: {backend_type.upper()}
432
+ - **Environment**: {environment.title()}
433
+ - **Generated**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
434
+
435
+ ## 🔄 Commands
436
+
437
+ ```bash
438
+ {chr(10).join(commands)}
439
+ ```
440
+
441
+ ## 🛡️ Security Considerations
442
+
443
+ """
444
+
445
+ if environment == "production":
446
+ response += """### Production Environment
447
+ - ✅ Use remote state storage
448
+ - ✅ Enable state locking
449
+ - ✅ Implement approval workflows
450
+ - ✅ Run security scans
451
+ - ✅ Backup state files regularly
452
+ - ✅ Use least privilege access
453
+ - ✅ Monitor all changes
454
+
455
+ """
456
+
457
+ response += """### General Security
458
+ - 🔒 Never commit sensitive data to version control
459
+ - 🔒 Use environment variables for secrets
460
+ - 🔒 Enable encryption for state files
461
+ - 🔒 Implement proper IAM policies
462
+ - 🔒 Regular security reviews
463
+
464
+ ## 📚 Additional Resources
465
+
466
+ - [Terraform Best Practices](https://www.terraform.io/docs/cloud/guides/recommended-practices/index.html)
467
+ - [State Management](https://www.terraform.io/docs/language/state/index.html)
468
+ - [Security Guidelines](https://www.terraform.io/docs/cloud/guides/recommended-practices/part1.html)
469
+ """
470
+
471
+ return [types.TextContent(type="text", text=response)]
472
+
473
+ async def _handle_convert_to_module(self, args: dict) -> list[types.TextContent]:
474
+ """Handle module conversion"""
475
+ config_content = args.get("config_content")
476
+ module_name = args.get("module_name")
477
+ extract_variables = args.get("extract_variables", True)
478
+ generate_examples = args.get("generate_examples", True)
479
+
480
+ response = f"""# 📦 Module Conversion: {module_name}
481
+
482
+ Converting Terraform configuration to a reusable module structure.
483
+
484
+ ## 📁 Module Structure
485
+
486
+ ```
487
+ modules/{module_name}/
488
+ ├── main.tf # Main configuration
489
+ ├── variables.tf # Input variables
490
+ ├── outputs.tf # Output values
491
+ ├── versions.tf # Provider requirements
492
+ ├── README.md # Documentation
493
+ └── examples/
494
+ └── basic/
495
+ ├── main.tf # Example usage
496
+ └── README.md # Example documentation
497
+ ```
498
+
499
+ """
500
+
501
+ # Generate variables.tf
502
+ if extract_variables:
503
+ variables = self.module_converter.extract_variables(config_content)
504
+ response += f"""## variables.tf
505
+
506
+ ```hcl
507
+ {variables}
508
+
509
+ variable "tags" {{
510
+ description = "A map of tags to assign to the resource"
511
+ type = map(string)
512
+ default = {{}}
513
+ }}
514
+ ```
515
+
516
+ """
517
+
518
+ # Generate main.tf (modified config)
519
+ response += f"""## main.tf
520
+
521
+ ```hcl
522
+ {config_content}
523
+ ```
524
+
525
+ """
526
+
527
+ # Generate outputs.tf
528
+ outputs = self.module_converter.generate_outputs(config_content, module_name)
529
+ response += f"""## outputs.tf
530
+
531
+ ```hcl
532
+ {outputs}
533
+ ```
534
+
535
+ """
536
+
537
+ # Generate versions.tf
538
+ response += f"""## versions.tf
539
+
540
+ ```hcl
541
+ terraform {{
542
+ required_version = ">= 1.0"
543
+
544
+ required_providers {{
545
+ aws = {{
546
+ source = "hashicorp/aws"
547
+ version = ">= 5.0"
548
+ }}
549
+ }}
550
+ }}
551
+ ```
552
+
553
+ """
554
+
555
+ # Generate example usage
556
+ if generate_examples:
557
+ response += f"""## examples/basic/main.tf
558
+
559
+ ```hcl
560
+ module "{module_name}" {{
561
+ source = "../../"
562
+
563
+ project_name = "example"
564
+ environment = "dev"
565
+
566
+ # Add module-specific variables here
567
+
568
+ tags = {{
569
+ Environment = "development"
570
+ Project = "example"
571
+ }}
572
+ }}
573
+
574
+ output "{module_name}_outputs" {{
575
+ description = "All outputs from the {module_name} module"
576
+ value = module.{module_name}
577
+ }}
578
+ ```
579
+
580
+ """
581
+
582
+ # Generate README.md
583
+ response += f"""## README.md
584
+
585
+ ```markdown
586
+ # {module_name.title()} Module
587
+
588
+ This module creates {module_name} infrastructure resources.
589
+
590
+ ## Usage
591
+
592
+ ```hcl
593
+ module "{module_name}" {{
594
+ source = "path/to/module"
595
+
596
+ project_name = "my-project"
597
+ environment = "production"
598
+
599
+ tags = {{
600
+ Environment = "production"
601
+ Team = "infrastructure"
602
+ }}
603
+ }}
604
+ ```
605
+
606
+ ## Requirements
607
+
608
+ | Name | Version |
609
+ |------|---------|
610
+ | terraform | >= 1.0 |
611
+ | aws | >= 5.0 |
612
+
613
+ ## Providers
614
+
615
+ | Name | Version |
616
+ |------|---------|
617
+ | aws | >= 5.0 |
618
+
619
+ ## Resources
620
+
621
+ [List of resources created by this module]
622
+
623
+ ## Inputs
624
+
625
+ [Generated from variables.tf]
626
+
627
+ ## Outputs
628
+
629
+ [Generated from outputs.tf]
630
+ ```
631
+
632
+ ## 🚀 Next Steps
633
+
634
+ 1. **Create module directory structure**
635
+ 2. **Save each file in appropriate location**
636
+ 3. **Test module with examples**
637
+ 4. **Add validation rules to variables**
638
+ 5. **Update documentation**
639
+ 6. **Version tag for release**
640
+
641
+ ## 🧪 Testing the Module
642
+
643
+ ```bash
644
+ # Navigate to example
645
+ cd examples/basic
646
+
647
+ # Initialize and test
648
+ terraform init
649
+ terraform plan
650
+ terraform apply
651
+
652
+ # Clean up
653
+ terraform destroy
654
+ ```
655
+ """
656
+
657
+ return [types.TextContent(type="text", text=response)]
658
+
659
+ async def _handle_format_code(self, args: dict) -> list[types.TextContent]:
660
+ """Handle code formatting"""
661
+ config_content = args.get("config_content")
662
+ sort_blocks = args.get("sort_blocks", True)
663
+
664
+ # Basic formatting improvements
665
+ formatted_config = config_content
666
+
667
+ # Normalize indentation (2 spaces)
668
+ lines = formatted_config.split('\n')
669
+ formatted_lines = []
670
+ indent_level = 0
671
+
672
+ for line in lines:
673
+ stripped = line.strip()
674
+
675
+ # Decrease indent for closing braces
676
+ if stripped == '}':
677
+ indent_level = max(0, indent_level - 1)
678
+
679
+ # Add indentation
680
+ if stripped:
681
+ formatted_lines.append(' ' * indent_level + stripped)
682
+ else:
683
+ formatted_lines.append('')
684
+
685
+ # Increase indent for opening braces
686
+ if stripped.endswith('{'):
687
+ indent_level += 1
688
+
689
+ formatted_config = '\n'.join(formatted_lines)
690
+
691
+ response = f"""# 🎨 Terraform Code Formatting
692
+
693
+ ## ✨ Formatted Configuration
694
+
695
+ ```hcl
696
+ {formatted_config}
697
+ ```
698
+
699
+ ## 📋 Formatting Applied
700
+
701
+ - ✅ Normalized indentation (2 spaces)
702
+ - ✅ Consistent spacing
703
+ - ✅ Proper block alignment
704
+ - ✅ Standard Terraform formatting
705
+
706
+ ## 🔧 Additional Formatting Tips
707
+
708
+ 1. **Use `terraform fmt`** - Run the official formatter
709
+ 2. **Configure editor** - Set up auto-formatting in your IDE
710
+ 3. **Pre-commit hooks** - Automatically format before commits
711
+ 4. **Consistent naming** - Use lowercase with underscores
712
+ 5. **Group related blocks** - Keep similar resources together
713
+
714
+ ## 📐 Style Guidelines
715
+
716
+ ```hcl
717
+ # Good: Consistent resource naming
718
+ resource "aws_instance" "web_server" {{
719
+ ami = data.aws_ami.ubuntu.id
720
+ instance_type = var.instance_type
721
+
722
+ tags = {{
723
+ Name = "web-server"
724
+ }}
725
+ }}
726
+
727
+ # Good: Aligned arguments
728
+ variable "instance_type" {{
729
+ description = "EC2 instance type"
730
+ type = string
731
+ default = "t3.micro"
732
+ }}
733
+ ```
734
+ """
735
+
736
+ return [types.TextContent(type="text", text=response)]
737
+
738
+ async def _handle_generate_docs(self, args: dict) -> list[types.TextContent]:
739
+ """Handle documentation generation"""
740
+ config_content = args.get("config_content")
741
+ module_name = args.get("module_name", "terraform-module")
742
+ include_examples = args.get("include_examples", True)
743
+
744
+ # Extract information from config
745
+ resources = re.findall(r'resource\s+"([^"]+)"\s+"([^"]+)"', config_content)
746
+ variables = re.findall(r'variable\s+"([^"]+)"\s*\{', config_content)
747
+ outputs = re.findall(r'output\s+"([^"]+)"\s*\{', config_content)
748
+ modules = re.findall(r'module\s+"([^"]+)"\s*\{', config_content)
749
+
750
+ response = f"""# 📚 Terraform Documentation: {module_name}
751
+
752
+ ## 📋 Module Overview
753
+
754
+ This Terraform configuration manages infrastructure resources with the following components:
755
+
756
+ """
757
+
758
+ # Resources section
759
+ if resources:
760
+ response += f"""## 🏗️ Resources Created
761
+
762
+ | Resource Type | Name | Description |
763
+ |---------------|------|-------------|
764
+ """
765
+ for resource_type, name in resources:
766
+ response += f"| `{resource_type}` | `{name}` | {resource_type.replace('_', ' ').title()} resource |\n"
767
+ response += "\n"
768
+
769
+ # Modules section
770
+ if modules:
771
+ response += f"""## 📦 Modules Used
772
+
773
+ | Module Name | Description |
774
+ |-------------|-------------|
775
+ """
776
+ for module in modules:
777
+ response += f"| `{module}` | External module for {module.replace('_', ' ')} |\n"
778
+ response += "\n"
779
+
780
+ # Variables section
781
+ if variables:
782
+ response += f"""## 📥 Input Variables
783
+
784
+ | Name | Description | Type | Default |
785
+ |------|-------------|------|---------|
786
+ """
787
+ for var in variables:
788
+ response += f"| `{var}` | Description for {var} | `string` | `null` |\n"
789
+ response += "\n"
790
+
791
+ # Outputs section
792
+ if outputs:
793
+ response += f"""## 📤 Outputs
794
+
795
+ | Name | Description |
796
+ |------|-------------|
797
+ """
798
+ for output in outputs:
799
+ response += f"| `{output}` | Output description for {output} |\n"
800
+ response += "\n"
801
+
802
+ # Usage examples
803
+ if include_examples:
804
+ response += f"""## 🚀 Usage Examples
805
+
806
+ ### Basic Usage
807
+
808
+ ```hcl
809
+ module "{module_name}" {{
810
+ source = "./modules/{module_name}"
811
+
812
+ # Required variables
813
+ project_name = "my-project"
814
+ environment = "production"
815
+
816
+ # Optional variables with custom values
817
+ # Add your variable overrides here
818
+
819
+ tags = {{
820
+ Environment = "production"
821
+ Team = "infrastructure"
822
+ Project = "my-project"
823
+ }}
824
+ }}
825
+ ```
826
+
827
+ ### Advanced Usage
828
+
829
+ ```hcl
830
+ module "{module_name}" {{
831
+ source = "./modules/{module_name}"
832
+
833
+ project_name = "advanced-project"
834
+ environment = "production"
835
+
836
+ # Advanced configuration options
837
+ # Customize based on your needs
838
+
839
+ tags = {{
840
+ Environment = "production"
841
+ Team = "platform"
842
+ CostCenter = "engineering"
843
+ Owner = "platform-team"
844
+ }}
845
+ }}
846
+
847
+ # Use module outputs
848
+ output "module_outputs" {{
849
+ description = "All outputs from {module_name} module"
850
+ value = module.{module_name}
851
+ }}
852
+ ```
853
+
854
+ """
855
+
856
+ # Requirements section
857
+ response += f"""## 📋 Requirements
858
+
859
+ | Name | Version |
860
+ |------|---------|
861
+ | terraform | >= 1.0 |
862
+ | aws | >= 5.0 |
863
+
864
+ ## 🔧 Installation & Setup
865
+
866
+ 1. **Clone or download** this module
867
+ 2. **Configure variables** in `terraform.tfvars`
868
+ 3. **Initialize Terraform**: `terraform init`
869
+ 4. **Plan deployment**: `terraform plan`
870
+ 5. **Apply configuration**: `terraform apply`
871
+
872
+ ## 🧪 Testing
873
+
874
+ ```bash
875
+ # Validate configuration
876
+ terraform validate
877
+
878
+ # Format code
879
+ terraform fmt
880
+
881
+ # Plan deployment
882
+ terraform plan
883
+
884
+ # Apply in test environment
885
+ terraform apply -var="environment=test"
886
+ ```
887
+
888
+ ## 🛡️ Security Considerations
889
+
890
+ - Review all security groups and IAM policies
891
+ - Enable encryption for all storage resources
892
+ - Use least privilege access principles
893
+ - Regular security audits and updates
894
+
895
+ ## 📞 Support
896
+
897
+ For questions or issues:
898
+ - Check the documentation
899
+ - Review example configurations
900
+ - Submit issues via your project's issue tracker
901
+
902
+ ## 📜 License
903
+
904
+ [Add your license information here]
905
+ """
906
+
907
+ return [types.TextContent(type="text", text=response)]
908
+
909
+ # ============================================================================
910
+ # MAIN ENTRY POINT FOR HF SPACES
911
+ # ============================================================================
912
+
913
+ async def main():
914
+ """Main function for running the MCP server"""
915
+
916
+ # Initialize the clean MCP server
917
+ terraform_server = CleanTerraformMCPServer()
918
+
919
+ # Log startup information
920
+ logger.info("🚀 Clean Terraform MCP Server starting...")
921
+ logger.info("🔧 Available tools: generate_terraform_config, validate_terraform_config, get_deployment_workflow, convert_to_module, format_terraform_code, generate_terraform_docs")
922
+ logger.info("📦 No external dependencies - pure Terraform tooling")
923
+
924
+ # Run the server with stdio transport for HF Spaces/MCP clients
925
+ async with stdio_server() as (read_stream, write_stream):
926
+ await terraform_server.server.run(
927
+ read_stream,
928
+ write_stream,
929
+ InitializationOptions(
930
+ server_name="terraform-mcp-server",
931
+ server_version="1.0.0",
932
+ capabilities=terraform_server.server.get_capabilities(
933
+ notification_options=NotificationOptions(),
934
+ experimental_capabilities={}
935
+ )
936
+ )
937
+ )
938
+
939
+ if __name__ == "__main__":
940
+ asyncio.run(main())#!/usr/bin/env python3
941
+ """
942
+ Clean Terraform MCP Server
943
+ Single responsibility: Terraform code generation, validation, and workflows
944
+ No external dependencies beyond MCP and standard library
945
+ """
946
+
947
+ import asyncio
948
+ import json
949
+ import re
950
+ import os
951
+ from typing import Dict, List, Optional, Any
952
+ from dataclasses import dataclass
953
+ from datetime import datetime
954
+ import logging
955
+
956
+ # MCP Server imports
957
+ import mcp.types as types
958
+ from mcp.server.models import InitializationOptions
959
+ from mcp.server import NotificationOptions, Server
960
+ from mcp.server.stdio import stdio_server
961
+
962
+ # Configure logging
963
+ logging.basicConfig(level=logging.INFO)
964
+ logger = logging.getLogger(__name__)
965
+
966
+ # ============================================================================
967
+ # TERRAFORM CODE TEMPLATES AND GENERATORS
968
+ # ============================================================================
969
+
970
+ class TerraformTemplates:
971
+ """Static Terraform code templates for different resource types"""
972
+
973
+ @staticmethod
974
+ def get_provider_block(provider: str, version: str = None) -> str:
975
+ """Generate provider configuration block"""
976
+ versions = {
977
+ "aws": "~> 5.0",
978
+ "azurerm": "~> 3.0",
979
+ "google": "~> 4.0",
980
+ "kubernetes": "~> 2.0"
981
+ }
982
+
983
+ version = version or versions.get(provider, "latest")
984
+
985
+ if provider == "aws":
986
+ return f'''terraform {{
987
+ required_version = ">= 1.0"
988
+ required_providers {{
989
+ aws = {{
990
+ source = "hashicorp/aws"
991
+ version = "{version}"
992
+ }}
993
+ }}
994
+ }}
995
+
996
+ provider "aws" {{
997
+ region = var.aws_region
998
+
999
+ default_tags {{
1000
+ tags = {{
1001
+ Project = var.project_name
1002
+ ManagedBy = "Terraform"
1003
+ Environment = var.environment
1004
+ }}
1005
+ }}
1006
+ }}'''
1007
+
1008
+ elif provider == "azurerm":
1009
+ return f'''terraform {{
1010
+ required_version = ">= 1.0"
1011
+ required_providers {{
1012
+ azurerm = {{
1013
+ source = "hashicorp/azurerm"
1014
+ version = "{version}"
1015
+ }}
1016
+ }}
1017
+ }}
1018
+
1019
+ provider "azurerm" {{
1020
+ features {{}}
1021
+ }}'''
1022
+
1023
+ elif provider == "kubernetes":
1024
+ return f'''terraform {{
1025
+ required_version = ">= 1.0"
1026
+ required_providers {{
1027
+ kubernetes = {{
1028
+ source = "hashicorp/kubernetes"
1029
+ version = "{version}"
1030
+ }}
1031
+ }}
1032
+ }}
1033
+
1034
+ provider "kubernetes" {{
1035
+ config_path = "~/.kube/config"
1036
+ }}'''
1037
+
1038
+ else:
1039
+ return f'''terraform {{
1040
+ required_version = ">= 1.0"
1041
+ required_providers {{
1042
+ {provider} = {{
1043
+ source = "hashicorp/{provider}"
1044
+ version = "{version}"
1045
+ }}
1046
+ }}
1047
+ }}
1048
+
1049
+ provider "{provider}" {{
1050
+ # Configure the {provider} provider
1051
+ }}'''
1052
+
1053
+ @staticmethod
1054
+ def get_common_variables(provider: str) -> str:
1055
+ """Generate common variables for a provider"""
1056
+ if provider == "aws":
1057
+ return '''variable "aws_region" {
1058
+ description = "AWS region"
1059
+ type = string
1060
+ default = "us-west-2"
1061
+ }
1062
+
1063
+ variable "project_name" {
1064
+ description = "Name of the project"
1065
+ type = string
1066
+ }
1067
+
1068
+ variable "environment" {
1069
+ description = "Environment name"
1070
+ type = string
1071
+ default = "dev"
1072
+
1073
+ validation {
1074
+ condition = contains(["dev", "staging", "prod"], var.environment)
1075
+ error_message = "Environment must be dev, staging, or prod."
1076
+ }
1077
+ }'''
1078
+
1079
+ elif provider == "azurerm":
1080
+ return '''variable "location" {
1081
+ description = "Azure region"
1082
+ type = string
1083
+ default = "East US"
1084
+ }
1085
+
1086
+ variable "project_name" {
1087
+ description = "Name of the project"
1088
+ type = string
1089
+ }
1090
+
1091
+ variable "environment" {
1092
+ description = "Environment name"
1093
+ type = string
1094
+ default = "dev"
1095
+
1096
+ validation {
1097
+ condition = contains(["dev", "staging", "prod"], var.environment)
1098
+ error_message = "Environment must be dev, staging, or prod."
1099
+ }
1100
+ }'''
1101
+
1102
+ else:
1103
+ return '''variable "project_name" {
1104
+ description = "Name of the project"
1105
+ type = string
1106
+ }
1107
+
1108
+ variable "environment" {
1109
+ description = "Environment name"
1110
+ type = string
1111
+ default = "dev"
1112
+ }'''
1113
+
1114
+ class TerraformCodeGenerator:
1115
+ """Pure Terraform code generation without external dependencies"""
1116
+
1117
+ def __init__(self):
1118
+ self.templates = TerraformTemplates()
1119
+
1120
+ def generate_vpc_config(self, name: str, options: Dict) -> str:
1121
+ """Generate AWS VPC configuration"""
1122
+ cidr = options.get("cidr", "10.0.0.0/16")
1123
+ azs = options.get("azs", ["us-west-2a", "us-west-2b"])
1124
+ enable_nat = options.get("enable_nat_gateway", True)
1125
+
1126
+ return f'''# {name} VPC Infrastructure Configuration
1127
+ {self.templates.get_provider_block("aws")}
1128
+
1129
+ {self.templates.get_common_variables("aws")}
1130
+
1131
+ variable "vpc_cidr" {{
1132
+ description = "CIDR block for VPC"
1133
+ type = string
1134
+ default = "{cidr}"
1135
+
1136
+ validation {{
1137
+ condition = can(cidrhost(var.vpc_cidr, 0))
1138
+ error_message = "VPC CIDR must be a valid IPv4 CIDR block."
1139
+ }}
1140
+ }}
1141
+
1142
+ # VPC Module
1143
+ module "{name}_vpc" {{
1144
+ source = "terraform-aws-modules/vpc/aws"
1145
+
1146
+ name = "${{var.environment}}-{name}-vpc"
1147
+ cidr = var.vpc_cidr
1148
+
1149
+ azs = {azs}
1150
+ public_subnets = [for k, v in {azs} : cidrsubnet(var.vpc_cidr, 8, k)]
1151
+ private_subnets = [for k, v in {azs} : cidrsubnet(var.vpc_cidr, 8, k + 10)]
1152
+
1153
+ enable_nat_gateway = {str(enable_nat).lower()}
1154
+ enable_vpn_gateway = false
1155
+ enable_dns_hostnames = true
1156
+ enable_dns_support = true
1157
+
1158
+ public_subnet_tags = {{
1159
+ Type = "Public"
1160
+ Tier = "Web"
1161
+ }}
1162
+
1163
+ private_subnet_tags = {{
1164
+ Type = "Private"
1165
+ Tier = "Application"
1166
+ }}
1167
+
1168
+ tags = {{
1169
+ Name = "${{var.environment}}-{name}-vpc"
1170
+ }}
1171
+ }}
1172
+
1173
+ # Outputs
1174
+ output "vpc_id" {{
1175
+ description = "ID of the VPC"
1176
+ value = module.{name}_vpc.vpc_id
1177
+ }}
1178
+
1179
+ output "vpc_cidr_block" {{
1180
+ description = "CIDR block of the VPC"
1181
+ value = module.{name}_vpc.vpc_cidr_block
1182
+ }}
1183
+
1184
+ output "public_subnets" {{
1185
+ description = "List of IDs of public subnets"
1186
+ value = module.{name}_vpc.public_subnets
1187
+ }}
1188
+
1189
+ output "private_subnets" {{
1190
+ description = "List of IDs of private subnets"
1191
+ value = module.{name}_vpc.private_subnets
1192
+ }}
1193
+
1194
+ output "nat_gateway_ips" {{
1195
+ description = "List of public Elastic IPs for NAT Gateway"
1196
+ value = module.{name}_vpc.nat_public_ips
1197
+ }}'''
1198
+
1199
+ def generate_ec2_config(self, name: str, options: Dict) -> str:
1200
+ """Generate AWS EC2 configuration"""
1201
+ instance_type = options.get("instance_type", "t3.micro")
1202
+ key_pair = options.get("key_pair_name", f"{name}-key")
1203
+ ami_filter = options.get("ami_filter", "amzn2-ami-hvm-*-x86_64-gp2")
1204
+
1205
+ return f'''# {name} EC2 Instance Configuration
1206
+ {self.templates.get_provider_block("aws")}
1207
+
1208
+ {self.templates.get_common_variables("aws")}
1209
+
1210
+ variable "instance_type" {{
1211
+ description = "EC2 instance type"
1212
+ type = string
1213
+ default = "{instance_type}"
1214
+
1215
+ validation {{
1216
+ condition = contains([
1217
+ "t3.nano", "t3.micro", "t3.small", "t3.medium", "t3.large",
1218
+ "t3.xlarge", "t3.2xlarge", "m5.large", "m5.xlarge"
1219
+ ], var.instance_type)
1220
+ error_message = "Instance type must be a valid instance type."
1221
+ }}
1222
+ }}
1223
+
1224
+ variable "key_pair_name" {{
1225
+ description = "Name of the AWS key pair"
1226
+ type = string
1227
+ default = "{key_pair}"
1228
+ }}
1229
+
1230
+ # Data sources
1231
+ data "aws_ami" "app_ami" {{
1232
+ most_recent = true
1233
+ owners = ["amazon"]
1234
+
1235
+ filter {{
1236
+ name = "name"
1237
+ values = ["{ami_filter}"]
1238
+ }}
1239
+
1240
+ filter {{
1241
+ name = "virtualization-type"
1242
+ values = ["hvm"]
1243
+ }}
1244
+ }}
1245
+
1246
+ data "aws_vpc" "default" {{
1247
+ default = true
1248
+ }}
1249
+
1250
+ data "aws_subnets" "default" {{
1251
+ filter {{
1252
+ name = "vpc-id"
1253
+ values = [data.aws_vpc.default.id]
1254
+ }}
1255
+ }}
1256
+
1257
+ # EC2 Instance Module
1258
+ module "{name}_instance" {{
1259
+ source = "terraform-aws-modules/ec2-instance/aws"
1260
+
1261
+ name = "{name}-instance"
1262
+
1263
+ instance_type = var.instance_type
1264
+ ami = data.aws_ami.app_ami.id
1265
+ key_name = var.key_pair_name
1266
+ monitoring = true
1267
+ vpc_security_group_ids = [aws_security_group.{name}_sg.id]
1268
+ subnet_id = data.aws_subnets.default.ids[0]
1269
+
1270
+ create_iam_instance_profile = true
1271
+ iam_role_description = "IAM role for {name} EC2 instance"
1272
+ iam_role_policies = {{
1273
+ AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
1274
+ }}
1275
+
1276
+ user_data_base64 = base64encode(local.user_data)
1277
+
1278
+ root_block_device = [
1279
+ {{
1280
+ encrypted = true
1281
+ volume_type = "gp3"
1282
+ throughput = 200
1283
+ volume_size = 20
1284
+ tags = {{
1285
+ Name = "{name}-root-block"
1286
+ }}
1287
+ }},
1288
+ ]
1289
+
1290
+ tags = {{
1291
+ Name = "{name}-instance"
1292
+ }}
1293
+ }}
1294
+
1295
+ # Security Group
1296
+ resource "aws_security_group" "{name}_sg" {{
1297
+ name_prefix = "{name}-sg"
1298
+ description = "Security group for {name} instance"
1299
+ vpc_id = data.aws_vpc.default.id
1300
+
1301
+ ingress {{
1302
+ description = "SSH"
1303
+ from_port = 22
1304
+ to_port = 22
1305
+ protocol = "tcp"
1306
+ cidr_blocks = ["10.0.0.0/8"]
1307
+ }}
1308
+
1309
+ ingress {{
1310
+ description = "HTTP"
1311
+ from_port = 80
1312
+ to_port = 80
1313
+ protocol = "tcp"
1314
+ cidr_blocks = ["0.0.0.0/0"]
1315
+ }}
1316
+
1317
+ ingress {{
1318
+ description = "HTTPS"
1319
+ from_port = 443
1320
+ to_port = 443
1321
+ protocol = "tcp"
1322
+ cidr_blocks = ["0.0.0.0/0"]
1323
+ }}
1324
+
1325
+ egress {{
1326
+ from_port = 0
1327
+ to_port = 0
1328
+ protocol = "-1"
1329
+ cidr_blocks = ["0.0.0.0/0"]
1330
+ }}
1331
+
1332
+ tags = {{
1333
+ Name = "{name}-security-group"
1334
+ }}
1335
+ }}
1336
+
1337
+ # User data script
1338
+ locals {{
1339
+ user_data = <<-EOT
1340
+ #!/bin/bash
1341
+ yum update -y
1342
+ yum install -y httpd
1343
+ systemctl start httpd
1344
+ systemctl enable httpd
1345
+ echo "<h1>Hello from {name}</h1>" > /var/www/html/index.html
1346
+
1347
+ # Install CloudWatch agent
1348
+ wget https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm
1349
+ rpm -U ./amazon-cloudwatch-agent.rpm
1350
+ EOT
1351
+ }}
1352
+
1353
+ # Outputs
1354
+ output "instance_id" {{
1355
+ description = "ID of the EC2 instance"
1356
+ value = module.{name}_instance.id
1357
+ }}
1358
+
1359
+ output "instance_public_ip" {{
1360
+ description = "Public IP address of the EC2 instance"
1361
+ value = module.{name}_instance.public_ip
1362
+ }}
1363
+
1364
+ output "instance_private_ip" {{
1365
+ description = "Private IP address of the EC2 instance"
1366
+ value = module.{name}_instance.private_ip
1367
+ }}
1368
+
1369
+ output "security_group_id" {{
1370
+ description = "ID of the security group"
1371
+ value = aws_security_group.{name}_sg.id
1372
+ }}'''
1373
+
1374
+ def generate_s3_config(self, name: str, options: Dict) -> str:
1375
+ """Generate AWS S3 bucket configuration"""
1376
+ versioning = options.get("versioning", True)
1377
+ encryption = options.get("encryption", True)
1378
+ public_access = options.get("block_public_access", True)
1379
+
1380
+ return f'''# {name} S3 Bucket Configuration
1381
+ {self.templates.get_provider_block("aws")}
1382
+
1383
+ {self.templates.get_common_variables("aws")}
1384
+
1385
+ variable "bucket_name" {{
1386
+ description = "Name of the S3 bucket (will be prefixed with random string)"
1387
+ type = string
1388
+ default = "{name}-bucket"
1389
+ }}
1390
+
1391
+ variable "enable_versioning" {{
1392
+ description = "Enable versioning for the S3 bucket"
1393
+ type = bool
1394
+ default = {str(versioning).lower()}
1395
+ }}
1396
+
1397
+ # Random suffix for bucket name uniqueness
1398
+ resource "random_string" "bucket_suffix" {{
1399
+ length = 8
1400
+ special = false
1401
+ upper = false
1402
+ }}
1403
+
1404
+ # S3 Bucket Module
1405
+ module "{name}_s3_bucket" {{
1406
+ source = "terraform-aws-modules/s3-bucket/aws"
1407
+
1408
+ bucket = "${{var.bucket_name}}-${{random_string.bucket_suffix.result}}"
1409
+
1410
+ # Versioning
1411
+ versioning = {{
1412
+ enabled = var.enable_versioning
1413
+ }}
1414
+
1415
+ # Server-side encryption
1416
+ server_side_encryption_configuration = {{
1417
+ rule = {{
1418
+ apply_server_side_encryption_by_default = {{
1419
+ sse_algorithm = "AES256"
1420
+ }}
1421
+ }}
1422
+ }}
1423
+
1424
+ # Public access block
1425
+ block_public_acls = {str(public_access).lower()}
1426
+ block_public_policy = {str(public_access).lower()}
1427
+ ignore_public_acls = {str(public_access).lower()}
1428
+ restrict_public_buckets = {str(public_access).lower()}
1429
+
1430
+ # Lifecycle configuration
1431
+ lifecycle_configuration = {{
1432
+ rule = [
1433
+ {{
1434
+ id = "delete_incomplete_multipart_uploads"
1435
+ status = "Enabled"
1436
+
1437
+ abort_incomplete_multipart_upload = {{
1438
+ days_after_initiation = 7
1439
+ }}
1440
+ }},
1441
+ {{
1442
+ id = "transition_to_ia"
1443
+ status = "Enabled"
1444
+
1445
+ transition = [
1446
+ {{
1447
+ days = 30
1448
+ storage_class = "STANDARD_IA"
1449
+ }},
1450
+ {{
1451
+ days = 90
1452
+ storage_class = "GLACIER"
1453
+ }}
1454
+ ]
1455
+ }}
1456
+ ]
1457
+ }}
1458
+
1459
+ tags = {{
1460
+ Name = "${{var.bucket_name}}-${{random_string.bucket_suffix.result}}"
1461
+ }}
1462
+ }}
1463
+
1464
+ # Bucket policy for additional security
1465
+ resource "aws_s3_bucket_policy" "{name}_bucket_policy" {{
1466
+ bucket = module.{name}_s3_bucket.s3_bucket_id
1467
+
1468
+ policy = jsonencode({{
1469
+ Version = "2012-10-17"
1470
+ Statement = [
1471
+ {{
1472
+ Sid = "DenyInsecureConnections"
1473
+ Effect = "Deny"
1474
+ Principal = "*"
1475
+ Action = "s3:*"
1476
+ Resource = [
1477
+ module.{name}_s3_bucket.s3_bucket_arn,
1478
+ "${{module.{name}_s3_bucket.s3_bucket_arn}}/*"
1479
+ ]
1480
+ Condition = {{
1481
+ Bool = {{
1482
+ "aws:SecureTransport" = "false"
1483
+ }}
1484
+ }}
1485
+ }}
1486
+ ]
1487
+ }})
1488
+ }}
1489
+
1490
+ # Outputs
1491
+ output "bucket_name" {{
1492
+ description = "Name of the S3 bucket"
1493
+ value = module.{name}_s3_bucket.s3_bucket_id
1494
+ }}
1495
+
1496
+ output "bucket_arn" {{
1497
+ description = "ARN of the S3 bucket"
1498
+ value = module.{name}_s3_bucket.s3_bucket_arn
1499
+ }}
1500
+
1501
+ output "bucket_domain_name" {{
1502
+ description = "Bucket domain name"
1503
+ value = module.{name}_s3_bucket.s3_bucket_bucket_domain_name
1504
+ }}
1505
+
1506
+ output "bucket_regional_domain_name" {{
1507
+ description = "Bucket regional domain name"
1508
+ value = module.{name}_s3_bucket.s3_bucket_bucket_regional_domain_name
1509
+ }}'''
1510
+
1511
+ def generate_eks_config(self, name: str, options: Dict) -> str:
1512
+ """Generate AWS EKS cluster configuration"""
1513
+ node_instance_type = options.get("node_instance_type", "t3.medium")
1514
+ min_nodes = options.get("min_nodes", 1)
1515
+ max_nodes = options.get("max_nodes", 3)
1516
+ desired_nodes = options.get("desired_nodes", 2)
1517
+ k8s_version = options.get("kubernetes_version", "1.28")
1518
+
1519
+ return f'''# {name} EKS Cluster Configuration
1520
+ {self.templates.get_provider_block("aws")}
1521
+
1522
+ {self.templates.get_common_variables("aws")}
1523
+
1524
+ variable "cluster_name" {{
1525
+ description = "Name of the EKS cluster"
1526
+ type = string
1527
+ default = "{name}-eks-cluster"
1528
+ }}
1529
+
1530
+ variable "kubernetes_version" {{
1531
+ description = "Kubernetes version"
1532
+ type = string
1533
+ default = "{k8s_version}"
1534
+ }}
1535
+
1536
+ variable "node_instance_type" {{
1537
+ description = "Instance type for EKS nodes"
1538
+ type = string
1539
+ default = "{node_instance_type}"
1540
+ }}
1541
+
1542
+ variable "node_group_min_size" {{
1543
+ description = "Minimum number of nodes"
1544
+ type = number
1545
+ default = {min_nodes}
1546
+ }}
1547
+
1548
+ variable "node_group_max_size" {{
1549
+ description = "Maximum number of nodes"
1550
+ type = number
1551
+ default = {max_nodes}
1552
+ }}
1553
+
1554
+ variable "node_group_desired_size" {{
1555
+ description = "Desired number of nodes"
1556
+ type = number
1557
+ default = {desired_nodes}
1558
+ }}
1559
+
1560
+ # Data sources
1561
+ data "aws_availability_zones" "available" {{
1562
+ filter {{
1563
+ name = "opt-in-status"
1564
+ values = ["opt-in-not-required"]
1565
+ }}
1566
+ }}
1567
+
1568
+ data "aws_caller_identity" "current" {{}}
1569
+
1570
+ # VPC for EKS
1571
+ module "vpc" {{
1572
+ source = "terraform-aws-modules/vpc/aws"
1573
+
1574
+ name = "${{var.cluster_name}}-vpc"
1575
+ cidr = "10.0.0.0/16"
1576
+
1577
+ azs = slice(data.aws_availability_zones.available.names, 0, 3)
1578
+ public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
1579
+ private_subnets = ["10.0.11.0/24", "10.0.12.0/24", "10.0.13.0/24"]
1580
+
1581
+ enable_nat_gateway = true
1582
+ single_nat_gateway = true
1583
+ enable_dns_hostnames = true
1584
+ enable_dns_support = true
1585
+
1586
+ public_subnet_tags = {{
1587
+ "kubernetes.io/role/elb" = "1"
1588
+ }}
1589
+
1590
+ private_subnet_tags = {{
1591
+ "kubernetes.io/role/internal-elb" = "1"
1592
+ }}
1593
+
1594
+ tags = {{
1595
+ "kubernetes.io/cluster/${{var.cluster_name}}" = "shared"
1596
+ }}
1597
+ }}
1598
+
1599
+ # EKS Cluster
1600
+ module "eks" {{
1601
+ source = "terraform-aws-modules/eks/aws"
1602
+
1603
+ cluster_name = var.cluster_name
1604
+ cluster_version = var.kubernetes_version
1605
+
1606
+ vpc_id = module.vpc.vpc_id
1607
+ subnet_ids = module.vpc.private_subnets
1608
+ cluster_endpoint_public_access = true
1609
+
1610
+ cluster_addons = {{
1611
+ coredns = {{
1612
+ most_recent = true
1613
+ }}
1614
+ kube-proxy = {{
1615
+ most_recent = true
1616
+ }}
1617
+ vpc-cni = {{
1618
+ most_recent = true
1619
+ }}
1620
+ aws-ebs-csi-driver = {{
1621
+ most_recent = true
1622
+ }}
1623
+ }}
1624
+
1625
+ eks_managed_node_groups = {{
1626
+ main = {{
1627
+ name = "${{var.cluster_name}}-nodes"
1628
+
1629
+ instance_types = [var.node_instance_type]
1630
+
1631
+ min_size = var.node_group_min_size
1632
+ max_size = var.node_group_max_size
1633
+ desired_size = var.node_group_desired_size
1634
+
1635
+ disk_size = 50
1636
+
1637
+ labels = {{
1638
+ Environment = var.environment
1639
+ NodeGroup = "main"
1640
+ }}
1641
+
1642
+ tags = {{
1643
+ Name = "${{var.cluster_name}}-node"
1644
+ }}
1645
+ }}
1646
+ }}
1647
+
1648
+ # Cluster access entry
1649
+ access_entries = {{
1650
+ admin = {{
1651
+ kubernetes_groups = []
1652
+ principal_arn = data.aws_caller_identity.current.arn
1653
+
1654
+ policy_associations = {{
1655
+ admin = {{
1656
+ policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy"
1657
+ access_scope = {{
1658
+ type = "cluster"
1659
+ }}
1660
+ }}
1661
+ }}
1662
+ }}
1663
+ }}
1664
+
1665
+ tags = {{
1666
+ Environment = var.environment
1667
+ Terraform = "true"
1668
+ }}
1669
+ }}
1670
+
1671
+ # Outputs
1672
+ output "cluster_endpoint" {{
1673
+ description = "Endpoint for EKS control plane"
1674
+ value = module.eks.cluster_endpoint
1675
+ }}
1676
+
1677
+ output "cluster_security_group_id" {{
1678
+ description = "Security group ID attached to the cluster control plane"
1679
+ value = module.eks.cluster_security_group_id
1680
+ }}
1681
+
1682
+ output "cluster_name" {{
1683
+ description = "Kubernetes Cluster Name"
1684
+ value = module.eks.cluster_name
1685
+ }}
1686
+
1687
+ output "cluster_arn" {{
1688
+ description = "The Amazon Resource Name (ARN) of the cluster"
1689
+ value = module.eks.cluster_arn
1690
+ }}
1691
+
1692
+ output "cluster_certificate_authority_data" {{
1693
+ description = "Base64 encoded certificate data required to communicate with the cluster"
1694
+ value = module.eks.cluster_certificate_authority_data
1695
+ }}
1696
+
1697
+ output "configure_kubectl" {{
1698
+ description = "Configure kubectl: make sure you're logged in with the correct AWS profile and run the following command to update your kubeconfig"
1699
+ value = "aws eks --region ${{var.aws_region}} update-kubeconfig --name ${{module.eks.cluster_name}}"
1700
+ }}
1701
+
1702
+ output "vpc_id" {{
1703
+ description = "ID of the VPC where the cluster security group belongs"
1704
+ value = module.vpc.vpc_id
1705
+ }}'''
1706
+
1707
+ def generate_azure_vnet_config(self, name: str, options: Dict) -> str:
1708
+ """Generate Azure Virtual Network configuration"""
1709
+ address_space = options.get("address_space", ["10.0.0.0/16"])
1710
+
1711
+ return f'''# {name} Azure Virtual Network Configuration
1712
+ {self.templates.get_provider_block("azurerm")}
1713
+
1714
+ {self.templates.get_common_variables("azurerm")}
1715
+
1716
+ variable "address_space" {{
1717
+ description = "Address space for the virtual network"
1718
+ type = list(string)
1719
+ default = {address_space}
1720
+ }}
1721
+
1722
+ # Resource Group
1723
+ resource "azurerm_resource_group" "{name}_rg" {{
1724
+ name = "${{var.environment}}-{name}-rg"
1725
+ location = var.location
1726
+
1727
+ tags = {{
1728
+ Environment = var.environment
1729
+ Project = var.project_name
1730
+ }}
1731
+ }}
1732
+
1733
+ # Virtual Network Module
1734
+ module "{name}_vnet" {{
1735
+ source = "Azure/vnet/azurerm"
1736
+
1737
+ resource_group_name = azurerm_resource_group.{name}_rg.name
1738
+ location = azurerm_resource_group.{name}_rg.location
1739
+ vnet_name = "${{var.environment}}-{name}-vnet"
1740
+ address_space = var.address_space
1741
+
1742
+ subnet_prefixes = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
1743
+ subnet_names = ["subnet1", "subnet2", "subnet3"]
1744
+
1745
+ tags = {{
1746
+ Environment = var.environment
1747
+ Project = var.project_name
1748
+ }}
1749
+ }}
1750
+
1751
+ # Outputs
1752
+ output "vnet_id" {{
1753
+ description = "The ID of the Virtual Network"
1754
+ value = module.{name}_vnet.vnet_id
1755
+ }}
1756
+
1757
+ output "vnet_name" {{
1758
+ description = "The name of the Virtual Network"
1759
+ value = module.{name}_vnet.vnet_name
1760
+ }}
1761
+
1762
+ output "subnet_ids" {{
1763
+ description = "The IDs of the subnets"
1764
+ value = module.{name}_vnet.vnet_subnets
1765
+ }}
1766
+
1767
+ output "resource_group_name" {{
1768
+ description = "The name of the resource group"
1769
+ value = azurerm_resource_group.{name}_rg.name
1770
+ }}'''
1771
+
1772
+ # ============================================================================
1773
+ # TERRAFORM VALIDATION AND UTILITIES
1774
+ # ============================================================================
1775
+
1776
+ class TerraformValidator:
1777
+ """Terraform configuration validation utilities"""
1778
+
1779
+ @staticmethod
1780
+ def validate_syntax(config: str) -> List[str]:
1781
+ """Basic Terraform syntax validation"""
1782
+ issues = []
1783
+
1784
+ # Check for terraform block
1785
+ if not re.search(r'terraform\s*\{', config):
1786
+ issues.append("⚠️ Missing terraform {} block")
1787
+
1788
+ # Check for provider block
1789
+ if not re.search(r'provider\s+"[^"]+"\s*\{', config):
1790
+ issues.append("⚠️ Missing provider configuration")
1791
+
1792
+ # Check for version constraints
1793
+ if not re.search(r'version\s*=\s*"[^"]+"', config):
1794
+ issues.append("⚠️ Missing provider version constraints")
1795
+
1796
+ # Check resource naming conventions
1797
+ resources = re.findall(r'resource\s+"([^"]+)"\s+"([^"]+)"', config)
1798
+ for resource_type, resource_name in resources:
1799
+ if re.search(r'[A-Z-]', resource_name):
1800
+ issues.append(f"⚠️ Resource '{resource_name}' should use lowercase with underscores")
1801
+
1802
+ # Check for variable descriptions
1803
+ variables = re.findall(r'variable\s+"([^"]+)"\s*\{', config)
1804
+ for var_name in variables:
1805
+ var_block = re.search(f'variable\\s+"{var_name}"\\s*\\{{([^}}]+)\\}}', config, re.DOTALL)
1806
+ if var_block and 'description' not in var_block.group(1):
1807
+ issues.append(f"💡 Variable '{var_name}' missing description")
1808
+
1809
+ return issues
1810
+
1811
+ @staticmethod
1812
+ def validate_best_practices(config: str) -> List[str]:
1813
+ """Check Terraform best practices"""
1814
+ suggestions = []
1815
+
1816
+ # Check for tags
1817
+ if 'tags' not in config.lower():
1818
+ suggestions.append("💡 Consider adding tags to resources")
1819
+
1820
+ # Check for remote state
1821
+ if 'backend' not in config:
1822
+ suggestions.append("💡 Consider using remote state storage")
1823
+
1824
+ # Check for data sources vs hardcoded values
1825
+ if re.search(r'ami-[a-f0-9]+', config):
1826
+ suggestions.append("💡 Consider using data sources instead of hardcoded AMI IDs")
1827
+
1828
+ # Check for variable validation
1829
+ if 'variable' in config and 'validation' not in config:
1830
+ suggestions.append("💡 Consider adding validation rules to variables")
1831
+
1832
+ # Check for outputs
1833
+ if 'resource' in config and 'output' not in config:
1834
+ suggestions.append("💡 Consider adding outputs for important resource attributes")
1835
+
1836
+ return suggestions
1837
+
1838
+ class TerraformWorkflows:
1839
+ """Terraform deployment workflow generators"""
1840
+
1841
+ @staticmethod
1842
+ def get_basic_workflow() -> List[str]:
1843
+ """Get basic Terraform workflow commands"""
1844
+ return [
1845
+ "terraform init",
1846
+ "terraform validate",
1847
+ "terraform fmt",
1848
+ "terraform plan",
1849
+ "terraform apply",
1850
+ "terraform output"
1851
+ ]
1852
+
1853
+ @staticmethod
1854
+ def get_production_workflow(backend_type: str = "s3") -> List[str]:
1855
+ """Get production-ready workflow commands"""
1856
+ commands = [
1857
+ f"# Production Terraform Workflow with {backend_type.upper()} backend",
1858
+ "",
1859
+ "# 1. Initialize with backend configuration",
1860
+ "terraform init",
1861
+ "",
1862
+ "# 2. Validate configuration",
1863
+ "terraform validate",
1864
+ "",
1865
+ "# 3. Format code",
1866
+ "terraform fmt",
1867
+ "",
1868
+ "# 4. Security scan (optional)",
1869
+ "# tfsec .",
1870
+ "",
1871
+ "# 5. Plan and save",
1872
+ "terraform plan -out=tfplan",
1873
+ "",
1874
+ "# 6. Review plan output carefully",
1875
+ "terraform show tfplan",
1876
+ "",
1877
+ "# 7. Apply saved plan",
1878
+ "terraform apply tfplan",
1879
+ "",
1880
+ "# 8. Verify outputs",
1881
+ "terraform output",
1882
+ "",
1883
+ "# 9. Clean up plan file",
1884
+ "rm tfplan"
1885
+ ]
1886
+
1887
+ if backend_type == "s3":
1888
+ commands.insert(4, "# Configure S3 backend if not done")
1889
+ commands.insert(5, "# terraform init -backend-config=backend.hcl")
1890
+ commands.insert(6, "")
1891
+
1892
+ return commands
1893
+
1894
+ @staticmethod
1895
+ def get_module_workflow() -> List[str]:
1896
+ """Get module development workflow"""
1897
+ return [
1898
+ "# Module Development Workflow",
1899
+ "",
1900
+ "# 1. Initialize module",
1901
+ "terraform init",
1902
+ "",
1903
+ "# 2. Validate module",
1904
+ "terraform validate",
1905
+ "",
1906
+ "# 3. Format module code",
1907
+ "terraform fmt -recursive",
1908
+ "",
1909
+ "# 4. Generate documentation",
1910
+ "# terraform-docs markdown . > README.md",
1911
+ "",
1912
+ "# 5. Test module (in examples/ directory)",
1913
+ "cd examples/basic",
1914
+ "terraform init",
1915
+ "terraform plan",
1916
+ "",
1917
+ "# 6. Clean up test",
1918
+ "terraform destroy",
1919
+ "cd ../.