In modern cloud infrastructure development, writing Terraform code manually can be time-consuming and error-prone—especially for teams that frequently deploy modular and scalable environments. There’s a growing need for tools that:
- Allow natural language input to describe infrastructure requirements.
- Automatically generate clean, modular Terraform code.
- Integrate with cloud authentication mechanisms.
- Save and organize code into execution-ready files.
This model bridges the gap between human-readable Infrastructure descriptions and machine-executable Terraform scripts, making infrastructure-as-code more accessible and efficient. To build this model, we utilize CodeGemma, a lightweight AI model optimized for coding tasks, which runs locally via Ollama.
In this blog, we explore how to build a Terraform code generator web app using:
- Flask for the web interface
- Ollama’s CodeGemma model for AI-powered code generation
- Azure CLI authentication using service principal credentials
- Modular Terraform file creation based on user queries
This tool empowers developers to describe infrastructure needs in natural language and receive clean, modular Terraform code ready for deployment.
Technologies Used
CodeGemma
CodeGemma is a family of lightweight, open-source models optimized for coding tasks. It supports code generation from natural language.
Running CodeGemma locally via Ollama means:
- No cloud dependency: You don’t need to send data to external APIs.
- Faster response times: Ideal for iterative development.
- Privacy and control: Your infrastructure queries and generated code stay on your machine.
- Offline capability: Ideal for use in restricted or secure environments.
- Zero cost: Since the model runs locally, there’s no usage fee or subscription required—unlike cloud-based AI services.
Flask
We chose Flask as the web framework for this project because of its:
- Simplicity and flexibility: Flask is a lightweight and easy-to-set-up framework, making it ideal for quick prototyping.
Initial Setup
- Install Python.
winget install Python.Python.3
- Install Visual Studio.
- Install Download Ollama on Windows.
- Pull and run the codegemma:7b.
ollama pull codegemma:7b ollama run codegemma:7b
- Install the Ollama Python library to use Gemma 3 in your Python projects.
pip install ollama
Folder Structure
Code
from flask import Flask, jsonify, request, render_template_string from ollama import generate import subprocess import re import os app = Flask(__name__) # Azure credentials CLIENT_ID = "Enter your credentials here." CLIENT_SECRET = "Enter your credentials here." TENANT_ID = "Enter your credentials here." auth_status = {"status": "not_authenticated", "details": ""} input_fields_html = "" def authenticate_with_azure(): try: result = subprocess.run( ["cmd.exe", "/c", "C:\Program Files\Microsoft SDKs\Azure\CLI2\wbin\az.cmd", "login", "--service-principal", "-u", CLIENT_ID, "-p", CLIENT_SECRET, "--tenant", TENANT_ID], capture_output=True, text=True, check=True ) auth_status["status"] = "success" auth_status["details"] = result.stdout except subprocess.CalledProcessError as e: auth_status["status"] = "failed" auth_status["details"] = e.stderr except Exception as ex: auth_status["status"] = "terminated" auth_status["details"] = str(ex) @app.route('/', methods=['GET', 'POST']) def home(): terraform_code = "" user_query = "" input_fields_html = "" if request.method == 'POST': user_query = request.form.get('query', '') base_prompt = ( "Generate modular Terraform code using best practices. " "Create separate files for main.tf, vm.tf, vars.tf, terraform.tfvars, subnet.tf, kubernetes_cluster etc. " "Ensure the code is clean and execution-ready. " "Use markdown headers like ## Main.tf: followed by code blocks." ) full_prompt = base_prompt + "n" + user_query try: response_cleaned = generate(model='codegemma:7b', prompt=full_prompt) terraform_code = response_cleaned.get('response', '').strip() except Exception as e: terraform_code = f"# Error generating code: {str(e)}" provider_block = f""" provider "azurerm" {{ features {{}} subscription_id = "Enter your credentials here." client_id = "{CLIENT_ID}" client_secret = "{CLIENT_SECRET}" tenant_id = "{TENANT_ID}" }}""" terraform_code = provider_block + "nn" + terraform_code with open('main.tf', 'w', encoding='utf-8') as f: f.write(terraform_code) # Create output directory output_dir = r"C:Usersriya.achkarpohreDesktopAItest7terraform_output" os.makedirs(output_dir, exist_ok=True) # Define output paths paths = { "main.tf": os.path.join(output_dir, "Main.tf"), "vm.tf": os.path.join(output_dir, "VM.tf"), "subnet.tf": os.path.join(output_dir, "Subnet.tf"), "vpc.tf": os.path.join(output_dir, "VPC.tf"), "vars.tf": os.path.join(output_dir, "Vars.tf"), "terraform.tfvars": os.path.join(output_dir, "Terraform.tfvars"), "kubernetes_cluster.tf": os.path.join(output_dir, "kubernetes_cluster.tf") } # Split response using markdown headers sections = re.split(r'##s*(.*?).tf:s*n+```(?:terraform)?n', terraform_code) # sections = ['', 'Main', '<code>', 'VM', '<code>', ...] for i in range(1, len(sections), 2): filename = sections[i].strip().lower() + '.tf' code_block = sections[i + 1].strip() # Remove closing backticks if present code_block = re.sub(r'```$', '', code_block) # Save to file if path is defined if filename in paths: with open(paths[filename], 'w', encoding='utf-8') as f: f.write(code_block) print(f"n--- Written: {filename} ---") print(code_block) else: print(f"n--- Skipped unknown file: {filename} ---") return render_template_string(f""" <html> <head><title>Terraform Generator</title></head> <body> <form method="post"> <center> <label>Enter your query:</label><br> <textarea name="query" rows="6" cols="80" placeholder="Describe your infrastructure requirement here..."></textarea><br><br> <input type="submit" value="Generate Terraform"> </center> </form> <hr> <h2>Generated Terraform Code:</h2> <pre>{terraform_code}</pre> <h2>Enter values for the required variables:</h2> <h2>Authentication Status:</h2> <pre>Status: {auth_status['status']}n{auth_status['details']}</pre> </body> </html> """) # Initial GET request return render_template_string(''' <html> <head><title>Terraform Generator</title></head> <body> <form method="post"> <center> <label>Enter your query:</label><br> <textarea name="query" rows="6" cols="80" placeholder="Describe your infrastructure requirement here..."></textarea><br><br> <input type="submit" value="Generate Terraform"> </center> </form> </body> </html> ''') authenticate_with_azure() @app.route('/authenticate', methods=['POST']) def authenticate(): authenticate_with_azure() return jsonify(auth_status) if __name__ == '__main__': app.run(debug=True)
Open Visual Studio, create a new file named file.py
, and paste the code into it. Then, open the terminal and run the script by typing:
python file.py
Flask Development Server
Code Structure Explanation
- Azure Authentication
- The app uses the Azure CLI (az.cmd) via Python’s subprocess.run() to authenticate with Azure using a service principal. This ensures secure access to Azure resources before generating Terraform code.
- User Query Handling
- When a user submits a query through the web form, it is captured using:
user_query = request.form.get('query', '')
- Prompt Construction
- The query is appended to a base prompt that instructs CodeGemma to generate modular Terraform code using best practices. This prompt includes instructions to split the code into files, such as main.tf, vm.tf, subnet.tf, etc.
- Code Generation via CodeGemma
- The prompt is sent to the CodeGemma:7b model using:
response_cleaned = generate(model='codegemma:7b', prompt=full_prompt)
- Saving the Full Response
- The entire generated Terraform code is first saved to a main.tf file as a backup.
- Output Directory Setup
- A specific output directory is created using os.makedirs() to store the split .tf files:
output_dir = r"C:Usersriya.achkarpohreDesktopAItest7terraform_output"
- File Path Mapping
- A dictionary maps expected filenames (such as main.tf and vm.tf) to their respective output paths. This ensures each section of the generated code is saved correctly.
- Code Splitting Logic
- The response is split using a regex-based approach, based on markdown headers like ## main.tf: followed by Terraform code blocks. This helps isolate each module.
- Conditional File Writing
- For each split section, the code checks if the filename exists in the predefined path dictionary:
- If defined, the code block is written to the corresponding file.
- If not defined, the section is skipped and logged as “unknown file”.
- For each split section, the code checks if the filename exists in the predefined path dictionary:
- Web Output Rendering
- The generated code and authentication status are displayed on the webpage using render_template_string().
Terminal
The Power of AI in Infrastructure Automation
This project demonstrates how combining AI models, such as CodeGemma, with simple tools like Flask and Terraform can revolutionize the way we approach cloud infrastructure provisioning. By allowing developers to describe their infrastructure in natural language and instantly receive clean, modular Terraform code, we eliminate the need for repetitive manual scripting and reduce the chances of human error.
Running CodeGemma locally via Ollama ensures:
- Full control over data
- Zero cost for code generation
- Fast and private execution
- Seamless integration with existing workflows
The use of Azure CLI authentication adds a layer of real-world applicability, making the generated code deployable in enterprise environments.
Whether you’re a cloud engineer, DevOps practitioner, or technical consultant, this tool empowers you to move faster, prototype smarter, and deploy infrastructure with confidence.
As AI continues to evolve, tools like this will become essential in bridging the gap between human intent and machine execution, making infrastructure-as-code not only powerful but also intuitive.
Source: Read MoreÂ