CI/CD Pipeline for .NET Lambdas Using GitHub Actions

Serverless applications in .NET are growing in popularity thanks to AWS Lambda’s support for .NET 6, .NET 7, and now .NET 8. But building, testing, and deploying these applications manually can be time-consuming and error-prone. That’s where CI/CD pipelines come in.

In this post, we’ll walk through how to set up a CI/CD pipeline using GitHub Actions to build, test, and deploy .NET AWS Lambda functions automatically.

Prerequisites

To follow along, you’ll need:

  • A .NET Lambda project (for example, created with dotnet new lambda.EmptyFunction)

  • An AWS account with IAM credentials

  • AWS CLI installed locally (for initial setup)

  • An S3 bucket to store deployment artifacts (optional)

  • The AWS CLI and Amazon.Lambda.Tools installed:

dotnet tool install -g Amazon.Lambda.Tools

Project Structure

Let’s assume the following structure:

MyLambdaFunction/
├── src/
│   └── MyLambdaFunction/
├── tests/
│   └── MyLambdaFunction.Tests/
├── .github/
│   └── workflows/
│       └── deploy.yml

Step 1: Create a GitHub Actions Workflow

Create the file .github/workflows/deploy.yml:

name: CI/CD for .NET Lambda

on:
  push:
    branches:
      - main

jobs:
  build-test-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.x'

      - name: Restore dependencies
        run: dotnet restore

      - name: Build
        run: dotnet build --configuration Release --no-restore

      - name: Run tests
        run: dotnet test --no-build --verbosity normal

      - name: Get Lambda Role ARN from SSM
        id: ssm
        run: |
          ROLE_ARN=$(aws ssm get-parameter --name "/lambda/MyLambdaFunction/RoleArn" --query "Parameter.Value" --output text)
          echo "role_arn=$ROLE_ARN" >> $GITHUB_OUTPUT
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_REGION: us-east-1

      - name: Deploy to AWS Lambda
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_REGION: us-east-1
        run: |
          cd src/MyLambdaFunction
          dotnet lambda deploy-function MyLambdaFunction \
            --region $AWS_REGION \
            --function-role ${{ steps.ssm.outputs.role_arn }}

Step 2: Set Up AWS Credentials for GitHub Actions

To securely authenticate your GitHub Actions workflow with AWS, you need to create an IAM user and store its access keys as GitHub secrets.

Create an IAM User in AWS

  1. Sign in to the AWS Console

  2. Go to IAM > Users > Add users

  3. Choose a username (e.g., github-actions-deploy)

  4. Enable Programmatic access

  5. Attach the AWSLambdaFullAccess policy or a custom policy with only necessary permissions

  6. Save the Access Key ID and Secret Access Key

Add Secrets to GitHub

  1. In your GitHub repo, go to Settings > Secrets and variables > Actions

  2. Add the following secrets:

    • AWS_ACCESS_KEY_ID

    • AWS_SECRET_ACCESS_KEY

Step 3: Use SSM Parameters to Avoid Manual Setup

You can avoid hardcoding values like the Lambda role ARN in your workflow by storing them in AWS Systems Manager (SSM) Parameter Store.

Store Required Values in SSM

Run this command locally to store the role ARN:

aws ssm put-parameter --name "/lambda/MyLambdaFunction/RoleArn" \
  --type String \
  --value "arn:aws:iam::<account-id>:role/<lambda-execution-role>"

Your GitHub Actions workflow can then retrieve this value dynamically before deployment.

Step 4: Do You Still Need Manual Deployment?

Yes, unless you ensure everything required for the deployment is pre-configured. Here's why:

  • The dotnet lambda deploy-function command creates the Lambda function if it doesn't exist, but requires details like the function role, memory size, etc.

  • If any required input is missing or the IAM role is misconfigured, the deployment will fail.

  • GitHub Actions is non-interactive, so any CLI prompts will break the pipeline.

When Manual First-Time Deployment is Needed

You should deploy manually the first time if:

  • The Lambda function does not yet exist

  • You haven't scripted or automated the full configuration

cd src/MyLambdaFunction
dotnet lambda deploy-function MyLambdaFunction

This confirms that the function, role, region, and permissions are all set up correctly.

When You Can Skip Manual Deployment

You can skip it if:

  • The Lambda function and IAM role already exist

  • You’ve stored the function role in SSM and reference it in your workflow

  • You pass all required CLI arguments directly in the workflow

This approach makes your pipeline truly hands-off from the beginning.

Conclusion

With this setup:

  • Every push to the main branch triggers your CI/CD pipeline

  • The pipeline builds, tests, and deploys your Lambda function

  • SSM parameters allow dynamic configuration without hardcoding

  • Manual setup can be avoided if resources are pre-provisioned

This makes your Lambda deployments consistent, automated, and production-ready.

References

0
Subscribe to my newsletter

Read articles from Renato Ramos Nascimento directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Renato Ramos Nascimento
Renato Ramos Nascimento

With over 14 years in software development, I specialize in backend systems using .NET, Python, and Java. I bring full lifecycle expertise, including requirements analysis, client/server and data layer development, automated testing (unit, integration, end-to-end), and CI/CD implementations using Docker, GitLab Pipelines, GitHub Actions, Terraform, and AWS CodeStar.