.github/skills/lab-creator/SKILL.md
Guide for creating new AI Gateway labs. Use when users want to create a new lab in the labs/ folder. This skill provides the standard lab structure, templates, and patterns used across the AI Gateway repository including Jupyter notebooks, Bicep infrastructure templates, APIM policies, and README documentation.
npx skillsauth add azure-samples/ai-gateway lab-creatorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill provides guidance for creating new labs in the AI Gateway repository.
Every lab follows a consistent structure under labs/<lab-name>/:
labs/<lab-name>/
├── <lab-name>.ipynb # Main Jupyter notebook (required)
├── main.bicep # Azure Bicep deployment template (required)
├── policy.xml # Azure API Management policy (required)
├── README.md # Lab documentation (required)
├── clean-up-resources.ipynb # Cleanup notebook (required)
├── params.json # Auto-generated, not committed
└── src/ # Supporting source code (optional)
Create a new folder under labs/ with a descriptive kebab-case name:
labs/<your-lab-name>/
Create <lab-name>.ipynb with these standard sections:
# APIM ❤️ Microsoft Foundry
## <Lab Title> lab

<Brief description of what this lab demonstrates>
### Prerequisites
- [Python 3.12 or later version](https://www.python.org/) installed
- [VS Code](https://code.visualstudio.com/) installed with the [Jupyter notebook extension](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter) enabled
- [Python environment](https://code.visualstudio.com/docs/python/environments#_creating-environments) with the [requirements.txt](../../../requirements.txt) or run `pip install -r requirements.txt` in your terminal
- [An Azure Subscription](https://azure.microsoft.com/free/) with [Contributor](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/privileged#contributor) + [RBAC Administrator](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/privileged#role-based-access-control-administrator) or [Owner](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/privileged#owner) roles
- [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) installed and [Signed into your Azure subscription](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively)
▶️ Click `Run All` to execute all steps sequentially, or execute them `Step by Step`...
import os, sys, json
sys.path.insert(1, '../../shared') # add the shared directory to the Python path
import utils
deployment_name = os.path.basename(os.path.dirname(globals()['__vsc_ipynb_file__']))
resource_group_name = f"lab-{deployment_name}"
resource_group_location = "westeurope"
# AI Services configuration
aiservices_config = [{"name": "foundry1", "location": "swedencentral"},
{"name": "foundry2", "location": "eastus2"}]
# Models configuration - adjust based on lab requirements
models_config = [{"name": "gpt-4.1-mini", "publisher": "OpenAI", "version": "2025-04-14", "sku": "GlobalStandard", "capacity": 100}]
# APIM configuration
apim_sku = 'Basicv2'
apim_subscriptions_config = [{"name": "subscription1", "displayName": "Subscription 1"}]
# API configuration
inference_api_path = "inference"
inference_api_type = "AzureOpenAIV1" # options: AzureOpenAI, AzureAI, OpenAI, PassThrough
inference_api_version = "v1"
foundry_project_name = deployment_name
utils.print_ok('Notebook initialized')
<a id='1'></a>
### 1️⃣ Verify the Azure CLI and the connected Azure subscription
The following commands ensure that you have the latest version of the Azure CLI and that the Azure CLI is connected to your Azure subscription.
output = utils.run("az account show", "Retrieved az account", "Failed to get the current az account")
if output.success and output.json_data:
current_user = output.json_data['user']['name']
tenant_id = output.json_data['tenantId']
subscription_id = output.json_data['id']
utils.print_info(f"Current user: {current_user}")
utils.print_info(f"Tenant ID: {tenant_id}")
utils.print_info(f"Subscription ID: {subscription_id}")
<a id='2'></a>
### 2️⃣ Create deployment using 🦾 Bicep
This lab uses [Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/overview?tabs=bicep) to declarative define all the resources that will be deployed in the specified resource group. Change the parameters or the [main.bicep](main.bicep) directly to try different configurations.
# Create the resource group if doesn't exist
utils.create_resource_group(resource_group_name, resource_group_location)
# Define the Bicep parameters
bicep_parameters = {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"apimSku": { "value": apim_sku },
"aiServicesConfig": { "value": aiservices_config },
"modelsConfig": { "value": models_config },
"apimSubscriptionsConfig": { "value": apim_subscriptions_config },
"inferenceAPIPath": { "value": inference_api_path },
"inferenceAPIType": { "value": inference_api_type },
"foundryProjectName": { "value": foundry_project_name },
}
}
# Write the parameters to the params.json file
with open('params.json', 'w') as bicep_parameters_file:
bicep_parameters_file.write(json.dumps(bicep_parameters))
# Run the deployment
output = utils.run(f"az deployment group create --name {deployment_name} --resource-group {resource_group_name} --template-file main.bicep --parameters params.json",
f"Deployment '{deployment_name}' succeeded", f"Deployment '{deployment_name}' failed")
<a id='3'></a>
### 3️⃣ Get the deployment outputs
We are now at the stage where we only need to retrieve the gateway URL and the subscription before we are ready for testing.
# Obtain all of the outputs from the deployment
output = utils.run(f"az deployment group show --name {deployment_name} -g {resource_group_name}", f"Retrieved deployment: {deployment_name}", f"Failed to retrieve deployment: {deployment_name}")
if output.success and output.json_data:
log_analytics_id = utils.get_deployment_output(output, 'logAnalyticsWorkspaceId', 'Log Analytics Id')
apim_service_id = utils.get_deployment_output(output, 'apimServiceId', 'APIM Service Id')
apim_resource_gateway_url = utils.get_deployment_output(output, 'apimResourceGatewayURL', 'APIM API Gateway URL')
apim_subscriptions = json.loads(utils.get_deployment_output(output, 'apimSubscriptions').replace("\'", "\""))
for subscription in apim_subscriptions:
subscription_name = subscription['name']
subscription_key = subscription['key']
utils.print_info(f"Subscription Name: {subscription_name}")
utils.print_info(f"Subscription Key: ****{subscription_key[-4:]}")
api_key = apim_subscriptions[0].get("key")
<a id='requests'></a>
### 🧪 Test the API using a direct HTTP call
Tip: Use the [tracing tool](../../../tools/tracing.ipynb) to track the behavior and troubleshoot the [policy](policy.xml).
Add your lab-specific test code here.
<a id='clean'></a>
### 🗑️ Clean up resources
When you're finished with the lab, you should remove all your deployed resources from Azure to avoid extra charges and keep your Azure subscription uncluttered.
Use the [clean-up-resources notebook](clean-up-resources.ipynb) for that.
Create main.bicep with this standard structure:
// ------------------
// PARAMETERS
// ------------------
param aiServicesConfig array = []
param modelsConfig array = []
param apimSku string
param apimSubscriptionsConfig array = []
param inferenceAPIType string = 'AzureOpenAI'
param inferenceAPIPath string = 'inference'
param foundryProjectName string = 'default'
// ------------------
// RESOURCES
// ------------------
// 1. Log Analytics Workspace
module lawModule '../../modules/operational-insights/v1/workspaces.bicep' = {
name: 'lawModule'
}
// 2. Application Insights
module appInsightsModule '../../modules/monitor/v1/appinsights.bicep' = {
name: 'appInsightsModule'
params: {
lawId: lawModule.outputs.id
customMetricsOptedInType: 'WithDimensions'
}
}
// 3. API Management
module apimModule '../../modules/apim/v3/apim.bicep' = {
name: 'apimModule'
params: {
apimSku: apimSku
apimSubscriptionsConfig: apimSubscriptionsConfig
lawId: lawModule.outputs.id
appInsightsId: appInsightsModule.outputs.id
appInsightsInstrumentationKey: appInsightsModule.outputs.instrumentationKey
}
}
// 4. AI Foundry
module foundryModule '../../modules/cognitive-services/v3/foundry.bicep' = {
name: 'foundryModule'
params: {
aiServicesConfig: aiServicesConfig
modelsConfig: modelsConfig
apimPrincipalId: apimModule.outputs.principalId
foundryProjectName: foundryProjectName
}
}
// 5. APIM Inference API
module inferenceAPIModule '../../modules/apim/v3/inference-api.bicep' = {
name: 'inferenceAPIModule'
params: {
policyXml: loadTextContent('policy.xml')
apimLoggerId: apimModule.outputs.loggerId
aiServicesConfig: foundryModule.outputs.extendedAIServicesConfig
inferenceAPIType: inferenceAPIType
inferenceAPIPath: inferenceAPIPath
}
}
// ------------------
// OUTPUTS
// ------------------
output logAnalyticsWorkspaceId string = lawModule.outputs.customerId
output apimServiceId string = apimModule.outputs.id
output apimResourceGatewayURL string = apimModule.outputs.gatewayUrl
output apimSubscriptions array = apimModule.outputs.apimSubscriptions
Create policy.xml with the base structure:
<policies>
<inbound>
<base />
<set-backend-service backend-id="{backend-id}" />
<!-- Add your custom inbound policies here -->
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<!-- Add your custom outbound policies here -->
</outbound>
<on-error>
<base />
</on-error>
</policies>
Create README.md with this structure:
---
name: "<Lab Display Name>"
architectureDiagram: images/<lab-name>.gif
categories: ["<Category>"]
services: ["Azure AI Foundry", "Azure OpenAI"]
shortDescription: "<One-line description>"
detailedDescription: "<Detailed description of what the lab demonstrates>"
authors: ["<github-username>"]
---
# APIM ❤️ AI Foundry
## [<Lab Title> lab](<lab-name>.ipynb)
[](<lab-name>.ipynb)
<Description of what this lab demonstrates and its key features>
### Prerequisites
- [Python 3.12 or later version](https://www.python.org/) installed
- [VS Code](https://code.visualstudio.com/) installed with the [Jupyter notebook extension](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter) enabled
- [Python environment](https://code.visualstudio.com/docs/python/environments#_creating-environments) with the [requirements.txt](../../../requirements.txt) or run `pip install -r requirements.txt` in your terminal
- [An Azure Subscription](https://azure.microsoft.com/free/) with [Contributor](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/privileged#contributor) + [RBAC Administrator](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/privileged#role-based-access-control-administrator) or [Owner](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/privileged#owner) roles
- [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) installed and [Signed into your Azure subscription](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively)
### 🚀 Get started
Proceed by opening the [Jupyter notebook](<lab-name>.ipynb), and follow the steps provided.
### 🗑️ Clean up resources
When you're finished with the lab, you should remove all your deployed resources from Azure to avoid extra charges and keep your Azure subscription uncluttered.
Use the [clean-up-resources notebook](clean-up-resources.ipynb) for that.
Create clean-up-resources.ipynb with these cells:
### 🗑️ Clean up resources
The categories for the labs should be chosen only from the following list:
- `AI Agents` - Model Context Protocol, agents, agentic workflows
- `Knowledge & Tools` - Retrieval, vector databases, tool use
- `Models Usage` - Using models via APIM, prompt engineering, response handling
- `Governance & Responsible AI` - Authentication, content safety, access control
- `Platform Capabilities` - APIM features like policies, logging, rate limits, monitoring, load balancing, etc.
When you're finished with the lab, you should remove all your deployed resources from Azure to avoid extra charges and keep your Azure subscription uncluttered.
import os, sys
sys.path.insert(1, '../../shared') # add the shared directory to the Python path
import utils
deployment_name = os.path.basename(os.path.dirname(globals()['__vsc_ipynb_file__']))
resource_group = f"lab-{deployment_name}"
utils.cleanup_resources(deployment_name, resource_group_name=resource_group)
Reference these modules from ../../modules/:
| Module Path | Purpose |
|-------------|---------|
| apim/v3/apim.bicep | Azure API Management instance |
| apim/v3/inference-api.bicep | APIM Inference API configuration |
| apim-streamable-mcp/ | APIM with MCP streaming support |
| cognitive-services/v3/foundry.bicep | AI Foundry with model deployments |
| operational-insights/v1/workspaces.bicep | Log Analytics Workspace |
| monitor/v1/appinsights.bicep | Application Insights |
| network/ | Networking infrastructure |
| apic/v1/ | Azure API Center |
Import the shared utilities in notebooks:
import os, sys, json
sys.path.insert(1, '../../shared')
import utils
Key functions from utils.py:
utils.run(command, success_msg, error_msg) - Execute Azure CLI commandsutils.print_ok(msg), utils.print_error(msg), utils.print_info(msg) - Formatted outpututils.create_resource_group(name, location) - Create Azure resource grouputils.get_deployment_output(output, key, label) - Extract Bicep outputsutils.cleanup_resources(deployment, resource_group_name) - Delete resourcesWhen creating a lab, assign it to one of these categories in the README frontmatter:
AI Agents & MCP - Model Context Protocol, agents, agentic workflowsModel Integration - Third-party models, AI Foundry SDKLoad Balancing & Routing - Backend pools, model routingSecurity & Access Control - Authentication, content safetyMonitoring & Logging - Tracing, metrics, loggingRate Limiting & Caching - Token limits, semantic cachingSpecialized Features - Realtime API, images, functionsOperations - FinOps, production deploymenttools
Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.
tools
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
development
Guide for creating Terraform files for Azure API Management (APIM) and related Azure services. Use when users want to create, modify, or understand Terraform configurations for APIM instances, APIs, backends, subscriptions, policies, products, loggers, diagnostics, and supporting infrastructure using the azurerm provider. This skill provides HCL syntax, resource definitions, and patterns from the Terraform Registry and this repository.
development
Guide for creating Azure API Management (APIM) XML policies. Use when users want to create, modify, or understand APIM policies including inbound/outbound processing, authentication, rate limiting, caching, transformations, AI gateway policies, and policy expressions. This skill provides policy syntax, examples, and C# policy expressions for request/response manipulation.