thomast1906/apiops-deployment
Guides deployment of Azure API Management infrastructure using Infrastructure as Code (Bicep/Terraform), CI/CD pipelines (GitHub Actions/Azure DevOps), and APIOps workflows. Use when deploying APIM, creating pipelines, or implementing dev→test→prod promotion strategies.
npx skills add https://github.com/thomast1906/github-copilot-agent-skills --skill apiops-deployment
Provides Infrastructure as Code (Bicep/Terraform) templates and CI/CD pipeline patterns for deploying Azure API Management following APIOps principles and phased deployment strategies.
Activate this skill when users need:
See DEPLOYMENT_PLANNING_GUIDE.md for complete 17-week, 7-phase plan
| Phase | Duration | Focus | Key Deliverables |
|-------|----------|-------|------------------|
| 1 | Week 1-2 | Core Infrastructure | VNet, APIM(dev), Front Door, Key Vault, monitoring |
| 2 | Week 3-4 | Authentication & IAM | Entra ID, Entra External ID, OAuth policies|
| 3 | Week 5-8 | API Onboarding (Dev) | 5 pilot APIs, policies, developer portal |
| 4 | Week 9-10 | Production Infrastructure | APIM(prod) Premium 3u, zone-redundant |
| 5 | Week 11-12 | Production APIs | Migrate 5 pilot APIs, performance testing |
| 6 | Week 13-15 | Governance & Scaling | API Center, additional APIs, APIOps automation |
| 7 | Week 16-17 | Operations Handoff | Runbooks, training, monitoring dashboards |
BEFORE writing any Bicep, check for Azure Verified Modules:
Tool: azure_bicep-get_azure_verified_module
ResourceType: "Microsoft.ApiManagement/service"
Why: AVM modules follow Microsoft best practices, reduce code duplication, tested at scale
Tool: mcp_azure_mcp_get_azure_bestpractices
Intent: "Azure API Management deployment best practices Bicep"
Tool: mcp_azure_mcp_documentation search
Query: "APIM VNet Internal Bicep deployment"
See references/IaC_TEMPLATES.md for complete Bicep/Terraform templates
param location string = 'uksouth'
param apimName string = 'apim-api-marketplace-prod-uks'
param publisherEmail string = '[email protected]'
param publisherName string = 'API Marketplace Team'
param vnetName string = 'vnet-apim-prod-uks'
param subnetName string = 'snet-apim'
resource vnet 'Microsoft.Network/virtualNetworks@2023-04-01' existing = {
name: vnetName
}
resource apim 'Microsoft.ApiManagement/service@2023-05-01-preview' = {
name: apimName
location: location
sku: {
name: 'Premium'
capacity: 3 // Zone-redundant: 3 units across 3 availability zones
}
properties: {
publisherEmail: publisherEmail
publisherName: publisherName
virtualNetworkType: 'Internal' // VNet Internal mode
virtualNetworkConfiguration: {
subnetResourceId: '${vnet.id}/subnets/${subnetName}'
}
customProperties: {
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'False'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'False'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'False'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'False'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'False'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'False'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'False'
}
disableGateway: false
}
identity: {
type: 'SystemAssigned' // Managed Identity for Key Vault access
}
zones: [
'1'
'2'
'3'
] // Zone redundancy for 99.99% SLA
}
output apimId string = apim.id
output managedIdentityPrincipalId string = apim.identity.principalId
Key Configuration:
virtualNetworkType: 'Internal')[1, 2, 3] for 99.99% SLAname: APIOps - Deploy APIM Infrastructure
on:
workflow_dispatch:
push:
branches: [main]
paths:
- 'infra/**'
- '.github/workflows/deploy-infra.yml'
env:
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
jobs:
# ===== Dev Environment =====
deploy-dev:
runs-on: ubuntu-latest
environment: development
steps:
- uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ env.AZURE_TENANT_ID }}
subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }}
- name: Deploy Bicep
uses: azure/arm-deploy@v2
with:
scope: resourcegroup
resourceGroupName: rg-apim-dev-uks
template: ./infra/main.bicep
parameters: ./infra/params/dev.bicepparam
failOnStdErr: false
- name: Smoke Test
run: |
# Verify APIM health endpoint
curl -f https://management.azure.com/subscriptions/${{ env.AZURE_SUBSCRIPTION_ID }}/resourceGroups/rg-apim-dev-uks/providers/Microsoft.ApiManagement/service/apim-api-marketplace-dev-uks?api-version=2023-05-01-preview \
-H "Authorization: Bearer $(az account get-access-token --query accessToken -o tsv)"
# ===== Test Environment (After Dev) =====
deploy-test:
needs: deploy-dev
runs-on: ubuntu-latest
environment: test
steps:
# Same steps as dev, use test.bicepparam
- uses: actions/checkout@v4
- uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ env.AZURE_TENANT_ID }}
subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }}
- uses: azure/arm-deploy@v2
with:
scope: resourcegroup
resourceGroupName: rg-apim-test-uks
template: ./infra/main.bicep
parameters: ./infra/params/test.bicepparam
failOnStdErr: false
# ===== Production (Manual Approval Required) =====
deploy-prod:
needs: deploy-test
runs-on: ubuntu-latest
environment:
name: production
# GitHub environment protection rule: Require manual approval
steps:
- uses: actions/checkout@v4
- uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ env.AZURE_TENANT_ID }}
subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }}
- name: Deploy Production APIM
uses: azure/arm-deploy@v2
with:
scope: resourcegroup
resourceGroupName: rg-apim-prod-uks
template: ./infra/main.bicep
parameters: ./infra/params/prod.bicepparam
failOnStdErr: false
- name: Production Smoke Test
run: |
# Verify Front Door → APIM connectivity
curl -f https://api.yourdomain.com/health
- name: Tag Release
run: |
git tag -a "apim-prod-$(date +%Y%m%d-%H%M%S)" -m "Production deployment"
git push origin --tags
Key Features:
needs)environment: production with protection rules)using './main.bicep'
param location = 'uksouth'
param environment = 'dev'
param apimSku = 'Developer' // £45/month
param apimCapacity = 1
param virtualNetworkType = 'Internal'
param enableFrontDoor = false // Dev: Direct APIM access
param tags = {
Environment: 'Development'
ManagedBy: 'IaC'
CostCenter: 'Engineering'
}
using './main.bicep'
param location = 'uksouth'
param environment = 'prod'
param apimSku = 'Premium' // £1,944/month (3 units)
param apimCapacity = 3 // Zone-redundant
param virtualNetworkType = 'Internal'
param enableFrontDoor = true // Prod: Front Door + Private Link
param enableApiCenter = true // £135/month
param tags = {
Environment: 'Production'
ManagedBy: 'IaC'
CostCenter: 'Operations'
Criticality: 'High'
}
Pattern: Single main.bicep template, environment-specific .bicepparam files for configuration
#!/bin/bash
# backup-apim.sh - Schedule daily via Azure Automation or GitHub Actions
APIM_NAME="apim-api-marketplace-prod-uks"
RESOURCE_GROUP="rg-apim-prod-uks"
STORAGE_ACCOUNT="stapimproduksbkp"
CONTAINER="apim-backups"
BACKUP_NAME="apim-backup-$(date +%Y%m%d-%H%M%S)"
# Trigger APIM backup to Storage Account
az apim backup create \
--name "$APIM_NAME" \
--resource-group "$RESOURCE_GROUP" \
--storage-account-name "$STORAGE_ACCOUNT" \
--storage-account-container "$CONTAINER" \
--backup-name "$BACKUP_NAME"
echo "Backup created: $BACKUP_NAME"
#!/bin/bash
# restore-apim.sh - Run during DR scenario
APIM_NAME="apim-api-marketplace-prod-uks"
RESOURCE_GROUP="rg-apim-prod-uks"
STORAGE_ACCOUNT="stapimproduksbkp"
CONTAINER="apim-backups"
BACKUP_NAME="apim-backup-20260128-120000" # Latest successful backup
# Restore APIM from backup
az apim backup restore \
--name "$APIM_NAME" \
--resource-group "$RESOURCE_GROUP" \
--storage-account-name "$STORAGE_ACCOUNT" \
--storage-account-container "$CONTAINER" \
--backup-name "$BACKUP_NAME"
echo "APIM restored from backup: $BACKUP_NAME"
Backup Retention:
Before production deployment, verify:
az bicep build, terraform validate)Error: The subnet is not valid for API Management instance
Solution:
Microsoft.ApiManagement/service: delegations: [
{
name: 'delegation'
properties: {
serviceName: 'Microsoft.ApiManagement/service'
}
}
]
Error: Front Door → APIM Private Link status Pending Approval
Solution:
az network private-endpoint-connection approve \
--resource-name apim-api-marketplace-prod-uks \
--resource-group rg-apim-prod-uks \
--name <connection-name> \
--type Microsoft.ApiManagement/service
az network private-endpoint-connection list \
--name apim-api-marketplace-prod-uks \
--resource-group rg-apim-prod-uks \
--type Microsoft.ApiManagement/service
Cause: APIM Premium with zone redundancy takes 30-60 minutes to deploy
Solution: Expected behavior. Use incremental deployments:
Optimization: Use --what-if flag to preview changes without deploying
Skill Version: 1.0
Last Updated: 29 January 2026
Primary Knowledge: DEPLOYMENT_PLANNING_GUIDE.md, references/IaC_TEMPLATES.md
Take thomast1906/apiops-deployment from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.