Code Migration Phase
Migrate source platform web application code to Azure App Service.
Prerequisites
- Assessment report completed
- Best practices loaded via
mcp_azure_mcp_get_azure_bestpracticestool
Rules
- Create all output in
<source-folder>-azure/— never modify the source directory - Use latest GA runtime stack for the target language
- Prefer managed identity over connection strings or API keys
- Use App Configuration for shared settings, Key Vault for secrets
- Always configure health check endpoint
Steps
- Load Best Practices — Use
mcp_azure_mcp_get_azure_bestpracticestool for App Service guidance - Create Project Structure — Set up the project inside the output directory
- Migrate Application Code — Adapt source code for App Service runtime
- Update Dependencies — Replace platform-specific SDKs with Azure equivalents
- Configure Startup — Set startup command or create Dockerfile
- Migrate Environment Variables — Map to App Settings / App Configuration / Key Vault
- Configure Database Connections — Switch to Azure database services with managed identity
- Add Health Check — Implement
/healthzendpoint for App Service health monitoring - Set Up Logging — Integrate Application Insights SDK
Key Configuration Files
Startup Command
For non-Docker deployments, configure the startup command in App Service:
| Runtime | Default Start | Custom Start |
|---|---|---|
| Node.js | npm start | Set in Configuration → General Settings |
| Python | gunicorn app:app | gunicorn --bind=0.0.0.0 --timeout 600 app:app |
| Java | Auto-detected | -Dserver.port=80 |
| .NET | Auto-detected | dotnet myapp.dll |
Dockerfile (When Needed)
Use a Dockerfile when the app requires custom system dependencies or multi-process setups:
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 8080
CMD ["node", "server.js"]⚠️ Port: App Service injects
PORTenv var. Always bind toprocess.env.PORT || 8080.
Application Insights Integration
// Add as FIRST import in entry point
const appInsights = require('applicationinsights');
appInsights.setup(process.env.APPLICATIONINSIGHTS_CONNECTION_STRING)
.setAutoCollectRequests(true)
.setAutoCollectExceptions(true)
.start();Database Migration Patterns
| Source | Azure Target | Connection Pattern |
|---|---|---|
| RDS PostgreSQL | Azure Database for PostgreSQL Flexible Server | Managed identity + @azure/identity |
| RDS MySQL | Azure Database for MySQL Flexible Server | Managed identity + @azure/identity |
| RDS SQL Server | Azure SQL Database | Managed identity + @azure/identity |
| Heroku Postgres | Azure Database for PostgreSQL Flexible Server | Managed identity |
| Cloud SQL | Azure SQL / PostgreSQL Flexible Server | Managed identity |
| MongoDB Atlas | Azure Cosmos DB for MongoDB | Connection string → managed identity |
Managed Identity Database Connection (Node.js)
const { DefaultAzureCredential } = require('@azure/identity');
const { Client } = require('pg');
async function main() {
const credential = new DefaultAzureCredential({
managedIdentityClientId: process.env.AZURE_CLIENT_ID
});
const token = await credential.getToken('https://ossrdbms-aad.database.windows.net/.default');
const client = new Client({
host: process.env.PGHOST,
database: process.env.PGDATABASE,
user: process.env.PGUSER,
password: token.token,
ssl: { rejectUnauthorized: true },
port: 5432
});
await client.connect();
// ... use client
}
main().catch(console.error);⚠️ Wrap in
async function main()— top-levelawaitis not supported in CommonJS or many Node.js entrypoints. For ESM, top-level await works only with"type": "module"inpackage.json.
Static Assets & CDN
If the source app serves static assets, consider:
- Azure Blob Storage + CDN for static files
- Azure Front Door for global distribution
- App Service built-in static file serving for simple cases
Background Workers
| Source Pattern | Azure Equivalent |
|---|---|
| Heroku Worker dyno | WebJobs (continuous) or separate Container App |
| Beanstalk Worker tier | WebJobs or Azure Functions |
| App Engine service (worker) | WebJobs or Azure Functions |
| Cron jobs | WebJobs (triggered) or Azure Functions Timer trigger |
Handoff to azure-prepare
After code migration is complete:
- Update
migration-status.md— mark Code Migration as ✅ Complete - Invoke azure-prepare — pass the assessment report context so it can:
- Use the service mapping as requirements input
- Generate IaC (Bicep/Terraform) for the mapped Azure services
- Create
azure.yamland.azure/preparation-manifest.md - Apply security hardening
- azure-prepare will then chain to azure-validate → azure-deploy