.NET Workloads on AWS App Runner

MODULE 3

Module 3: .NET Web API on AWS App Runner

 HANDS-ON LAB

Lab Objectives

In this hands-on lab, you’ll create a .NET Web API that uses a DynamoDB table, containerize it, and host it in AWS App Runner. You can perform this lab on a Windows PC, macOS, or Linux computer, or on a Cloud9 environment in the cloud.

You’ll begin by creating a DynamoDB table and .NET 6 Web API project that accesses it. After testing that locally, you’ll use Docker and the AWS deployment tool for .NET CLI to containerize and deploy the software to the Amazon Elastic Container Registry (ECR). Then, you’ll create the App Runner service, along with the AWS artifacts needed to talk to DynamoDB, which will include an IAM role, a VPC connector, and a VPC endpoint for DynamoDB. You’ll test your application in the cloud and verify the app can retrieve data from DynamoDB. You’ll monitor your application in App Runner console and review logs. Lastly, you’ll update your project and push an update to ECR, and see App Runner automatically deploy it. Lastly, you’ll delete your application and its resources.

This lab has 12 steps:

  1. Set up AWS
  2. Set up your Development Environment
  3. Create DynamoDB Table
  4. Create .NET Web API project
  5. Test locally
  6. Publish Web API project to ECR
  7. Create IAM Role
  8. Create App Runner service
  9. Create a VPC Endpoint for DynamoDB
  10. Test in the Cloud
  11. Deploy an Update
  12. Shut it Down

 Time to Complete

90 minutes

Implementation

Step 1: Set up AWS

In this step, you’ll set up your AWS environment.

If you’re already developing and deploying to AWS App Runner, and have the AWS Deployment Tool for .NET CLI installed, you can fast-forward to Step 3.

 1. Obtain an AWS Account

Use an existing AWS account or create an AWS account. Do not use a Production account.

 2. Choose AWS region

Sign in to the AWS console and choose an AWS region to work in that supports AWS App Runner and supports DynamoDB.

3. Create a Development Environment

If using your local machine for this lab, proceed to Step 2.

If using Cloud9, continue below.

Set up a Cloud9 environment from the AWS console:

A. In the AWS console, navigate to Cloud9 and click Create environment.

B. Name the environment AppRunnerLab.

C. Leave the defaults and click Create.

D. Wait for the environment to be created, which will take several minutes. If environment creation fails because the t2.micro instance type is not available in the region, repeat the above steps and select a different small instance type. If the instance type is not in the free tier, be mindful of the rate you will be charged for the duration of the lab.

Check Your Work

You should now have:

✓ An AWS account

✓ Know how to sign in to the AWS management console

✓ Selected a region to work in

✓ A local machine or Cloud9 environment available

Step 2: Set up your Development Environment

In this step, you will install software to set up a development environment. Skip over items you already have installed.

1. Install the AWS CLI

Install the AWS Command Line Interface (CLI).

2. Configure the AWS CLI

Configure the AWS CLI so it is linked to a user in your AWS account.

In a command/terminal window, configure the region with this command:aws configure

3. Install the .NET SDK

Install the .NET 6 SDK. Download and install the SDK for your operating system.

If using Cloud9, you can run these commands under Install Required Tools (Step 1 and Step 2). Then, run the following command: ./dotnet-install.sh -c LTS.

4. Install the AWS Development Tool for .NET CLI

In a command/terminal window, install the AWS Deployment tool for .NET CLI with this command:

dotnet tool install -g aws.deploy.tools

5. Install Docker

Install Docker Desktop. If you already have Docker, be aware that you need version 17.05 or later.

6. Install an IDE

Install an IDE, such as Microsoft Visual Studio 2022 (Windows), Visual Studio Code (Linux, macOS, Windows), or JetBrains Rider (Linux, macOS, Windows). Ensure you have installed the options or extensions for .NET web development in C#.

Check Your Work

You should now have:

✓ All prerequisite software installed

✓ Configured the AWS CLI for your AWS user and region

Step 3: Create DynamoDB Table

In this step, you'll create a DynamoDB table named Weather and create some data records.

 1. Create DynamoDB table in AWS console

In the AWS console, navigate to Amazon DynamoDB and click Create table:

    A. Table name: Weather
    B. Partition key: Location
    C. Sort key: Timestamp
    D. Click Create table

 2. Populate Table with Items

Click on the Weather table name to get to its detail page, then click Explore table items. Add these items:

A. Click Create item and JSON view. Enter the JSON below (Dallas, morning) and click Create Item.

{
  "Location": {
    "S": "Dallas"
  },
  "Timestamp": {
    "S": "2022-07-23T06:00:00"
  },
  "Summary": {
    "S": "Hot"
  },
  "TempC": {
    "N": "33"
  },
  "TempF": {
    "N": "92"
  }
}

B. In the same way, add the second item (Dallas, mid-day) with this JSON:

{
  "Location": {
    "S": "Dallas"
  },
  "Timestamp": {
    "S": "2022-07-23T12:00:00"
  },
  "Summary": {
    "S": "Scorching"
  },
  "TempC": {
    "N": "43"
  },
  "TempF": {
    "N": "109"
  }
}

C. Add the third item (Dallas, evening) with this JSON:.

{
  "Location": {
    "S": "Dallas"
  },
  "Timestamp": {
    "S": "2022-07-23T18:00:00"
  },
  "Summary": {
    "S": "Hot"
  },
  "TempC": {
    "N": "36"
  },
  "TempF": {
    "N": "97"
  }
}

D. Add the fourth item (Minneapolis, morning) with this JSON:

{
  "Location": {
    "S": "Minneapolis"
  },
  "Timestamp": {
    "S": "2022-07-23T06:00:00"
  },
  "Summary": {
    "S": "Cool"
  },
  "TempC": {
    "N": "13"
  },
  "TempF": {
    "N": "56"
  }
}

E. Add the fifth item (Minneapolis, mid-day) with this JSON:

{
  "Location": {
    "S": "Minneapolis"
  },
  "Timestamp": {
    "S": "2022-07-23T12:00:00"
  },
  "Summary": {
    "S": "Balmy"
  },
  "TempC": {
    "N": "22"
  },
  "TempF": {
    "N": "72"
  }
}

F. Add the sixth item (Minneapolis, evening) with this JSON: 

{
  "Location": {
    "S": "Minneapolis"
  },
  "Timestamp": {
    "S": "2022-07-23T18:00:00"
  },
  "Summary": {
    "S": "Balmy"
  },
  "TempC": {
    "N": "19"
  },
  "TempF": {
    "N": "67"
  }
}

Check Your Work

You should now have:

✓ A DynamoDB table named Weather, populated with 6 items.

Step 4: Create .NET Web API project

In this step, you'll use the dotnet new command to create a Web API project, and update its code to retrieve data from the DynamoDB table.

1. CD to a development folder

Open a command/terminal window and CD to a development folder.:

2. Create a .NET WebAPI Project

Run the dotnet new command below to create a new Web API project named HelloAppRunnerVpc.

dotnet new webapi -n HelloAppRunnerVpc --framework net6.0

3. Open project in your IDE

Open the HelloAppRunnerVpc project in your IDE.

If you have an AWS Toolkit installed for your IDE, set the region in AWS Explorer to the region you selected in Step 1.

4. Review the Generated Project

The generated project is a WeatherForecast API, very commonly used in .NET samples.

To try it, press F5 and test it with Swagger. You'll see the service has a /WeatherForecast action that returns mock weather data JSON.

Stop the program from running.

5. Add AWS SDK NuGet Package

In the command/terminal window, CD to the project folder. Run the dotnet add package command below to add the AWS SDK NuGet package AWSSDK.DynamoDBv2 to the project:

cd HelloAppRunnerVpc
 dotnet add package AWSSDK.DynamoDBv2

6. Modify Program.cs

Open Program.cs in the code editor, and remove or comment out this statement, which won’t be needed with App Runner:

//app.UseHttpsRedirection();

 7. Code the WeatherForecast class

OpenWeatherForecast.cs and replace it with the code below. This class holds an item retrieved from the Weather table:

namespace HelloAppRunnerVpc;

public class WeatherForecast
{
    public DateTime Date { get; set; }
    public int TemperatureC { get; set; }
    public int TemperatureF { get; set; }
    public string? Summary { get; set; }
}

 8. Code the WeatherForecastController class

Open WeatherForecastController.cs in the Controllers folder, and replace with the code below. Update RegionEndpoint.USEast1 with the region you are working in. This code implements a health check method at the root of the service, and a WeatherForecast method at /WeatherForecast that takes a location parameter and retrieves data for it from the DynamoDB Weather table. It performs a table scan to find records whose partition key matches the location. The results are output as an array of JSON records.
using Amazon;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DocumentModel;
using Microsoft.AspNetCore.Mvc;

namespace HelloAppRunnerVpc.Controllers;

[ApiController]
[Route("")]
public class WeatherForecastController : ControllerBase
{
    static readonly RegionEndpoint region = RegionEndpoint.USEast1;

    private readonly ILogger _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

    [HttpGet("")]
    public string GetHealthcheck()
    {
        return "Healthcheck: Healthy";
    }

    [HttpGet("WeatherForecast")]
    public async Task<IEnumerable<WeatherForecast>> GetWeatherForecast(string location = "Dallas")
    {
        List<WeatherForecast> forecasts = new List<WeatherForecast>();

        try
        {
            _logger.LogInformation($"00 enter GET, location = {location}");

            var client = new AmazonDynamoDBClient(region);
            Table table = Table.LoadTable(client, "Weather");

            var filter = new ScanFilter();
            filter.AddCondition("Location", ScanOperator.Equal, location);

            var scanConfig = new ScanOperationConfig()
            {
                Filter = filter,
                Select = SelectValues.SpecificAttributes,
                AttributesToGet = new List<string> { "Location", "Timestamp", "TempC", "TempF", "Summary" }
            };

            _logger.LogInformation($"10 table.Scan");

            Search search = table.Scan(scanConfig);

            List<Document> matches;
            do
            {
                _logger.LogInformation($"20 table.GetNextSetAsync");
                matches = await search.GetNextSetAsync();
                foreach (var match in matches)
                {
                    forecasts.Add(new WeatherForecast
                    {
                        Date = Convert.ToDateTime(match["Timestamp"]),
                        TemperatureC = Convert.ToInt32(match["TempC"]),
                        TemperatureF = Convert.ToInt32(match["TempF"]),
                        Summary = Convert.ToString(match["Summary"])
                    });
                }
            } while (!search.IsDone);

            _logger.LogInformation($"30 exited results loop");

        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "90 Exception");
        }

        _logger.LogInformation($"99 returning {forecasts.Count} results");

        return forecasts.ToArray();
    }
}

9. Save Changes and Build

Save your changes and ensure the project builds.

Check Your Work

You should now have:

✓ A Web API project named HelloAppRunnerVpc

✓ Have set your region in WeatherForecastController.cs

Step 5: Test locally

In this step, you'll test the Web API locally and confirm data retrieval from DynamoDB.

 1. Debug the Project

Press F5 in your IDE and wait for the app to build and launch in a browser.

 2. Test the Health Check action

In the browser, remove the Swagger path from the URL to hit the service root, and you should see a health check message. App Runner will regularly ping the site to check health.

 3. Test the Weather Forecast action for Dallas

Add /WeatherForecast?location=Dallas to the end of the URL path. You should see weather forecast data JSON appear, with values you created in the DynamoDB table in Step 1.

4. Test the Weather Forecast action for Minneapolis

Change the URL path to end in /WeatherForecast?location=Minneapolis. Now you see figures for that city.

5. Test the Weather Forecast action for an Invalid Location

Try another location name, and you see an empty response because there is no data for it in the table..

6. Stop the Program

Stop the program from running.

Although it was straightforward for our app to access DynamoDB when testing locally, that won't be the case in the cloud, because App Runner is restricted to public endpoints by default. We'll have to take the extra steps of adding a VPC connector for App Runner and a matching VPC endpoint for DynamoDB.

Check Your Work

You should now have:

✓ Tested your Web API project locally.

✓ Confirmed DynamoDB data is retrieved.

Step 6: Publish Web API project to ECR

In this step, you'll use the AWS .NET deployment tool to containerize your project and push the container image to Amazon Elastic Container Registry (ECR).

1. Create a Dockerfile

Add a text file to your project folder named Dockerfile with this code:
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["HelloAppRunnerVpc.csproj", "."]
RUN dotnet restore "./HelloAppRunnerVpc.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "HelloAppRunnerVpc.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "HelloAppRunnerVpc.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "HelloAppRunnerVpc.dll"]

2. Deploy to ECR

Deploy your project as a container image to ECR:

a. In the command/terminal window, run this command to initiate deployment, specifying your preferred region. (Figure 1)

dotnet aws deploy --region us-west-2

b. Select the choice to Push Container Images to Amazon Elastic Container Registry (ECR)

c. On the Current Settings prompt, enter the number to change the Image tag, and set it too latest. (Figure 2)

d. Confirm deployment by pressing Enter.

3. Wait for Deployment

Wait for deployment to complete.

4. Confirm deployment in AWS console

Confirm your container image has been deployed to ECR. In the AWS console, navigate to ECR. A repository named helloapprunnervpc is listed.

Check Your Work

You should now have:

✓ Containerized your project with a Dockerfile.

✓ Deployed a container image to Amazon ECR.

Step 7: Create IAM Role

In this step, you'll use the AWS console to create an IAM role named AppRunnerInstanceDynamoDB. This role will allow the App Runner EC2 instances to access the DynamoDB table.

1. Create a Policy for DynamoDB Table Access

Create a policy that allows access to the Weather DynamoDB table:    

    a. Navigate to Identity and Access Management (IAM).

    b. Select Policies from the left pane and click Create policy.

    c. Create the policy and enter the JSON below, replacing [account] with your 12-digit AWS account number, and [region] with your region.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Action": "dynamodb:*",
            "Resource": "arn:aws:dynamodb:[region]:[account]:table/Weather"
        }
    ]
}

    d. Click Next: Tags and Next: Review. Name the policy ddb-Weather and click Create policy.

2. Create a Role for EC2 Instances

Create a role for App Runner EC2 instances:

    a. Select Roles from the left pane and click Create role.

    b. For Trusted entity type, select AWS service. (Figure 1)

    c. For Use case, select EC2 and click Next. (Figure 1)

    d. Search for and select these permissions: ddb-Weather, AmazonDynamoDBFullAccess, and AWSAppRunnerFullAccess. Then click Next. (Figure 2)

    e. On the Trust relationships tab, click Edit trust policy. Replace with this JSON and click Update policy. (Figure 5)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "ec2.amazonaws.com",
                    "tasks.apprunner.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

    f. Name the role AppRunnerInstanceDynamoDB and click Create role. (Figure 3)

Check Your Work

You should now have:

✓ An IAM role named AppRunnerInstanceDynamoDB with 3 policies. 

Step 8: Create App Runner service

In this step, you'll create the App Runner service and a VPC connector.

 1. Create Service

In the AWS console, navigate to AWS App Runner and click Create service.

    a. Repository type: Container registry. (Figure 1)

    b. Provider: Amazon ECR.

    c. Click Browse and select the container you deployed to ECR in Step 6. (Figure 2)

    d. For Deployment settings trigger, select Automatic.

    e. For ECR access role, select Create new service role.

    f. Click Next.

 2. Configure service

On the Configure service page,

    a. Service name HelloAppRunnerVpc.

    b. b. Port: 80.

3. Configure Instance Role

Expand the Security section and set Instance role to AppRunnerInstanceDynamoDB.

4. Create VPC Connector

Expand the Networking section and create a VPC connector:

    a. Under Networking, select Custom VPC.

    b. Under VPC connector, click Add new.

    c. VPC connector name: AppRunnerDynamoDB. (Figure 1)

    d. VPC: select your default VPC.

    e. Subnets: select all subnets.

    f. Security groups: select your default security group. (Figure 2)

    g. Click Add. If you get an error message that one of your subnets doesn't support App Runner services, remove it from the Subnets list and click Add again. (Figure 2)

    h. Click Next and Create & deploy.

5. Wait for Deployment

Wait for deployment, which will take several minutes. This is a good time for a break.

6. Record Service URL

When service deployment is complete, you'll see a Create service succeeded message.

Record the default domain URL. This is your service URL.

Refresh the Event log, and you should see confirmation the service is running.

7. Browse to Service URL

Browse to the URL, and you should see your health check. Our service is hosted in App Runner, but it isn't yet able to access DynamoDB. We've created the App Runner VPC connector, but we still need to create a matching VPC endpoint for DynamoDB.

Check Your Work

You should now have:

✓ A running AWS App Runner service named HelloAppRunnerVpc.

✓ The service endpoint URL.

✓ Confirmed the health check URL responds in a browser.

Step 9: Create a VPC Endpoint for DynamoDB

In this step, you'll create a VPC endpoint for DynamoDB.

1. In the AWS console, navigate to VPC

2. Create endpoint

Select Endpoints from the left panel and click Create endpoint.

    a. Name: vpc-endpoint-dynamodb. (Figure 1)

    b. b. Service category:: AWS service.

    c. Service: enter DynamoDB in the search box and select com.amazonaws.region.dynamodb.

    d. VPC: select your default VPC.

    e. Route tables: select the Main route table.

    f. Click Create endpoint. (Figure 2)

Check Your Work

You should now have:

A VPC endpoint for DynamoDB named vpc-endpoint-dynamodb.

Step 10: Test in the Cloud

Now we're ready to put it all together and test the Web API in the cloud.

 1. Visit Service URL

In a browser, visit the service URL you recorded in Step 6. You see the health check response.

 2. Test Weather Forecast action for Dallas

Add /WeatherForecast?location=Dallas at the end of the path. Now you see records from Dallas that you entered in Step 1

 3. Test Weather Forecast action for Minneapolis

3. Change the end of the path to /WeatherForecast?location=Minneapolis, and you see records for that location.

Congratulations! Your Web API is hosted in AWS App Runner, and the service is talking to DynamoDB.

Check Your Work

You should now have:

✓ Confirmed your AWS App Runner service is able to retrieve data from the DynamoDB table.

Step 11: Deploy an Update

In this step, you’ll update your web API, push an updated container, and watch App Runner automatically deploy the updated container to the service. It will do this because we configured Automatic Deployments in Step 8 when we created the service.

1. Modify the service

In your IDE, make a change to the GetWeatherForecast method such that the response is slightly different. For example, you could add an exclamation point to the Summary field.

2. Build and test locally

Build and test the project locally to ensure it works as expected. 

3. Redeploy the container

Stop the program. In a command/terminal window, again run dotnet aws deploy to deploy an updated container to ECR, just as you did in Step 6. Ensure the tag is still latest.

4. Monitor service redeployment

In the AWS App Runner console, watch your service. Shortly after the new container is deployed, App Runner will automatically deploy the update.

5. Test the updated Service

Wait for the update to complete, then test your service by browsing to it as you did in Step 10. Confirm you now see the new version of your output from the service.

Check Your Work

You should now have:

✓ Updated your Web API project with a change.

✓ Deployed an updated container to ECR.

✓ Confirmed App Runner automatically deployed the update. 

Step 12: Shut it down

Feel free to experiment with changes to your project to test your knowledge.

When you're all done with the project, shut it down. You don't want to accrue charges for something you're not using.

1. Delete the App Runner service

In the AWS console, navigate to App Runner and delete the HelloAppRunnerVpc service

2. Delete the DynamoDB table

Navigate to DynamoDB and delete the Weather table

3. Delete the container image

Navigate to ECR and delete the helloapprunnervpc container image.

4. Delete the Cloud9 environment

If using Cloud9, navigate to Cloud9 and delete the AppRunnerLab environment.

Check Your Work

You should now have:

✓ Deleted the App Runner service.

✓ Deleted the DynamoDB table.

✓ Deleted the ECR container image. 

Summary

In this lab, you created a DynamoDB table and populated it with data. You generated a .NET Web application project with the dotnet new command. You coded a Web API that retrieves weather data from the DynamoDB table. You deployed a container to ECR. You created an App Runner service to host the Web API. You create an App Runner VPC connector and a VPC endpoint for DynamoDB to connect the two services could connect. You tested the application, now hosted on AWS, and saw it work. You deployed an updated container to ECR and saw it automatically deployed to the App Runner service. Lastly, you deallocated your application from AWS.

Was this page helpful?

Skills Assessment