Auto-Generate Terraform Modules Using OpenAI Function Calling
Build a tool that takes plain English descriptions and generates production-ready Terraform modules using OpenAI's function calling API. No more starting from scratch.
Writing Terraform modules from scratch is tedious. You know what you want ā "an S3 bucket with versioning, lifecycle rules, and encryption" ā but translating that into correct HCL takes time.
This tool takes plain English and outputs production-ready Terraform modules using OpenAI function calling.
What We're Building
$ python generate.py "Create an EKS cluster with managed node groups,
autoscaling between 2-10 nodes, and IRSA enabled"
Generating...
ā
Generated: ./output/eks-cluster/
main.tf (87 lines)
variables.tf (34 lines)
outputs.tf (18 lines)
README.mdWhy Function Calling (Not Just a Prompt)?
With a plain prompt, you get text output that might not be valid HCL. Function calling forces the model to output structured data matching a schema you define ā no hallucinated syntax.
# Without function calling: model returns markdown with ```hcl blocks
# You parse it, hope the HCL is valid, handle errors
# With function calling: model returns structured JSON matching your schema
# You render the files directly ā always valid structureSetup
pip install openai python-dotenv# .env
OPENAI_API_KEY=sk-...Step 1: Define the Schema
Tell OpenAI exactly what structure we want back:
# schemas.py
TERRAFORM_MODULE_SCHEMA = {
"name": "generate_terraform_module",
"description": "Generate a complete Terraform module for the described infrastructure",
"parameters": {
"type": "object",
"properties": {
"module_name": {
"type": "string",
"description": "Snake case name for the module (e.g., eks_cluster, s3_bucket)"
},
"description": {
"type": "string",
"description": "What this module creates"
},
"main_tf": {
"type": "string",
"description": "Complete contents of main.tf"
},
"variables_tf": {
"type": "string",
"description": "Complete contents of variables.tf with all variable blocks"
},
"outputs_tf": {
"type": "string",
"description": "Complete contents of outputs.tf"
},
"required_providers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"source": {"type": "string"},
"version": {"type": "string"}
}
},
"description": "List of required Terraform providers"
},
"example_usage": {
"type": "string",
"description": "Example module usage in HCL"
}
},
"required": ["module_name", "main_tf", "variables_tf", "outputs_tf", "required_providers"]
}
}Step 2: The Generator
# generator.py
import json
import os
from openai import OpenAI
from schemas import TERRAFORM_MODULE_SCHEMA
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
SYSTEM_PROMPT = """You are a senior DevOps engineer and Terraform expert.
Generate production-ready Terraform modules following these standards:
1. Use variables for all configurable values with descriptions and types
2. Include sensible defaults but no hardcoded values
3. Add tags variable with default empty map (merge with local tags)
4. Include lifecycle rules where appropriate
5. Follow Terraform best practices (prevent_destroy for critical resources)
6. Use latest stable provider versions
7. Add count or for_each for scalability where applicable
8. Include all necessary IAM policies with least-privilege principles
9. Always output resource IDs, ARNs, and names
For AWS resources, use us-east-1 as default region in examples but make it configurable."""
def generate_terraform_module(description: str) -> dict:
"""Generate a Terraform module from a plain English description."""
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Generate a Terraform module for: {description}"}
],
tools=[{
"type": "function",
"function": TERRAFORM_MODULE_SCHEMA
}],
tool_choice={"type": "function", "function": {"name": "generate_terraform_module"}},
temperature=0.2 # Low temp for consistent, correct code
)
tool_call = response.choices[0].message.tool_calls[0]
return json.loads(tool_call.function.arguments)Step 3: File Writer
# writer.py
import os
def write_module(module_data: dict, output_dir: str = "./output"):
"""Write generated Terraform module to disk."""
module_name = module_data["module_name"]
module_dir = os.path.join(output_dir, module_name)
os.makedirs(module_dir, exist_ok=True)
# Write main.tf
with open(os.path.join(module_dir, "main.tf"), "w") as f:
f.write(module_data["main_tf"])
# Write variables.tf
with open(os.path.join(module_dir, "variables.tf"), "w") as f:
f.write(module_data["variables_tf"])
# Write outputs.tf
with open(os.path.join(module_dir, "outputs.tf"), "w") as f:
f.write(module_data["outputs_tf"])
# Write versions.tf with required providers
providers = module_data.get("required_providers", [])
if providers:
versions_content = 'terraform {\n required_providers {\n'
for p in providers:
versions_content += f' {p["name"]} = {{\n'
versions_content += f' source = "{p["source"]}"\n'
versions_content += f' version = "{p["version"]}"\n'
versions_content += ' }\n'
versions_content += ' }\n}\n'
with open(os.path.join(module_dir, "versions.tf"), "w") as f:
f.write(versions_content)
# Write README
readme = f"""# {module_name}
{module_data.get('description', '')}
## Usage
```hcl
{module_data.get('example_usage', '# See variables.tf for all options')}Variables
See variables.tf for all available variables.
Outputs
See outputs.tf for all available outputs.
Generated by Terraform Module Generator """ with open(os.path.join(module_dir, "README.md"), "w") as f: f.write(readme)
return module_dir
---
## Step 4: CLI Interface
```python
# generate.py
import sys
import os
from dotenv import load_dotenv
from generator import generate_terraform_module
from writer import write_module
load_dotenv()
def main():
if len(sys.argv) < 2:
print("Usage: python generate.py '<description>'")
print("Example: python generate.py 'S3 bucket with versioning and lifecycle rules'")
sys.exit(1)
description = " ".join(sys.argv[1:])
print(f"Generating Terraform module for: {description}")
print("Calling OpenAI...")
module_data = generate_terraform_module(description)
output_path = write_module(module_data)
files = os.listdir(output_path)
print(f"\nā
Generated: {output_path}/")
for f in sorted(files):
size = os.path.getsize(os.path.join(output_path, f))
lines = sum(1 for _ in open(os.path.join(output_path, f))) if f.endswith('.tf') else '-'
print(f" {f} ({lines} lines)" if f.endswith('.tf') else f" {f}")
if __name__ == "__main__":
main()
Example Output
python generate.py "RDS PostgreSQL with Multi-AZ, automated backups, and parameter group"Generated variables.tf excerpt:
variable "db_name" {
type = string
description = "Name of the database to create"
}
variable "engine_version" {
type = string
default = "15.4"
description = "PostgreSQL engine version"
}
variable "instance_class" {
type = string
default = "db.t3.medium"
description = "RDS instance class"
}
variable "multi_az" {
type = bool
default = true
description = "Enable Multi-AZ deployment"
}
variable "backup_retention_period" {
type = number
default = 7
description = "Days to retain automated backups"
}Add Validation Step
Run terraform validate automatically after generation:
import subprocess
def validate_module(module_dir: str) -> bool:
result = subprocess.run(
["terraform", "init", "-backend=false"],
cwd=module_dir, capture_output=True, text=True
)
if result.returncode != 0:
print(f"Init failed: {result.stderr}")
return False
result = subprocess.run(
["terraform", "validate"],
cwd=module_dir, capture_output=True, text=True
)
if result.returncode == 0:
print("ā
Terraform validation passed")
return True
else:
print(f"ā Validation failed: {result.stderr}")
return FalseThis tool generates a working starting point for any Terraform module in seconds. It's not perfect ā always review before applying ā but it eliminates the blank-page problem and gets the boilerplate right.
Pair AI-generated modules with KodeKloud Terraform labs to understand what each resource does before deploying.
Today I Fixed
Short real fixes from production ā posted daily
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam ā just practical engineering content.
Related Articles
Build an AI-Powered CI/CD Pipeline Failure Analyzer with LangChain
Build a tool that automatically reads CI/CD failure logs, uses LangChain + Claude to diagnose the root cause, and posts a clear explanation with fix suggestions to your PR.
Build an AI-Powered DevOps Chatbot with Streamlit on Kubernetes
Build a DevOps assistant chatbot that answers infrastructure questions, generates kubectl commands, and explains errors ā deployed as a Streamlit app on Kubernetes.
Build LLM-Powered Runbook Automation with Haystack and Kubernetes
Turn your static runbooks into an AI system that answers 'what do I do when X happens' with step-by-step instructions retrieved from your actual documentation.