Azure API Management
Microsoft's enterprise API management platform. Supports hybrid and multi-cloud, provides developer portal and detailed analytics capabilities.
Server
Azure API Management
Overview
Azure API Management (APIM) is Microsoft's comprehensive enterprise-grade API management platform that enables organizations to publish, secure, transform, maintain, and monitor APIs across hybrid and multi-cloud environments. Designed specifically for enterprise scenarios, Azure API Management provides a unified solution for API lifecycle management with features including a developer portal, advanced analytics, policy-based transformations, and extensive security capabilities. As a platform-as-a-service offering, APIM supports the complete API lifecycle from design and development to deployment and retirement, while offering seamless integration with Microsoft's ecosystem including Azure Active Directory, Microsoft 365, and other Azure services.
Details
Azure API Management 2025 edition continues to strengthen its position as a leading enterprise API management solution with enhanced hybrid and multi-cloud capabilities. The platform operates through multiple components including the API gateway for traffic routing and policy enforcement, management plane for configuration and analytics, developer portal for API discovery and testing, and self-hosted gateways for on-premises and multi-cloud deployments. APIM excels in enterprise environments with its comprehensive security model, detailed analytics and monitoring capabilities, policy-based request/response transformations, and built-in developer experience tools. The service supports various deployment models including consumption-based pricing for lightweight scenarios and premium tiers for enterprise features like VNet integration, multi-region deployment, and advanced security capabilities.
Key Features
- Hybrid and Multi-Cloud Support: Self-hosted gateways for on-premises and cross-cloud deployments
- Enterprise Security: Azure AD integration, OAuth, API keys, client certificates, and WAF protection
- Developer Portal: Customizable portal for API documentation, testing, and developer onboarding
- Advanced Analytics: Comprehensive metrics, custom dashboards, and business intelligence integration
- Policy Engine: Flexible request/response transformation, routing, and business logic implementation
- Scalability and High Availability: Auto-scaling, multi-region deployment, and availability zones support
Advantages and Disadvantages
Advantages
- Comprehensive enterprise features with strong Microsoft ecosystem integration and Azure AD seamless authentication
- Excellent hybrid and multi-cloud support enabling consistent API management across diverse infrastructure environments
- Rich developer experience with customizable portal, documentation generation, and API testing capabilities
- Advanced analytics and monitoring providing detailed insights into API usage patterns and business metrics
- Policy-based architecture offering flexible request/response transformations and business logic implementation
- Enterprise-grade security and compliance features supporting regulatory requirements and enterprise governance
Disadvantages
- Higher cost compared to lightweight alternatives with complex pricing models across multiple service tiers
- Microsoft ecosystem dependency potentially limiting integration options with non-Microsoft technologies
- Learning curve for complex enterprise features requiring significant training and expertise
- Resource overhead for simple use cases where lightweight solutions might be more appropriate
- Vendor lock-in concerns affecting portability to other cloud providers or on-premises environments
- Complex configuration for advanced scenarios requiring deep understanding of Azure services and networking
Reference Links
Code Examples
ARM Template for API Management Deployment
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"apiManagementServiceName": {
"type": "string",
"metadata": {
"description": "Name of the API Management service"
}
},
"publisherEmail": {
"type": "string",
"metadata": {
"description": "Email address of the publisher"
}
},
"publisherName": {
"type": "string",
"metadata": {
"description": "Name of the publisher"
}
},
"sku": {
"type": "string",
"defaultValue": "Standard",
"allowedValues": [
"Developer",
"Standard",
"Premium"
],
"metadata": {
"description": "The pricing tier of this API Management service"
}
},
"skuCount": {
"type": "int",
"defaultValue": 1,
"metadata": {
"description": "The instance size of this API Management service"
}
}
},
"variables": {
"apiManagementServiceName": "[parameters('apiManagementServiceName')]"
},
"resources": [
{
"type": "Microsoft.ApiManagement/service",
"apiVersion": "2021-08-01",
"name": "[variables('apiManagementServiceName')]",
"location": "[resourceGroup().location]",
"sku": {
"name": "[parameters('sku')]",
"capacity": "[parameters('skuCount')]"
},
"properties": {
"publisherEmail": "[parameters('publisherEmail')]",
"publisherName": "[parameters('publisherName')]",
"customProperties": {
"Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10": "False",
"Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11": "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"
},
"virtualNetworkType": "None"
}
}
],
"outputs": {
"apiManagementServiceName": {
"type": "string",
"value": "[variables('apiManagementServiceName')]"
},
"gatewayUrl": {
"type": "string",
"value": "[concat('https://', variables('apiManagementServiceName'), '.azure-api.net')]"
}
]
}
Bicep Template for Modern Deployment
// main.bicep - Modern deployment template
@description('Name of the API Management service')
param apiManagementServiceName string
@description('Email address of the publisher')
param publisherEmail string
@description('Name of the publisher')
param publisherName string
@description('The pricing tier of this API Management service')
@allowed([
'Developer'
'Standard'
'Premium'
'Consumption'
])
param sku string = 'Standard'
@description('The instance size of this API Management service')
param skuCount int = 1
@description('Location for all resources')
param location string = resourceGroup().location
// API Management Service
resource apiManagement 'Microsoft.ApiManagement/service@2021-08-01' = {
name: apiManagementServiceName
location: location
sku: {
name: sku
capacity: skuCount
}
properties: {
publisherEmail: publisherEmail
publisherName: publisherName
customProperties: {
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'False'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': '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'
}
virtualNetworkType: 'None'
}
}
// Application Insights for monitoring
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: '${apiManagementServiceName}-insights'
location: location
kind: 'web'
properties: {
Application_Type: 'web'
}
}
// Logger for Application Insights
resource logger 'Microsoft.ApiManagement/service/loggers@2021-08-01' = {
parent: apiManagement
name: 'applicationinsights'
properties: {
loggerType: 'applicationInsights'
credentials: {
instrumentationKey: appInsights.properties.InstrumentationKey
}
isBuffered: true
resourceId: appInsights.id
}
}
// Named values (formerly properties)
resource namedValue 'Microsoft.ApiManagement/service/namedValues@2021-08-01' = {
parent: apiManagement
name: 'backend-url'
properties: {
displayName: 'backend-url'
value: 'https://api.backend.example.com'
secret: false
}
}
output gatewayUrl string = 'https://${apiManagement.name}.azure-api.net'
output managementUrl string = 'https://${apiManagement.name}.management.azure-api.net'
output portalUrl string = 'https://${apiManagement.name}.portal.azure-api.net'
API and Operations Configuration
// api-configuration.bicep - API and operations setup
param apiManagementServiceName string
param backendUrl string
resource apiManagement 'Microsoft.ApiManagement/service@2021-08-01' existing = {
name: apiManagementServiceName
}
// Backend configuration
resource backend 'Microsoft.ApiManagement/service/backends@2021-08-01' = {
parent: apiManagement
name: 'products-backend'
properties: {
description: 'Products API Backend'
url: backendUrl
protocol: 'http'
circuitBreaker: {
rules: [
{
failureCondition: {
count: 3
percentage: 50
statusCodeRanges: [
{
min: 500
max: 599
}
]
}
name: 'backend-health'
tripDuration: 'PT1M'
}
]
}
}
}
// API definition
resource api 'Microsoft.ApiManagement/service/apis@2021-08-01' = {
parent: apiManagement
name: 'products-api'
properties: {
displayName: 'Products API'
description: 'API for product management'
serviceUrl: backendUrl
path: 'products'
protocols: [
'https'
]
subscriptionRequired: true
format: 'openapi+json'
value: loadTextContent('openapi.json')
}
}
// API operations
resource getProductsOperation 'Microsoft.ApiManagement/service/apis/operations@2021-08-01' = {
parent: api
name: 'get-products'
properties: {
displayName: 'Get Products'
method: 'GET'
urlTemplate: '/'
description: 'Retrieve all products'
responses: [
{
statusCode: 200
description: 'Success'
representations: [
{
contentType: 'application/json'
sample: '[{"id": 1, "name": "Product 1", "price": 29.99}]'
}
]
}
]
}
}
resource getProductOperation 'Microsoft.ApiManagement/service/apis/operations@2021-08-01' = {
parent: api
name: 'get-product'
properties: {
displayName: 'Get Product by ID'
method: 'GET'
urlTemplate: '/{id}'
description: 'Retrieve a specific product'
templateParameters: [
{
name: 'id'
description: 'Product identifier'
type: 'string'
required: true
}
]
responses: [
{
statusCode: 200
description: 'Success'
representations: [
{
contentType: 'application/json'
sample: '{"id": 1, "name": "Product 1", "price": 29.99}'
}
]
}
{
statusCode: 404
description: 'Product not found'
}
]
}
}
// Products subscription
resource productsSubscription 'Microsoft.ApiManagement/service/subscriptions@2021-08-01' = {
parent: apiManagement
name: 'products-subscription'
properties: {
scope: api.id
displayName: 'Products API Subscription'
state: 'active'
}
}
Policy Configuration Examples
<!-- API-level policies -->
<policies>
<inbound>
<!-- CORS policy -->
<cors allow-credentials="true">
<allowed-origins>
<origin>https://myapp.example.com</origin>
<origin>https://admin.example.com</origin>
</allowed-origins>
<allowed-methods preflight-result-max-age="120">
<method>GET</method>
<method>POST</method>
<method>PUT</method>
<method>DELETE</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
<expose-headers>
<header>*</header>
</expose-headers>
</cors>
<!-- Rate limiting -->
<rate-limit calls="100" renewal-period="60" />
<quota calls="10000" renewal-period="2629746" />
<!-- Authentication -->
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized">
<openid-config url="https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid_configuration" />
<audiences>
<audience>api://products-api</audience>
</audiences>
<issuers>
<issuer>https://sts.windows.net/{tenant-id}/</issuer>
</issuers>
</validate-jwt>
<!-- IP filtering -->
<ip-filter action="allow">
<address-range from="192.168.1.1" to="192.168.1.255" />
<address>203.0.113.5</address>
</ip-filter>
<!-- Request transformation -->
<set-header name="X-API-Version" exists-action="override">
<value>v2</value>
</set-header>
<set-query-parameter name="api-version" exists-action="override">
<value>2023-01-01</value>
</set-query-parameter>
<!-- Backend URL rewriting -->
<rewrite-uri template="/api/v2/products{uri}" />
</inbound>
<backend>
<!-- Backend timeout -->
<forward-request timeout="30" />
</backend>
<outbound>
<!-- Response transformation -->
<set-header name="X-Powered-By" exists-action="override">
<value>Azure API Management</value>
</set-header>
<!-- Cache response -->
<cache-store duration="300" />
<!-- Mock response for testing -->
<choose>
<when condition="@(context.Request.Headers.GetValueOrDefault("X-Mock-Response", "") == "true")">
<return-response>
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>{
"id": 1,
"name": "Mock Product",
"price": 99.99,
"description": "This is a mock response"
}</set-body>
</return-response>
</when>
</choose>
</outbound>
<on-error>
<!-- Error handling -->
<set-header name="X-Error-Source" exists-action="override">
<value>API Management</value>
</set-header>
<set-body>@{
return new JObject(
new JProperty("error", new JObject(
new JProperty("code", context.LastError.Reason),
new JProperty("message", context.LastError.Message),
new JProperty("timestamp", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"))
))
).ToString();
}</set-body>
</on-error>
</policies>
Advanced Policy with C# Expressions
<!-- Advanced policy with conditional logic -->
<policies>
<inbound>
<!-- Dynamic backend selection -->
<choose>
<when condition="@(context.Request.Headers.GetValueOrDefault("X-Environment", "") == "staging")">
<set-backend-service base-url="https://staging-api.example.com" />
</when>
<when condition="@(context.Request.Headers.GetValueOrDefault("X-Environment", "") == "testing")">
<set-backend-service base-url="https://testing-api.example.com" />
</when>
<otherwise>
<set-backend-service base-url="https://production-api.example.com" />
</otherwise>
</choose>
<!-- Custom authentication -->
<choose>
<when condition="@(context.Request.Headers.ContainsKey("X-API-Key"))">
<validate-header header-name="X-API-Key" failed-validation-httpcode="401" failed-validation-error-message="Invalid API Key">
<value>@{
string apiKey = context.Request.Headers.GetValueOrDefault("X-API-Key", "");
// Custom validation logic
return new string[] { "valid-api-key-1", "valid-api-key-2" }.Contains(apiKey);
}</value>
</validate-header>
</when>
<otherwise>
<validate-jwt header-name="Authorization" failed-validation-httpcode="401">
<openid-config url="https://login.microsoftonline.com/common/v2.0/.well-known/openid_configuration" />
</validate-jwt>
</otherwise>
</choose>
<!-- Request logging -->
<log-to-eventhub logger-id="eventhub-logger">@{
return new JObject(
new JProperty("timestamp", DateTime.UtcNow.ToString()),
new JProperty("requestId", context.RequestId),
new JProperty("method", context.Request.Method),
new JProperty("url", context.Request.Url.ToString()),
new JProperty("headers", context.Request.Headers.Select(h => new JProperty(h.Key, string.Join(";", h.Value)))),
new JProperty("userId", context.User?.Id)
).ToString();
}</log-to-eventhub>
</inbound>
<outbound>
<!-- Response time header -->
<set-header name="X-Response-Time" exists-action="override">
<value>@(context.Elapsed.TotalMilliseconds.ToString())</value>
</set-header>
</outbound>
</policies>
PowerShell Deployment Script
# deploy-apim.ps1 - PowerShell deployment automation
param(
[Parameter(Mandatory=$true)]
[string]$ResourceGroupName,
[Parameter(Mandatory=$true)]
[string]$ApiManagementName,
[Parameter(Mandatory=$true)]
[string]$PublisherEmail,
[Parameter(Mandatory=$true)]
[string]$PublisherName,
[string]$Location = "East US",
[string]$Sku = "Standard"
)
# Connect to Azure
Connect-AzAccount
# Create resource group if it doesn't exist
$resourceGroup = Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue
if (-not $resourceGroup) {
New-AzResourceGroup -Name $ResourceGroupName -Location $Location
Write-Host "Created resource group: $ResourceGroupName"
}
# Deploy API Management
$deploymentParameters = @{
apiManagementServiceName = $ApiManagementName
publisherEmail = $PublisherEmail
publisherName = $PublisherName
sku = $Sku
}
$deployment = New-AzResourceGroupDeployment `
-ResourceGroupName $ResourceGroupName `
-TemplateFile "main.bicep" `
-TemplateParameterObject $deploymentParameters `
-Verbose
if ($deployment.ProvisioningState -eq "Succeeded") {
Write-Host "API Management service deployed successfully!" -ForegroundColor Green
Write-Host "Gateway URL: $($deployment.Outputs.gatewayUrl.Value)" -ForegroundColor Cyan
Write-Host "Management URL: $($deployment.Outputs.managementUrl.Value)" -ForegroundColor Cyan
Write-Host "Portal URL: $($deployment.Outputs.portalUrl.Value)" -ForegroundColor Cyan
} else {
Write-Error "Deployment failed with state: $($deployment.ProvisioningState)"
}
# Configure additional resources
$context = Get-AzApiManagement -ResourceGroupName $ResourceGroupName -Name $ApiManagementName
# Import API from OpenAPI specification
$api = Import-AzApiManagementApi `
-Context $context `
-SpecificationFormat "OpenApi" `
-SpecificationPath "openapi.json" `
-Path "products" `
-Protocol @("https")
Write-Host "API imported successfully: $($api.Name)" -ForegroundColor Green
# Create product
$product = New-AzApiManagementProduct `
-Context $context `
-ProductId "products-product" `
-Title "Products API" `
-Description "Product management API" `
-LegalTerms "Terms of use" `
-SubscriptionRequired $true `
-ApprovalRequired $false
# Add API to product
Add-AzApiManagementApiToProduct `
-Context $context `
-ProductId $product.ProductId `
-ApiId $api.ApiId
Write-Host "Product created and API added: $($product.Title)" -ForegroundColor Green
Monitoring and Diagnostics Configuration
// monitoring.bicep - Comprehensive monitoring setup
param apiManagementServiceName string
param logAnalyticsWorkspaceName string
resource apiManagement 'Microsoft.ApiManagement/service@2021-08-01' existing = {
name: apiManagementServiceName
}
// Log Analytics Workspace
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
name: logAnalyticsWorkspaceName
location: resourceGroup().location
properties: {
sku: {
name: 'PerGB2018'
}
retentionInDays: 30
}
}
// Diagnostic settings
resource diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
scope: apiManagement
name: 'default'
properties: {
workspaceId: logAnalyticsWorkspace.id
logs: [
{
category: 'GatewayLogs'
enabled: true
retentionPolicy: {
enabled: true
days: 30
}
}
{
category: 'WebSocketConnectionLogs'
enabled: true
retentionPolicy: {
enabled: true
days: 30
}
}
]
metrics: [
{
category: 'Gateway Requests'
enabled: true
retentionPolicy: {
enabled: true
days: 30
}
}
{
category: 'Capacity'
enabled: true
retentionPolicy: {
enabled: true
days: 30
}
}
]
}
}
// Alert rules
resource responseTimeAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = {
name: 'High Response Time'
location: 'global'
properties: {
description: 'Alert when average response time exceeds 5 seconds'
severity: 2
enabled: true
scopes: [
apiManagement.id
]
evaluationFrequency: 'PT1M'
windowSize: 'PT5M'
criteria: {
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
allOf: [
{
name: 'ResponseTime'
metricName: 'Duration'
operator: 'GreaterThan'
threshold: 5000
timeAggregation: 'Average'
}
]
}
actions: [
{
actionGroupId: resourceId('Microsoft.Insights/actionGroups', 'default-action-group')
}
]
}
}