Wednesday 28 December 2022

How to deploy .net7 Core lambda using AWS CDK with docker image?

Automating AWS resource creation through AWS CDK is now much more manageable. Instead of creating long Cloudformation templates manually now we just have to write a few lines of wrapper code in our favorite programing language and we by using a few CLI commands it's possible now. Her we are demonstrating in .net C#.

Prerequisites: npm, AWS CLI, Visual Studio 2022

Steps:

Install was cdk by using npm

npm install -g aws-cdk


Create a CDK Console app Lob.Cdk by this command. 

cdk init app --language csharp


Create a minimal web API Lob.WebApi in .net7 and add a package reference 
Amazon.Lambda.AspNetCoreServer.Hosting
and inject service 
builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi);

So entire Program.cs would be 

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi);

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Dockerfile

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base


FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["Lob.WebApi.csproj", "./"]
RUN dotnet restore "Lob.WebApi.csproj"
COPY . .


FROM build AS publish
RUN dotnet publish "Lob.WebApi.csproj" -c Release -o /app/publish /p:UseAppHost=false


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


Modify CdkStak.cd in Lob.Cdk as
using Amazon.CDK;
using Amazon.CDK.AWS.APIGateway;
using Amazon.CDK.AWS.Lambda;
using Constructs;
using System.IO;
using Resource = Amazon.CDK.AWS.APIGateway.Resource;

namespace Lob.Cdk
{
    public class CdkStack : Stack
    {
        internal CdkStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
        {
            // The code that defines your stack goes here

            try
            {
                var webapiFunction = new Function(scope: this, id: "Lob-WebApi-Function-" + CdkConfig.Tag.Env, new FunctionProps
                {
                    Runtime = Runtime.FROM_IMAGE,
                    Handler =Handler.FROM_IMAGE,

                    Code = Code.FromAssetImage(directory: Path.Combine(CdkConfig.ProjectRootDirectory,"Lob.WebApi"), new AssetImageCodeProps
                    { 
                        Cmd = new[] { "LambdaAPI" }
                    }),
                });





                var webApiGateway = new LambdaRestApi(this, "Lob-WebApi-RestApi-" + CdkConfig.Tag.Env, new LambdaRestApiProps
                {
                    Handler = webapiFunction
                });

                Resource weatherForecast = webApiGateway.Root.AddResource("WeatherForecast");
                
                Resource item  = weatherForecast.AddResource("all");
                item.AddMethod("GET"); // GET /WeatherForecast/all

                
            }
            catch (System.Exception ex)
            {

                //throw;
            }
        }
    }
}


Once Compilation is finished use the following commands to bootstrap the CDK it will create a vital Cloudformation stack in AWS account, you can use additional 
--tag to add tag, --toolkit-stack-name to provide custom stack name for toolkit.

cdk bootstrap --no-bootstrap-customer-key --tags Env=DEV 

Now synth app to create cloudformation template
cdk synth

Now deploy resources in AWS account
cdk deploy

You will get success message 



Once done feel free to destroy your resources using the following command
cdk destroy

References:

https://docs.aws.amazon.com/cdk/v2/guide/cli.html

https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html


Common Error:

If you are try to achieve this in Win64 OS in administrator mode you may face cdk command not found.

Solution: 

Provide execution policy by following command

Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted


AWS CDK stack deployment into multiple accounts using dotnet

We can also plan for deploying stacks in to cross-account and for that we need to tweak program.cs

using Amazon.CDK;
using Amazon.CDK.AWS.StepFunctions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Lob.Cdk
{
    sealed class Program
    {
        public static void Main(string[] args)
        {
            CdkConfig.ProjectRootDirectory = Directory.GetParent(System.AppContext.BaseDirectory).Parent.Parent.Parent.Parent.FullName;

            Amazon.CDK.Environment makeEnv(string account, string region)
            {
                return new Amazon.CDK.Environment
                {
                    Account = account,
                    Region = region
                };
            }

            var app = new App();
            
            
            var devEnv = makeEnv(account: "1111111111", region: "us-east-1");
            var testEnv = makeEnv(account: "2222222222", region: "us-east-1");

            var stackDev = new CdkStack(app, $"LobStackTest-{CdkConfig.Tag.Env}", new StackProps{ Env = devEnv });
            Tags.Of(stackDev).Add("TagKey", CdkConfig.Tag.Application);
 

            CdkConfig.Tag.Env = "QA";
            var stackTest = new CdkStack(app, $"LobStackTest-{CdkConfig.Tag.Env}", new StackProps { Env = testEnv });
            Tags.Of(stackTest).Add("TagKey", CdkConfig.Tag.Application);

            

            app.Synth();


        }

     
    }
}


We need to configure AWS config for multiple accounts [default] for 1111111111 & [default-qa] for 2222222222  in such a way:

[default]
aws_access_key_id=<aws_access_key_id>
aws_secret_access_key=<aws_secret_access_key>
aws_session_token=<aws_session_token>
region=us-east-1



[default-qa]
aws_access_key_id=<aws_access_key_id>
aws_secret_access_key=<aws_secret_access_key>
aws_session_token=<aws_session_token>
region=us-east-1

And then to bootstrap, synth and deploy we can use the following commands:

cdk bootstrap 1111111111/us-east-1 --no-bootstrap-customer-key --tags Env=DEV 
cdk bootstrap --profile default-qa --no-bootstrap-customer-key --tags Env=QA --trust 1111111111--trust-for-lookup 1111111111--cloudformation-execution-policies 'arn:aws:iam::aws:policy/AdministratorAccess' aws://2222222222/us-east-1

cdk synth

cdk deploy LobStackTest-DEV --profile "default"
cdk deploy LobStackTest-QA --profile "default-qa"
#or all once
cdk deploy --all

cdk destroy LobStackTest-DEV --profile "default"
cdk destroy LobStackTest-QA --profile "default-qa"
#or all once
cdk destroy --all