Use GitHub Agentic Workflows to test Bicep deployments

Use AI to improve sustainability. When I first heard that I thought it was not possible. But then it got me thinking…. There are many things which I can do better when designing an Azure environment. But making checks for it all with the help of policies and tests will take a lot of work. Now I could use AI to create those tests and policies for me but they will change very often I assume. So why not use AI to perform the tests? That’s what I was thinking and I wanted to make a test to see how it would look. This blogpost will show you what I made so you can try this out yourself too or use these techniques for other things.

My goal was to take a bicep file and test it for best practices. Also I wanted to check if the regions that are used are the best for this situation based on the emissions and latency.
I’ve already written a blogpost before on how you can measure the latency between regions using PowerShell. And I’ve given several talks about using the entsoe transparancy platform. So with these tools I should be able to do some measurements.

The next step of the puzzle are the GitHub Agentic Workflows that have been introduces a while ago. With these workflows you can create a GitHub Action that uses AI to run tasks.

I’ve created my setup in this repository. I will now explain what it does and how it works and how you can build this too.

In the repository you will see the folder Bicep which holds a bicep file. I’m not going into much detail about this file now. But just know this one was set up to have mistakes in it. In this example I only have one bicep file. Normally you would have more but for this sake of this demo it’s easier to have only one. Adjusting the solution to work on more files should be very simple.

Next we have the data folder which holds some information about the different EIC codes for Entsoe, this is needed for determine the best location. I’ve explained this more in my talks about this. Then we have the scripts folder. Here it starts to get intersting.

First you see the Get-RegionEnergy.ps1 script. This script will take the regions and token and mappingfilepath (the one in data) and for every region returns how much green vs fossil energy was generated. You could also tackle this by having a system periodically gather data about this and present which region is best over a longer period. This depends on your usecase too. If you are deploying infrastructure which will last for a long time you might want to look over a longer period. But if you are deploying infrastructure which will periodically be redeployed or changed you might want to look over a shorter period. You can adapt these scripts to fit your needs.

The next script Get-RegionLatency is just a wrapper for the module I made. And the Test-BicepLocally file is an attempt to get some tests written in code. But as you see in this file already it can be hard to get this right. Especially with bigger and more complex files.

The next important file is the “best-practices.md” file. This file is kind of like a skill file for your AI system. It has a bunch of best practices listed for the bicep files. The AI system will use this file to determine what to analyze in the bicep file.

You will also find a “DECISIONS.md” and “PLAN.md” file. These are generated during the process. I’ve used GitHub Copilot to help me create this system. These files are used as memory for the coding agent so that when I want to apply changes it can use these files to understand what the goal of the project is and why certain decisions have been made.

Last is the .github folder. this is were the magic happens. the aw folder you can ignore, just as the .yml files. These will be generated once you compile the GitHub Agentic Workflow. The important part is the “sustainability-analyzer.md” file. At the top of the file you see:

---
# =============================================================================
# GitHub Agentic Workflow: Sustainability & Best Practices Analyzer
# =============================================================================
# This workflow uses an AI agent (GitHub Copilot) to analyze Bicep templates for:
# 1. Sustainability - checking region energy mix via ENTSO-E API
# 2. Best practices - checking against rules in best-practices.md
# 3. Latency - verifying dependent resources are close enough
#
# The workflow is triggered MANUALLY via workflow_dispatch.
# The Bicep template is NOT deployed - this is purely analytical.
#
# For more info on GitHub Agentic Workflows: https://github.github.com/gh-aw/
# =============================================================================

# --------------------------------------------------------------------------
# TRIGGER: Manual dispatch only - gives you full control over when analysis runs
# --------------------------------------------------------------------------
on: workflow_dispatch

# --------------------------------------------------------------------------
# ENGINE: Uses GitHub Copilot as the AI backbone for the agentic workflow
# --------------------------------------------------------------------------
engine: copilot

# --------------------------------------------------------------------------
# PERMISSIONS: Required for the agent to read and create issues
# --------------------------------------------------------------------------
permissions:
  issues: read

# --------------------------------------------------------------------------
# TOOLS: Grant the agent access to create issues for reporting results
# --------------------------------------------------------------------------
tools:
  github:
    toolsets: [issues]

If you are used to GitHub Actions this should look quite familiar. This describes when to run the github actions and what permissions are needed. You see also the “engine” which describes to use copilot. You can if you want use different systems here too. You can also bring your own key and set different baseurls for the models so as far as I have understood you should be able to run this also locally if you have these models hosted (in case you don’t want the data to go to other parties). There are also options to integrate it with Microsoft Foundry.

The next part of the file looks like this:

# --------------------------------------------------------------------------
# MCP SCRIPTS: Custom tools the AI agent can call during analysis
# These scripts run on the GitHub Actions runner (outside the agent sandbox)
# and provide real-time data from external APIs.
# --------------------------------------------------------------------------
mcp-scripts:

  # Tool 1: Query ENTSO-E API for renewable energy percentage per region
  # The agent calls this with a comma-separated list of Azure region IDs
  # and receives back JSON with the renewable energy % for each region.
  get-region-energy:
    description: "Query the ENTSO-E Transparency Platform API to get the current renewable energy percentage for EU Azure regions. Returns JSON with renewable vs fossil energy breakdown per region. Use this to evaluate sustainability of region choices in Bicep templates."
    inputs:
      regions:
        type: string
        required: true
        description: "Comma-separated Azure region IDs (e.g., 'westeurope,polandcentral,swedencentral'). Must be EU regions."
    run: |
      pwsh -NoProfile -Command '& "./scripts/Get-RegionEnergy.ps1" -Regions ($env:INPUT_REGIONS -split ",")'
    env:
      ENTSOE_TOKEN: "${ }"
    timeout: 120

  # Tool 2: Check network latency between two Azure regions
  # The agent calls this for each pair of dependent resources to verify
  # they are close enough for acceptable performance.
  get-region-latency:
    description: "Get the network latency in milliseconds between two Azure regions. Use this to check if dependent resources (like App Service and Database) are close enough. Returns JSON with latency in ms and a status (OK/Warning/Critical). Threshold: <2ms same region, <10ms acceptable, >10ms needs justification."
    inputs:
      source:
        type: string
        required: true
        description: "Source Azure region ID (e.g., 'westeurope')"
      destination:
        type: string
        required: true
        description: "Destination Azure region ID (e.g., 'polandcentral')"
    run: |
      pwsh -NoProfile -Command '& "./scripts/Get-RegionLatency.ps1" -Source $env:INPUT_SOURCE -Destination $env:INPUT_DESTINATION -Online'
    timeout: 60

# --------------------------------------------------------------------------
# SAFE OUTPUTS: After analysis, create a GitHub Issue with the findings
# This is the approved way to produce output in gh-aw (MCP scripts are read-only)
# The AI agent will create an issue with its analysis report.
# --------------------------------------------------------------------------
safe-outputs:
  create-issue: {}

---

Here it will use a local MCP server to present the scripts as tools that can be used by the AI agent. It describes the parameters/inputs that can be used and what they do. This way the AI system can use it. There is also a general explanation of what the script does so the AI agent can determine when to use this tool. It’s possible to import these descriptions from different files incase you want to have shared mcp scripts over multiple workflows. You can also add mcp servers that exist in different locations to this use the mcp-servers attribute. There are many other types of tools you can add.

The safe-outputs determines how the output should be handled. There are many different ways you can do this described in the docs. I’ve opted to create an issue once the pipeline runes. You can set multiple different settings for this but I opted for the defaults.

As you notices the file started with three dashes and now these are closed. This marks the end of the yaml based portion of this pipeline. The rest is the prompt that needs to be used by the agent. The prompt looks like this:

# Sustainability & Best Practices Bicep Analyzer

You are an expert Azure infrastructure reviewer specializing in sustainability, security, and cost optimization. Your task is to analyze a Bicep template against best-practice rules and real-time energy data.

## Your Mission

Analyze the Bicep template in this repository (`bicep/main.bicep`) and produce a comprehensive report covering sustainability, security, cost optimization, and performance. This template is for **demonstration purposes only** and is NOT deployed.

## Step-by-Step Instructions

### Step 1: Read the Bicep Template

Read the file `bicep/main.bicep`. Identify:
- All Azure resources being deployed
- The `location` parameter/variable for each resource (which Azure region)
- SKU/tier settings for each resource
- Redundancy settings (LRS/ZRS/GRS, zoneRedundant)
- Network/security configurations (protocols, TLS settings, public access)
- Resource dependencies (which resources communicate with each other)

### Step 2: Read the Best Practices Rules

Read the file `best-practices.md`. This contains the rules you must check against:
- RULE-001: Redundancy Consistency
- RULE-002: SKU Over-Provisioning (LB & Front Door)
- RULE-003: App Service Plan Over-Sizing
- RULE-004: Internal Communication Encryption (Zero Trust)
- RULE-005: Database Connection Encryption
- RULE-006: Sustainable Region Selection
- RULE-007: Latency Between Dependent Resources

### Step 3: Get Energy Data

Extract the unique Azure regions from the Bicep template (look at `location` parameters and where resources are deployed). Then call the `get-region-energy` tool with those regions to get real-time renewable energy percentages from the ENTSO-E API.

For example, if the template uses `westeurope` and `polandcentral`, call:
```
get-region-energy(regions: "westeurope,polandcentral")
```

### Step 4: Check Latency

Identify pairs of resources that are **dependent on each other** (e.g., App Service → SQL Database, Front Door → Backend). If they are in different regions, call the `get-region-latency` tool for each pair.

For example:
```
get-region-latency(source: "westeurope", destination: "polandcentral")
```

### Step 5: Produce the Report

Create a detailed GitHub Issue with the following structure:

#### Report Header
```
## 🌍 Sustainability & Best Practices Analysis Report
**Analyzed:** `bicep/main.bicep`
**Date:** [current date]
**Scope:** EU regions only
```

#### Summary Table
Create a table with columns: Rule ID | Severity | Status | Resource | Finding

#### Detailed Findings
For each rule violation found, provide:
1. **What's wrong** - specific line/resource in the Bicep
2. **Why it matters** - impact on sustainability/security/cost
3. **Recommended fix** - specific Bicep change to resolve it

#### Sustainability Scores
Show the energy data for each region:
- Region name
- Renewable energy percentage (from ENTSO-E)
- Assessment (Good >50%, Moderate 30-50%, Poor <30%)

#### Latency Analysis
For each dependent resource pair in different regions:
- Source → Destination
- Measured latency
- Assessment (OK/Warning/Critical)

#### Recommendations Priority
Order recommendations by impact:
1. Security issues (RULE-004, RULE-005) — fix immediately
2. Reliability issues (RULE-001) — fix before production
3. Sustainability issues (RULE-006, RULE-007) — plan migration
4. Cost issues (RULE-002, RULE-003) — optimize in next sprint

## Important Notes

- This is an EU-only analysis tool. All regions should be European Azure regions.
- The Bicep template is NOT deployed. This is a static analysis for advisory purposes.
- Use the actual data from the MCP script tools (ENTSO-E API, AzNetworkLatency) in your report.
- If an API call fails, note the error but continue with the other checks.
- Be specific about which Bicep resources and properties trigger each rule violation.

This describes how to perform the test. It will start by reading the bicep file. Then it imports the best practices file. It will then check for the energy data and the latency data. Then it will generate a report which is specified how I want it to look.

In the repository you can look at the issues to see an example of the outcome.

After you created this md file you want to make sure the github cli is installed by running

gh extension install github/gh-aw

After that you can use the compile command

gh aw compile

this will generate the actual yml files. Once you push this to a github repository the action will be recognized and show up in the actions tab. From there you can run it manually (or it will run automatically if you set different triggers).

Of course running an agent like this will use energy and tokens. So you can’t use it in every situation. But if you are deploying big infrastructure projects. Making sure you size right and use the best regions could have a significant impact. And having a tool like this to check it could be the difference between a company agreeing that the checks can be performed or not at all because it would take to much work. Setting something like this up can be done quite quickly and the impact it could make it high! So I hope this has inspired you, if you ever use this I would love to hear your experiences in the comments or via private messages.