.NET on AWS Blog

Deploy to Elastic Beanstalk with GitHub Actions

AWS Elastic Beanstalk makes deploying, managing, and scaling .NET web applications on Amazon Web Services easy. This powerful service is available to anyone developing or hosting .NET apps on IIS.

In this post, I’ll demonstrate how to set up a continuous deployment pipeline to Elastic Beanstalk directly from GitHub using GitHub Actions. With just a few configuration steps, you can automatically build, test, and deploy your .NET code to Elastic Beanstalk environments whenever you push changes to your repository.

Overview of solution

For this demonstration, we will use the ASP.NET Core MVC Web Application template in Visual Studio to create a sample application. The source code will be hosted in a GitHub repository, which will trigger GitHub Actions to handle the continuous integration and delivery (CI/CD) pipeline.

GitHub Actions will build, and package the .NET application on every push to the repo. The artifacts will be staged in an Amazon Simple Storage Service (Amazon S3) bucket to deploy to Elastic Beanstalk. Using Elastic Beanstalk will provide a managed environment to run the ASP.NET application and auto-scale based on demand.

This end-to-end solution utilizes Amazon S3 and Elastic Beanstalk, that integrate nicely with GitHub for source control and Actions for CI/CD. The default MVC application gives us the necessary code to showcase setting up the pipeline stages. We will configure the GitHub workflows, AWS access, and Elastic Beanstalk settings to enable automatic deployments on each code change.

Figure 1: Solution overview

Figure 1: Solution overview

Walkthrough

To perform this walkthrough, you’ll need the following:

  1. An AWS account.
  2. A GitHub account.
  3. Git installed on your development machine.
  4. Visual Studio installed on your development machine.
  5. General familiarity with GitHub.
  6. General familiarity with AWS and the IAM service.

Walkthrough steps:

  1. Create a new GitHub repository to store the code
  2. Create an Elastic Beanstalk Application & Environment for deploying .NET Code
  3. Publish and push the ASP.NET MVC application code to the GitHub repository
  4. Configure a GitHub workflow that defines the CI/CD pipeline stages
  5. Continuous Deployment to Elastic Beanstalk Environment

Prerequisites

For this walkthrough, you should have the following prerequisites:

  • An AWS account – with access to create IAM User
  • Visual Studio 2022
  • GitHub Account

Step 1: Create a new GitHub repository to store the code

To get started, we need to create a new GitHub repository that will host our .NET code. This will be the central source control repository that triggers our CI/CD pipeline.

Log into your GitHub account and click the “+” icon in the top right and select “New repository”. Give the repository a name like “dotnet-mvc-app”. You can initialize it with a README file and gitignore for Visual Studio.

Make sure to create the repo in your personal account or an organization you have access to. We’ll need to connect GitHub Actions workflows and deploy keys later which require admin permissions.

Figure 2: Create repository

Figure 2: Create repository

Step 2: Create an Elastic Beanstalk Application & Environment

In the Elastic Beanstalk console, choose Create New Environment to set up an environment for deploying our .NET code.

Under Environment Tier, select Web server environment to host a web application.

Figure 3: Configure environment

Figure 3: Configure environment

Name the Application something like “dotnet-mvc-app-host” and tag the app as dotnet-mvc.

Figure 4: Application configuration

Figure 4: Application configuration

Optional, if needed, provide the Environment name and Domain. I choose to go with the default.

Figure-5: Environment configuration

Figure-5: Environment configuration

For Platform, select .NET Core on Linux. The AWS Elastic Beanstalk .NET Core on Linux platform is a set of platform versions for .NET Core applications that run on the Linux operating system. Under Application Code, select Sample application to start with a basic default app. We’ll deploy our own code later.

Figure 6: Platform configuration

Figure 6: Platform configuration

Select Single Instance configuration for the demo. Then choose Next.

Figure 7: Preset configuration

Figure 7: Preset configuration

Moving on to Configuring Service Access,

  • Service role name – Enter the name for an IAM role that Elastic Beanstalk will create to assume as a service role. Beanstalk will attach the required managed policies to it. For more details, follow Elastic Beanstalk service role
Figure 8: Configure Service Access

Figure 8: Configure Service Access

You can choose to create the role by yourself and choose Use an existing service role. Here are the details to create the role:

  • EC2 key pair – Select an EC2 key pair to securely log in to your EC2 instances. Optional, if you want to login to the EC2.
  • EC2 instance profile – Choose an IAM instance profile with managed policies that allow your EC2 instances to perform required operations. For more details, follow Elastic Beanstalk instance profile
Figure 9: Access Review

Figure 9: Access review

Figure 10: Access review

Figure 10: Access review

With all required configuration filled and the rest of the information left as defaults, choose Skip to review.

Figure 11: Configure service access

Figure 11: Configure service access

Finally, review all the settings and launch the environment.

Figure 12: Review Elastic Beanstalk configuration

Figure 12: Review Elastic Beanstalk configuration

Once the environment changes to “Healthy”, you can access the sample application using the environment URL (under Domain). We’re now ready to deploy our own .NET code using GitHub Actions.

Figure 13: Elastic Beanstalk environment

Figure 13: Elastic Beanstalk environment

Figure 14: Sample application

Figure 14: Sample application

Step 3: Publish and push the ASP.NET MVC application code to the GitHub repository

First, clone the empty GitHub repository we created earlier to your local machine:

Figure 15: Git clone

Figure 15: Git clone

Next, open Visual Studio and create a new ASP.NET Core Web Application. For Location choose the path where we cloned the repo and check “Place Solution and Project in the same directory”. Select the Model-View-Controller template and target .NET 8.0 which is the current LTS release.

Figure 16: Configure New Project

Figure 16: Configure New Project

Figure 17: Additional information

Figure 17: Additional information

Build and run the application locally to verify it works.

Figure 18: Application running in local environment

Figure 18: Application running in local environment

As we created the project in the cloned folder, simply commit and push the code from Visual Studio.

Figure 20: Git commit in Visual Studio

Figure 19: Git commit in Visual Studio

Step 4: Configure a GitHub workflow that defines the CI/CD pipeline stages

To set up continuous integration and delivery, we need to create a GitHub workflow configuration file. This will define the pipeline stages that run on code changes. Create a new YAML file such as .github/workflows/dotnet.yml with the following code:

name: .NET

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

env:
  PROJECT_LOCATION: dotnet-mvc/dotnet-mvc.csproj
  ELASTIC_BEANSTALK_NAME: dotnet-linux
  ELASTIC_BEANSTALK_ENV_NAME: Dotnet-linux-env

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4
    - name: Setup .NET
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: 8.0.x
    - name: Restore dependencies
      run: dotnet restore $PROJECT_LOCATION
    - name: Build
      run: dotnet build --no-restore $PROJECT_LOCATION
    - name: Pack
      run: |
        dotnet publish $PROJECT_LOCATION -c Release --output "${{ github.workspace }}/${{github.run_id}}"
        cd ${{github.run_id}}
        zip -r ../${{github.run_id}}.zip .
    - name: Upload Artifacts
      uses: actions/upload-artifact@v4.3.3
      with: 
        name: deploy-files
        path: ${{github.run_id}}.zip
  deploy:

    runs-on: ubuntu-latest
    needs: build

    steps:
    - uses: actions/download-artifact@v4.1.7
    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-region: us-east-1
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

Step 5: Continuous Deployment to Elastic Beanstalk Environment

To enable automatic deployments to Elastic Beanstalk, we need to configure some additional steps in our GitHub workflow. First, create an IAM user with permissions to access Elastic Beanstalk and S3. Attach the AWSElasticBeanstalkFullAccess and AmazonS3FullAccess managed policies.

Figure 21: IAM User

Figure 20: IAM User

Generate an access key and secret for this IAM user.

Figure 22: Access key generation

Figure 21: Access key generation

Add these as encrypted secrets in the GitHub repo settings:

Figure 23: GitHub Secrets

Figure 22: GitHub Secrets

Next, update the  .github/workflows/dotnet.yml file with deployment steps:

    deploy:

    runs-on: ubuntu-latest
    needs: build

    steps:
    - uses: actions/download-artifact@v4.1.7
    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-region: us-east-1
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    - name: Deploy
      run: |
        aws s3 cp "deploy-files/${{github.run_id}}.zip" s3://elasticbeanstalk-us-east-1-${{ secrets.AWS_ACCOUNT_ID }}/artifact/dotnet-mvc-app/
        aws elasticbeanstalk create-application-version --application-name $ELASTIC_BEANSTALK_NAME --version-label ${{github.run_id}} --description ${{github.run_id}} --source-bundle S3Bucket="elasticbeanstalk-us-east-1-${{ secrets.AWS_ACCOUNT_ID }}",S3Key="artifact/dotnet-mvc-app/${{github.run_id}}.zip"
        aws elasticbeanstalk update-environment --application-name $ELASTIC_BEANSTALK_NAME --environment-name $ELASTIC_BEANSTALK_ENV_NAME --version-label ${{github.run_id}}
        aws elasticbeanstalk wait environment-updated --application-name $ELASTIC_BEANSTALK_NAME --environment-name $ELASTIC_BEANSTALK_ENV_NAME

This will deploy the built artifacts to the Elastic Beanstalk environment on each run. Now, commits and pushes to the repo will trigger an end-to-end pipeline:

  • Build, publish .NET app
  • Package artifacts
  • Copy to S3
  • Deploy to Elastic Beanstalk
Figure 24: GitHub Action Deploy

Figure 23: GitHub Action Deploy

Drilling down, you can view the build log and deploy log.

Figure 25: Build log

Figure 24: Build log

We can monitor the deployment status in the GitHub workflow logs and Elastic Beanstalk console. Once deployed, access the application URL to view the changes live!

Figure 28: Application running on Elastic Beanstalk

Figure 25: Application running on Elastic Beanstalk

Now that we have continuous delivery set up, let’s make a change to our application code to see it deploy automatically. In Visual Studio, open a view file like Views/Home/Index.cshtml. Make a simple visible change like updating the page heading. Commit this change and push it to GitHub.

Figure 29: Home Index update

Figure 26: Home Index update

This will trigger our CI/CD workflow. We’ll see a new run start in the Actions tab.

Figure 30: GitHub Actions Log

Figure 27: GitHub Actions Log

Once complete, the updated .NET application will be deployed to our Elastic Beanstalk environment automatically. After a few minutes, reload the application URL. We should now see our updated page heading! This demonstrates how code changes are continuously built, and released to Elastic Beanstalk through GitHub commits. Our pipeline automatically deploys the latest app version in just minutes!

Figure 31: Updated page on Elastic Beanstalk

Figure 28: Updated page on Elastic Beanstalk

Clean up

To avoid incurring future charges, delete the resources by terminating the Elastic Beanstalk environment and deleting the GitHub repo.

Conclusion

In this post, we walked through setting up a CI/CD pipeline for .NET applications using GitHub Actions and AWS Elastic Beanstalk. By leveraging these cloud services, we can automatically build, test, and deploy our .NET code whenever we push changes to our repository.

The end-to-end solution showcases some key benefits:

  • GitHub Actions provides easy YAML-based workflows to define pipeline stages for building, testing, and deployment.
  • Elastic Beanstalk gives a managed environment to run .NET apps without provisioning servers.
  • Changes pushed to GitHub trigger automatic deployments to Elastic Beanstalk environments.
  • The pipeline enables continuous delivery of applications from development to production.

Setting up this automation provides speed and reliability to the software delivery process. Code is shipped to users faster while maintaining quality through automated testing. This walkthrough demonstrated how GitHub and AWS can streamline deployments for .NET developers.

For more information on AWS Elastic Beanstalk, refer to the AWS Elastic Beanstalk Developer Guide.