šŸŽ‰ DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

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.

DevOpsBoysMay 29, 20265 min read
Share:Tweet

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

bash
$ 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.md

Why 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.

python
# 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 structure

Setup

bash
pip install openai python-dotenv
bash
# .env
OPENAI_API_KEY=sk-...

Step 1: Define the Schema

Tell OpenAI exactly what structure we want back:

python
# 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

python
# 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

python
# 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

bash
python generate.py "RDS PostgreSQL with Multi-AZ, automated backups, and parameter group"

Generated variables.tf excerpt:

hcl
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:

python
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 False

This 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

Browse fixes
Newsletter

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

Comments