.NET Workloads on AWS Lambda

MODULE 6

Module 6: Hands-on Lab: Create and Deploy Lambda Functions

 HANDS-ON LAB

Lab Objectives

In these labs you will put into practice what you have learned in this course

You will create a variety of .NET 6/7 Lambda functions, deploy, and invoke them.

There are 3 labs in this module:

Lab 1: .NET 6 Web App running on Arm64
Lab 2: Invoking a Lambda function from a C# program on your computer
Lab 3: Invoking one Lambda function from another

Prerequisites

You have an AWS account.

You have an AWS user with the AdministratorAccess policy attached, see the section - A note on permissions, in module 3 for more details./p>

You have installed the .NET 6 SDK.

You have installed the AWS Extensions for .NET CLI (dotnet lambda ...).

You have installed the AWS Lambda for .NET Core Templates.

You have installed PowerShell. If you need to install it for Window/Mac/Linux, see https://github.com/PowerShell/PowerShell.

You can find more information on the above tooling in module 2.

You have an S3 bucket for CloudFormation stacks. If you do not, follow the below instructions.

 Time to Complete

45 minutes

Lab 1: A .NET 6 Web App running on Arm64

Step 1: Create the project

In this you will create a .NET serverless project.

1. Create serverless .NET project

dotnet new serverless.AspNetCoreWebApp -n AspNetCoreWebApp
This will create the project inside the AspNetCoreWebApp\src\AspNetCoreWebApp directory.
2. Open project
Open this project in your favorite IDE.

Step 2: Make a few changes to the code

In this step, you will modify the generated project code.
1. Update Index.cshtml.cs
Once you have the project open in your IDE, open Pages/Index.cshtml.cs in the code editor.

You can leave the using statement and namespace as is, but replace the IndexModel class with the following:
public class IndexModel : PageModel
{
    public string? Architecture { get; set; }
    public string? DotnetVersion { get; set; }
    
    public void OnGet()
    {
        Architecture = System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture.ToString();
        DotnetVersion = Environment.Version.ToString();
    }
}

2. Update Index.cshtml

Open the Pages/Index.cshtml file.

Replace the content of the file down to the </h2> with:
@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
  <h1 class="display-4">Welcome to .NET Lambda functions on AWS!</h1>
  <h2>Your application is using .NET <code>@Model.DotnetVersion</code>, <code>@Model.Architecture</code>.</h2>
</div>
Leave the <svg> element in place.

These changes will let you see what version of .NET you are running, and the type of processor you are using

Step 3: Configure Processor Architecture

In this step, you'll set the processor architecture to Arm64.
1. Change the processor architecture to Arm64
One more change.

Open the serverless.template file.

Find the "Handler" key, and the next line add:
"Architectures": ["arm64"],
Your block of code should look like the following now:
"AspNetCoreFunction": {
  "Type": "AWS::Serverless::Function",
  "Properties": {
    "Handler": "AspNetCoreWebApp::AspNetCoreWebApp.LambdaEntryPoint::FunctionHandlerAsync",
    "Architectures": ["arm64"],
2. Save and Build
Save all your changes and build the application to make sure you don’t get any compilation errors.

Step 4: Deploy the function

In this step, you'll deploy and test your Lambda function.
1. Deploy function to AWS
From the command line run:
dotnet lambda deploy-serverless --stack-name AspNetCoreWebApp --s3-bucket your-unique-bucket-name1234
Sit back and wait as AWS deploys your Lambda function.

You will see output as each step of the deployment as it is worked on, and completed.
8/9/2022 1:45 PM     AspNetCoreFunctionProxyResourcePermissionProd CREATE_COMPLETE
8/9/2022 1:45 PM     AspNetCoreWebApp                         CREATE_COMPLETE
Stack finished updating with status: CREATE_COMPLETE

Output Name                    Value
------------------------------ --------------------------------------------------
ApiURL                         https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/Prod/
2. Create a .NET WebAPI Project
At the end of the output, there will be an ApiURL, open this in your browser.

Step 5: Clean up

In this step, you'll delete the serverless project from AWS.
1. Delete the Serverless Project
To remove all resources created during the run:
dotnet lambda delete-serverless --stack-name AspNetCoreWebApp

Lab 2: Invoking a Lambda function from a C# program on your computer

Step 1: Create the Lambda function

In this step, you will create an empty Lambda project.

1. Create the project

If you are still in the directory you created for the previous lab, move out of that to a clean directory. Create a new Lambda function from the command line:

dotnet new lambda.EmptyFunction -n GetS3Buckets
This will create the project inside the AspNetCoreWebApp\src\AspNetCoreWebApp directory.

Step 2: Code changes

In this step, you will modify the generated project code.

1. Add package

Change to the GetS3Buckets\src\ GetS3Buckets folder and add the AWS SDK S3 package to the project:

cd GetS3Buckets\src\ GetS3Buckets
dotnet add package AWSSDK.S3

2. Update Function.cs

Open Function.cs in your IDE, and replace the code with:

using Amazon.Lambda.Core;
using Amazon.S3;
using Amazon.S3.Model;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace GetS3Buckets;

public class Function
{
   public async Task<IEnumerable<string>> FunctionHandler(ILambdaContext context)
    {
        var s3Client = new AmazonS3Client();

        ListBucketsRequest listBucketsRequest = new ListBucketsRequest();
        ListBucketsResponse listBucketsResponse = await s3Client.ListBucketsAsync(listBucketsRequest);

        var bucketNames = listBucketsResponse.Buckets.Select(b => b.BucketName);

        return bucketNames;
    }
}

Step 3: Deploy the function

In this step, you'll deploy and test your Lambda function.

1. Deploy function to AWS

Deploy the function to AWS using:

dotnet lambda deploy-function GetS3Buckets

When asked to select a role, choose the option to create a new one. Use the name GetS3BucketsRole, for the role name.

When asked to attach a policy, choose the option for AWSLambdaBasicExecutionRole, it is number 6 on my list.

Step 4: Add permission to ListAllMyBuckets

In this step, you'll add permissions to list your S3 buckets.

1. Create IAM policy

The policy you attached to the role does not have the required permission to list your S3 buckets.

Create a new file called S3ListAllMyBucketsPolicy.json.

Paste the following into the file -

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:ListAllMyBuckets",
            "Resource": "*"
        }
    ]
}

2. Add policy to role

Add the policy to the GetS3BucketsRole:.

aws iam put-role-policy --role-name GetS3BucketsRole --policy-name ListAllMyBuckets --policy-document file://S3ListAllMyBucketsPolicy.json

Wait a few moments for the permissions to be applied.

Step 5: Invoke Lambda function from the command line

In this step, you'll deploy and test your Lambda function.

1. Invoke function from the command line

Before creating a C# program to invoke the function, try invoking it from the command line:

dotnet lambda invoke-function --function-name GetS3Buckets

You should see output that lists all your buckets.

This verifies that the Lambda function works as expected.

Step 6: Invoke Lambda function from the command line

In this step, you'll create a C# program that invokes yur Lambda function.

1. Create a console application

Create a .NET console application using:

dotnet new console -n GetS3BucketsCallFromLocal

2. Add AWSSDK.Lambda package

Change to the GetS3BucketsCallFromLocal folder.

cd GetS3BucketsCallFromLocal

Add the AWS SDK Lambda package, this lets you invoke Lambda functions:

dotnet add package AWSSDK.Lambda

3. Update Program.cs

Open the Program.cs file and replace it with:

using System.Text.Json;
using Amazon.Lambda;
using Amazon.Lambda.Model;

AmazonLambdaClient client = new AmazonLambdaClient();

var request = new InvokeRequest
{
    FunctionName = "GetS3Buckets"
};

var result = await client.InvokeAsync(request);

var buckets = JsonSerializer.Deserialize<IEnumerable<string>>(result.Payload);

foreach (var bucket in buckets)
{
    Console.WriteLine(bucket);
}

Step 7: Try it out

In this step, you'll test the console program and invoke your Lambda function.

1. Run the console application

From the command line of the GetS3BucketsCallFromLocal directory, run:

dotnet run 

You should see a list of bucket names.

Step 8: Clean up

You are going to use this function in the next lab, so no clean up for now.

Lab 3: Invoking one Lambda function from another

Step 1: Create the GetS3Buckets function

Complete the previous lab.

Step 2: Create a Lambda function

In this step, you will create a Lambda function to call the GetS3Buckets functions.

1. Create empty Lambda project

From the command line run:

dotnet new lambda.EmptyFunction -n GetS3BucketsCallFromLambdaFunction

2. Change folder

Change to the GetS3BucketsCallFromLambdaFunction\src\GetS3BucketsCallFromLambdaFunction directory.

Step 3: Update code

In this step, you will update the project code.

1. Add AWSSDK.Lambda package

Add the AWS SDK Lambda package to the project:

dotnet add package AWSSDK.Lambda

2. Update Function.cs

Open the Function.cs file, and replace the code with:

using Amazon.Lambda;
using Amazon.Lambda.Core;
using Amazon.Lambda.Model;
using System.Text.Json;
// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace GetS3BucketsCallFromLambdaFunction;

public class Function
{
    
    public async Task<IEnumerable<string>> FunctionHandler(ILambdaContext context)
    {
        AmazonLambdaClient client = new AmazonLambdaClient();

        var request = new InvokeRequest
        {
            FunctionName = "GetS3Buckets",
        };

        var result = await client.InvokeAsync(request);

        var bucketNames = JsonSerializer.Deserialize<IEnumerable<string>>(result.Payload);

        return bucketNames;
    }
}

In this example, the GetS3BucketsCallFromLambdaFunction Lambda function is calling the GetS3Buckets function, and returning the response, making no changes to the response.

Step 4: Deploy GetS3BucketsCallFromLambdaFunction

In this step, you will deploy the GetS3BucketsCallFromLambdaFunction Lambda function to AWS.

1. Deploy the function

From the command line, run:

dotnet lambda deploy-function GetS3BucketsCallFromLambdaFunction

2. Create role

Create a new role for the function named 

GetS3BucketsCallFromLambdaFunctionRole.

3. Attach policy to role

Attach the AWSLambdaBasicExecutionRole policy to the role.

Step 5: Try to invoke GetS3BucketsCallFromLambdaFunction

In this step, you will try invoking the function.

1. Deploy the function

Try invoking GetS3BucketsCallFromLambdaFunctionRole:

dotnet lambda invoke-function --function-name GetS3BucketsCallFromLambdaFunction

You will get an error like the below:

Payload:
{
  "errorType": "AmazonLambdaException",
  "errorMessage": "User: arn:aws:sts::000000000000:assumed-role/GetS3BucketsCallFromLambdaFunctionRole/GetS3BucketsCallFromLambdaFunction 
is not authorized to perform: lambda:InvokeFunction on resource: arn:aws:lambda:us-east-1:000000000000:function:GetS3Buckets because no 
identity-based policy allows the lambda:InvokeFunction action",

This is because GetS3BucketsCallFromLambdaFunction requires permissions to invoke GetS3Buckets.

In the next step you will add an inline policy granting GetS3BucketsCallFromLambdaFunction the permission it needs

Step 6: Give GetS3BucketsCallFromLambdaFunction permission to invoke GetS3Buckets

In this step, you will give GetS3BucketsCallFromLambdaFunction permissions to invoke GetS3Buckets.

1. Get ARN of GetS3Buckets

Before you can grant invocation rights to GetS3BucketsCallFromLambdaFunction, you need to get the Amazon Resource Name (ARN) of GetS3Buckets.

You can do this in a couple of ways. First from the AWS Console. Get to the Lambda service, and select the GetS3Buckets function

You can click Copy ARN from both of the highlighted areas.

To get the ARN of GetS3Buckets from the command line, run:

dotnet lambda get-function-config GetS3Buckets

You will see output that looks like -

Project Home: https://github.com/aws/aws-extensions-for-dotnet-cli,

 https://github.com/aws/aws-lambda-dotnet

Name:                         GetS3Buckets
Arn:                          arn:aws:lambda:us-east-1:000000000000:function:GetS3Buckets
Package Type:                 Zip
Runtime:                      dotnet6
Function Handler:             GetS3Buckets::GetS3Buckets.Function::FunctionHandler
Last Modified:                2022-08-10T13:58:29.211+0000
Memory Size:                  256
Ephemeral Storage Size:       512
Role:                         arn:aws:iam::000000000000:role/GetS3Buckets
Timeout:                      30
Version:                      $LATEST
State:                        Active
Last Update Status:           Successful
KMS Key ARN:                  (default) aws/lambda

Lots of useful information, but what you want is on the second line of the table, the Arn.

Create a file named InvokeGetS3Buckets.json, add the following to it:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action":[
                "lambda:InvokeFunction"
            ],
            "Resource": "arn:aws:lambda:us-east-1:000000000000:function:GetS3Buckets"
        }
    ]
}
Repl

Replace the "Resource" with the ARN of your Lambda function that you got from the command line or from the Console UI.

2. Update permissions

Run the following:

aws iam put-role-policy --role-name GetS3BucketsCallFromLambdaFunctionRole
 --policy-name InvokeGetS3BucketsFunction 
 --policy-document file://InvokeGetS3Buckets.json

It may take a few minutes for the permissions to update on AWS.

Step 7: Invoke GetS3BucketsCallFromLambdaFunction again

In this step, you will again invoke the function.

1. Invoke the function with dotnet lambda invoke-function

This time you will be able to invoke the function:

dotnet lambda invoke-function --function-name GetS3BucketsCallFromLambdaFunction

Conclusion

In these labs you put what you have learned into practice. You first created a web application that is accessible from anywhere in the world. You then deployed a Lambda function which you invoked directly from a C# program on your computer. Then you built on the second lab by deploying another Lambda function and invoking one Lambda function from another. You also had to resolve permission issues as they arose.

From here you can experiment with using other AWS services with Lambda functions and building more complex Lambda applications.

Was this page helpful?

Skills Assessment