Bulding infrastructure as code (IaC) solutions often requires managing complex configurations and dependencies, and if you’re not careful, you can quickly create a tangled mess. If you then apply AI tools like GitHub Copilot to assist you, you might end up with even more chaos if the AI doesn’t have the right context to work with.

To address this challenge, we can leverage the principles of context engineering to provide AI tools with the necessary context to generate accurate and efficient IaC code. This involves creating a structured environment where the AI can access relevant information about the infrastructure requirements, existing configurations, and best practices. In this post, I’ll share some of the techniques I’ve found useful when applying context engineering to IaC projects, particularly when using Terraform and GitHub Copilot.

The best way to do this is constantly changing. As of fall 2025 however, and especially when using GitHub Copilot, a good approach is to create a few well-structured markdown files that are consumed by agents and prompts. Typical files that are useful to include are:

  • ARCHITECTURE.md: Describes the overall architecture and design principles of the infrastructure.
  • PRODUCT.md: Provides details about the specific product or application that the infrastructure supports.
  • CONTRIBUTING.md: Outlines the contribution guidelines and best practices for adding new IaC code.
  • .github/copilot-instructions.md: Contains specific instructions and context for GitHub Copilot to follow when generating IaC code.

In the next sections, I’ll walk you through an example of how to set up a context engineering environment for IaC using Terraform and GitHub Copilot.

Step 1: Define Clear Infrastructure Requirements

The first step in context engineering for IaC is to define clear infrastructure requirements. This includes specifying the desired state of the infrastructure, the resources needed, and any constraints or dependencies. By providing this information upfront, we can help the AI understand the context in which it is generating code.

⚠️ DO NOT LET THE AI GUESS YOUR REQUIREMENTS! Always be explicit and clear about what you want to achieve.

If you’re working with Microsoft Azure, I would recommend reading up on the Azure Well-Architected Framework to get a good understanding of best practices and guidelines for building secure, reliable, and efficient cloud solutions. Then use Azure Verified Modules to ensure that your IaC code adheres to these best practices.

⚠️ ALWAYS KNOW WHAT YOU ARE DOING! Always use verified and trusted modules to ensure the quality and security of your infrastructure. Even when using Azure Verified Modules, always review the code to ensure it meets your specific requirements and standards and understand what it does. Using modules from others also means that you’re adding a dependency that you don’t fully control, so be cautious.

As an example, let’s say we want to create a Infrastructure as Code pattern module that creates an Azure App Service with a SQL Database backend. Typical scenario for both web applications, APIs, and so on.

I always start either with an existing project, or scaffolds the project manually to ensure that I have full control over the code structure and dependencies. My experience to date is that vibe coding infrastructure as code with AI becomes is an resiped for caos. To get it right, you will end up with prompt engineering that takes at least as much effort and time as just copying in modules wither from the AVM project, og just quicly draft it up yourself. After all, AI modules are built to be creative and solve our problems, not necessarily follow strict rules and guidelines wich is a fundamental requirement when building infrastructure as code.

terraform/ # Root folder for Terraform configurations
├── main.tf                # Main Terraform configuration file
├── variables.tf           # Input variables
├── outputs.tf             # Output values
├── env.dev.tfvars         # Environment-specific variables for development
└── terraform.tf           # Terraform backend and provider configurations

This structure provides a clear separation of concerns, making it easier to manage and maintain the IaC code. Depending on your project, you might want to add more files or folders to organize your code further of course, but key here is to keep it simple and manageable.

I’ll focus on the main.tf file for now, as this is where the core infrastructure resources will be defined. The following file uses Azure Verified Modules to create the necessary resources for our web application and SQL Database. It also includes a virtual network and private DNS zones for secure connectivity, however, it does not include the the code need to wire everything together, like connecting the web app or database server to the virtual network. This is intentional as I want to demonstrate how we can use context engineering and AI promots to handle the rest.

terraform/main.tf
 1module "naming" {
 2  source  = "Azure/naming/azurerm"
 3  version = "0.3.0"
 4}
 5
 6module "avm-res-resources-resourcegroup" {
 7  source  = "Azure/avm-res-resources-resourcegroup/azurerm"
 8  version = "0.2.1"
 9
10  location = var.location
11  name     = var.resource_group_name
12}
13
14module "avm-res-web-serverfarm" {
15  source  = "Azure/avm-res-web-serverfarm/azurerm"
16  version = "1.0.0"
17
18  resource_group_name = module.avm-res-resources-resourcegroup.name
19  location            = var.location
20  name                = var.web_serverfarm_name
21  os_type             = "Linux"
22}
23
24module "avm-res-web-site" {
25  source  = "Azure/avm-res-web-site/azurerm"
26  version = "0.19.1"
27
28  resource_group_name      = module.avm-res-resources-resourcegroup.name
29  location                 = var.location
30  service_plan_resource_id = module.avm-res-web-serverfarm.resource_id
31  name                     = var.web_site_name
32  kind                     = "webapp"
33  os_type                  = "Linux"
34}
35
36module "avm-res-sql-server" {
37  source  = "Azure/avm-res-sql-server/azurerm"
38  version = "0.1.6"
39
40  resource_group_name = module.avm-res-resources-resourcegroup.name
41  location            = var.location
42  server_version      = "12.0"
43  name                = module.naming.sql_server.name_unique
44
45  azuread_administrator = var.sql_server_entra_id_administrator
46}
47
48module "avm-res-network-virtualnetwork" {
49  source  = "Azure/avm-res-network-virtualnetwork/azurerm"
50  version = "0.16.0"
51
52  parent_id = module.avm-res-resources-resourcegroup.resource_id
53  location  = var.location
54
55  name          = var.virtual_network_name
56  address_space = var.virtual_network_address_space
57}
58
59module "avm-ptn-network-private-link-private-dns-zones" {
60  source  = "Azure/avm-ptn-network-private-link-private-dns-zones/azurerm"
61  version = "0.22.1"
62
63  location  = var.location
64  parent_id = module.avm-res-resources-resourcegroup.resource_id
65
66  virtual_network_link_default_virtual_networks = {
67    "vnet1" = {
68      virtual_network_resource_id = module.avm-res-network-virtualnetwork.resource_id
69      resolution_policy           = "NxDomainRedirect"
70    }
71  }
72
73  private_link_private_dns_zones = {
74    azure_sql_server = {
75      zone_name = "privatelink.database.windows.net"
76    }
77  }
78}

The variables.tf file contains the input variables needed for the module. If you’re failiar with Terraform, this is fairly standard stuff. The key here is to provide clear and concise descriptions for each variable to help the AI understand their purpose and usage.

terraform/variables.tf
 1variable "subscription_id" {
 2  type = string
 3  description = "(Required) Subscription ID used by the AzureRM provider"
 4}
 5
 6variable "location" {
 7  type = string
 8  description = "(Required) Location for all resources that are not global."
 9}
10
11variable "resource_group_name" {
12  type = string
13  description = "(Required) The name of the Resource Group in which to create the resources."
14}
15
16variable "web_serverfarm_name" {
17  type = string
18  description = "(Required) The name of the App Service Plan."
19}
20
21variable "web_site_name" {
22  type = string
23  description = "(Required) The name of the Web App."
24}
25
26variable "virtual_network_name" {
27  type = string
28  description = "(Required) The name of the Virtual Network."
29}
30
31variable "virtual_network_address_space" {
32  type = list(string)
33  description = "(Required) Virtual Network address Space."
34}
35
36variable "sql_server_entra_id_administrator" {
37  type = object({
38    azuread_authentication_only = bool
39    login_username              = string
40    object_id                   = string
41    tenant_id                   = optional(string)
42  })
43  description = "(Required) Entra ID administrator details"
44}

Step 2: Provide Contextual Information

As mentioned earlier, providing contextual information is crucial for effective context engineering. This can be done by creating markdown files that describe the architecture, product details, and contribution guidelines. These files should be well-structured and easy to understand. Creating them on our own, from scratch, on the other hand is a bit waste of time. Instead, we can use AI tools like GitHub Copilot to help us generate these files based on our specific requirements, and it can be done using a failry simple slash prompt like this:

.github/prompts/iac.context-engineering.prompt.md
 1---
 2agent: agent
 3description: Prompt for configuring context engineering documentation files.
 4---
 5
 6You are an expert in cloud infrastructure and AI automation. Your task is to create a comprehensive, concise set of documentation files for this project that will serve both human developers and AI agents.
 7
 8**Scope**: Analyze all project files EXCEPT the following files: `README.md`, `prompts.md`.
 9
10**Existing Files**: If `ARCHITECTURE.md`, `PRODUCT.md`, `CONTRIBUTING.md`, or `.github/copilot-instructions.md` already exist, analyze their content to ensure consistency and propose how to align them with the current project state.
11
12## Documentation Files to Create
13
14### 1. `ARCHITECTURE.md` (max 2 pages)
15
16**Purpose**: Technical reference for developers and architects
17
18**Content Requirements**:
19
20- Overall system architecture and component relationships
21- Architectural patterns and design principles applied
22- Key technical decisions with rationale
23- Technology stack and dependencies
24- Infrastructure-as-Code structure and patterns
25- Security architecture and considerations
26- All diagrams MUST use Mermaid syntax
27
28**Before proceeding**: If architecture patterns, security requirements, or component relationships are unclear, ask clarifying questions.
29
30### 2. `PRODUCT.md` (max 2 pages)
31
32**Purpose**: Product vision and feature documentation
33
34**Content Requirements**:
35
36- Project purpose and vision
37- Target audience and use cases
38- Key features and functionalities
39- User needs addressed by the project
40- Success criteria and goals
41- Roadmap or future considerations (if applicable)
42
43**Before proceeding**: If project goals, target users, or core features are unclear, ask clarifying questions.
44
45### 3. `CONTRIBUTING.md` (max 1 page)
46
47**Purpose**: Developer onboarding and contribution guidelines
48
49**Content Requirements**:
50
51- Development setup and prerequisites
52- Code contribution workflow
53- Coding standards and conventions
54- Testing requirements
55- Pull request and review process
56- MUST align with architecture patterns from `ARCHITECTURE.md`
57- MUST support product vision from `PRODUCT.md`
58
59### 4. `.github/copilot-instructions.md` (max 1 page)
60
61**Purpose**: Context for AI-assisted development
62
63**Content Requirements**:
64
65- High-level project overview
66- Key concepts and terminology
67- References to `ARCHITECTURE.md` for technical context
68- References to `PRODUCT.md` for feature context
69- References to `CONTRIBUTING.md` for development guidelines
70- Important constraints or guardrails for AI suggestions
71- Common tasks or workflows
72
73## Quality Standards
74
75Each file must be:
76
77- **Well-structured**: Clear hierarchy with appropriate headings
78- **Concise**: Respect page limits without sacrificing clarity
79- **Accurate**: Based on actual project structure and code
80- **Actionable**: Provide practical guidance for intended audience
81- **Consistent**: Terminology and concepts aligned across all files
82- **AI-friendly**: Clear language suitable for both human and AI consumption
83
84## Process
85
861. Analyze project structure, code, and existing documentation
872. Ask clarifying questions if any aspect is ambiguous
883. Generate or update all four documentation files
894. Ensure cross-references between files are accurate
905. Validate that each file serves its intended purpose and respects page limits

This will pompt will analyze the existing project files and generate the necessary documentation files based on the specified requirements. Just remember to review and refine the generated files to ensure they meet your needs. Depending on the comlexity of your project and the AI model used to generate them, the content can be anything from good to extremely useless. Always review and refine the generated files to ensure they meet your needs.

Step 3: Create Effective Prompts and Agents

The next steps is often very project dependent, and needs a lot of iterations to get it right. For smaller, simply IaC projects I’ve found the following two slash prompts and agenta to be usefull.

  • /iac.plan: An agent that analyzes the existing IaC code and documentation files to identify gaps, inconsistencies, or areas for improvement. It then generates a detailed plan outlining the necessary changes or additions to enhance the IaC codebase.
  • /iac.implement: An agent that takes the plan generated by the /iac.plan agent and implements the changes in the IaC codebase. This includes adding missing configurations, updating existing resources, and ensuring that the code adheres to best practices and project requirements.

If you like, feel free to use the following templates to get started.

/iac.plan Prompt and Agent

It takes three files

  • .github/prompts/iac.plan.prompt.md
  • .github/agents/iac.plan.agent.md
  • .github/shared/plan-template.md
.github/prompts/iac.plan.prompt.md
1---
2agent: iac.plan
3description: Create a detailed implementation plan.
4---
5
6Analyze my feature request deeply, then ask me 3 questions to clarify the requirements. Only then start the planning workflow.
.github/agents/iac.plan.agent.md
 1---
 2description: "Senior Cloud Architect and planner focused on detailed IaC implementation plans."
 3tools:
 4  [
 5    "edit/createFile",
 6    "edit/createDirectory",
 7    "edit/editFiles",
 8    "search",
 9    "Azure MCP/search",
10    "usages",
11    "problems",
12    "fetch",
13    "todos",
14    "runSubagent",
15  ]
16handoffs:
17  - label: Start Implementation
18    agent: iac.implement
19    prompt: "Implement the plan defined in the file: ${planFilename}. Use TDD principles and follow the verification steps."
20    send: true
21---
22
23# Planning Agent
24
25You are a **Senior Cloud Architect** specializing in Azure and Terraform. Your goal is to translate feature requests into precise, actionable, and architecturally sound implementation plans.
26
27## Workflow
28
291. **Context & Research**:
30
31   - Read `ARCHITECTURE.md` and `PRODUCT.md` to understand the system design and constraints.
32   - Use the `runSubagent` tool to research complex Azure services or Terraform module specifics if you are unsure.
33   - _Crucial_: If the user's request lacks specific details (e.g., subnet CIDRs, naming conventions, SKU tiers), **ask clarifying questions** before generating the plan.
34
352. **Drafting the Plan**:
36
37   - Read the template found at `.github/shared/plan-template.md`. You **MUST** strictly follow this structure for the output file.
38   - Create a detailed breakdown including:
39     - Specific files to edit (e.g., `terraform/main.tf`).
40     - Specific Terraform resources/modules to add or modify.
41     - Variable changes and `tfvars` updates.
42     - Verification steps (manual and automated).
43     - Use Markdown checklist syntax for tasks that shall be done.
44
453. **Review & Refine**:
46
47   - Present the plan to the user for feedback.
48   - Iterate based on their input.
49
504. **Finalize**:
51   - Save the approved plan to a file named `iac.plan.{feature-name}.md` in the project root folder.
52   - Ensure the filename is unique and descriptive.
53   - Verify one last time that the file content matches the template structure.
.github/shared/plan-template.md
 1---
 2title: [Short descriptive title of the feature]
 3date_created: [YYYY-MM-DD]
 4last_updated: [YYYY-MM-DD]
 5---
 6
 7# Implementation Plan: <feature>
 8
 9[Brief description of the requirements and goals of the feature]
10
11## Architecture and design
12
13Describe the high-level architecture and design considerations.
14
15## Tasks
16
17Break down the implementation into smaller, manageable tasks using a Markdown checklist format.
18
19## Open questions
20
21Review and validate tasks against architecture and design, then outline 1-3 open questions or uncertainties that need to be clarified.

/iac.implement Prompt and Agent

It takes two files

  • .github/prompts/iac.implement.prompt.md
  • .github/agents/iac.implement.agent.md
.github/prompts/iac.implement.prompt.md
1---
2agent: iac.implement
3description: Execute a detailed implementation plan as a Spec-Driven developer.
4---
5
6Follow the given implementation plan to write high-quality, fully tested, maintainable code. Use specifications to guide and validate behavior.
.github/agents/iac.implement.agent.md
 1---
 2description: "Execute a detailed implementation plan as a Spec-Driven developer."
 3---
 4
 5# Spec-Driven Development Implementation Agent
 6
 7Expert SDD developer generating high-quality, fully tested, maintainable code for the given implementation plan.
 8
 9## Spec-Driven Development
10
111. **Load Plan**: Locate and read the active implementation plan file (e.g., `iac.plan.{feature}.md`).
122. **Execute Tasks**: Work through the items in the **Tasks** section.
13   - You may group multiple non-conflicting tasks into a single iteration if efficient.
14   - For complex tasks, proceed sequentially.
153. **Implement**: Write code to satisfy the requirements.
164. **Validate**: Run validation to ensure correctness and catch regressions.
175. **Refactor**: Improve code quality while keeping tests green.
186. **Track Progress**: Edit the plan file to mark tasks as completed (change `[ ]` to `[x]`) after verification.
19
20## Core principles
21
22- **Incremental Progress**: Small, safe steps keeping the system working.
23- **Spec-Driven**: Specifications guide and validate behavior.
24- **Quality Focus**: Follow existing patterns and conventions.
25- **Source of Truth**: The `iac.plan.*.md` file is the authoritative source for tasks and status.
26
27## Success criteria
28
29- All tasks in the plan file are marked as completed (`[x]`).
30- Acceptance criteria satisfied for each task.
31- Specifications validated through passing tests (unit, integration, full suite).

Step 4: Start using the Agents

With the context files in place and the prompts and agents defined, you can now start using the /iac.plan and /iac.implement agents to help you manage and develop your IaC projects more effectively. Simply invoke the /iac.plan agent with your feature request, review and refine the generated plan, then hand it off to the /iac.implement agent to execute the plan and implement the necessary changes in your IaC codebase.

OK, if you just made a huge plan, and tried to implement a complex feature, it will most likely fail. So again, don’t give the agent to much information at once. Break it down into smaller pieces, and iterate frequently. Example prompts that works well for me with the example code described initially are:

GitHub Copilot prompt
/iac.plan Create a plan on how to integrate the Azure Web App to the Virtual Network

Given the prompt instructions on our /iac.plan agent, it will first ask you clarifying questions, then generate a detailed plan on how to implement the feature. Most likely it will ask how large subnet it should create and if the web application also should have private endpoint. Once you have reviewed and refined the plan, you can hand it off to the /iac.implement agent to execute the plan and implement the necessary changes in your IaC codebase.

Then, we can invoke a new /iac.plan prompt to do the same with the SQL Server.

GitHub Copilot prompt
/iac.plan Create a plan on how to connect the Azure SQL Server to the Virtual Network

In most cases it will just go perfectly fine. In other cases, you might need to help it a bit along the way. Just remember to keep the iterations small and manageable.

Step 5: Spec-Driven Development

Once you have mastered the basics of context engineering for IaC using Terraform and GitHub Copilot, you can start exploring more advanced techniques and tools to further enhance your workflow. This might include integrating other AI tools, automating more complex tasks, or scaling your IaC projects to handle larger and more complex infrastructures. One larger initiative I find very interesting is to combine context engineering with spec-driven development (SDD) principles to create a fully automated IaC development pipeline. This would involve using AI agents to not only generate and implement IaC code but also to continuously validate and verify the infrastructure against predefined specifications and requirements.

GitHub Spec-Kit is a tool that are really promising, and that I would defintively recommend checking out. It really hit the AI community as a huge storm in late 2025, and is still evolving rapidly with more than 51k stars on GitHub. For small to medium sized IaC projects, it will be overkill for sure, but for larger project, or project that combine IaC with application code, it might be more usefull as it has a more strict approach, and kind of enforces small mini waterfalls for each feature, while still being very agile and iterative.

Command Description
/speckit.constitution Create or update project governing principles and development guidelines
/speckit.specify Define what you want to build (requirements and user stories)
/speckit.clarify Clarify underspecified areas (recommended before /speckit.plan)
/speckit.plan Create technical implementation plans with your chosen tech stack
/speckit.tasks Generate actionable task lists for implementation
/speckit.analyze Cross-artifact consistency & coverage analysis (run after /speckit.tasks, before /speckit.implement)
/speckit.implement Execute all tasks to build the feature according to the plan

GitHub Spec-Kit is for example the tool that I have been using to create this site from scratch, and still uses to fix bugs and add new features.

Learning Resources