Custom Azure Bicep chat modes for GitHub Copilot

In GitHub Copilot, you have three built-in chat modes: Ask, Edit, and Agent. With a custom chat mode, you can create your own mode by extending the agent with your own behaviour. For example, you can add behaviour so the agent acts as a coding planner, prompt optimiser, or PowerShell expert, etc.

In this blog, you will learn about:

  • What custom chat mode is
  • The difference between a custom chat mode and custom instructions
  • How to create and how to use your own chat mode

Additionally, I created two custom chat modes specifically for Azure Bicep, and I will share my tips and lessons learned. The model used in this blog post is GPT-5, and the Bicep MCP, Azure MCP and GitHub Copilot for Azure tools are used.

A custom chat mode defines a persona, workflow, or role. With a custom chat mode, you specify how the agent should behave and what role it has when working with your code. A custom chat mode could be a non-technical role, such as a feature planner that documents plans for a new feature in your application. As a follow-up, you can use a technical persona that implements the feature based on the documentation created by the feature planner.

In essence, it’s a specialised behaviour assigned to the agent for a specific task.

A chat mode is only activated when it is selected. It is defined in a markdown file named <chat mode name>.chatmode.md and stored in the .github/chatmodes folder.

Custom instructions describe what the agent should do. For example, the agent can follow guidelines about coding best practices or naming conventions. These instructions are automatically applied to all GitHub Copilot Chat prompts.

Custom instructions are applied in every GitHub Copilot Chat window. They are defined in a markdown file named copilot-instructions.md, located in the .github folder.

Both are powerful features in GitHub Copilot Chat. They can be used on their own or combined, and each serves a different purpose:

  • Use Custom Instructions when you want a consistent way of writing code, such as β€œalways add the description decorator on parameters.”
  • Use a Custom Chat Mode when you want the agent to take on a role, for example, acting as a cloud architect to review or explain specific infrastructure choices.

To define your own chat mode, you need to follow a specific format. The file must be written in markdown and must be saved in the folder .github/chatmodes/<name>.chatmode.md. This markdown file is structured as follows:

---
description: 'description of the custom chat mode'
tools: ['tools the chat mode has access to (comma separated)']
model: 'Define a model here, e.g. GPT-5. If empty, the selected model will be used'
---

<your custom behaviour here>

Learn more about the file on the official documentation page.

After the markdown file is saved in the chat modes folder, you can select the custom chat mode under the modes dropdown:

To showcase custom chat modes, you will see two examples. Both are focused on the context of Azure Bicep:

  • Bicep Plan chat mode
  • Bicep Implement chat mode

Each chat mode has a different purpose. Let’s see what they do:

In the Plan mode, the agent acts and behaves as an expert in Azure engineering, with a speciality in Azure Bicep Infrastructure as Code. The agent has access to several MCP tools to improve context quality:

  • #microsoft-docs – retrieves official Microsoft documentation to provide valid context;
  • #fetch – browses the web, for example, to retrieve the latest Azure Verified Module version;
  • #todos – creates a to-do list for the agent;
  • and other tools available in the Azure MCP, GitHub Copilot for Azure and Bicep MCP (available in the Bicep VSCode extension).

The goal of the Plan chat mode is to gather a wide range of information about the user’s context. For example, if the user asks for the implementation of a storage account, GitHub Copilot creates a markdown file grounded in tool calls to Microsoft documentation, Azure Verified Modules (which module must be used and its latest version), and Azure best practices.

Chat mode markdown file

---
description: "I act as implementation planner for your Azure Bicep IaC task."
tools:
  [
    "editFiles",
    "fetch",
    "microsoft-docs",
    "azure_design_architecture",
    "get_bicep_best_practices",
    "bestpractices",
    "bicepschema",
    "azure_get_azure_verified_module",
    "todos",
  ]
---

# Azure Bicep Infrastructure Planning

Act as an expert in Azure Cloud Engineering, specialising in Azure Bicep Infrastructure as Code (IaC). Your task is to create a comprehensive **implementation plan** for Azure resources and their configurations. The plan must be written to **`.bicep-planning-files/INFRA.{goal}.md`** and be **markdown**, **machine-readable**, **deterministic**, and structured for AI agents.

## Core requirements

- Use deterministic language to avoid ambiguity.
- **Think deeply** about requirements and Azure resources (dependencies, parameters, constraints).
- **Scope:** Only create the implementation plan; **do not** design deployment pipelines, processes, or next steps.
- **Write-scope guardrail:** Only create or modify files under `.bicep-planning-files/` using `#editFiles`. Do **not** change other workspace files. If the folder `.bicep-planning-files/` does not exist, create it.
- Ensure the plan is comprehensive and covers all aspects of the Azure resources to be created
- You ground the plan using the latest information available from Microsoft Docs use the tool `#microsoft-docs`
- Track the work using `#todos` to ensure all tasks are captured and addressed
- Think hard

## Focus areas

- Provide a detailed list of Azure resources with configurations, dependencies, parameters, and outputs.
- **Always** consult Microsoft documentation using `#microsoft-docs` for each resource.
- Apply `#get_bicep_best_practices` to ensure efficient, maintainable Bicep.
- Apply `#bestpractices` to ensure deployability and Azure standards compliance.
- Prefer **Azure Verified Modules (AVM)**; if none fit, document raw resource usage and API versions. Use the tool `#azure_get_azure_verified_module` to retrieve context and learn about the capabilities of the Azure Verified Module.
  - Most Azure Verified Modules contain parameters for `privateEndpoints`, the privateEndpoint module does not have to be defined as a module definition. Take this into account.
  - Use the latest Azure Verified Module version. Fetch this version at `https://github.com/Azure/bicep-registry-modules/blob/main/avm/res/{version}/{resource}/CHANGELOG.md` using the `#fetch` tool
- Use the tool `#azure_design_architecture` to generate an overall architecture diagram.
- Generate a network architecture diagram to illustrate connectivity.

## Output file

- **Folder:** `.bicep-planning-files/` (create if missing).
- **Filename:** `INFRA.{goal}.md`.
- **Format:** Valid Markdown.

## Implementation plan structure

```markdown
---
goal: [Title of what to achieve]
---

# Introduction

[1–3 sentences summarizing the plan and its purpose]

## Resources

<!-- Repeat this block for each resource -->

### {resourceName}

```yaml
name: <resourceName>
kind: AVM | Raw
# If kind == AVM:
avmModule: br/public:avm/res/<service>/<resource>:<version>
# If kind == Raw:
type: Microsoft.<provider>/<type>@<apiVersion>

purpose: <one-line purpose>
dependsOn: [<resourceName>, ...]

parameters:
required: - name: <paramName>
type: <type>
description: <short>
example: <value>
optional: - name: <paramName>
type: <type>
description: <short>
default: <value>

outputs:

- name: <outputName>
  type: <type>
  description: <short>

references:
docs: {URL to Microsoft Docs}
avm: {module repo URL or commit} # if applicable
```

# Implementation Plan

{Brief summary of overall approach and key dependencies}

## Phase 1 β€” {Phase Name}

**Objective:** {objective and expected outcomes}

{Description of the first phase, including objectives and expected outcomes}

<!-- Repeat Phase blocks as needed: Phase 1, Phase 2, Phase 3, … -->

- IMPLEMENT-GOAL-001: {Describe the goal of this phase, e.g., "Implement feature X", "Refactor module Y", etc.}

| Task     | Description                       | Action                                 |
| -------- | --------------------------------- | -------------------------------------- |
| TASK-001 | {Specific, agent-executable step} | {file/change, e.g., resources section} |
| TASK-002 | {...}                             | {...}                                  |

## High-level design

{High-level design description}
```

Prompt

I am tasked with the implementation of a storage account including private networking using a private endpoint (using blob groupId). The virtual network used is an existing virtual network named vnet-01 and subnet snet-pe-01. The virtual network is present in the resource group rg-networking.

Additionally, I want to use an existing private DNS zone to register the private endpoint. This DNS zone is present in the resource group rg-dns-zones. The deployment region is west europe. Finally, the existing resources are created in the same subscription, so the target scope is resource group.

Output

Depending on the prompt, the output file can be larger and contain more context. In this case, the output file includes information about the Azure Verified Module for the storage account, as well as details about the existing resources. Finally, an implementation plan with follow-up tasks is created.

The output:

The output generated by the Plan chat mode. 

The plan is grounded through Bicep MCP, Azure MCP, and GitHub Copilot for Azure tool calls:

Tool calls from Azure MCP server, Bicep MCP and GitHub Copilot for Azure

The implementation plan will serve as context in the upcoming Implement chat mode.

In the Implement chat mode, the agent acts as an expert in Azure engineering, with a speciality in Azure Bicep Infrastructure as Code. Unlike the Plan mode, its focus is on implementation. The agent has access to several MCP tools to improve context quality:

  • #editFiles – creates or edits Bicep templates;
  • #todos – creates a to-do list for the agent based on the markdown plan file;
  • #runCommands – runs Bicep restore, lint and format commands in the terminal;
  • #terminalLastCommand – checks if the last command had any errors;
  • It is also recommended to install the extensions: Azure MCP, GitHub Copilot for Azure and Bicep MCP (available in the Bicep VSCode extension) for the Implement chat mode.

The goal of the Implement chat mode is to execute the infrastructure plan generated in the Plan mode (you can also use Implement standalone). This chat mode breaks the plan into actionable tasks, generates the Bicep code, and validates it using the build/lint command. If linting fails, the agent will attempt to fix the issues and run linting again.

Chat mode markdown file

---
description: "I act as an Azure Bicep Infrastructure as Code coding specialist."
tools:
  [
    "editFiles",
    "fetch",
    "runCommands",
    "terminalLastCommand",
    "get_bicep_best_practices",
    "azure_get_azure_verified_module",
    "todos",
  ]
---

# Azure Bicep Infrastructure as Code coding Specialist

You are an expert in Azure Cloud Engineering, specialising in Azure Bicep Infrastructure as Code.

## Key tasks

- Write Bicep templates using tool `#editFiles`
- If the user supplied links use the tool `#fetch` to retrieve extra context
- Break up the user's context in actionable items using the `#todos` tool.
- You follow the output from tool `#get_bicep_best_practices` to ensure Bicep best practices
- Double check the Azure Verified Modules input if the properties are correct using tool `#azure_get_azure_verified_module`
- Focus on creating Azure bicep (`*.bicep`) files. Do not include any other file types or formats.

## Pre-flight: resolve output path

- Prompt once to resolve `outputBasePath` if not provided by the user.
- Default path is: `infra/bicep/{goal}`.
- Use `#runCommands` to verify or create the folder (e.g., `mkdir -p <outputBasePath>`), then proceed.

## Testing & validation

- Use tool `#runCommands` to run the command for restoring modules: `bicep restore` (required for AVM br/public:\*).
- Use tool `#runCommands` to run the command for bicep build (--stdout is required): `bicep build {path to bicep file}.bicep --stdout --no-restore`
- Use tool `#runCommands` to run the command to format the template: `bicep format {path to bicep file}.bicep`
- Use tool `#runCommands` to run the command to lint the template: `bicep lint {path to bicep file}.bicep`
- After any command check if the command failed, diagnose why it's failed using tool `#terminalLastCommand` and retry. Treat warnings from analysers as actionable.
- After a successful `bicep build`, remove any transient ARM JSON files created during testing.

## The final check

- All parameters (`param`), variables (`var`) and types are used; remove dead code.
- AVM versions or API versions match the plan.
- No secrets or environment-specific values hardcoded.
- The generated Bicep compiles cleanly and passes format checks.

Prompt

Make sure to read planning file thoroughly before implementing the Bicep template. Break up the file into actionable tasks by using #todos. Use the path and Bicep file: ./bicep/main.bicep. If this path or file does not exist, create them.

Make sure to attach the plan file as context in GitHub Copilot chat.

Output

In the output, you can see the generated Bicep template, including the reference to the latest version of the storage account AVM module with a private endpoint configuration. Additionally, in the top-right corner, you can see the agent’s to-do list, and in the chat window, you can see the prompt along with the code generation and validation actions performed by the agent.

The output:

The output generated by the Implement chat mode.

During trial and error while refining the chat modes, I came across some GitHub Copilot configurations that might be useful for these and other chat modes:

  • The chat modes use instructions to run terminal commands to build, lint, and format Bicep files, as well as to create files or folders. In Visual Studio Code, you can add these commands to an allow list. This allows GitHub Copilot to run terminal commands without approval. To configure this, go to the settings in Visual Studio Code and search for chat.tools.terminal.autoApprove to enable auto approval for commands such as bicep, mkdir, and cd.
    Note: Be careful what you put on this list, as it can run destructive commands without approval.

  • Depending on your prompt, the agent might send many requests, and you can reach the default limit of 25 requests before a continuation approval is required. You can configure the maximum number of requests without approval through the chat.agent.maxRequests setting in Visual Studio Code.

  • Make use of task lists via the #todos tool. It’s currently in the experimental stage, but can be enabled in Visual Studio Code settings β†’ chat.todoListTool.enabled.

    An example of what todos look like:
GitHub Copilot todos in action

While experimenting with custom chat modes for Bicep, I have learned that predictability is difficult, as you will get different results every time you run the agent, even with the same prompt. One-shot code generation is not realistic (yet); you still need to do small restructuring or rewriting, but even with imperfect results, you save significant time compared to manual coding, especially when combined with MCP tooling. Also, it takes some time and fine-tuning to create chat modes that are tailored to your use case. And I learned that keeping behaviour sentences short and concrete makes the agent more reliable.

The ability to custom chat modes in GitHub Copilot tailored to your scenario is, in my opinion, a real game-changer. It significantly helps you generate code tailored to your scenario, and it simplifies your prompts too. While custom chat modes still require fine-tuning and won’t always deliver perfect results, the time savings and flexibility they provide make them a powerful addition to your toolkit.

If you’re already using GitHub Copilot, I highly recommend trying out custom chat modes and exploring how they can boost your productivity. This is not only limited to Azure Bicep, but also applicable to YAML, PowerShell, and so on.

GitHub repository containing the modes (including custom instructions) that I used in this blog: https://github.com/johnlokerse/azure-bicep-github-copilot

Leave a comment