ワンクリックで
azure-api-management
Generate APIM configs with policies, products, and developer portal
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Generate APIM configs with policies, products, and developer portal
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Generate Cloudflare AI configs with Workers AI, Vectorize, and AI Gateway. Use when the user wants to run AI inference at the edge, build RAG pipelines, or proxy AI providers through Cloudflare.
Generate D1 SQLite database schemas, queries, and edge-native patterns. Use when the user wants to create, query, or manage Cloudflare D1 databases with Workers or Pages.
Generate Workers KV configs for global key-value storage at the edge. Use when the user wants to store and retrieve data globally with low-latency reads from Cloudflare's edge network.
Generate Cloudflare Pages configs with SSR, functions, and edge deployment. Use when the user wants to deploy full-stack web applications with static assets, server-side rendering, or API functions on Cloudflare Pages.
Generate Cloudflare Queues for reliable message processing at the edge. Use when the user wants to implement task queues, background processing, fan-out patterns, or event-driven architectures on Cloudflare.
Generate R2 storage configs with S3 compatibility, lifecycle, and public access. Use when the user wants to store, serve, or manage objects with zero egress fees on Cloudflare R2.
| name | azure-api-management |
| description | Generate APIM configs with policies, products, and developer portal |
| invocation | /azure-api-management [operation] |
| arguments | [{"name":"operation","description":"Operation (api, policy, product, subscription, portal)","required":false}] |
| allowed_tools | ["Read","Glob","Grep","Write","Edit","Bash"] |
You are an Azure API Management expert. Generate production-ready APIM configurations for API governance, security, and developer experience.
Determine from user input or $ARGUMENTS:
resource apim 'Microsoft.ApiManagement/service@2023-05-01-preview' = {
name: 'apim-${appName}-${environment}'
location: location
sku: {
name: 'Standard' // Consumption, Developer, Basic, Standard, Premium
capacity: 1 // Scale units (not for Consumption)
}
identity: {
type: 'SystemAssigned'
}
properties: {
publisherName: publisherName
publisherEmail: publisherEmail
virtualNetworkType: 'None' // None, External, Internal
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'
}
apiVersionConstraint: {
minApiVersion: '2021-08-01'
}
}
tags: commonTags
}
// Application Insights for APIM
resource apimLogger 'Microsoft.ApiManagement/service/loggers@2023-05-01-preview' = {
parent: apim
name: 'appinsights-logger'
properties: {
loggerType: 'applicationInsights'
credentials: {
instrumentationKey: appInsights.properties.InstrumentationKey
}
isBuffered: true
}
}
resource apimDiagnostics 'Microsoft.ApiManagement/service/diagnostics@2023-05-01-preview' = {
parent: apim
name: 'applicationinsights'
properties: {
loggerId: apimLogger.id
alwaysLog: 'allErrors'
logClientIp: true
httpCorrelationProtocol: 'W3C'
verbosity: 'information'
sampling: {
percentage: 100
samplingType: 'fixed'
}
frontend: {
request: {
headers: []
body: { bytes: 0 }
}
response: {
headers: []
body: { bytes: 0 }
}
}
backend: {
request: {
headers: []
body: { bytes: 0 }
}
response: {
headers: []
body: { bytes: 0 }
}
}
}
}
Import OpenAPI specification:
resource ordersApi 'Microsoft.ApiManagement/service/apis@2023-05-01-preview' = {
parent: apim
name: 'orders-api'
properties: {
displayName: 'Orders API'
description: 'API for managing orders'
path: 'orders'
protocols: ['https']
subscriptionRequired: true
subscriptionKeyParameterNames: {
header: 'X-API-Key'
query: 'api-key'
}
apiRevision: '1'
apiVersion: 'v1'
apiVersionSetId: apiVersionSet.id
serviceUrl: 'https://app-orders-${environment}.azurewebsites.net'
format: 'openapi+json'
value: loadTextContent('openapi/orders-api.json')
authenticationSettings: {
oAuth2AuthenticationSettings: [
{
authorizationServerId: oauthServer.name
}
]
}
}
}
// API Version Set
resource apiVersionSet 'Microsoft.ApiManagement/service/apiVersionSets@2023-05-01-preview' = {
parent: apim
name: 'orders-api-version-set'
properties: {
displayName: 'Orders API'
versioningScheme: 'Segment' // Segment (/v1/), Header, Query
versionHeaderName: 'Api-Version'
versionQueryName: 'api-version'
}
}
// Individual operations (if not importing OpenAPI)
resource getOrders 'Microsoft.ApiManagement/service/apis/operations@2023-05-01-preview' = {
parent: ordersApi
name: 'get-orders'
properties: {
displayName: 'Get Orders'
method: 'GET'
urlTemplate: '/'
description: 'Retrieve all orders'
responses: [
{
statusCode: 200
description: 'List of orders'
representations: [
{
contentType: 'application/json'
schemaId: 'orderSchema'
typeName: 'OrderList'
}
]
}
]
}
}
// Named values (configuration)
resource backendUrl 'Microsoft.ApiManagement/service/namedValues@2023-05-01-preview' = {
parent: apim
name: 'backend-url'
properties: {
displayName: 'Backend URL'
value: 'https://app-orders-${environment}.azurewebsites.net'
secret: false
}
}
// Named value from Key Vault
resource apiSecret 'Microsoft.ApiManagement/service/namedValues@2023-05-01-preview' = {
parent: apim
name: 'api-signing-key'
properties: {
displayName: 'API Signing Key'
keyVault: {
secretIdentifier: 'https://kv-${appName}.vault.azure.net/secrets/api-signing-key'
}
secret: true
}
}
Global (all APIs) policy:
<!-- Global policy applied to all APIs -->
<policies>
<inbound>
<!-- Rate limiting by subscription -->
<rate-limit-by-key
calls="100"
renewal-period="60"
counter-key="@(context.Subscription?.Key ?? context.Request.IpAddress)"
increment-condition="@(context.Response.StatusCode >= 200 && context.Response.StatusCode < 400)" />
<!-- CORS -->
<cors allow-credentials="true">
<allowed-origins>
<origin>https://portal.myapp.com</origin>
<origin>https://admin.myapp.com</origin>
</allowed-origins>
<allowed-methods preflight-result-max-age="300">
<method>GET</method>
<method>POST</method>
<method>PUT</method>
<method>DELETE</method>
<method>OPTIONS</method>
</allowed-methods>
<allowed-headers>
<header>Authorization</header>
<header>Content-Type</header>
<header>X-Request-Id</header>
</allowed-headers>
</cors>
<!-- Add request ID for tracing -->
<set-header name="X-Request-Id" exists-action="skip">
<value>@(Guid.NewGuid().ToString())</value>
</set-header>
<!-- IP filtering -->
<ip-filter action="allow">
<address-range from="10.0.0.0" to="10.255.255.255" />
<address>203.0.113.50</address>
</ip-filter>
</inbound>
<backend>
<forward-request timeout="30" />
</backend>
<outbound>
<!-- Remove sensitive headers -->
<set-header name="X-Powered-By" exists-action="delete" />
<set-header name="X-AspNet-Version" exists-action="delete" />
<set-header name="Server" exists-action="delete" />
<!-- Add security headers -->
<set-header name="X-Content-Type-Options" exists-action="override">
<value>nosniff</value>
</set-header>
<set-header name="X-Frame-Options" exists-action="override">
<value>DENY</value>
</set-header>
<set-header name="Content-Security-Policy" exists-action="override">
<value>default-src 'none'</value>
</set-header>
</outbound>
<on-error>
<!-- Custom error response -->
<set-status code="500" reason="Internal Server Error" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>@{
return new JObject(
new JProperty("error", new JObject(
new JProperty("code", context.Response.StatusCode),
new JProperty("message", "An error occurred processing your request."),
new JProperty("requestId", context.RequestId.ToString())
))
).ToString();
}</set-body>
</on-error>
</policies>
JWT validation policy:
<policies>
<inbound>
<!-- Validate JWT from Entra ID -->
<validate-jwt
header-name="Authorization"
failed-validation-httpcode="401"
failed-validation-error-message="Unauthorized"
require-expiration-time="true"
require-scheme="Bearer"
require-signed-tokens="true">
<openid-config url="https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration" />
<required-claims>
<claim name="aud" match="any">
<value>api://my-api-client-id</value>
</claim>
<claim name="roles" match="any">
<value>Orders.Read</value>
<value>Orders.ReadWrite</value>
</claim>
</required-claims>
</validate-jwt>
<!-- Extract claims for backend -->
<set-header name="X-User-Id" exists-action="override">
<value>@(context.Request.Headers.GetValueOrDefault("Authorization","").AsJwt()?.Claims.GetValueOrDefault("oid", "unknown"))</value>
</set-header>
<set-header name="X-User-Email" exists-action="override">
<value>@(context.Request.Headers.GetValueOrDefault("Authorization","").AsJwt()?.Claims.GetValueOrDefault("email", "unknown"))</value>
</set-header>
</inbound>
</policies>
Caching policy:
<policies>
<inbound>
<!-- Cache lookup -->
<cache-lookup vary-by-developer="false"
vary-by-developer-groups="false"
caching-type="prefer-external"
downstream-caching-type="none">
<vary-by-query-parameter>page</vary-by-query-parameter>
<vary-by-query-parameter>pageSize</vary-by-query-parameter>
<vary-by-header>Accept</vary-by-header>
</cache-lookup>
</inbound>
<outbound>
<!-- Cache store (only successful responses) -->
<cache-store duration="300" cache-response="@(context.Response.StatusCode == 200)" />
</outbound>
</policies>
Request/response transformation policy:
<policies>
<inbound>
<!-- Transform request -->
<set-body>@{
var body = context.Request.Body.As<JObject>(preserveContent: true);
body["timestamp"] = DateTime.UtcNow.ToString("o");
body["source"] = "apim";
return body.ToString();
}</set-body>
<!-- Rewrite URL -->
<rewrite-uri template="/api/v2/orders/{orderId}" copy-unmatched-params="true" />
<!-- Set backend dynamically -->
<set-backend-service base-url="@{
var region = context.Request.Headers.GetValueOrDefault("X-Region", "eastus");
return region == "westus"
? "https://app-orders-westus.azurewebsites.net"
: "https://app-orders-eastus.azurewebsites.net";
}" />
</inbound>
<outbound>
<!-- Transform response -->
<set-body>@{
var response = context.Response.Body.As<JObject>();
var wrapper = new JObject(
new JProperty("data", response),
new JProperty("meta", new JObject(
new JProperty("requestId", context.RequestId.ToString()),
new JProperty("apiVersion", "v1"),
new JProperty("timestamp", DateTime.UtcNow.ToString("o"))
))
);
return wrapper.ToString();
}</set-body>
</outbound>
</policies>
Circuit breaker and retry policy:
<policies>
<backend>
<!-- Retry with exponential backoff -->
<retry condition="@(context.Response.StatusCode >= 500)"
count="3"
interval="1"
max-interval="30"
delta="2"
first-fast-retry="true">
<forward-request timeout="10" buffer-request-body="true" />
</retry>
</backend>
</policies>
Backend with load balancing:
resource backend 'Microsoft.ApiManagement/service/backends@2023-05-01-preview' = {
parent: apim
name: 'orders-backend-pool'
properties: {
type: 'Pool'
description: 'Load-balanced backend pool'
pool: {
services: [
{
id: '/backends/orders-eastus'
priority: 1
weight: 3
}
{
id: '/backends/orders-westus'
priority: 1
weight: 1
}
{
id: '/backends/orders-fallback'
priority: 2
weight: 1
}
]
}
circuitBreaker: {
rules: [
{
failureCondition: {
count: 5
errorReasons: ['Server errors']
interval: 'PT1M'
statusCodeRanges: [
{ min: 500, max: 599 }
]
}
name: 'circuit-breaker-rule'
tripDuration: 'PT1M'
acceptRetryAfter: true
}
]
}
}
}
resource backendEastus 'Microsoft.ApiManagement/service/backends@2023-05-01-preview' = {
parent: apim
name: 'orders-eastus'
properties: {
url: 'https://app-orders-eastus.azurewebsites.net'
protocol: 'http'
tls: {
validateCertificateChain: true
validateCertificateName: true
}
credentials: {
header: {}
query: {}
}
}
}
// Product definition
resource product 'Microsoft.ApiManagement/service/products@2023-05-01-preview' = {
parent: apim
name: 'standard-api'
properties: {
displayName: 'Standard API Access'
description: 'Standard tier with rate limiting'
subscriptionRequired: true
approvalRequired: true
subscriptionsLimit: 5
state: 'published'
terms: 'By subscribing, you agree to the Terms of Service.'
}
}
// Associate API with product
resource productApi 'Microsoft.ApiManagement/service/products/apis@2023-05-01-preview' = {
parent: product
name: ordersApi.name
}
// Product policy (rate limiting per product)
resource productPolicy 'Microsoft.ApiManagement/service/products/policies@2023-05-01-preview' = {
parent: product
name: 'policy'
properties: {
value: '''
<policies>
<inbound>
<rate-limit calls="1000" renewal-period="3600" />
<quota calls="10000" renewal-period="86400" />
</inbound>
</policies>
'''
format: 'xml'
}
}
// Premium product with higher limits
resource premiumProduct 'Microsoft.ApiManagement/service/products@2023-05-01-preview' = {
parent: apim
name: 'premium-api'
properties: {
displayName: 'Premium API Access'
description: 'Premium tier with higher rate limits'
subscriptionRequired: true
approvalRequired: false
state: 'published'
}
}
resource premiumProductPolicy 'Microsoft.ApiManagement/service/products/policies@2023-05-01-preview' = {
parent: premiumProduct
name: 'policy'
properties: {
value: '''
<policies>
<inbound>
<rate-limit calls="10000" renewal-period="3600" />
<quota calls="100000" renewal-period="86400" />
</inbound>
</policies>
'''
format: 'xml'
}
}
// Subscription
resource subscription 'Microsoft.ApiManagement/service/subscriptions@2023-05-01-preview' = {
parent: apim
name: 'partner-subscription'
properties: {
displayName: 'Partner Access'
scope: '/products/${product.name}'
state: 'active'
allowTracing: false
}
}
resource "azurerm_api_management" "main" {
name = "apim-${var.app_name}-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
publisher_name = var.publisher_name
publisher_email = var.publisher_email
sku_name = "Standard_1" # Consumption, Developer_1, Basic_1, Standard_1, Premium_1
identity {
type = "SystemAssigned"
}
protocols {
enable_http2 = true
}
security {
enable_backend_ssl30 = false
enable_backend_tls10 = false
enable_backend_tls11 = false
enable_frontend_ssl30 = false
enable_frontend_tls10 = false
enable_frontend_tls11 = false
}
sign_in {
enabled = true
}
tags = var.common_tags
}
resource "azurerm_api_management_api" "orders" {
name = "orders-api"
resource_group_name = azurerm_resource_group.main.name
api_management_name = azurerm_api_management.main.name
revision = "1"
display_name = "Orders API"
path = "orders"
protocols = ["https"]
subscription_required = true
service_url = "https://app-orders-${var.environment}.azurewebsites.net"
subscription_key_parameter_names {
header = "X-API-Key"
query = "api-key"
}
import {
content_format = "openapi+json"
content_value = file("${path.module}/openapi/orders-api.json")
}
}
resource "azurerm_api_management_api_policy" "orders" {
api_name = azurerm_api_management_api.orders.name
api_management_name = azurerm_api_management.main.name
resource_group_name = azurerm_resource_group.main.name
xml_content = <<XML
<policies>
<inbound>
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" require-scheme="Bearer">
<openid-config url="https://login.microsoftonline.com/${var.tenant_id}/v2.0/.well-known/openid-configuration" />
<required-claims>
<claim name="aud"><value>${var.api_client_id}</value></claim>
</required-claims>
</validate-jwt>
<rate-limit-by-key calls="100" renewal-period="60"
counter-key="@(context.Request.Headers.GetValueOrDefault(\"Authorization\",\"\").AsJwt()?.Claims.GetValueOrDefault(\"oid\",\"anonymous\"))" />
</inbound>
<backend>
<forward-request timeout="30" />
</backend>
<outbound>
<set-header name="X-Powered-By" exists-action="delete" />
<set-header name="Server" exists-action="delete" />
</outbound>
<on-error />
</policies>
XML
}
resource "azurerm_api_management_product" "standard" {
product_id = "standard-api"
api_management_name = azurerm_api_management.main.name
resource_group_name = azurerm_resource_group.main.name
display_name = "Standard API Access"
description = "Standard tier with rate limiting"
subscription_required = true
approval_required = true
subscriptions_limit = 5
published = true
}
resource "azurerm_api_management_product_api" "standard_orders" {
api_name = azurerm_api_management_api.orders.name
product_id = azurerm_api_management_product.standard.product_id
api_management_name = azurerm_api_management.main.name
resource_group_name = azurerm_resource_group.main.name
}
# Custom domain
resource "azurerm_api_management_custom_domain" "main" {
api_management_id = azurerm_api_management.main.id
gateway {
host_name = "api.${var.domain}"
key_vault_id = azurerm_key_vault_certificate.api.secret_id
}
developer_portal {
host_name = "developer.${var.domain}"
key_vault_id = azurerm_key_vault_certificate.portal.secret_id
}
}
# Self-hosted gateway
resource "azurerm_api_management_gateway" "on_prem" {
name = "on-premises-gateway"
api_management_id = azurerm_api_management.main.id
description = "Self-hosted gateway for on-premises APIs"
location_data {
name = "On-Premises DC"
city = "New York"
region = "United States"
}
}
// Internal VNET integration (Premium tier)
resource apimInternal 'Microsoft.ApiManagement/service@2023-05-01-preview' = {
name: 'apim-${appName}-${environment}'
location: location
sku: {
name: 'Premium'
capacity: 1
}
properties: {
publisherName: publisherName
publisherEmail: publisherEmail
virtualNetworkType: 'Internal' // Gateway accessible only via VNET
virtualNetworkConfiguration: {
subnetResourceId: apimSubnet.id
}
}
}
// Private DNS zone for internal APIM
resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: 'azure-api.net'
location: 'global'
}
resource privateDnsLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
parent: privateDnsZone
name: 'apim-vnet-link'
location: 'global'
properties: {
virtualNetwork: {
id: vnet.id
}
registrationEnabled: false
}
}
| Feature | Consumption | Developer | Basic | Standard | Premium |
|---|---|---|---|---|---|
| SLA | 99.95% | None | 99.95% | 99.95% | 99.99% |
| Cache | External | 10MB | 50MB | 1GB | 5GB |
| VNET | No | Yes | No | No | Yes |
| Multi-region | No | No | No | No | Yes |
| Self-hosted GW | No | Yes | No | No | Yes |
| Dev portal | No | Yes | Yes | Yes | Yes |
| Scale units | Auto | 1 | 2 | 4 | 12/region |
| Price model | Per call | Fixed | Fixed | Fixed | Fixed |