5
(1)

Recently I’ve had a challenge of moving some Azure functions to AWS Lambda, some of these functions interact with Office 365 through Graph API, and we also had a function for uploading multipart files to Azure blob storage that needed to be replaced with S3 bucket.

Setup development environment

It was the first time I wanted to develop something in AWS so first I needed to understand the similarities between Azure and AWS and try to create the resources I needed in AWS.

Stack

First thing you need to create when you login to AWS CloudFormation console is a Stack.

Stack gives you ability to manage set of resources as a single unit, just like Azure resource group, however creating a stack is a bit different, in Azure portal you just need to give your resource group a name and you’re done! but in AWS you need to either use a sample template or create one if you don’t have it.

A template is basically a JSON script you can create by yourself or create it through AWS designer tool, each template has this structure:

  • Description
  • Metadata
  • Parameters
  • Mappings
  • Conditions
  • Outputs
  • Resouces

The only required section is Resources where you define your resources, I will leave it to you if you want to learn more about this structure, but for now we will create it from Designer, login to AWS, from services select CloudFormation, you will see the list of Stacks, click on Create stack and choose Create template in designer:

From resource types, find S3 and drag Bucket and drop it to the designer:

As you can see JSON script is creating automatically for you in bottom panel, click on Create Stack, click Next and give your stack a name, click next and at the end click on Create stack button.

Congratulations! you just took the first step 🙂

IAM (Identity and Access Management)

Another problem I faced during the deployment from Visual Studio was getting an error message that the user is not authorized to perform some operations like creating a role, so we need to grant the relevant permissions for the user that deploys the functions otherwise you will get some errors while deploying your solution to AWS.

So, what I did, I created a role with the permissions I needed (in production you may assign only the minimum required permissions set).

You will need that Role ARN later when we are going to deploy our functions via AWS Explorer in Visual Studio.

Also, if your user doesn’t have an access key and secret key, you will need them, so while you are in IAM, go to Users, click on your username, and from Security credentials tab generate those keys.

This is all we need for now to be able to start developing our AWS Lambda functions.

Visual Studio Toolkit

You should know that currently only .Net Core is supported for developing your AWS solutions, and to start a new project you first need to download AWS Toolkit from here.

After installing the toolkit, open Visual Studio and create a new project, as you can see there are 4 templates added Visual Studio:

What are the differences between AWS Lambda Project and AWS Serverless Application?

AWS Lambda Project is suitable for developing individual Lambda function while AWS Serverless Application gives you more options for not only develop multiple functions at one time but using AWS CloudFormation template to define more resources such as creating a database, roles and more. you also need to consider consumption plans and prices!

What we needed for that project was AWS Lambda Project, after selecting your template you can choose one of the blueprints in the next window:

As I intended to develop multiple Lambda functions, I did select Empty Function but you can select other blueprints as you wish.

So, let’s continue and talk about the challenges!

Debugging

When you choose Visual Studio to develop your solution, one of the reasons you have in your mind is because you can easily debug your code! but after hit F5 you will see this error message:

So next step is installing Amazon Lambda TestTool, you can find it from here.

All you need to do is to open Package Manager Console and run this code.

dotnet tool install --global Amazon.Lambda.TestTool-2.1 --version 0.9.5

If you hit F5 again, you can debug your functions!

Multiple Lambda functions

As I needed to have multiple functions for different needs, I added another class to the project and expected to see it when I opened Lambda Test Tool, but it wasn’t there! there is a default configuration file in the project called aws-lambda-tools-defaults.json, if you open this file you can find all information about the default function, but what if I want more than one??

There are two ways of achieving that:

  1. Create another project for your new function (not a really good solution when you have multiple functions and want to share some of your code and also manage them all in one place).
  2. AWS Serverless Template (this is what we need!)

Serverless template it’s not just for defining multiple functions, it gives you a lot of features that I will talk about in this blog, but first let’s add it to the project.

Right click on your project from add menu select AWS Serverless Template.

Next step is to reference from ‘aws-lambda-tools-defaults.json’ to ‘serverless.template’, open ‘aws-lambda-tools-defaults.json’ file and change it to:

{
    "Information" : [
        "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.",
        "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.",
        "dotnet lambda help",
        "All the command line options for the Lambda command can be specified in this file."
    ],
    "profile"     : "RaminAhmadi",
    "region"      : "eu-west-2",
    "configuration" : "Release",
    "framework"     : "netcoreapp2.1",
    "template"      : "serverless.template"
}

Next open ‘serverless.template’ and under Resources section you can define your functions:

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Transform" : "AWS::Serverless-2016-10-31",
  "Description" : "Starting template for an AWS Serverless Application.",
  "Parameters" : {
  },
  "Resources" : {
    "UppercaseFunction" : {
      "Type" : "AWS::Serverless::Function",
      "Properties": {
        "Handler": "AWSSampleProject::AWSSampleProject.Function::FunctionHandler",
        "Runtime": "dotnetcore2.1",
        "CodeUri": "",
        "Description": "Returns input as uppercase",
        "MemorySize": 256,
        "Timeout": 30,
        "Role": null,
        "Policies": [ "AWSLambdaFullAccess" ]
      }
    },
	"LowercaseFunction" : {
      "Type" : "AWS::Serverless::Function",
      "Properties": {
        "Handler": "AWSSampleProject::AWSSampleProject.Function2::FunctionHandler",
        "Runtime": "dotnetcore2.1",
        "CodeUri": "",
        "Description": "Returns input as lowercase",
        "MemorySize": 256,
        "Timeout": 30,
        "Role": null,
        "Policies": [ "AWSLambdaFullAccess" ]
      }
    }
  },
  "Outputs" : {
    "ApiURL" : {
        "Description" : "API endpoint URL for Prod environment",
        "Value" : { "Fn::Sub" : "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" }
    }
  }
}

If you hit F5 and open Function dropdown, you will find your functions this time!

Deploy Lambda functions

Before talking about some advance topics, let’s deploy our functions to AWS, right click on the project and click Publish to AWS Lambda.

First you need to create a profile, so click Add, then enter your information such as Profile Name, Access Key ID, Secret Access Key and Account Number (your username).

Next select the Region, Configuration, Stack you created in the first step, and also S3 Bucket assigned to your Stack:

And now click Publish.

You probably get this error that the user is not authorized to perform CreateRole or other operations, these kind of errors are related to permissions, so if you remember in step 2 we created a role, so let’s add it to the serverless.template.

Open serverless.template, and for each function set the policy (role ARN you copied from step 2)

"Role": "arn:aws:iam::33333333:role/CandCLambdaRole"

Try to publish again and this time you should see complete message.

Now go to AWS console, navigate to Lambda and you can find your Lambda functions!

Environment variables

In Azure projects you simply open up local.settings.json and define your variables, also in Azure portal you will navigate to your function app, from Platform features you can select Configuration and then you are able to define your variables.

How environments variables can be defined for AWS?

Local variables

For debugging purpose, just right click on the project, click properties, the from left select Debug, the you can add your variables:

Then you can use this code to get your values:

string _accessKey = Environment.GetEnvironmentVariable("ACCESSKEY");
string _secretKey = Environment.GetEnvironmentVariable("SECRETKEY");
string _sharepointUrl = Environment.GetEnvironmentVariable("SHAREPOINTURL");

Global variables

It’s also possible to define some global variables that can be used across all of your functions when you deploy your solution, if you open serverless.template file, you can add a new section called Globals and inside that you can define your environment variables:

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Transform" : "AWS::Serverless-2016-10-31",
  "Description" : "Starting template for an AWS Serverless Application.",
  "Parameters" : {
  },
  "Globals" : {
	"Function" : {
		"Environment" : {
			"Variables" : {
					"ACCESSKEY":"My access key",
					"SECRETKEY":"My secret key",
					"SHAREPOINTURL":"raminahmadi.sharepoint.com"
				}
			}
		}
	}
}

If you publish your functions again, navigate to Lambda from AWS console, then click on any of your functions, you can see Environment variables:

Exposing the Lambda function via HTTP

In Azure it’s easy to create an HTTP Trigger function, and you can define your method, access to HTTPRequest, ILogger and so on, but how is it possible to exposing the Lambda function via HTTP?

AWS has a service called API Gateway and it’s designed to help you connect your function to a gateway and expose it via HTTP, one way is configure everything from the AWS console, but I want to show you how I’ve done it through Visual Studio.

If you open up serverless.template file, you can see a section called Outputs, here you can setup your API endpoint URL for different environment:

"Outputs": {
    "ApiURL": {
        "Description": "API endpoint URL for Prod environment",
        "Value": {
            "Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
        }
    }
}

Also each resources (Lambda functions) has a section called Events, here you can define how your function can be exposed.

"Events": {
    "uppercase": {
        "Type": "Api",
        "Properties": {
            "Path": "/api/uppercase",
            "Method": "Get"
        }
    }
}

After setting up your events and your Outputs, publish your functions again, navigate to API Gateway and you will find your endpoint:

Working with events

Now it’s time to make our functions more than some methods with one string input!

Amazon.Lambda.APIGatewayEvents is nuget package provides a couple of useful types (APIGatewayProxyRequest and APIGatewayProxyResponse) which contain information about incoming HTTP requests and allow us to construct responses that the API Gateway understands. , so let’s add this package to the project and change our code to:

public APIGatewayProxyResponse FunctionHandler(APIGatewayProxyRequest req, ILambdaContext context)
{
    var name = req.QueryStringParameters["name"];
    var body = req.Body;

    var response = name.ToUpper();
    return new APIGatewayProxyResponse
    {
        StatusCode = (int)HttpStatusCode.OK,
        Body = response,
        Headers = new Dictionary<string, string>
        {
            { "Content-Type", "application/json" },
            { "Access-Control-Allow-Origin", "*" },
            {"Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" }
        }
    };
}

As you can see APIGatewayProxyRequest gives you all you need from HTTPRequest such as QueryStringParameters, Headers or Body, and APIGatewayProxyResponse encapsulates the API Gateway response.

If you publish your code again, and test your method, you will get a result like below.

Logging

You may already noticed ILambdaContext as second parameter of your method, this interface provides a logger property of type ILambdaLogger which has to methods for logging, Log and LogLine.

You can use those two methods anywhere in your code and log messages will be written into AWS CloudWatch log trails, to enable CloudWatch logs for your API Gateway go to AWS console and navigate to API Gateway, from left select Stages and then select Prod, on the right, click on Logs/Tracing and select Enable CloudWatch Logs if it’s not already selected.

After click Save Changes, you will get this error message:

You need to create a role and assign AmazonAPIGatewayPushToCloudWatchLogs permission to that role ( AWSLambdaFullAccess already includes this permission).

Copy the ARN of the role you created, go to your API and from the left click Settings, and paste the ARN in the CloudWatch log role ARN field:

I added this line in my code to see how it works:

context.Logger.LogLine("Input has converted to the uppercase value");

To see the logs, go to AWS console and find CloudWatch service, from left navigation, select Logs, find your Lambda function and select it, here you can find your logs:

Enabling CORS

In Azure it’s simple to enable CORS, you just navigate to your app service, select CORS, enable it and add your origins:

But you will pulling your hairs out when you want to enable CORS in AWS, especially if you are exposing Lambda from API Gateway, after trying many solutions, the only worked solution for me was using the utility I found from here, so the only thing you need to do is create a text file and paste this lines:

path=/api/uppercase
allowed-origins=*
allowed-headers=Authorization,Content-Type,x-requested-with,Access-Control-Allow-Headers,Access-Control-Allow-Origin,Access-Control-Allow-Methods
allowed-methods=get,options

path=/api/lowercase
allowed-origins=mydomain.com,myhost.com
allowed-headers=Content-Type,x-requested-with,Access-Control-Allow-Headers,Access-Control-Allow-Origin,Access-Control-Allow-Methods
allowed-methods=get,options

As you can see, it’s possible to define the allowed methods, headers and also origins, in my case as I wanted to send Authorization header because I needed to use Graph API, I also included that in the headers, otherwise you’re going to get an error if you have a custom header and it’s not there.

After saving the file as .txt, you just need to run the following command:

DotNet EnableCORS -accesskey BAABAABLACKSHEEPFZ5A -secretkey MYSUPERSUPERSECRETKEY -region us-east-1 -apiid myapi5idv5 c:\\deployment\\cors.txt

To find your API Id, just navigate to the API Gateway and you can find your API Id:

If you are interested in what this utility does as I was, you can find the source code from here.

There is an important step you need to remember to do after enabling the CORS and it’s to deploy the API, so go to API Gateway, select your API, from Actions menu, select deploy API:

Select your stage and then click on Deploy.

For me enabling CORS was important as we had a client app exposing the API and it couldn’t happen without enabling CORS.

What’s next?

In next post I’m going to show you how you can upload multipart files to AWS S3 bucket and explain the challenges and limitations I faced during the implementation.

Wrap-up

In this post I tried to explain all of the challenges I had for setting up the AWS environment to be able to port our Azure functions to AWS Lambda and as you may already know, the AWS documentation is not really what you expect to cover all your questions and your needs, especially if you are a .Net developer!

Thanks for reading, have fun and happy coding!

How useful was this post?

Click on a star to rate it!

Average rating 5 / 5. Vote count: 1

No votes so far! Be the first to rate this post.