// ============================================================================
// Azure APIM MCP Demo - Infrastructure
// ============================================================================
// This Bicep template deploys the complete infrastructure for the APIM MCP demo:
// - Azure Functions for backend APIs
// - Azure API Management as MCP Server gateway
// - Application Insights for monitoring
// - Storage Account for Functions runtime
// ============================================================================
@description('Location for all resources')
param location string = resourceGroup().location
@description('Unique prefix for resource names')
@minLength(3)
@maxLength(11)
param namePrefix string = 'apimmcp'
@description('Environment name (dev, staging, prod)')
@allowed(['dev', 'staging', 'prod'])
param environment string = 'dev'
@description('Administrator email for APIM')
param apimPublisherEmail string
@description('Organization name for APIM')
param apimPublisherName string = 'APIM MCP Demo'
@description('APIM SKU')
@allowed(['Developer', 'Basic', 'Standard', 'Premium', 'Consumption'])
param apimSku string = 'Developer'
// ============================================================================
// Variables
// ============================================================================
var uniqueSuffix = uniqueString(resourceGroup().id)
var storageAccountName = toLower('${namePrefix}${uniqueSuffix}')
var functionAppName = '${namePrefix}-func-${environment}'
var appServicePlanName = '${namePrefix}-asp-${environment}'
var appInsightsName = '${namePrefix}-insights-${environment}'
var apimName = '${namePrefix}-apim-${environment}'
var logAnalyticsName = '${namePrefix}-logs-${environment}'
// ============================================================================
// Log Analytics Workspace (for Application Insights)
// ============================================================================
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2023-09-01' = {
name: logAnalyticsName
location: location
properties: {
sku: {
name: 'PerGB2018'
}
retentionInDays: 30
}
}
// ============================================================================
// Application Insights
// ============================================================================
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: appInsightsName
location: location
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: logAnalytics.id
publicNetworkAccessForIngestion: 'Enabled'
publicNetworkAccessForQuery: 'Enabled'
}
}
// ============================================================================
// Storage Account (for Azure Functions)
// ============================================================================
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = {
name: storageAccountName
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
properties: {
supportsHttpsTrafficOnly: true
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
allowSharedKeyAccess: false // Using Managed Identity instead
publicNetworkAccess: 'Enabled'
networkAcls: {
defaultAction: 'Allow'
bypass: 'AzureServices'
}
}
}
// Storage Blob Data Owner role for Function App
resource storageBlobDataOwnerRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(storageAccount.id, functionApp.id, 'Storage Blob Data Owner')
scope: storageAccount
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')
principalId: functionApp.identity.principalId
principalType: 'ServicePrincipal'
}
}
// Storage Account Contributor role for Function App
resource storageAccountContributorRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(storageAccount.id, functionApp.id, 'Storage Account Contributor')
scope: storageAccount
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')
principalId: functionApp.identity.principalId
principalType: 'ServicePrincipal'
}
}
// Storage Queue Data Contributor role for Function App
resource storageQueueDataContributorRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(storageAccount.id, functionApp.id, 'Storage Queue Data Contributor')
scope: storageAccount
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')
principalId: functionApp.identity.principalId
principalType: 'ServicePrincipal'
}
}
// ============================================================================
// App Service Plan (Basic tier - doesn't require file share)
// ============================================================================
resource appServicePlan 'Microsoft.Web/serverfarms@2023-12-01' = {
name: appServicePlanName
location: location
sku: {
name: 'B1'
tier: 'Basic'
}
properties: {
reserved: false
}
}
// ============================================================================
// Azure Function App
// ============================================================================
resource functionApp 'Microsoft.Web/sites@2023-12-01' = {
name: functionAppName
location: location
kind: 'functionapp'
identity: {
type: 'SystemAssigned'
}
properties: {
serverFarmId: appServicePlan.id
httpsOnly: true
siteConfig: {
appSettings: [
{
name: 'AzureWebJobsStorage__accountName'
value: storageAccount.name
}
{
name: 'FUNCTIONS_EXTENSION_VERSION'
value: '~4'
}
{
name: 'FUNCTIONS_WORKER_RUNTIME'
value: 'node'
}
{
name: 'WEBSITE_NODE_DEFAULT_VERSION'
value: '~20'
}
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: appInsights.properties.InstrumentationKey
}
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: appInsights.properties.ConnectionString
}
]
ftpsState: 'Disabled'
minTlsVersion: '1.2'
nodeVersion: '~20'
cors: {
allowedOrigins: [
'https://portal.azure.com'
]
}
}
}
}
// ============================================================================
// Azure API Management
// ============================================================================
resource apim 'Microsoft.ApiManagement/service@2023-09-01-preview' = {
name: apimName
location: location
sku: {
name: apimSku
capacity: apimSku == 'Consumption' ? 0 : 1
}
properties: {
publisherEmail: apimPublisherEmail
publisherName: apimPublisherName
}
}
// ============================================================================
// APIM Logger (Application Insights)
// ============================================================================
resource apimLogger 'Microsoft.ApiManagement/service/loggers@2023-09-01-preview' = {
parent: apim
name: 'appinsights-logger'
properties: {
loggerType: 'applicationInsights'
resourceId: appInsights.id
credentials: {
instrumentationKey: appInsights.properties.InstrumentationKey
}
}
}
// ============================================================================
// APIM Named Values (for policy configuration)
// ============================================================================
resource namedValueBackendUrl 'Microsoft.ApiManagement/service/namedValues@2023-09-01-preview' = {
parent: apim
name: 'BackendFunctionUrl'
properties: {
displayName: 'BackendFunctionUrl'
value: 'https://${functionApp.properties.defaultHostName}'
secret: false
}
}
resource namedValueFunctionKey 'Microsoft.ApiManagement/service/namedValues@2023-09-01-preview' = {
parent: apim
name: 'FunctionAppKey'
properties: {
displayName: 'FunctionAppKey'
value: listKeys('${functionApp.id}/host/default', '2023-12-01').functionKeys.default
secret: true
}
}
// ============================================================================
// APIM Backend (Azure Functions)
// ============================================================================
resource apimBackend 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
parent: apim
name: 'functions-backend'
properties: {
url: 'https://${functionApp.properties.defaultHostName}/api'
protocol: 'http'
description: 'Azure Functions Backend'
credentials: {
header: {
'x-functions-key': [
'{{FunctionAppKey}}'
]
}
}
}
dependsOn: [
namedValueFunctionKey
]
}
// ============================================================================
// APIM Product (MCP Server)
// ============================================================================
resource apimProduct 'Microsoft.ApiManagement/service/products@2023-09-01-preview' = {
parent: apim
name: 'mcp-server'
properties: {
displayName: 'MCP Server'
description: 'MCP (Model Context Protocol) Server API access'
subscriptionRequired: true
approvalRequired: false
state: 'published'
}
}
// ============================================================================
// APIM API (MCP Server)
// ============================================================================
resource mcpApi 'Microsoft.ApiManagement/service/apis@2023-09-01-preview' = {
parent: apim
name: 'mcp-server-api'
properties: {
displayName: 'MCP Server API'
description: 'Azure APIM exposed as an MCP (Model Context Protocol) Server'
path: 'mcp'
protocols: [
'https'
]
subscriptionRequired: true
serviceUrl: 'https://${functionApp.properties.defaultHostName}/api'
apiType: 'http'
}
}
// ============================================================================
// APIM API Operation (MCP JSON-RPC endpoint)
// ============================================================================
resource mcpOperation 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = {
parent: mcpApi
name: 'mcp-jsonrpc'
properties: {
displayName: 'MCP JSON-RPC'
method: 'POST'
urlTemplate: '/'
description: 'Handle MCP protocol JSON-RPC 2.0 messages including initialize, tools/list, and tools/call'
request: {
description: 'MCP JSON-RPC 2.0 request'
representations: [
{
contentType: 'application/json'
examples: {
'tools/list': {
value: {
jsonrpc: '2.0'
method: 'tools/list'
id: '1'
}
}
}
}
]
}
responses: [
{
statusCode: 200
description: 'MCP JSON-RPC 2.0 response'
representations: [
{
contentType: 'application/json'
}
]
}
]
}
}
// ============================================================================
// Link API to Product
// ============================================================================
resource apiProductLink 'Microsoft.ApiManagement/service/products/apis@2023-09-01-preview' = {
parent: apimProduct
name: mcpApi.name
}
// ============================================================================
// Outputs
// ============================================================================
@description('Azure Function App URL')
output functionAppUrl string = 'https://${functionApp.properties.defaultHostName}'
@description('APIM Gateway URL')
output apimGatewayUrl string = apim.properties.gatewayUrl
@description('MCP Server Endpoint')
output mcpEndpoint string = '${apim.properties.gatewayUrl}/mcp'
@description('APIM Developer Portal URL')
output apimPortalUrl string = apim.properties.developerPortalUrl
@description('Application Insights Connection String')
output appInsightsConnectionString string = appInsights.properties.ConnectionString
@description('Resource Group Name')
output resourceGroupName string = resourceGroup().name