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
Sign in to the AWS Console
Go to IAM > Users > Add users
Choose a username (e.g.,
github-actions-deploy
)Enable Programmatic access
Attach the
AWSLambdaFullAccess
policy or a custom policy with only necessary permissionsSave the Access Key ID and Secret Access Key
Add Secrets to GitHub
In your GitHub repo, go to Settings > Secrets and variables > Actions
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 pipelineThe 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
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.