We get asked to make this call often enough that it's worth writing down once. The honest answer is "it depends on where the rest of your infrastructure lives" — but the details of triggers, cold starts, and developer experience are where teams actually get surprised after they've already committed.

Key takeaways
  • If your identity, data, and networking are already in Azure or AWS, that gravity usually outweighs feature differences between the two functions platforms.
  • Durable Functions and Step Functions solve the same orchestration problem with different programming models — code-first vs state-machine-first.
  • Cold start behavior depends heavily on the plan (Consumption vs Premium/Flex Consumption for Azure; standard vs SnapStart/Provisioned Concurrency for Lambda) — compare like for like.
  • Local development is genuinely different day to day: Azure Functions Core Tools vs SAM/the AWS Toolkit for Visual Studio.

Triggers and bindings

Azure Functions' input/output bindings let you declare a dependency (a Cosmos DB document, a queue message, a blob) directly on the method signature, and the runtime wires it up. AWS Lambda takes a more explicit approach — you receive an event payload shaped by the trigger source and call the AWS SDK yourself to read or write other services. Bindings reduce boilerplate for simple cases; explicit SDK calls give you more control and are easier to unit test without mocking the runtime's binding infrastructure.

csharp
// Azure Functions: binding-driven
[Function("ProcessOrder")]
public async Task Run(
    [QueueTrigger("orders")] OrderMessage message,
    [CosmosDBOutput("orders", "orders")] IAsyncCollector<Order> output)
{
    await output.AddAsync(MapOrder(message));
}
csharp
// AWS Lambda: explicit SDK calls
public async Task FunctionHandler(SQSEvent sqsEvent, ILambdaContext context)
{
    foreach (var record in sqsEvent.Records)
    {
        var message = JsonSerializer.Deserialize<OrderMessage>(record.Body);
        await _dynamoDb.PutItemAsync(BuildOrderItem(message));
    }
}

Cold starts

Both platforms have a "cheap but occasionally slow" tier and a "pay more, stay warm" tier. Azure's Consumption plan has variable cold starts similar in shape to Lambda's default behavior; the Premium plan keeps instances warm for a cost. Lambda's equivalent options are Provisioned Concurrency (always warm, reserved cost) and SnapStart for .NET (snapshot-based restore, billed like a normal invocation). If cold starts matter for your workload, compare the warm-tier option on each side rather than the default free-tier behavior — that's where the real gap (or lack of one) shows up.

Durable orchestration

Durable Functions lets you write orchestration logic as ordinary-looking C# (with await calls to other functions) that the runtime checkpoints and replays for you. AWS Step Functions uses an external state machine defined in JSON (Amazon States Language) that calls Lambda functions as steps. Durable Functions feels more natural if your team wants to stay in C# for the orchestration logic itself; Step Functions gives you a visual, auditable state machine that's easier to reason about for compliance-heavy workflows, at the cost of a second language (ASL) to learn.

Local development

Azure Functions Core Tools runs the full Functions host locally, including trigger emulation, and integrates directly with Visual Studio and VS Code debugging. AWS's SAM CLI and the AWS Toolkit provide similar local invocation and step-through debugging for Lambda, plus sam local start-api for API Gateway-fronted functions. Both are solid; the difference shows up more in the surrounding tooling (Azure's tighter VS integration vs AWS's broader ecosystem of third-party tools) than in the core loop.

Side-by-side

Azure FunctionsAWS Lambda
Warm-tier cold start fixPremium / Flex Consumption planProvisioned Concurrency or SnapStart
OrchestrationDurable Functions (code-first)Step Functions (state-machine-first)
Local toolingAzure Functions Core ToolsSAM CLI / AWS Toolkit
Trigger modelDeclarative bindingsExplicit SDK calls
Billing granularityPer-execution, GB-secondsPer-execution, GB-seconds
Our honest takeThe platform choice matters less than most teams expect once you're past a proof of concept. Where you already run your database, identity provider, and networking usually decides this for you — fighting that gravity to use a “better” serverless platform rarely pays off.

Need help with this in production?

astrivo helps teams design, build, and modernize systems like this. Let's talk about your context.