Azure DevOps with AWS Elastic Beanstalk

This is one chapter of a multi-part tutorial where we discuss how to integrate Azure DevOps with AWS.

AWS Elastic Beanstalk is a great platform to host your ASP.Net applications. It supports traditional .NET full framework applications running on IIS and plethora of other application such as Java, Ruby, Containers, Go and Python to name a few. In this tutorial we will learn how to integrate AWS Elastic Beanstalk with Azure DevOps. We will learn following topics. If you are new to AWS Elastic Beanstalk, it’s recommended to follow the topics in-order.

  1. Introduction into AWS Elastic Beantalk
  2. Introduction into Azure DevOps pipeline we will be building in this tutorial
  3. How to upload the web deployment package to Amazon S3 with Azure DevOps pipeline
  4. How to create an AWS Elastic Beanstalk application
  5. Elastic Beanstalk under the hood
  6. Completing the Elastic Beanstalk Azure DevOps pipeline
  7. Advanced Elastic Beanstalk deployment with PowerShell

This is one chapter of a series of posts where we discuss how to integrate Azure DevOps with AWS.

Introduction into AWS Elastic Beantalk

If you are not familiar with AWS Elastic Beanstalk quickly go through the following introductory video.

Introduction into Azure DevOps pipeline we will be building in this tutorial

We will create a series of build steps to deploy an ASP.NET application into AWS Elastic Beanstalk using Azure DevOps pipelines. Following video will walk you through the high level design.

Important debugging tips (Video timestamp: 3min:40sec). Depending on the validity of your development self-signed certificate, you may get an error message that says.

Exception has occurred: CLR/System.InvalidOperationException. An unhandled exception of type 'System.InvalidOperationException' occurred in System.Private.CoreLib.dll: 'Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found or is out of date.

For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054.’

You can fix this error by generating a new certificate using the following commands so that you can first clean the old, potentially expired, certificates & start your localhost website with a valid self-signed SSL certificate.

dotnet dev-certs https --clean
dotnet dev-certs https --trust

How to upload the web deployment package to Amazon S3 with Azure DevOps pipeline

So far we got our ASP.NET core application into Azure Repository. In the following video tutorial we create our Azure DevOps pipeline so that it can build the Elastic Beanstalk web deployment package and upload it to Amazon S3. Once our deployment package is in S3, we can point Elastic Beanstalk to deploy it.  

In the video we gave AmazonS3FullAccess to the Azure DevOps agent. Ideally, in production environments you give fine grained permissions so that the pipeline can upload the deployment package only to a specific folder. You can configure the IAM permissions such that, you grant access only to a specific folder inside Amazon S3.

How to create an AWS Elastic Beanstalk application

In the previous steps we got our Elastic Beanstalk deployment package ready. We now need an Elastic Beanstalk application to deploy our newly created deployment package. Following video tutorial explains how to create an Elastic Beanstalk application

  • [Video timestamp: 0sec – 30sec] We create an AWS Elastic Beanstalk application called MyTigerApp in AWS console. You can also create this programmatically.
  • [Video timestamp: 30sec –  7min] We create an environment called MyTigerApp-Dev. You can have as many environments as possible to host your test, pre-production workloads. Note that in the video, we provided a key pair. This allows us to remote login into the underlying instances and explore them. This is good to identify any issues. However, in production environments, you run the instances without a key pair so that you can’t remote login into them. You can also attach an EC2 instance profile to the instances spin up by Elastic Beanstalk so that if your application needs access to AWS resources like Amazon Dynamo DB the permission to access those resources can come from EC2 instance role.
  • [Video timestamp: 7min – 10min 49sec] We download the deployment zip file we uploaded to S3 (in the previous section) and manually deploy it to MyTigerApp-Dev environment. After a few moments, check whether the website is working.

Elastic Beanstalk under the hood

Behind the scene Elastic Beanstalk uses AWS CloudFormation to orchestrate its resources. In the following video tutorial we will explore how Elastic Beanstalk gets provisioned and how you can remote login into Elastic Beanstalk to get operating system level customizations. You can also define your own Amazon Machine Images (AMI) with custom software pre-baked into the environment. For example, if your ASP.NET app’s charting/PDF printing library requires you to have Direct X installed on the managed environment, you can’t easily do that with Azure App Services. However, because Elastic Beanstalk allows customization of AMIs you can install the software you want. Ability to customize the underlying operating system becomes valuable when you want to install your own security appliances such as enterprise wide monitoring systems. Read more how you can customize Elastic Beanstalk AMIs here.

Completing the Elastic Beanstalk Azure DevOps pipeline

In the previous sections, we got our Elastic Beanstalk deployment package created and uploaded into Amazon S3. We also created an Elastic Beanstalk environment. The final piece left is to trigger the Elastic Beanstalk to take the deployment package from Amazon S3 and make an automatic deployment. In the following video, we do the final wiring to do an automatic deployment from the time a user commits a change to Azure repository.

Advanced Elastic Beanstalk deployment with PowerShell

In all the previous sections, we have done the Elastic Beanstalk integration with Azure DevOps using Azure DevOps market place plugins. Using plugins is good as long as you are not doing advanced customizations. From the time you start creating complex DevOps pipelines, you start to observe the limitations of the plugins. In this section we will study how we can program everything we did before using AWS tools for PowerShell. Since all AWS APIs are exposed in PowerShel, you should be able to create more advanced DevOps pipelines using this method. If you are a .NET developer or prefer to use old school command line, you can still program everything we discuss here using your preferred programming language. I like PowerShell based approach because you don’t need to compile the code but it provides almost similar programming constructs.

The automation PowerShell script is available at

https://github.com/cloudopian/azure-devops-on-aws/blob/master/MyTigerApp/ElasticBeanstalkDeployment/AdvancedDeployment.ps1

This script allows you to parameterize Elastic Beanstalk application names and environment names. This allows you to dynamically spin up environments. For example, if you are a tester who wants to have a brand new environment called, MyTigerApp-Test1 you can now execute an Azure DevOps pipline with a new parameter value to create the environment on the fly. Think about a workflow you can create inside your organization where the testers can test a given change set in a clean environment by running this pipeline.

Param(
$appName="MyTigerApp",
$appVersion="18",
$s3SourceBucket="my-build-artifact",
$s3SourceKey="MyTigerAppDeliveries/MyTigerApp.zip",
$appEnvName="MyTigerApp-Dev",
$sleepTime=5
)

$appDescription="Application to demonstrate AWS Elastic Beanstalk"
$app=Get-EBApplication -ApplicationName $appName

if($app -eq $null){
    Write-Host "Creating a new EB application $appName"
    New-EBApplication -ApplicationName $appName -Description $appDescription
}

$currentApp=Get-EBApplicationVersion -ApplicationName $appName -VersionLabel $appVersion
if($currentApp -eq $null){
    Write-Host "Creating a new EB application version $appVersion"
    New-EBApplicationVersion -ApplicationName $appName -VersionLabel $appVersion -Description $appName -AutoCreateApplication $false -SourceBundle_S3Bucket $s3SourceBucket -SourceBundle_S3Key $s3SourceKey  -Force -Process $true
    while ((Get-EBApplicationVersion -ApplicationName $appName -VersionLabel $appVersion | Select-Object Status -ExpandProperty Status) -ne "Processed"){
     Start-Sleep -s $sleepTime
     Write-Host "Waiting $appName status changed to processed"
    }
    Write-Host "$appName version $appVersion status changed to processed"
}

$isEnvTermating=$false

do{
    $appEnv=(Get-EBEnvironment -ApplicationName $appName -EnvironmentName $appEnvName -IncludeDeleted $false)
    if($appEnv -eq $null){
        Write-Host "Creating a new environment"
        New-EBEnvironment -ApplicationName $appName -EnvironmentName $appEnvName -Description "$appEnvName for $appName" -SolutionStackName "64bit Windows Server Core 2019 v2.5.6 running IIS 10.0" -OptionSetting @{Namespace="aws:autoscaling:launchconfiguration";OptionName="InstanceType";Value="t3.small"} , @{Namespace="aws:autoscaling:asg";OptionName="MaxSize";Value="2"} -VersionLabel $appVersion -Force
    
        while(((Get-EBEnvironment -ApplicationName $appName -EnvironmentName $appEnvName -IncludeDeleted $false)| select -ExpandProperty Status) -ne "Ready"){
            Write-Host "Waiting the environment $appEnvName become ready for $appName"
        }
        Write-Host "$appName version $appVersion is ready at http://"$appEnv.EndpointURL    
        }

    if($appEnv -ne $null -and $appEnv.Status -ne "Terminating" -and $appEnv.Status -ne "Terminated"){
        Write-Host "Updating the environment"
        Update-EBEnvironment -ApplicationName $appName -EnvironmentName $appEnvName -VersionLabel $appVersion -OptionSetting @{Namespace="aws:autoscaling:launchconfiguration";OptionName="InstanceType";Value="t3.small"} , @{Namespace="aws:autoscaling:asg";OptionName="MaxSize";Value="2"} -Description "Deployed by Azure DevOps" -Force
        while(( (Get-EBEnvironment -ApplicationName $appName -EnvironmentName $appEnvName -IncludeDeleted $false) | select -ExpandProperty Status) -ne "Ready"){
            Write-Host "Waiting the environment $appEnvName become ready for $appName after deploying the version $appVersion"
        }
        Write-Host "$appName version $appVersion is ready at http://"$appEnv.EndpointURL
    }

    $isEnvTerminating=($appEnv.Status -eq "Terminating")
    if($isEnvTerminating){
     Write-Host "The environment $appEnvName of the app $appName is terminating, I will try to create a one once it is terminated"
     Start-Sleep -s $sleepTime
    }
}while($isEnvTerminating)

[Line numbers 1-8] We pass parameters to the PowerShell scripts. You can define Azure DevOps variables and pass them into the script.

[Line number 13-16] We check whether there is an Elastic Beanstalk application with a given name. If not, we create a new Elastic Beanstalk application. This is the power of PowerShell based method. If you use plugins, you expect to have an Elastic Beanstalk application with the given name.

[Line number 18-27] We upgrade the application version

[Line number 29] If you terminate the environment, it will be under a special state called “Terminating” until it is fully terminated. During this time, Elastic Beanstalk spends some time cleaning old artifacts. If you deploy a new application to the already terminating environment, it’s going to fail. We use this boolean variable to check whether the environment is in terminating state and if yes, we wait until the environment is fully terminated before creating a new one with the same old name.

[Line number 33-41] There is no environment with the given name. Therefore we are creating a new Elastic Beanstalk environment.

[Line number 43-50] If you deploy a new version of your software to the Elastic Beanstalk environment, it will take few seconds/minutes before the update is completed. If you try to deploy another version during this update period, your last update is going to fail. In this case, we will wait until the last update is fully deployed before attempting to deploy another version.

[Line number 31-57] You can see that we are in a loop until a deployment can be done. Until the environment turns into an acceptable state, we constantly pole the status of the environment. The polling period can be adjusted with the sleepTime.

Important Tip: When you run the script, you may get an access denied error when the build agent try to create the Elastic Beanstalk environment. This may be because you don’t have certain permission to create AWS resources like roles. Look at the log files and try to identify the error.

Fix the error by properly passing the Elastic Beanstalk instance profile and service roles. Read: https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options-general.html

This is one chapter of a multi-part tutorial where we discuss how to integrate Azure DevOps with AWS. For the next chapter visit Azure DevOps with AWS Fargate.

2 thoughts on “Azure DevOps with AWS Elastic Beanstalk”

  1. Pingback: Azure DevOps with AWS CodeDeploy - Cloudopian

  2. Pingback: Azure DevOps with AWS - Cloudopian

Leave a Reply

Discover more from Cloudopian

Subscribe now to keep reading and get access to the full archive.

Continue reading

Scroll to Top