.NET on AWS Blog

Deploy to Elastic Beanstalk with Azure DevOps

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

In this post, I’ll walk through setting up a continuous integration and deployment pipeline for an ASP.NET Core application, using Azure DevOps, Amazon Simple Storage Service (Amazon S3), and AWS Elastic Beanstalk.

First, I’ll create a simple ASP.NET MVC sample app in Visual Studio and push the source code to an Azure DevOps Git repository. Next, I’ll configure an Azure Pipelines build pipeline that will trigger on every code commit. The build pipeline will compile the application and package the binaries into artifacts. These build artifacts will be uploaded to an Amazon S3 bucket, which will act as a staging area for deployments. On the deployment side, I’ll set up an Azure release pipeline to integrate with Elastic Beanstalk. The release pipeline will create a new application version and deploy the same.

Overview of solution

This solution sets up a continuous integration and deployment pipeline to build, test, and deploy an ASP.NET Core application from Azure DevOps to AWS Elastic Beanstalk. The source code is stored in an Azure DevOps Git repository. Azure Pipelines provides the automation to build, test, package, and release the app on every code change. Elastic Beanstalk then runs the application in a managed environment.

Figure 1: Architecture

Figure 1: Architecture

Walkthrough

Steps:

  1. Create Azure DevOps Git Repository
  2. Provision Elastic Beanstalk Application & Environment
  3. Push ASP.NET Code to Azure DevOps Repo
  4. Configure Azure DevOps Service Connections
  5. Set Up Continuous Integration Pipeline
  6. Configure Continuous Delivery to Elastic Beanstalk

Prerequisites

For this walkthrough, you should have the following prerequisites installed and configured:

Step 1: Create Azure DevOps Git Repository

The first step is to create a Git repository in Azure DevOps to store the application source code. This will serve as the central repository that will trigger our continuous integration and delivery workflow.

To create a new Git repo:

1. Log into your Azure DevOps organization and navigate to the project where you want to create the repository.

2. Select Repos > Files to open the Files view.

3. Choose the New Repository button.

4. Give your repository a name. Initialize it with a README and .gitignore if desired.

5. Choose Create to create the empty Git repository.

Now you have an Azure DevOps Git repo ready to store your application source code. Later, you’ll push your .NET application code to this repository. The Azure DevOps Git repository will act as the single source of truth for your code. Commits pushed to this repository will trigger your continuous integration pipeline to build, test, and deploy the application automatically.

Figure 2: New Repo in Azure DevOps

Figure 2: New Repo in Azure DevOps

Step 2: Provision Elastic Beanstalk Application & Environment

In the Elastic Beanstalk console, choose Create New Environment to set up an environment for deploying your .NET code. You can follow the same steps listed in my previous post, Deploy to Elastic Beanstalk with GitHub Actions.

Choose Web Server Environment. Name the Application something like “dotnet-mvc-app-host-ado“. For Platform, choose.NET core on Linux. Under Application Code, choose Sample Application to start with a basic default app. You’ll set up your deployment pipeline with your own code later. Choose Single Instance configuration for the demo. Then choose Next.

Next, configure service access. For Service Role, you can either create a new role or use an existing role.

  • 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
  • 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 3: Configure Service Access

Figure 3: Configure Service Access

With all required configuration entered, leave the rest of the information as default and choose to skip to review.

Note: The former instructions assume the AWS account has the default VPC. If not, or you want to use another VPC, use the VPC with internet access in the next page instead of choosing “skip to review”. For details on enabling VPC internet access, follow Enable VPC internet access using internet gateways

Finally, review all the settings and launch the environment.

Figure 4: Review Elastic Beanstalk configuration

Figure 4: Review Elastic Beanstalk configuration

Once the environment status changes to Healthy, you can access the sample application using the environment URL (under Domain). You’re now ready to deploy your own .NET code using Azure DevOps.

Figure 5: Running Sample App

Figure 5: Running Sample App

Step 3: Push ASP.NET Code to Azure DevOps Repo

Clone the repo you created in Azure DevOps. Create a new ASP.NET Core Web Application. For Location choose the path where you cloned the repo and check Place Solution and Project in the same directory. Choose dotnet-mvc for the name of the project. Select the Model-View-Controller template and target .NET 8.0 which is the current LTS release.

Note: Following similar steps, as in the previous post – Step 3: Publish and push the ASP.NET MVC application code to the GitHub repository.

Build and run the application locally to verify it works.

Figure 6: Localhost App

Figure 6: Localhost App

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

Figure 7: Git commit and push

Figure 7: Git commit and push

Step 4: Configure Azure DevOps Service Connections

Create an IAM user with permissions to access Elastic Beanstalk and S3. Attach the AWSElasticBeanstalkFullAccess and AmazonS3FullAccess managed policies.

Figure 8: IAM User Role and Access

Figure 8: IAM User Role and Access

Generate an access key and secret for the IAM user for CLI Use case.

Figure 9: Access Key

Figure 9: Access Key

For creating Service Connections, follow the steps at Supply task credentials using a service connection.

If you don’t see AWS connection type, install AWS Toolkit for Azure DevOps from the marketplace. For instructions on installing extension, follow Install extensions.

Figure 10: Azure DevOps Service Connection

Figure 10: Azure DevOps Service Connection

Step 5: Set Up Continuous Integration Pipeline

Navigate to the Pipelines section and create a new pipeline. Select your Azure Repos Git repository and select Starter pipeline. It will create a azure-pipelines.yml file in the repo. Replace it with the code below.

trigger:
- main
 
pool:
  vmImage: ubuntu-latest
 
variables:
  buildConfiguration: 'Release'
  debug: 'Verbose'
  project: 'dotnet-mvc/dotnet-mvc.csproj'
  artifact_bucket: 'elasticbeanstalk-us-east-1-123456789012'
  s3_path: 'dotnet-mvc-app'
 
steps:
  - task: DotNetCoreCLI@2
    inputs:
      command: 'restore'
      projects: $(project)
      feedsToUse: 'select'
  
  - task: DotNetCoreCLI@2
    inputs:
      command: 'build'
      projects: $(project)
      arguments: '--configuration $(buildConfiguration)'
  
  - task: DotNetCoreCLI@2
    inputs:
      command: 'publish'
      publishWebProjects: false
      projects: $(project)
      arguments: '--output $(Build.ArtifactStagingDirectory)/$(Build.BuildId) -c $(buildConfiguration)'
      zipAfterPublish: false
      modifyOutputPath: false
 
  - task: ArchiveFiles@2
    inputs:
      rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId)'
      includeRootFolder: false
      archiveType: 'zip'
      archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
      replaceExistingArchive: true
 
  - task: S3Upload@1
    inputs:
      awsCredentials: 'AWS'
      regionName: 'us-east-1'
      bucketName: '$(artifact_bucket)'
      targetFolder: $(s3_path)
      sourceFolder: '$(Build.ArtifactStagingDirectory)'
      globExpressions: '$(Build.BuildId).zip'

Note: update the variables accordingly.

This will define the build steps that run on code changes. The build pipeline is set to do the following:

  • Restore of Nuget Packages
  • Build the Project
  • Package (Zip the artifact needed)
  • Upload it S3.

Choose Save and Run.

Figure 11: Build Log

Figure 11: Build Log

Configure Continuous Delivery to Elastic Beanstalk

Create a New Release pipeline with Empty – Template and specify Dev as the stage name.

Figure 12: Release Pipeline

Figure 12: Release Pipeline

Choose the Artifact type Build and select the build pipeline you created in the last step.

Figure 13: Azure DevOps Release Pipeline’s Build Artifact

Figure 13: Azure DevOps Release Pipeline’s Build Artifact

Enable continuous deployment, to trigger the release on every build.

Figure 14: Release Pipeline Continuous Deployment Trigger

Figure 14: Release Pipeline Continuous Deployment Trigger

Give the Release Definition a name, dotnet-mvc-app-release.

Choose Dev – Stage to define deployment steps. You will use AWS Elastic Beanstalk Create Version and AWS Elastic Beanstalk Deploy Application Tasks.

  • AWS Elastic Beanstalk Create Version – Creates a new version of an application that can be deployed subsequently to an Elastic Beanstalk environment associated with the application. Follow the link for more information on the about the task.
  • AWS Elastic Beanstalk Deploy Application – Deploys a new version of an application to an Elastic Beanstalk environment associated with the application. Follow the link for more information on the about the task.

Configure the Tasks

Create an Elastic Beanstalk Revision:

  • For Credentials, choose AWS you created earlier
  • For Region, choose the region of Elastic Beanstalk
  • For Application, Elastic Beanstalk Application Name
  • For Deployment Bundle Type, choose Pre-existing application bundle in Amazon S3
  • For Deployment Bundle Object Key, use the variable to arrive dynamic value for the respective Build
  • For Version Label, choose a unique value with $(Release.DeploymentID).
Figure 15: Release Pipeline Task Create Elastic Beanstalk Revision

Figure 15: Release Pipeline Task Create Elastic Beanstalk Revision

Deploy to Elastic Beanstalk:

  • For Credentials, choose AWS you created earlier
  • For region, choose the region of Elastic Beanstalk
  • For Application, Elastic Beanstalk Application Name
  • For Environment, Elastic Beanstalk Environment Name
  • For Deployment Bundle Type, Existing application version
  • For Version Label, choose a unique value
Figure 16: Release Pipeline Task Deploy to Elastic Beanstalk

Figure 16: Release Pipeline Task Deploy to Elastic Beanstalk

Once deployment is successful, access the application URL to view the changes live!

Figure 17: Application Post Deployment

Figure 17: Application Post Deployment

Now that you have continuous delivery set up, make a change to your 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 Repo.

Figure 18: Code Change

Figure 18: Code Change

This will trigger your CI/CD workflow.

Build Pipeline:

Figure 19: Continuous Build Log

Figure 19: Continuous Build Log

Release Pipeline:

Figure 20: Continuous Deployment Log

Figure 20: Continuous Deployment Log

Updates are live in the Environment.

Figure 21: Application Post Change Deployment

Figure 21: Application Post Change Deployment

Thus, code changes are continuously built and released to Elastic Beanstalk using Azure DevOps. Your Build and Release Pipeline automatically builds, packages and deploys the latest updates in just minutes.

Clean up

To avoid incurring future charges, delete the resources by terminating the Elastic Beanstalk environment and deleting the Azure DevOps Pipeline and Repository.

Conclusion

In this post, I walked through setting up a CI/CD pipeline for .NET applications using Azure DevOps and AWS Elastic Beanstalk. By leveraging these cloud services, you can automatically build, package, and deploy our .NET code whenever you push changes to your repository.

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

– Azure DevOps Build Pipeline provides easy YAML-based workflows to define pipeline steps for building, testing, and packaging the artifact.

– Azure DevOps Release pipeline enable with the deployments.

– Elastic Beanstalk gives a managed environment to run .NET apps without provisioning servers.

– Changes pushed to Azure Repository trigger automatic build and deployments to Elastic Beanstalk environments.

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 Azure DevOps and AWS can streamline deployments for .NET developers. For more information, view the AWS Toolkit for Microsoft Azure DevOps User Guide.