Why this is hard to get right
The Real Cost of Vague Infrastructure Prompts
Marcus is a senior platform engineer at a mid-sized SaaS company. His team is building shared Terraform modules for three product teams, each shipping to AWS. He has a clear mental picture of what the S3 + CloudFront module needs to do — but translating that into a reusable, well-documented spec is where things keep breaking down.
He starts where most engineers do: a quick message to an AI assistant. "Create a Terraform module for our AWS S3 and CloudFront setup." The output comes back fast. It looks reasonable at first glance — a main.tf, a few variables, an output or two. But within minutes, Marcus spots the problems:
- The bucket policy uses
aws:SecureTransportbut not Origin Access Control, which his security team banned two versions ago. - There are 23 input variables with no defaults and no validation rules.
- The module doesn't distinguish between dev and prod, so every environment gets the same WAF configuration.
- The resource naming convention doesn't match the org's standard, meaning the PR will get rejected in review.
Marcus edits and re-prompts. Then edits again. He spends 90 minutes going in circles, adjusting one thing at a time, because each new response fixes one problem and introduces another. The root issue isn't the AI — it's that his original prompt gave no constraints, no context, and no structured deliverables.
The next time, Marcus approaches it differently. He thinks through the spec before prompting: which AWS services, which provider versions, which environments, what security controls are non-negotiable, how many inputs the interface can have before it becomes a burden. He writes a prompt that acts like a design brief — not a casual request.
The AI response changes dramatically. It returns a clean Inputs table with types, defaults, and validation blocks. It includes an Outputs table engineers can reference immediately. The resource naming follows the org convention. The WAF toggle is optional, off by default in dev, on in prod. The module has 11 inputs — under the cap Marcus set.
The PR goes through review in one round. Two product teams adopt the module within a week. Marcus reuses the same prompt structure — adjusted for a different service — the following sprint.
The lesson isn't that AI is better or worse at Terraform. It's that infrastructure design requires upfront decisions, and a vague prompt defers all those decisions to the AI, which has no idea what your org's standards, environments, or security posture look like. A structured prompt front-loads those decisions. It turns a guessing game into a reliable output every time.
Common mistakes to avoid
Skipping Provider and Terraform Version Constraints
When you don't specify your Terraform version or AWS provider version, the AI defaults to patterns that may use deprecated resources or syntax incompatible with your pipeline. For example,
aws_cloudfront_distributionOAC configuration differs significantly between provider 4.x and 5.x. Always include both version constraints to get output you can actually apply without rewriting blocks.Not Defining Environment-Specific Behavior
Asking for a 'reusable' module without specifying dev, staging, and prod differences forces the AI to invent behavior. It either applies prod-grade settings everywhere (expensive and overkill) or strips them out entirely (unsafe). Specify which toggles differ by environment — WAF, logging, retention, ACM cert behavior — so the module handles each correctly without extra inputs.
Leaving Security Posture Undefined
Omitting security requirements like least privilege, OAC vs. OAI, bucket policy rules, or encryption settings causes the AI to make assumptions that fail security reviews. These aren't stylistic choices — they're organizational requirements. State your security constraints explicitly: which IAM patterns are approved, which legacy features are banned, and which compliance controls are mandatory.
Requesting Output Without Specifying Format
Asking for a 'module design' without specifying deliverables — variables table, outputs table, resource list, usage example — produces a wall of HCL code with no documentation. That's harder to review than the module itself. Name the exact output sections you need so the result matches how your team actually reviews and hands off module specs.
Setting No Limit on Input Variables
Unrestricted prompts routinely produce modules with 20+ inputs, most of which have no defaults and no validation. That makes the module hard to use and impossible to enforce. Cap your input count explicitly — 10 to 15 is a practical ceiling for most modules — and require defaults and validation rules so callers can't pass invalid values.
Ignoring Naming and Tagging Conventions
Every org has resource naming and tagging standards. When you don't include them, the AI invents its own — and your PR gets rejected in the first review cycle. Include your naming pattern directly in the prompt (e.g.,
{env}-{team}-{service}-{region}) and specify which tags are required so the module enforces them by default.
The transformation
Create a Terraform module for our AWS setup and make it reusable.
You’re a **senior DevOps engineer**. Design a Terraform module spec for an **AWS S3 + CloudFront static site** used by our marketing team. 1. **Context:** Environments: dev, staging, prod. Region: us-east-1. Terraform: 1.6+. AWS provider: 5.x. 2. **Requirements:** Private S3 bucket, CloudFront OAC, HTTPS with ACM cert in us-east-1, optional WAF toggle. 3. **Deliverables:** List **variables** (types, defaults, validation), **outputs**, resource naming rules, and a short **usage example**. 4. **Constraints:** Follow least privilege. Don’t use deprecated resources. Keep the interface under **12 inputs**. Format as: Overview, Inputs table, Outputs table, Resource list, Example.
Why this works
Role Assignment Sharpens Technical Depth
The After Prompt opens with 'You're a senior DevOps engineer.' This single line changes the register of every response. The AI produces output written to the standard of someone who has shipped production modules — not beginner tutorials. It prioritizes OAC over legacy OAI, references least privilege correctly, and avoids anti-patterns that junior-level prompts routinely surface.
Version Context Prevents Deprecated Patterns
The After Prompt explicitly states 'Terraform: 1.6+. AWS provider: 5.x.' Without these constraints, the AI draws from its full training corpus, which includes outdated patterns from provider 3.x and 4.x. Pinning versions eliminates that ambiguity and ensures the HCL syntax, resource arguments, and feature availability all match what your pipeline can actually run.
Capped Input Count Forces Interface Discipline
The After Prompt instructs 'Keep the interface under 12 inputs.' This constraint forces the AI to make tradeoffs — combining related variables, choosing sensible defaults, and omitting edge-case inputs. Without a cap, AI-generated modules sprawl. With one, the output mirrors how experienced engineers design reusable modules: minimal surface area, maximum coverage.
Named Deliverables Produce Reviewable Output
The After Prompt lists exact output sections: 'Overview, Inputs table, Outputs table, Resource list, Example.' This transforms the response from a code dump into a structured design document. Each section maps to a real review checkpoint — variables get validated, outputs get referenced, and the usage example gets copied directly into a calling module.
Explicit Security Requirements Block Risky Defaults
The After Prompt states 'Follow least privilege. Don't use deprecated resources.' and specifies OAC over generic CloudFront access. These aren't suggestions — they're constraints the AI treats as hard requirements. The result is a module that passes security review without manual remediation, because the design brief itself encoded your org's security posture.
The framework behind the prompt
The Theory Behind Effective Infrastructure Prompts
Terraform module design is fundamentally a interface design problem, not a coding problem. The HCL you write is a consequence of the decisions you make upfront: which variables are required, which are optional, what the defaults should be, how resources should be named, and where the module's responsibility ends and the caller's begins.
This is why vague prompts fail so consistently for infrastructure work. When you write "create a reusable Terraform module," you're deferring every interface decision to the AI — which has no knowledge of your org's security posture, naming conventions, provider version constraints, or environment topology. The AI fills those gaps with plausible-sounding defaults that may be technically valid but organizationally wrong.
The Structured Specification Pattern — used in the After Prompt on this page — mirrors how experienced platform engineers approach module design before writing code. It separates four distinct concerns:
- Context: What's the environment? What versions, regions, and accounts are in scope?
- Requirements: What must the module do? What security controls are non-negotiable?
- Deliverables: What does the output look like? Variables table, outputs table, usage example?
- Constraints: What's out of scope? How complex can the interface get?
This pattern draws on principles from API-first design — the idea that you design the interface before the implementation. In Terraform, the inputs and outputs table is the API. Defining it clearly before code generation prevents the most common failure mode: a module with a clean interior but an unusable interface.
The input cap constraint — "keep the interface under 12 inputs" — applies the cognitive load principle from software UX research. Interfaces with more than 7 to 12 configurable options require documentation just to use safely. Forcing the AI to stay under that threshold produces modules that callers can adopt without reading source code.
Finally, specifying provider versions grounds the AI's output in a deterministic version window. Terraform's AWS provider has introduced breaking changes between major versions — particularly around OAC vs. OAI for CloudFront, and S3 bucket configuration splitting in provider 4.x. Without version constraints, the AI blends patterns from multiple incompatible versions, producing output that looks correct but fails on terraform plan.
Prompt variations
You're a senior platform engineer. Design a Terraform module spec for a GCP Cloud Run + External HTTP(S) Load Balancer deployment used by backend API teams.
- Context: Environments: dev, staging, prod. Region: us-central1. Terraform: 1.6+. Google provider: 5.x. Project IDs differ per environment.
- Requirements: Cloud Run service with VPC connector, HTTPS load balancer with Google-managed SSL, IAM invoker binding for internal service accounts, optional Cloud Armor policy toggle.
- Deliverables: Variables table with types, defaults, and validation. Outputs table. Resource naming convention. Usage example for a Node.js API service.
- Constraints: No public unauthenticated access without explicit override. Keep interface under 14 inputs. All resources must include required labels: env, team, cost-center.
Format as: Overview, Inputs table, Outputs table, Resource list, Security notes, Example.
You're a senior cloud infrastructure architect. Design a Terraform module spec for an Azure AKS cluster used by multiple product teams inside an enterprise Azure subscription.
- Context: Environments: nonprod, prod. Terraform: 1.6+. AzureRM provider: 3.x. Single subscription with shared VNet and centralized Log Analytics workspace.
- Requirements: System and user node pools with autoscaling. Azure CNI networking. Managed identity (no service principal). Integration with existing Log Analytics workspace via resource ID input. Optional Azure Policy add-on toggle.
- Deliverables: Variables with types, defaults, validation. Outputs including cluster FQDN, kubelet identity object ID, and OIDC issuer URL. Resource naming that follows Azure CAF. Minimal usage example.
- Constraints: No public API server without allowlist. Restrict node pool VM size to Standard_D series. Cap interface at 15 inputs.
Format as: Overview, Inputs table, Outputs table, Resource naming rules, Usage example.
You're a senior network engineer with deep AWS expertise. Design a Terraform module spec for a reusable AWS VPC with public, private, and isolated subnets across three availability zones.
- Context: AWS Organizations environment. Each account gets one VPC. Region: configurable. Terraform: 1.5+. AWS provider: 5.x. Transit Gateway ID passed as an input for inter-account routing.
- Requirements: Public subnets with NAT Gateway (one per AZ in prod, one shared in non-prod). Private subnets route through NAT. Isolated subnets have no internet route. VPC Flow Logs to S3 required in all environments.
- Deliverables: Full variables table with CIDR validation using regex. Outputs table including subnet IDs grouped by tier, route table IDs, and VPC ID. Resource naming using account alias and environment. Usage example showing both prod and non-prod configurations.
- Constraints: No default VPC creation. No hardcoded CIDR blocks. Cap at 16 inputs. All resources tagged with env, account-id, and managed-by=terraform.
Format as: Overview, Inputs table, Outputs table, Resource list, CIDR design notes, Usage example.
You're a Terraform instructor helping a junior engineer build their first reusable module. Design a simple Terraform module spec for an AWS S3 static website with no CloudFront, no WAF, and no ACM — just a publicly accessible bucket with a website configuration.
- Context: Single AWS account. Single environment. Region: configurable. Terraform: 1.4+. AWS provider: 5.x.
- Requirements: S3 bucket with static website hosting enabled. Bucket policy allowing public read. Optional index and error document inputs. Output the website endpoint URL.
- Deliverables: Variables table with clear descriptions and defaults. Single outputs table. A complete usage example a junior engineer can copy and run.
- Constraints: Keep interface under 6 inputs. Use plain language in all descriptions. Avoid advanced features like lifecycle rules or replication.
Format as: Overview, Inputs table, Outputs table, Usage example, Common mistakes to avoid.
When to use this prompt
Platform Engineers standardizing modules
Create consistent Terraform module interfaces across teams with shared naming, validations, and outputs.
Product managers defining infra requirements
Turn launch needs into a concrete module spec that engineering can estimate and build without rework.
Marketing teams shipping static sites safely
Generate a secure S3 + CloudFront module plan with HTTPS and optional WAF for campaign pages.
Customer success teams provisioning demo environments
Specify dev, staging, and prod behaviors so demos spin up reliably with minimal inputs.
Pro tips
- 1
Specify your org’s naming standard so you avoid refactors during review.
- 2
Define which settings must stay optional so you keep the module interface small.
- 3
Add compliance requirements like encryption, logging, and retention so the design includes guardrails.
- 4
State your default environment differences so the module handles prod safely without extra inputs.
When you need to design a stack of related modules — say, a VPC module, a security group module, and an ECS module that all call each other — a single flat prompt breaks down. The AI loses track of which variables belong to which module and which outputs flow between them.
Use a layered prompt structure instead:
- Prompt for the lowest-level module first (VPC). Get the Inputs and Outputs tables.
- Copy the Outputs table into a second prompt for the next module (security groups). Specify which VPC outputs it consumes as inputs.
- Repeat for each layer, passing outputs downstream as inputs.
This mirrors how you'd actually design a module composition in code. Each prompt stays focused and bounded. The AI doesn't have to invent inter-module contracts because you've defined them explicitly.
For the final composition prompt, include a 'Module dependency diagram' request in your deliverables. Ask the AI to list which module outputs map to which module inputs in a table. This gives you a reviewable contract before you write a single line of HCL.
This technique also helps with refactoring. When an existing module changes its outputs, you can re-run the downstream prompts with the updated interface and get a clean diff of what needs to change across the stack.
Run through this checklist before writing your Terraform module prompt. Each item maps to a specific part of the After Prompt structure.
Context (Section 1):
- Terraform version (e.g., 1.6+)
- Provider name and version (e.g., AWS provider 5.x)
- Target environments (dev, staging, prod)
- Region or regions
Requirements (Section 2):
- AWS/GCP/Azure services involved
- Security controls that are non-negotiable
- Features that must be toggleable vs. always-on
- Any integration points (existing VPC, existing IAM roles)
Deliverables (Section 3):
- Variables table with types, defaults, and validation
- Outputs table
- Resource naming convention
- Usage example with realistic values
Constraints (Section 4):
- Maximum number of input variables
- Deprecated resources or patterns to avoid
- Required tags
- Naming pattern (write it out explicitly)
If you can't answer any item on this list, resolve it with your team before prompting. The AI cannot make organizational decisions for you — it can only implement the decisions you give it.
If your infrastructure must meet compliance frameworks — SOC 2, HIPAA, PCI DSS, FedRAMP — your Terraform module prompts need an additional section that most engineers skip.
Add a Compliance section (Section 5) to the After Prompt pattern:
'5. Compliance requirements: This module must satisfy SOC 2 Type II controls for availability and confidentiality. Required: S3 server-side encryption (SSE-S3 or SSE-KMS), S3 access logging enabled by default, CloudFront access logs to a separate audit bucket, and all resources tagged with data-classification=internal. Block public access at the bucket level using aws_s3_bucket_public_access_block. Do not expose the S3 bucket URL directly — all access must route through CloudFront.'
This framing does three things:
- It gives the AI a named framework (SOC 2) it can draw on for additional best practices you might have missed.
- It converts audit requirements into concrete resource configurations.
- It documents compliance intent inside the design spec, which your auditors can reference.
For PCI DSS or HIPAA, name the specific controls (e.g., 'PCI DSS Requirement 10.2 — log all access to cardholder data environments'). The AI knows these frameworks and will apply them with more precision than generic 'be secure' instructions.
When not to use this prompt
When This Prompt Pattern Is Not the Right Tool
Don't use a module design prompt when you already have a working module and need to write a specific resource configuration. At that point, you need a code generation prompt, not a spec prompt. Prompting for a spec when code is the deliverable adds an unnecessary review step.
Avoid this pattern for one-off infrastructure that will never be reused. If you're spinning up a single S3 bucket for a short-term project, a module spec is overkill. Use a direct resource prompt or a simpler template instead.
This pattern doesn't replace architectural review. A well-structured prompt can produce a technically sound module spec, but it can't evaluate whether your architecture is right for your scale, cost model, or organizational constraints. Use the output as a starting point for human review — not a substitute for it.
Don't use this for existing modules undergoing incremental edits. If you're adding one variable to a stable module, a full spec prompt produces too much noise. Instead, prompt for a targeted diff: "Given this existing variables table, propose one new variable for X that follows the same conventions."
- For ad-hoc resource configs: use a resource-specific prompt
- For architectural decisions: involve a human architect
- For compliance certification: layer compliance-specific prompts on top of this pattern
Troubleshooting
The AI generates HCL code instead of a structured spec with tables
Explicitly forbid code output in your prompt. Add: 'Do not write HCL. Output a design spec only, using markdown tables for variables and outputs.' If you want a usage example, add: 'The usage example should be pseudocode showing variable values, not functional HCL.' This forces the AI into documentation mode rather than code generation mode — which is what you need for design review.
Variable defaults are missing or set to null across the board
Add a defaults requirement to your deliverables section. Write: 'Every optional variable must have a sensible default. For Boolean toggles, default to false in dev and note the prod override. For string variables, provide a realistic example value as the default where a true default is not appropriate.' Then add: 'Mark required variables explicitly with required in the Default column.' This gives the AI a clear schema to follow.
Security controls appear in some resources but not others inconsistently
Move security requirements to a numbered checklist at the end of the prompt. Add: 'Before finalizing your response, verify that every resource in your output complies with: 1) least privilege IAM, 2) no deprecated OAI configuration, 3) encryption enabled at rest. If any resource cannot meet these requirements, note the exception explicitly.' Checklists at the end of a prompt act as a self-review step that catches omissions before the output reaches you.
The module design does not distinguish between dev and prod environments
List each environment difference as a concrete variable behavior, not a general statement. Replace 'handle dev and prod differently' with: 'The variable enable_waf defaults to false in dev (caller sets environment=dev) and must be explicitly set to true for prod. The variable log_retention_days defaults to 7 in dev and 90 in prod — use a validation rule to enforce minimum values per environment.' Concrete examples propagate consistently; abstract instructions do not.
Resource naming in the output doesn't match our org's convention
Provide a real example name in the prompt, not just the pattern. Instead of 'use env-team-service naming,' write: 'Resource names must follow {var.environment}-{var.team}-{var.service_name}, for example prod-platform-static-site. Apply this pattern to every named resource including the S3 bucket, CloudFront distribution ID prefix, and IAM policy names.' A concrete example anchors the pattern far more reliably than a format string alone.
How to measure success
How to Evaluate the AI Output
A strong Terraform module spec from this prompt type should meet these criteria before your team reviews it:
Structure completeness:
- Inputs table includes name, type, description, default, and validation for every variable
- Outputs table includes name, description, and value reference for every output
- Resource list names every AWS/GCP/Azure resource the module will create
- Usage example uses realistic values — not placeholder strings like "my-bucket"
Interface quality:
- Total inputs stay at or below the cap you specified
- Optional variables have explicit defaults — none set to
nullwithout documented justification - Boolean toggles default to the safe state (typically
false)
Security alignment:
- No deprecated resource patterns appear (OAI, legacy bucket ACLs, etc.)
- IAM references follow least privilege
- Encryption and logging are present where you required them
Naming and tagging:
- Resource names follow your specified pattern — check at least three resources
- Required tags appear in every taggable resource
Red flags to reject and reprompt:
- More inputs than your cap allows
- Missing validation blocks on constrained variables
- Security controls applied inconsistently across resources
- Naming convention applied to some resources but not others
Now try it on something of your own
Reading about the framework is one thing. Watching it sharpen your own prompt is another — takes 90 seconds, no signup.
Get a complete Terraform module spec — variables table, outputs, naming rules, and usage example — tailored to your stack and security standards.
Try one of these
Frequently asked questions
Yes — the structure applies to any AWS, GCP, or Azure service. Replace the service names and requirements in sections 1 and 2 of the After Prompt with your target resources. Keep the deliverables section (variables, outputs, naming, example) unchanged. The constraints section — input cap, least privilege, no deprecated resources — adapts to any provider with minor wording changes.
Restate the cap more forcefully and ask it to justify every input over 8. Add this line to your prompt: 'If a variable is only needed in edge cases, convert it to a local value with a sensible default. Every input must serve at least two expected callers.' This forces the AI to consolidate, not expand. You can also list the inputs you consider essential to anchor the design.
Include the pattern directly in the prompt — don't describe it abstractly. For example: 'Resource names must follow {env}-{team}-{service} using the input variables var.environment, var.team, and var.service_name.' If you also provide a real example name like prod-platform-static-site, the AI will apply it consistently across every resource in the output.
Start with the spec, not the code. A spec — variables table, outputs table, resource list, usage example — is faster to review, easier to correct, and gives you a design checkpoint before any code is written. Once your team approves the spec, you can prompt the AI separately to generate the HCL for each section. This two-step approach cuts rework significantly.
Design separate modules per provider — don't try to abstract across clouds in a single module. Prompt for each provider independently using the same spec structure but provider-specific constraints. Then create a wrapper or composition layer that calls both. This keeps each module simple, testable, and maintainable without introducing abstraction that breaks every time a provider API changes.
Yes. Add a fifth section to the After Prompt structure: '5. Existing module context: The current module has these inputs [list them] and these outputs [list them]. Identify gaps, deprecated patterns, and inputs that should be consolidated. Propose a migration path that preserves backward compatibility.' This shifts the AI from designing from scratch to auditing and improving an existing interface.
Security constraints buried in a long paragraph get ignored. Move them to a numbered list with explicit wording like 'Must use OAC, not OAI — OAI is deprecated as of provider 5.x.' Use the word 'must' rather than 'prefer' or 'try to.' If the AI still misses a constraint, repeat it at the end of the prompt as a checklist: 'Before responding, verify your design meets: [constraint 1], [constraint 2]...'
Explicitly request them in your deliverables section. Add: 'For each variable, include a validation block where the value can be misused — for example, CIDR ranges, environment names, and instance types.' You can also provide one example validation rule in the prompt itself. The AI mirrors the structure you model, so a single concrete example of a validation block will propagate to other appropriate variables.