Deploying & Dockerizing Angular 8 with .NET Core Application to AWS ECS (Linux) using AWS toolkit for Visual Studio 2019

Jay Raj Mishra
7 min readSep 19, 2019

Setting up an Angular application in AWS ECS FARGATE for production use is quite simple but not entirely straight forward for beginners. This article helps you do a basic yet effective such setup.

The rate of adoption of Docker as a containerized solution is soaring. A lot of companies are now using Docker containers to run apps. In a lot of scenarios, using Docker containers can be a better approach than spinning up a full-blown virtual machine.

In this post, I’ll break down all the steps I took to successfully install and run a web application built on the SPA Framework Angular with .NET Core Application with a screenshot view.

AWS toolkit for Visual Studio

The AWS Toolkit for Visual Studio is an extension for Microsoft Visual Studio running on Microsoft Windows that makes it easier for developers to develop, debug, and deploy .NET applications using Amazon Web Services. With the AWS Toolkit for Visual Studio, you’ll be able to get started faster and be more productive when building AWS applications.

The AWS Toolkit for Visual Studio is available via the Visual Studio Marketplace and supports Visual Studio 2017 and 2019. The AWS Toolkit for 2013 and 2015 is contained in the AWS SDK and Tools for .NET install package.

The Steps to run the Angular apps in AWS Container you need to consider following steps | before this (download docker for windows, AWS toolkit for visual studio)

  1. Modifying Dockerfile
  • Adding the dotnet 2.2 sdk or latest
  • Adding the Node 10 or latest

2. Modifying Startup.cs File

Add UseAngularCliServer

3. Modifying and Adding SpaRoot in .csproj file

4. wwwroot folder (By default, it will set the environment in production for deployment, so from the wwwroot file it will transfer all the file to the container)

In Dockerfile.file

FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80

FROM microsoft/dotnet:2.2-sdk AS build
WORKDIR /src
COPY YourProjectName/YourProjectName.csproj YourProjectName/
RUN dotnet restore YourProjectName/YourProjectName.csproj

COPY . .
WORKDIR /src/YourProjectName
RUN apt-get update -yq && apt-get upgrade -yq && apt-get install -yq curl git nano
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - && apt-get install -yq nodejs build-essential
COPY YourProjectName/ClientApp/package.json YourProjectName/ClientApp/

RUN npm install -g npm

RUN cd YourProjectName/ClientApp \
&& npm install --silent

RUN dotnet build YourProjectName.csproj -c Release -o /app

FROM build AS publish
RUN dotnet publish YourProjectName.csproj -c Release -o /app

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "yourprojectname.dll"]

In Startup.cs

  • Include UseSpa and AddSpaStaticFiles
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;

namespace SKUnest
{
public class SKUnestStartup
{
public SKUnestStartup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "wwwroot/ClientApp/dist/src";
});
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();

app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});

app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501

spa.Options.SourcePath = "ClientApp";

if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
spa.Options.StartupTimeout = TimeSpan.FromSeconds(60); // Increase the timeout if angular app is taking longer to startup
}
});
}
}
}

In YourProjectName.csproj

  • Adding the ItemGroup and Target Name
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>.</DockerfileContext>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<IsPackable>false</IsPackable>
<SpaRoot>YourClientApp\</SpaRoot>
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>

<!-- Set this to true if you enable server-side prerendering -->
<BuildServerSideRenderer>false</BuildServerSideRenderer>
<AssemblyName>YourProjectName</AssemblyName>
<RootNamespace>YourProjectName</RootNamespace>
<Version>1.7.0</Version>
<Authors>YourAuthour</Authors>
<Company>YourCompany</Company>
<Product>YourDashboard</Product>
<PackageId>YourProjectName</PackageId>
<Description>YourProjectName Dashboards Description </Description>
<PackageProjectUrl></PackageProjectUrl>
<DockerTargetOS>Linux</DockerTargetOS>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="2.1.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.0.1916590" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.1" />
</ItemGroup>

<ItemGroup>
<!-- Don't publish the SPA source files, but do show them in the project files list -->
<Content Remove="$(SpaRoot)**" />
<None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
</ItemGroup>

<ItemGroup>
<Content Include="ClientApp\angular.json" />
<Content Include="ClientApp\e2e\tsconfig.e2e.json" />
<Content Include="ClientApp\package-lock.json" />
<Content Include="ClientApp\package.json" />
<Content Include="ClientApp\src\tsconfig.app.json" />
<Content Include="ClientApp\src\tsconfig.spec.json" />
<Content Include="ClientApp\tsconfig.json" />
<Content Include="ClientApp\tslint.json" />
</ItemGroup>

<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
</Target>

<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build --prod" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build:ssr --prod" Condition=" '$(BuildServerSideRenderer)' == 'true' " />

<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="$(SpaRoot)dist\**; $(SpaRoot)dist-server\**" />
<DistFiles Include="$(SpaRoot)node_modules\**" Condition="'$(BuildServerSideRenderer)' == 'true'" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>

<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<TypeScriptTarget>ES2015</TypeScriptTarget>
<TypeScriptJSXEmit>None</TypeScriptJSXEmit>
<TypeScriptModuleKind>ES6</TypeScriptModuleKind>
<TypeScriptCompileOnSaveEnabled>True</TypeScriptCompileOnSaveEnabled>
<TypeScriptNoImplicitAny>False</TypeScriptNoImplicitAny>
<TypeScriptRemoveComments>False</TypeScriptRemoveComments>
<TypeScriptOutFile />
<TypeScriptOutDir />
<TypeScriptGeneratesDeclarations>False</TypeScriptGeneratesDeclarations>
<TypeScriptNoEmitOnError>True</TypeScriptNoEmitOnError>
<TypeScriptSourceMap>True</TypeScriptSourceMap>
<TypeScriptMapRoot />
<TypeScriptSourceRoot />
</PropertyGroup>

</Project>

Now lets begin the process and follow below screenshot steps for Angular with .Net Core application in AWS ECS.

— Step 1 : Create New Project

— Step 2: Add your Project Name

— Step 3: Choose the latest .NET Core and select API and unchecked the Configure for HTTPS and choose Linux for docker

— Step 4: After you setup the project from visual studio, open the command prompt and go to the folder of the project eg; c:\YourProjectFolder\YourProject and use Angular CLI command,Use ng new (Angular CLI command) https://cli.angular.io/

— Step 5: Configure the Routing/CSS/and other command dialog box as required.

— Step 6: After you create the Angular apps from the command prompt, you will see the ClientApp in your project folder and you need to modify the dockerfile and startup.cs file

— Step 7: Unchecked the Launch browser from Project solution properties and Remove the url “api/values”

— Step 8: Click on the Publish

— Step 9: Change the publish folder eg; C:\YourProjectFolder\YourProjectName\wwwroot . It should publish the file to wwwroot folder of your project.

— Step 10: Click on “Create Profile”

— Step 11: Click on “Publish”

— Step 12: Click on “Publish Container to AWS”

— Step 13: Choose the Region, Docker Repository and Tag and click on Next

Note : It will create new Docker Repository, if the docker repository doesnot exist.

— Step 13: Choose ECS Cluster, Launch Type, CPU Maximum (vCPU), Memory Maximum (GB) according to the requirement of your application.

  • VPC Subsets, Security Group
  • Checked the Assign Public IP address

— Step 14: Create Load Balancer Name (As from this you will get the public url to access your application)

— Step 15 : Attach policies from “IAM” in Roles — ecsTaskExecutionRole or you can create new Role also.

— Step 16: Click on the publish

Finally, Angular 8 with .NET Core in Linux Container application will deploy in the AWS Container.

— With Visual Studio its so simple to publish the application in AWS using AWS Toolkit.

So that’s it. We’ve managed to run our application on AWS inside ECS.

--

--

Jay Raj Mishra

Researcher | Solution Developer | Tech Enthusiast | Being Agile | techWebinarNepal Founder