Chapter 10 · Azure Cloud Migrate
Subchapter 10.23
references/services/functions/lambda-to-functions.mdMarkdown10 KBView on GitHub
Detailed guidance for migrating AWS Lambda functions to Azure Functions.
| AWS Service | Azure Equivalent |
|---|---|
| Lambda | Azure Functions |
| API Gateway | Azure Functions HTTP Trigger / API Management |
| S3 | Azure Blob Storage |
| S3 Event | Azure Blob Storage + Event Grid |
| DynamoDB | Cosmos DB |
| SQS | Azure Service Bus / Storage Queue |
| SNS | Azure Event Grid |
| EventBridge | Azure Event Grid |
| CloudWatch | Application Insights / Azure Monitor |
| IAM Roles | Managed Identity + Azure RBAC |
| CloudFormation / SAM | Bicep / ARM Templates |
| Rekognition | Azure AI Computer Vision (Image Analysis) |
| AWS Lambda | Azure Functions |
|---|---|
exports.handler | app.http(), app.storageBlob(), etc. (v4) |
event object | request / blob / trigger-specific param |
context object | context (InvocationContext) |
callback | Return value |
function.json (v1-v3) | Inline bindings in code (v4 JS, v2 Python) |
| AWS Trigger | Azure Trigger | Notes |
|---|---|---|
| API Gateway (REST/HTTP) | app.http() | Direct equivalent |
| S3 Event | app.storageBlob() | Use source: 'EventGrid' for reliability |
| SQS | app.storageQueue() or app.serviceBusQueue() | Service Bus for advanced scenarios |
| SNS | app.eventGrid() | Event Grid is push-based |
| EventBridge | app.eventGrid() | Map event patterns to filters |
| CloudWatch Events (Scheduled) | app.timer() | NCRONTAB expressions |
| DynamoDB Streams | Cosmos DB Change Feed trigger | Via app.cosmosDB() |
For language-specific migration rules, correct/incorrect patterns, and code examples, see the runtime reference for the target language:
| Runtime | Migration Patterns |
|---|---|
| JavaScript (Node.js v4) | runtimes/javascript.md — Lambda Migration Rules |
| Python (v2) | runtimes/python.md — Lambda Migration Rules |
| TypeScript (v4) | runtimes/typescript.md |
| C# (Isolated Worker) | runtimes/csharp.md |
| Java | runtimes/java.md |
| PowerShell | runtimes/powershell.md |
REQUIRED for Azure Functions:
src/
├── app.js (or function_app.py) # Main entry point
├── host.json # Function host configuration
├── local.settings.json # Local development settings
├── package.json (or requirements.txt)
├── [helper-modules] # Business logic
└── tests/ # Test files
❌ NEVER create:
├── [functionName]/ # No individual function directories
│ ├── function.json # No function.json (JS v4, Python v2)
│ └── index.js✅ Use managed identity connections for Azure Functions storage:
AzureWebJobsStorage__blobServiceUri
AzureWebJobsStorage__queueServiceUri
AzureWebJobsStorage__tableServiceUri
✅ Use specific endpoint variables for other services:
COMPUTER_VISION_ENDPOINT
STORAGE_ACCOUNT_URL
SOURCE_CONTAINER_NAME
❌ Avoid:
CONNECTION_STRING (use managed identity)
API_KEY (use managed identity)⚠️ CRITICAL: When deploying blob triggers with
source: 'EventGrid'on Flex Consumption, there are three infrastructure requirements that are NOT automatically handled and will cause silent trigger failures if missed.
On Flex Consumption, trigger groups only start when there’s work to do. But the blob extension needs to be running to create the Event Grid subscription that would deliver work — a chicken-and-egg problem.
Solution: Configure alwaysReady for the blob trigger group in the function app’s scaleAndConcurrency:
// In api.bicep — functionAppConfig section
scaleAndConcurrency: {
alwaysReady: [
{
name: 'blob'
instanceCount: 1
}
]
instanceMemoryMB: 2048
maximumInstanceCount: 100
}Without this, the trigger group never starts → Event Grid subscription never gets created → no events are delivered → function never triggers.
The blob extension internally uses Storage Queues for poison-message tracking when source: 'EventGrid' is configured. Without the queue endpoint, the function fails to index with:
Unable to find matching constructor while trying to create an instance of QueueServiceClient.
Expected: serviceUri. Found: credential, clientId, blobServiceUriSolution: Always enable the queue endpoint alongside blob when using EventGrid source:
// In identity-based storage configuration
AzureWebJobsStorage__blobServiceUri: storageAccount.properties.primaryEndpoints.blob
AzureWebJobsStorage__queueServiceUri: storageAccount.properties.primaryEndpoints.queue // REQUIRED for EventGrid source
AzureWebJobsStorage__credential: 'managedidentity'
AzureWebJobsStorage__clientId: managedIdentityClientIdAlso assign Storage Queue Data Contributor RBAC role to the UAMI.
Do NOT create Event Grid event subscriptions via CLI. The az eventgrid system-topic event-subscription create command requires a webhook validation handshake that consistently fails on Flex Consumption with “response code Unknown” (timeout during cold start).
Solution: Deploy the Event Grid system topic and event subscription as Bicep resources. ARM handles the webhook validation internally and reliably:
// eventGrid.bicep
resource systemTopic 'Microsoft.EventGrid/systemTopics@2024-06-01-preview' = {
name: 'evgt-${storageAccountName}'
location: location
properties: {
source: storageAccount.id
topicType: 'Microsoft.Storage.StorageAccounts'
}
}
resource eventSubscription 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2024-06-01-preview' = {
parent: systemTopic
name: 'blob-trigger-sub'
properties: {
destination: {
endpointType: 'WebHook'
properties: {
// ARM resolves system key and handles validation at deployment time
endpointUrl: 'https://${functionApp.properties.defaultHostName}/runtime/webhooks/blobs?functionName=${functionName}&code=${listKeys('${functionApp.id}/host/default', '2023-12-01').systemKeys.blobs_extension}'
}
}
filter: {
includedEventTypes: [ 'Microsoft.Storage.BlobCreated' ]
subjectBeginsWith: '/blobServices/default/containers/${sourceContainerName}/'
}
}
}RBAC requirement: Assign EventGrid EventSubscription Contributor role to the UAMI.
When using UAMI, DefaultAzureCredential() without arguments tries SystemAssigned first and fails. Always pass the client ID:
const { DefaultAzureCredential } = require('@azure/identity');
const credential = new DefaultAzureCredential({
managedIdentityClientId: process.env.AZURE_CLIENT_ID
});Add AZURE_CLIENT_ID as an app setting pointing to the UAMI client ID:
appSettings: {
AZURE_CLIENT_ID: managedIdentity.outputs.clientId
}AzureWebJobsStorage__credential: 'managedidentity'
AzureWebJobsStorage__clientId: managedIdentityClientId
AzureWebJobsStorage__blobServiceUri: storageAccount.properties.primaryEndpoints.blob
AzureWebJobsStorage__queueServiceUri: storageAccount.properties.primaryEndpoints.queue| Role | Scope | Purpose |
|---|---|---|
| Storage Blob Data Owner | Storage Account | Read source blobs, write destination blobs |
| Storage Queue Data Contributor | Storage Account | Poison-message queue for blob extension |
| EventGrid EventSubscription Contributor | Resource Group | Create/manage Event Grid subscriptions |
| Cognitive Services User | Cognitive Services Account | Call Computer Vision API |
| Monitoring Metrics Publisher | Application Insights | Emit telemetry |
| AWS | Azure |
|---|---|
@aws-sdk/client-rekognition | @azure-rest/ai-vision-image-analysis |
DetectFaces | Image Analysis People feature |
FaceDetails[].BoundingBox (relative 0-1) | peopleResult.values[].boundingBox (pixel coordinates) |
⚠️ Package version:
@azure-rest/ai-vision-image-analysisis still in beta. The^1.0.0semver does NOT resolve. Pin explicitly:"@azure-rest/ai-vision-image-analysis": "1.0.0-beta.3"
AWS Rekognition returns relative coordinates (0-1). Azure AI returns pixel coordinates. Convert for consistent face processing:
const sharp = require('sharp');
const metadata = await sharp(imageBuffer).metadata();
const faces = result.body.peopleResult.values.map(person => ({
BoundingBox: {
Width: person.boundingBox.width / metadata.width,
Height: person.boundingBox.height / metadata.height,
Left: person.boundingBox.x / metadata.width,
Top: person.boundingBox.y / metadata.height
}
}));// computerVision.bicep
resource computerVision 'Microsoft.CognitiveServices/accounts@2024-10-01' = {
kind: 'ComputerVision'
properties: {
disableLocalAuth: true // Enterprise policy compliance — no API keys
}
}