Why multi-cloud fluency matters
Enterprise clients arrive with existing cloud agreements, security approvals, and ops teams. When an OEM’s infrastructure standard is AWS, proposing Azure adds months of procurement friction for zero user value. The architect’s job is designing systems whose core — .NET services, containers, message-driven patterns — moves between clouds with only the managed-service bindings changing.
The mapping I use
- Compute: App Service → Elastic Beanstalk / App Runner; AKS → EKS; ECS Fargate is the sweet spot for containerized .NET APIs without cluster overhead.
- Data: Azure MySQL → RDS MySQL / Aurora; blob storage → S3 (and S3’s lifecycle policies are excellent for telemetry archiving).
- Messaging: Service Bus → SQS/SNS; for IoT ingestion, AWS IoT Core’s MQTT broker is a genuinely strong managed option.
- Identity & secrets: Managed Identity → IAM roles; Key Vault → Secrets Manager. IAM is more granular and more ways to get wrong — budget review time.
- Observability: App Insights → CloudWatch + X-Ray, typically supplemented with a third-party APM.
What transfers and what doesn’t
- Transfers cleanly: containerized .NET services, RabbitMQ/MQTT patterns, IaC discipline, network segmentation principles.
- Needs rework: identity integration, deployment pipelines, cost model assumptions — egress and NAT pricing surprise Azure-first teams.
- Never assume service parity: read the limits page before committing an architecture, not after.
Worked example: the same .NET API on ECS Fargate
The container is identical to the Azure deployment — only the infrastructure binding changes. Task definition, trimmed to what matters:
{
"family": "sessions-api",
"cpu": "512", "memory": "1024",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"taskRoleArn": "arn:aws:iam::123456789:role/sessions-api-role",
"containerDefinitions": [{
"name": "api",
"image": "123456789.dkr.ecr.ap-south-1.amazonaws.com/sessions-api:1.42.0",
"portMappings": [{ "containerPort": 8080 }],
"secrets": [{
"name": "Db__ConnectionString",
"valueFrom": "arn:aws:secretsmanager:ap-south-1:123456789:secret:db-conn"
}],
"healthCheck": { "command": ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"] }
}]
}
IAM task role ≈ managed identity, Secrets Manager ≈ Key Vault, the health check contract is unchanged — the mapping in practice.
FAQ
Yes — modern .NET runs excellently on ECS, Lambda, and App Runner, and the AWS SDK for .NET is mature. The days of .NET meaning Azure-only are long gone.
Portable core, committed edges. Abstracting every managed service behind interfaces costs more than the theoretical migration it protects against — keep business logic cloud-free and accept binding at the infrastructure layer.
Both work. Azure wins on .NET toolchain and enterprise identity; AWS wins on IoT Core maturity and global reach. Client context breaks the tie far more often than technology.