How to Use LocalStack for Seamless AWS Development in .NET

LocalStack is a fully functional local AWS cloud stack that lets you emulate AWS services directly on your machine. Let's learn how to set up and run your .NET application locally on LocalStack.

Rahul Pulikkot Nath
Rahul Pulikkot Nath

Table of Contents

Testing against live AWS services can be costly, time-consuming, and sometimes impractical when building cloud-based applications.

This is where LocalStack comes in.

LocalStack is a fully functional local AWS cloud stack that lets you emulate AWS services directly on your machine.

In this article, you’ll learn how to:

  • Set up and use LocalStack to simulate AWS services locally.
  • Build a .NET application that interacts with DynamoDB, SQS, and Lambda.
  • Configure LocalStack and integrate it into your development workflow.
  • Test and debug your application locally without deploying it to AWS.

Let’s dive in!

Thanks to AWS for sponsoring this post in my .NET on AWS Series

What is LocalStack?

LocalStack is an open-source platform that provides a local environment for simulating over 80 AWS services, such as DynamoDB, SQS, and Lambda.

It allows developers to work on applications without needing an active AWS account or incurring infrastructure costs during development.

It enables faster, cost-effective development and testing by providing lightweight, in-memory implementations of cloud services. This allows developers to develop and validate cloud-native applications locally with minimal infrastructure overhead.

Setting Up LocalStack

The fastest way to get started with LocalStack is through the LocalStack CLI, which allows you to manage and start LocalStack from your command line. Ensure you have Docker installed before proceeding, as LocalStack runs inside a Docker container.

LocalStack GUI with one local stack instance running.

In addition to the CLI, there are several other options for managing your LocalStack instance:

  • LocalStack Desktop: Provides a desktop interface for interacting with your LocalStack instance via a UI.
  • LocalStack Docker Extension: Integrates with Docker Desktop, enabling you to manage LocalStack directly from the Docker interface.
  • Docker Compose: Use Docker Compose to configure and start LocalStack with multiple services in a containerized setup.
  • Docker CLI: Start and manage LocalStack directly using the Docker CLI.
  • Helm: Deploy LocalStack within a Kubernetes cluster using Helm.

These options all run LocalStack inside a Docker container, offering flexibility in how you choose to manage your local environment.

Check out the documentation here to get set up.

Creating AWS Resources in LocalStack

LocalStack offers different ways you can create resources.

Most options that's available to create resources in AWS, also works with LocalStack.

Below, I show you how to create resources using the GUI and the CLI.

In a future post, we will explore how you can seamlessly provision resources using AWS CDK.

Creating Resources Using LocalStack GUI

The LocalStack GUI allows you to provision resources.

Let's use it to create our DynamoDB table.

Navigate to Resource Browser → DynamoDB and click 'Create' at the bottom of the UI.

Create DynamoDB Table in LocalStack using the GUI tool

This prompts you to enter the details of the table, such as name, keys, etc.

Clicking Submit creates the resource in your LocalStack instance.

Creating Resources Using LocalStack CLI

You can also create resources using the AWS CLI.

I am using the default LocalStack endpoint URL here which is localhost:4566

aws --endpoint-url=http://localhost:4566 sqs create-queue --queue-name 'weather-data'

Alternatively, you can install the LocalStack AWS CLI, which provides a wrapper over the AWS CLI.

The above command can now be simplified using the below.

awslocal sqs create-queue --queue-name 'weather-data'

This creates a SQS resource in your LocalStack instance.

Configuring .NET App to Connect To LocalStack

Now that we have a DynamoDB Table and an SQS instance on our LocalStack instance let's see how we can connect to this from our .NET application.

Based on your preferences, we can use a few options to set up our .NET application to talk to LocalStack resources.

Configuring .NET AWS Clients Individually

You can configure the .NET clients individually when registering them in the application container (or wherever you are instantiating it).

if (builder.Environment.IsDevelopment())
{
    var localStackUrl = "http://localhost:4566";
    var localStackRegion = "ap-southeast-2";
    
    builder.Services.AddSingleton<IAmazonDynamoDB>(_ => new
        AmazonDynamoDBClient(new AmazonDynamoDBConfig
        {
            ServiceURL = localStackUrl,
            AuthenticationRegion = localStackRegion
        }));
}
else
{
    builder.Services.AddAWSService<IAmazonDynamoDB>();
}

The above code injects a different instance of AmazonDynamoDBClient by overriding the ServiceURL and the AuthenticationRegion properties on the configuration object.

This ensures the app talks to the LocalStack instance of DynamoDB when running in a local development environment.

Configuring .NET AWS Client Globally using AWS Options

Configuring each of the clients individually can soon become messy and tedious.

Also, I wouldn't say I like this approach since it is a lot of conditional code based on the environment.

AWS provides an easier way to override the configurations at a global level, by overriding the AWS Options globally.

var options = builder.Configuration.GetAWSOptions();
options.DefaultClientConfig.ServiceURL = "http://localhost:4566";
options.DefaultClientConfig.AuthenticationRegion = "ap-southeast-2";
builder.Services.AddDefaultAWSOptions(options);

This is quickly done by getting the AWSOptions using the GetAWSOptions call and then updating the ServiceURL and AuthenticationRegion properties on it and registering it back into the container.

Any .NET client registered into the container will now use this default AWS Options object when creating a client instance.

Configuring AWS Options using appsettings.json file

AWS also provides a way to override the AWS Options using the appsettings.json file.

You can use the below configuration to override the ServiceURL and default region.

  "AWS": {
    "Region": "ap-southeast-2",
    "ServiceURL": "http://localhost:4566",
    "AuthenticationRegion": "ap-southeast-2"
  }

Now, for LocalStack, I will add this configuration to my appsettings.Development.json file. The configuration will only be applied when I run the app on my local development environment.

Deploying .NET Lambda Functions to LocalStack

You can deploy Lambda functions to the local stack using the GUI or the CLI.

First, we must ensure our lambda function is packaged into a deployable unit.

You can do this using the dotnet lambda CLI.

dotnet lambda package

To deploy the package Lambda Function to LocalStack we can use the aws CLI as below.

 awslocal lambda create-function `
    --function-name weatherforecast-service `
    --runtime dotnet8 `
    --zip-file "fileb://bin/Release/net8.0/WeatherForecast.Service.zip" `
    --handler "WeatherForecast::WeatherForecast.Function::FunctionHandler" `
    --role arn:aws:iam::000000000000:role/lambda-role

We must use the fileb:// format and provide the path to the Lambda artifact packaged in the previous step.

This deploys the Lambda Function to LocalStack.

Setting up SQS Trigger on LocalStack for Lambda Functions

To wire up Lambda Triggers, in this case every time a message comes into our SQS let's again use the LocalStack CLI.

Amazon SQS and AWS Lambda Triggers in .NET
Learn how to process SQS messages from AWS Lambda Function. We will learn how to set up and trigger a .NET Lambda Function using SQS and the various configurations associated.
awslocal lambda create-event-source-mapping `
    --function-name weatherforecast-service `
    --batch-size 10 `
    --event-source-arn arn:aws:sqs:ap-southeast-2:000000000000:weather-data 

Any time a new message comes to the weather-data SQS queue it will automatically trigger our Lambda Function.

You can find the complete source code for this here.

AWS