All skills
microsoft avatar

/azure-cloud-migrate

@ae5e585
by microsoftmicrosoft/skills3.1k stars
351

Assess and migrate cross-cloud workloads to Azure with reports and code conversion. Supports Lambda→Functions, Beanstalk/Heroku/App Engine→App Service, Fargate/Kubernetes/Cloud Run/Spring Boot→Container Apps. WHEN: migrate Lambda to Functions, AWS to Azure, migrate Beanstalk, migrate Heroku, migrate App Engine, Cloud Run migration, Fargate to ACA, ECS/Kubernetes/GKE/EKS to Container Apps, Spring Boot to Container Apps, cross-cloud migration.

Use this Skill: https://skilld.dev/gh/microsoft/skills/azure-cloud-migrate

This session only. Nothing lands on disk.

referencesservicesfunctionsruntimesjavascript.md

≈2.2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

JavaScript (Node.js) — Azure Functions v4 Triggers & Bindings

Model: JavaScript v4 programming model. NO function.json files. Import: const { app, input, output } = require('@azure/functions');

Lambda Migration Rules

Shared rules (bindings over SDKs, latest runtime, identity-first auth) → global-rules.md

JS-specific:

  • Use extraInputs / extraOutputs with binding path expressions (e.g., {queueTrigger}) for dynamic blob I/O
  • Access metadata via context.triggerMetadata
  • package.json: "@azure/functions": "^4.0.0"

Correct Migration Pattern

const { app, input, output } = require('@azure/functions');

// Use bindings for blob I/O instead of BlobServiceClient SDK
const blobInput = input.storageBlob({
  path: 'source-container/{queueTrigger}',
  connection: 'AzureWebJobsStorage'
});

const blobOutput = output.storageBlob({
  path: 'destination-container/{queueTrigger}',
  connection: 'AzureWebJobsStorage'
});

app.storageQueue('processImage', {
  queueName: 'image-processing',
  connection: 'AzureWebJobsStorage',
  extraInputs: [blobInput],
  extraOutputs: [blobOutput],
  handler: async (queueItem, context) => {
    const sourceBlob = context.extraInputs.get(blobInput);
    context.log(`Processing blob: ${queueItem}`);
    // Process the blob...
    context.extraOutputs.set(blobOutput, processedBuffer);
  }
});

❌ Do NOT use legacy v1-v3 module.exports — always use app.*() registration.

HTTP Trigger

app.http('httpFunction', {
  methods: ['GET', 'POST'],
  authLevel: 'anonymous',
  handler: async (request, context) => {
    const name = request.query.get('name') || (await request.text());
    return { body: `Hello, ${name}!` };
  }
});

Blob Storage

// Trigger (use EventGrid source for reliability)
app.storageBlob('blobTrigger', {
  path: 'samples-workitems/{name}',
  connection: 'AzureWebJobsStorage',
  source: 'EventGrid',
  handler: async (blob, context) => {
    context.log(`Blob: ${context.triggerMetadata.name}, Size: ${blob.length}`);
  }
});

// Input binding
const blobInput = input.storageBlob({
  path: 'samples-workitems/{queueTrigger}',
  connection: 'AzureWebJobsStorage'
});

// Output binding
const blobOutput = output.storageBlob({
  path: 'samples-output/{name}-out',
  connection: 'AzureWebJobsStorage'
});

⚠️ Flex Consumption + EventGrid Source Requirements: When using source: 'EventGrid' on a Flex Consumption plan, three infrastructure requirements MUST be met or the trigger will silently fail:

  1. Always-ready instances: Configure alwaysReady: [{ name: 'blob', instanceCount: 1 }] in Bicep. Without this, the trigger group never starts and the Event Grid webhook endpoint is never registered.
  2. Queue endpoint: Set AzureWebJobsStorage__queueServiceUri in app settings. The blob extension uses queues internally for poison-message tracking with EventGrid source, even though you're not using a queue trigger.
  3. Event Grid subscription via Bicep/ARM: Do NOT create event subscriptions via CLI — webhook validation times out on Flex Consumption. Deploy as a Bicep resource using listKeys() to obtain the blobs_extension system key.

See lambda-to-functions.md for full Bicep patterns.

Using Azure AI Services with UAMI

When calling Azure AI services (Computer Vision, etc.) from a function, use DefaultAzureCredential with explicit UAMI client ID:

const { DefaultAzureCredential } = require('@azure/identity');
const createClient = require('@azure-rest/ai-vision-image-analysis').default;

const credential = new DefaultAzureCredential({
  managedIdentityClientId: process.env.AZURE_CLIENT_ID  // Required for UAMI
});
const client = createClient(process.env.COMPUTER_VISION_ENDPOINT, credential);

const result = await client.path('/imageanalysis:analyze').post({
  body: { url: blobUrl },
  queryParameters: { features: ['People'] }  // Use 'People' for face detection
});

Note: @azure-rest/ai-vision-image-analysis is still in beta. Pin explicitly: "1.0.0-beta.3" — the ^1.0.0 semver range does NOT resolve.

Queue Storage

// Trigger
app.storageQueue('queueTrigger', {
  queueName: 'myqueue-items',
  connection: 'AzureWebJobsStorage',
  handler: async (queueItem, context) => {
    context.log('Queue item:', queueItem);
  }
});

// Output
const queueOutput = output.storageQueue({
  queueName: 'outqueue',
  connection: 'AzureWebJobsStorage'
});

Timer

app.timer('timerFunction', {
  schedule: '0 */5 * * * *', // Every 5 minutes (NCRONTAB)
  handler: async (myTimer, context) => {
    context.log('Timer fired at:', myTimer.scheduleStatus.last);
  }
});

Event Grid

// Trigger
app.eventGrid('eventGridTrigger', {
  handler: async (event, context) => {
    context.log('Event:', event.subject, event.eventType);
  }
});

// Output
const eventGridOutput = output.eventGrid({
  topicEndpointUri: 'MyEventGridTopicUriSetting',
  topicKeySetting: 'MyEventGridTopicKeySetting'
});

Cosmos DB

// Trigger (Change Feed)
app.cosmosDB('cosmosDBTrigger', {
  connectionStringSetting: 'CosmosDBConnection',
  databaseName: 'mydb',
  containerName: 'mycontainer',
  createLeaseContainerIfNotExists: true,
  handler: async (documents, context) => {
    documents.forEach(doc => context.log('Changed doc:', doc.id));
  }
});

// Input
const cosmosInput = input.cosmosDB({
  connectionStringSetting: 'CosmosDBConnection',
  databaseName: 'mydb',
  containerName: 'mycontainer',
  id: '{id}',
  partitionKey: '{partitionKey}'
});

// Output
const cosmosOutput = output.cosmosDB({
  connectionStringSetting: 'CosmosDBConnection',
  databaseName: 'mydb',
  containerName: 'mycontainer'
});

Service Bus

// Queue Trigger
app.serviceBusQueue('sbQueueTrigger', {
  queueName: 'myqueue',
  connection: 'ServiceBusConnection',
  handler: async (message, context) => {
    context.log('Message:', message);
  }
});

// Topic Trigger
app.serviceBusTopic('sbTopicTrigger', {
  topicName: 'mytopic',
  subscriptionName: 'mysubscription',
  connection: 'ServiceBusConnection',
  handler: async (message, context) => {
    context.log('Topic message:', message);
  }
});

// Output
const sbOutput = output.serviceBusQueue({
  queueName: 'outqueue',
  connection: 'ServiceBusConnection'
});

Event Hubs

// Trigger
app.eventHub('eventHubTrigger', {
  eventHubName: 'myeventhub',
  connection: 'EventHubConnection',
  cardinality: 'many',
  handler: async (events, context) => {
    events.forEach(event => context.log('Event:', event));
  }
});

// Output
const ehOutput = output.eventHub({
  eventHubName: 'outeventhub',
  connection: 'EventHubConnection'
});

Table Storage

// Input
const tableInput = input.table({
  tableName: 'mytable',
  partitionKey: '{partitionKey}',
  rowKey: '{rowKey}',
  connection: 'AzureWebJobsStorage'
});

// Output
const tableOutput = output.table({
  tableName: 'mytable',
  connection: 'AzureWebJobsStorage'
});

SQL

// Trigger
app.generic('sqlTrigger', {
  trigger: { type: 'sqlTrigger', tableName: 'dbo.MyTable', connectionStringSetting: 'SqlConnection' },
  handler: async (changes, context) => {
    changes.forEach(change => context.log('Change:', change));
  }
});

// Input
const sqlInput = input.sql({
  commandText: 'SELECT * FROM dbo.MyTable WHERE Id = @Id',
  commandType: 'Text',
  parameters: '@Id={id}',
  connectionStringSetting: 'SqlConnection'
});

// Output
const sqlOutput = output.sql({
  commandText: 'dbo.MyTable',
  connectionStringSetting: 'SqlConnection'
});

SignalR

// Output
const signalROutput = output.generic({
  type: 'signalR',
  hubName: 'myhub',
  connectionStringSetting: 'AzureSignalRConnectionString'
});

SendGrid

const sendGridOutput = output.generic({
  type: 'sendGrid',
  apiKey: 'SendGridApiKey',
  from: 'noreply@example.com',
  to: '{email}'
});

Using Bindings with Functions

// Combine trigger with input/output bindings
app.http('processItem', {
  methods: ['POST'],
  extraInputs: [cosmosInput],
  extraOutputs: [queueOutput],
  handler: async (request, context) => {
    const doc = context.extraInputs.get(cosmosInput);
    context.extraOutputs.set(queueOutput, JSON.stringify(doc));
    return { body: 'Processed' };
  }
});

Full reference: Azure Functions JavaScript developer guide

Source: SKILL.md on GitHub

No alerts15d3 checks · Risk SAFE
  • Gen Agent Trust Hub15d

    This skill facilitates cloud workload migration to Azure with a focus on security best practices. It includes security considerations such as an attack surface for indirect prompt injection during source code analysis and the handling of sensitive credentials. While these warrant review, the skill provides robust patterns for secure secret management and identity-based access. See detailed analysis for context.

  • Socket15d

    No alerts

  • Snyk15d

    Risk: LOW · No issues

Signed by skilld at ae5e585. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated last month
metadata
{
  "author": "Microsoft",
  "version": "1.3.2"
}

README badge

README badge for microsoft/skills/azure-cloud-migrate