AWS DevOps Blog

Extending AWS CodeBuild with Custom Build Environments for the .NET Framework

Thanks to Greg Eppel, Sr. Solutions Architect, Microsoft Platform for this great blog that describes how to create a custom CodeBuild build environment for the .NET Framework.

AWS CodeBuild is a fully managed build service that compiles source code, runs tests, and produces software packages that are ready to deploy. CodeBuild provides curated build environments for programming languages and runtimes such as Android, Go, Java, Node.js, PHP, Python, Ruby, and Docker. CodeBuild now supports builds for the Microsoft Windows Server platform, including a prepackaged build environment for .NET Core on Windows. If your application uses the .NET Framework, you will need to use a custom Docker image to create a custom build environment that includes the Microsoft proprietary Framework Class Libraries. For information about why this step is required, see our FAQs. In this post, I’ll show you how to create a custom build environment for .NET Framework applications and walk you through the steps to configure CodeBuild to use this environment.

Build environments are Docker images that include a complete file system with everything required to build and test your project. To use a custom build environment in a CodeBuild project, you build a container image for your platform that contains your build tools, push it to a Docker container registry such as Amazon Elastic Container Registry (Amazon ECR), and reference it in the project configuration. When it builds your application, CodeBuild retrieves the Docker image from the container registry specified in the project configuration and uses the environment to compile your source code, run your tests, and package your application.

Requirements

To follow the steps in this post and build the Docker container image, you need to have Docker for Windows (which is installed if you use a Windows Server with Containers AMI), the AWS Command Line interface, and Git installed. In this post, I’m using Visual Studio Build Tools 2017.

Step 1: Launch EC2 Windows Server 2016 with Containers

  1. In the Amazon EC2 console, in your region, launch an Amazon EC2 instance from a Microsoft Windows Server 2016 Base with Containers AMI.
  2. Increase disk space on the boot volume to at least 50 GB to account for the larger size of containers required to install and run Visual Studio Build Tools.
  3. When the instance is running, connect to it using Remote Desktop.

Step 2: Build and push the Docker image

  1. Open an elevated command prompt (that is, right-click on your favorite shell and choose Run as Administrator).
  2. Create a directory:
    mkdir C:\BuildTools
    cd C:\BuildTools
  3. Save the following content to C:\BuildTools\Dockerfile:
    # escape=`
    
    FROM microsoft/dotnet-framework:4.7.2-runtime
    
    SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
    
    #Install NuGet CLI
    ENV NUGET_VERSION 4.4.1
    RUN New-Item -Type Directory $Env:ProgramFiles\NuGet; `
        Invoke-WebRequest -UseBasicParsing https://dist.nuget.org/win-x86-commandline/v$Env:NUGET_VERSION/nuget.exe -OutFile $Env:ProgramFiles\NuGet\nuget.exe
    
    # Install VS Test Agent
    RUN Invoke-WebRequest -UseBasicParsing https://download.visualstudio.microsoft.com/download/pr/12210068/8a386d27295953ee79281fd1f1832e2d/vs_TestAgent.exe -OutFile vs_TestAgent.exe; `
        Start-Process vs_TestAgent.exe -ArgumentList '--quiet', '--norestart', '--nocache' -NoNewWindow -Wait; `
        Remove-Item -Force vs_TestAgent.exe; `
    # Install VS Build Tools
        Invoke-WebRequest -UseBasicParsing https://download.visualstudio.microsoft.com/download/pr/12210059/e64d79b40219aea618ce2fe10ebd5f0d/vs_BuildTools.exe -OutFile vs_BuildTools.exe; `
        # Installer won't detect DOTNET_SKIP_FIRST_TIME_EXPERIENCE if ENV is used, must use setx /M
        setx /M DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1; `
        Start-Process vs_BuildTools.exe -ArgumentList '--add', 'Microsoft.VisualStudio.Workload.MSBuildTools', '--add', 'Microsoft.VisualStudio.Workload.NetCoreBuildTools', '--add', 'Microsoft.VisualStudio.Workload.WebBuildTools;includeRecommended', '--quiet', '--norestart', '--nocache' -NoNewWindow -Wait; `
        Remove-Item -Force vs_buildtools.exe; `
        Remove-Item -Force -Recurse \"${Env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\"; `
        Remove-Item -Force -Recurse ${Env:TEMP}\*; `
        Remove-Item -Force -Recurse \"${Env:ProgramData}\Package Cache\"
    
    # Set PATH in one layer to keep image size down.
    RUN setx /M PATH $(${Env:PATH} `
        + \";${Env:ProgramFiles}\NuGet\" `
        + \";${Env:ProgramFiles(x86)}\Microsoft Visual Studio\2017\TestAgent\Common7\IDE\CommonExtensions\Microsoft\TestWindow\" `
        + \";${Env:ProgramFiles(x86)}\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\")
    
    # Install Targeting Packs
    RUN @('4.0', '4.5.2', '4.6.2', '4.7.2') `
        | %{ `
            Invoke-WebRequest -UseBasicParsing https://dotnetbinaries.blob.core.windows.net/referenceassemblies/v${_}.zip -OutFile referenceassemblies.zip; `
            Expand-Archive -Force referenceassemblies.zip -DestinationPath \"${Env:ProgramFiles(x86)}\Reference Assemblies\Microsoft\Framework\.NETFramework\"; `
            Remove-Item -Force referenceassemblies.zip; `
        }
  4. Run the following command in that directory. This process can take a while. It depends on the size of EC2 instance you launched. In my tests, a t2.2xlarge takes less than 30 minutes to build the image and produces an approximately 15 GB image.
    docker build -t buildtools2017:latest -m 2GB .
  5. Run the following command to test the container and start a command shell with all the developer environment variables:
    docker run -it buildtools2017
  6. Create a repository in the Amazon ECS console. For the repository name, type buildtools2017. Choose Next step and then complete the remaining steps.
  7. Execute the following command to generate authentication details for our registry to the local Docker engine. Make sure you have permissions to the Amazon ECR registry before you execute the command.
    aws ecr get-login
  8. In the same command prompt window, copy and paste the following commands:
    docker tag buildtools2017:latest [YOUR ACCOUNT #].dkr.ecr.[YOUR REGION].amazonaws.com/ buildtools2017:latest
    docker push [YOUR ACCOUNT #].dkr.ecr.[YOUR REGION].amazonaws.com/buildtools2017:latest

    Note: Make sure you replace [YOUR ACCOUNT #] with your AWS account number and [YOUR REGION] with the region you are using.

Step 3: Configure AWS CodeCommit

  1. Before you can access CodeCommit for the first time, you must complete the initial configuration steps.
  2. In the CodeCommit console, create a repository named DotNetFrameworkSampleApp. On the Configure email notifications page, choose Skip.
  3. Clone a .NET Framework Docker sample application from GitHub. The repository includes a sample ASP.NET Framework that we’ll use to demonstrate our custom build environment.On the EC2 instance, open a command prompt and execute the following commands:
    git clone https://github.com/Microsoft/dotnet-framework-docker-samples.git
    cd dotnet-framework-docker-samples
    del /Q /S .git
    cd aspnetapp
    git init
    git add . 
    git commit -m "First commit"
    git remote add origin https://git-codecommit.[YOURREGION].amazonaws.com/v1/repos/DotNetFrameworkSampleApp
    git remote -v
    git push -u origin master
  4. Navigate to the CodeCommit repository and confirm that the files you just pushed are there.

Step 4: Configure build spec

To build your .NET Framework application with CodeBuild you use a build spec, which is a collection of build commands and related settings, in YAML format, that AWS CodeBuild can use to run a build. You can include a build spec as part of the source code or you can define a build spec when you create a build project. In this example, I include a build spec as part of the source code.

  1. In the root directory of your source directory, create a YAML file named buildspec.yml.
  2. Copy the following contents to buildspec.yml:
    version: 0.2
    
    phases:
      pre_build:
        commands:
         - New-Item -ItemType Junction -Path C:\Src -Value $Env:CODEBUILD_SRC_DIR
         - cd C:\Src  
         - nuget.exe restore
      build:
        commands:
         - msbuild 
  3. Save the changes to the buildspec.yml and use the following commands to add the file to the CodeCommit repository:
    git add . 
    git commit -m "Added a build spec file"
    git push

Step 5: Configure CodeBuild

At this point, we have a Docker image with Visual Studio Build Tools installed and stored in the Amazon ECR registry. We also have a sample ASP.NET Framework application in a CodeCommit repository. Now we are going to set up CodeBuild to build the ASP.NET Framework application.

  1. In the Amazon ECR console, choose the repository that was pushed earlier with the docker push command. On the Permissions tab, choose Add.
    1. For Sid, field give it a unique name.
    2. For Effect, choose Allow.
    3. For Principal, type codebuild.amazonaws.com.
    4. For Action, choose Pull only actions.
    5. Choose Save All.
  2. Go to the CodeBuild console, and choose Create Project.
  3. For Project name, type DotNetFrameworkSampleApp.
  4. For Source Provider, choose AWS CodeCommit and then choose the called DotNetFrameworkSampleApp repository.
  5. For Environment Image, choose Specify a Docker image.
  6. For Environment type, choose Windows.
  7. For Custom image type, choose Amazon ECR.
  8. For Amazon ECR repository, choose the Docker image with the Visual Studio Build Tools installed, buildtools2017. Your configuration should look like the image below:

  9. Choose Continue and then Save and Build to create your CodeBuild project and start your first build. You can monitor the status of the build in the console. You can also configure notifications that will notify subscribers whenever builds succeed, fail, go from one phase to another, or any combination of these events.

Summary

CodeBuild supports a number of platforms and languages out of the box. By using custom build environments, it can be extended to other runtimes. In this post, I showed you how to build a .NET Framework environment on a Windows container and demonstrated how to use it to build .NET Framework applications in CodeBuild.

We’re excited to see how customers extend and use CodeBuild to enable continuous integration and continuous delivery for their Windows applications. Feel free to share what you’ve learned extending CodeBuild for your own projects. Just leave questions or suggestions in the comments.