AWS DevOps & Developer Productivity Blog

Automated CI/CD pipeline for .NET Core Lambda functions using AWS extensions for dotnet CLI

The trend of building AWS Serverless applications using AWS Lambda is increasing at an ever-rapid pace. Common use cases for AWS Lambda include data processing, real-time file processing, and extract, transform, and load (ETL) for data processing, web backends, internet of things (IoT) backends, and mobile backends. Lambda natively supports languages such as Java, Go, PowerShell, Node.js, C#, Python, and Ruby. It also provides a Runtime API that allows you to use any additional programming languages to author your functions.

.NET framework occupies a significant footprint in the technology landscape of enterprises. Nowadays, enterprise customers are modernizing .NET framework applications to .NET Core using AWS Serverless (Lambda). In this journey, you break down a large monolith service into multiple smaller independent and autonomous microservices using.NET Core Lambda functions

When you have several microservices running in production, a change management strategy is key for business agility and time-to-market changes. The change management of .NET Core Lambda functions translates to how well you implement an automated CI/CD pipeline using AWS CodePipeline. In this post, you see two approaches for implementing CI/CD for .NET Core Lambda functions: creating a pipeline with either two or three stages.

Creating a pipeline with two stages

In this approach, you define the pipeline in CodePipeline with two stages: AWS CodeCommit and AWS CodeBuild. CodeCommit is the fully-managed source control repository that stores the source code for .NET Core Lambda functions. It triggers CodeBuild when a new code change is published. CodeBuild defines a compute environment for the build process. It builds the .NET Core Lambda function and creates a deployment package (.zip). Finally, CodeBuild uses AWS extensions for Dotnet CLI to deploy the Lambda packages (.zip) to the Lambda environment. The following diagram illustrates this architecture.

 

CodePipeline with CodeBuild and CodeCommit stages.

CodePipeline with CodeBuild and CodeCommit stages.

Creating a pipeline with three stages

In this approach, you define the pipeline with three stages: CodeCommit, CodeBuild, and AWS CodeDeploy.

CodeCommit stores the source code for .NET Core Lambda functions and triggers CodeBuild when a new code change is published. CodeBuild defines a compute environment for the build process and builds the .NET Core Lambda function. Then CodeBuild invokes the CodeDeploy stage. CodeDeploy uses AWS CloudFormation templates to deploy the Lambda function to the Lambda environment. The following diagram illustrates this architecture.

CodePipeline with CodeCommit, CodeBuild and CodeDeploy stages.

CodePipeline with CodeCommit, CodeBuild and CodeDeploy stages.

Solution Overview

In this post, you learn how to implement an automated CI/CD pipeline using the first approach: CodePipeline with CodeCommit and CodeBuild stages. The CodeBuild stage in this approach implements the build and deploy functionalities. The high-level steps are as follows:

  1. Create the CodeCommit repository.
  2. Create a Lambda execution role.
  3. Create a Lambda project with .NET Core CLI.
  4. Change the Lambda project configuration.
  5. Create a buildspec file.
  6. Commit changes to the CodeCommit repository.
  7. Create your CI/CD pipeline.
  8. Complete and verify pipeline creation.

For the source code and buildspec file, see the GitHub repo.

Prerequisites

Before you get started, you need the following prerequisites:

Creating a CodeCommit repository

You first need a CodeCommit repository to store the Lambda project source code.

1. In the Repository settings section, for Repository name, enter a name for your repository.

2. Choose Create.

Name a repository

 

 

 

 

 

 

 

 

3. Initialize this repository with a markdown file (readme.md). You need this markdown file to create documentation about the repository.

4. Set up an AWS Identity and Access Management (IAM) credential to CodeCommit. Alternatively, you can set up SSH-based access. For instructions, see Setup for HTTPS users using Git credentials and Setup steps for SSH connections to AWS CodeCommit repositories on Linux, MacOS, or Unix. You need this to work with the CodeCommit repository from the development environment.

5. Clone the CodeCommit repository to a local folder.

Proceed to the next step to create an IAM role for Lambda execution.

Creating a Lambda execution role

Every Lambda function needs an IAM role for execution. Create an IAM role for Lambda execution with the appropriate IAM policy, if it doesn’t exist already. You’re now ready to create a Lambda function project using .NET Core Command Line Interface (CLI).

Creating a Lambda function project

You have multiple options for creating .NET Core Lambda function projects, such as using Visual Studio 2019, Visual Studio Code, and .NET Core CLI. In this post, you use .NET Core CLI.

By default, .NET Core CLI doesn’t support Lambda projects. You need the Amazon.Lambda.Templates nuget package to create your project.

  1. Install the nuget package Amazon.Lambda.Templates to have all the Amazon Lambda project templates in the development environment. See the following CLI Command.
    dotnet new -i Amazon.Lambda.Templates::*
  2. Verify the installation with the following CLI Command.
    dotnet new

    You should see the following output reflecting the presence of various Lambda templates in the development environment. You also need to install AWS extensions for Dotnet Lambda CLI to deploy and invoke Lambda functions from the terminal or command prompt.dotnet cli command listing lambda project templates

  3. To install the extensions, enter the following CLI Commands.
    dotnet tool install -g Amazon.Lambda.Tools
    dotnet tool update -g Amazon.Lambda.Tools
    

    You’re now ready to create a Lambda function project in the development environment.

  4. Navigate to the root of the cloned CodeCommit repository (which you created in the previous step).
  5. Create the Lambda function by entering the following CLI Command.
    dotnet new lambda.EmptyFunction --name Dotnetlambda4 --profile default --region us-east-1

    After you create your Lambda function project, you need to make some configuration changes.

Changing the Lambda function project configuration

When you create a .NET Core Lambda function project, it adds the configuration file aws-lambda-tools-defaults.json at the root of the project directory. This file holds the various configuration parameters for Lambda execution. You want to make sure that the function role is set to the IAM role you created earlier, and that the profile is set to default.

The updated aws-lambda-tools-defaults.json file should look like the following code:

{
  "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": "default",
  "region": "us-east-1",
  "configuration": "Release",
  "framework": "netcoreapp3.1",
  "function-runtime": "dotnetcore3.1",
  "function-memory-size": 256,
  "function-timeout": 30,
  "function-handler": "Dotnetlambda4::Dotnetlambda4.Function::FunctionHandler",
  "function-role": "arn:aws:iam::awsaccountnumber:role/testlambdarole"
}

After you update your project configuration, you’re ready to create the buildspec.yml file.

Creating a buildspec file

As a prerequisite to configuring the CodeCommit stage, you created a Lambda function project. For the CodeBuild stage, you need to create a buildspec file.

 

Create a buildspec.yml file with the following definition and save it at the root of the CodeCommit directory:

version: 0.2
env:
  variables:
    DOTNET_ROOT: /root/.dotnet
  secrets-manager:
    AWS_ACCESS_KEY_ID_PARAM: CodeBuild:AWS_ACCESS_KEY_ID
    AWS_SECRET_ACCESS_KEY_PARAM: CodeBuild:AWS_SECRET_ACCESS_KEY
phases:
  install:
    runtime-versions:
      dotnet: 3.1
  pre_build:
    commands:
      - echo Restore started on `date`
      - export PATH="$PATH:/root/.dotnet/tools"
      - pip install --upgrade awscli
      - aws configure set profile $Profile
      - aws configure set region $Region
      - aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID_PARAM
      - aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY_PARAM
      - cd Dotnetlambda4
      - cd src
      - cd Dotnetlambda4
      - dotnet clean 
      - dotnet restore
  build:
    commands:
      - echo Build started on `date`
      - dotnet new -i Amazon.Lambda.Templates::*
      - dotnet tool install -g Amazon.Lambda.Tools
      - dotnet tool update -g Amazon.Lambda.Tools
      - dotnet lambda deploy-function "Dotnetlambda4" --function-role "arn:aws:iam::yourawsaccount:role/youriamroleforlambda" --region "us-east-1"

You’re now ready to commit your changes to the CodeCommit repository.

Committing changes to the CodeCommit repository

To push changes to your CodeCommit repository, enter the following git commands.

git add --all
git commit –a –m “Initial Comment”
git push

After you commit the changes, you can create your CI/CD pipeline using CodePipeline.

Creating a CI/CD pipeline

To create your pipeline with a CodeCommit and CodeBuild stage, complete the following steps:

  1. In the Pipeline settings section, for Pipeline name, enter a name.
  2. For Service role, select New service role.
  3. For Role name, use the auto-generated name.
  4. Select Allow AWS CodePipeline to create a service role so it can be used with this new pipeline.
  5. Choose Next.Choose Pipeline settings
  6. In the Source section, for Source provider, choose AWS CodeCommit.
  7. For Repository name, choose your repository.
  8. For Branch name, choose your branch.
  9. For Change detection options, select Amazon CloudWatch Events.
  10. Choose Next.Populating the Source stage
  11. In the Build section, for Build provider, choose AWS CodeBuild.Populating the CodeBuild stage
  12. For Environment image, choose Managed image.
  13. For Operating system, choose Ubuntu.
  14. For Image, choose aws/codebuild/standard:4.0.
  15. For Image version, choose Always use the latest image for this runtime versionSelecting Codebuild runtime
  16. CodeBuild needs to assume an IAM service role to get the required privileges for successful build operation.Create a new service role for the CodeBuild project.Selecting the Service role
  17. Attach the following IAM policy to the role:
    
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "SecretManagerRead",
                "Effect": "Allow",
                "Action": [
                    "secretsmanager:GetRandomPassword",
                    "secretsmanager:GetResourcePolicy",
                    "secretsmanager:UntagResource",
                    "secretsmanager:GetSecretValue",
                    "secretsmanager:DescribeSecret",
                    "secretsmanager:ListSecretVersionIds",
                    "secretsmanager:ListSecrets",
                    "secretsmanager:TagResource"
                ],
                "Resource": "*"
            }
        ]
    }
    
  18. You now need to define the compute and environment variables for CodeBuild. For Compute, select your preferred compute.
  19. For Environment variables, enter two variables. For Region, enter your preferred Region. For Profile, Enter Value as default. Selecting CodeBuild env optionsThis allows the environment to use the default AWS profile in the build process.
  20. To set up an AWS profile, the CodeBuild environment needs AccessKeyId and SecretAccessKey. As a best practice, configure AccessKeyId and SecretAccessKey as secrets in AWS Secrets Manager and reference it in buildspec.yml. On the Secrets Manager console, choose Store a new secret.
  21. For Select secret type, select Other type of secrets.Selecting secret types
  22. Configure secrets AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.Configuring secrets
  23. For the encryption key, choose DefaultEncryptionKey.
  24. Choose Next.
  25. For Secret name, enter CodeBuild.Secret name
  26. Leave the rest of selections as default and choose Store.Commented code
  27. In the Add deploy stage section, choose Skip deploy stage.Add Deploy stage

Completing and verifying your pipeline

After you save your pipeline, push the code changes of the Lambda function from the local repository to the remote CodeCommit repository.

After a few seconds, you should see the activation of the CodeCommit stage and transition to CodeBuild stage. Pipeline creation can take up to a few minutes.

CodePipeline

You can verity your pipeline on the CodePipeline console. This should deploy the Lambda function changes to the Lambda environment.

Cleaning up

If you no longer need the following resources, delete them to avoid incurring further charges:

  • CodeCommit repository
  • CodePipeline project
  • CodeBuild project
  • IAM role for Lambda execution
  • Lambda function

Conclusion

In this post, you implemented an automated CI/CD for .NET Core Lambda functions using two stages of CodePipeline: CodeCommit and CodeBuild. You can apply this solution to your own use cases.

About the author

Sundararajan Narasiman works as Senior Partner Solutions Architect with Amazon Web Services.