Tuesday, June 24, 2025

Deploying two .NET images to Azure Containers App service

We will create a .NET solution involving a Blazor WASM project that communicates with a WebAPI project through REST calls. We will then dockerize both projects and have them orchestrated through docker-compose for testing purposes. Finally, we will deploy both images to Azure Container Apps.

Source Code: https://github.com/medhatelmasry/BlazorApiACR

Prerequisites

  • .NET 9.0
  • Azure CLI
  • Visual Studio Code
  • Docker Desktop

Getting Started

We will run terminal window commands that will create the following:

  1. A solution folder named BlazorApiACR
  2. A Minimal API project named WeatherApiBackend
  3. A Blazor WASM project named BlazorFrontend

Run the following commands in a suitable working directory:

mkdir BlazorApiACR
cd BlazorApiACR
dotnet new sln
dotnet new webapi --no-https -o WeatherApiBackend
dotnet sln add WeatherApiBackend/WeatherApiBackend.csproj
dotnet new blazorwasm --no-https -o BlazorFrontend
dotnet sln add BlazorFrontend/BlazorFrontend.csproj

Enable CORS in the WebAPI project

In the WeatherApiBackend/Program.cs file, add this code right before “var app = builder.Build();”:


// Add CORS Service
builder.Services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
{
    builder
        .AllowAnyOrigin()
        .AllowAnyMethod()
        .AllowAnyHeader();
}));

Also, in WeatherApiBackend/Program.cs, add this code right after “var app = builder.Build();”:

app.UseCors("CorsPolicy");

Link Frontend to Backend

We must change the Weather Forecast endpoint so that it points to the WebAPI endpoint. 

  • View WeatherApiBackend/Properties/launchSettings.json and note the value of applicationUrl. Although I will be using http://localhost:5025, you must adjust the URL to your environment.
  • Edit BlazorFrontend/Program.cs and change this statement:

builder.Services.AddScoped(
    sp => new HttpClient
    {
        BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
    }
);

TO

builder.Services.AddScoped(
    sp => new HttpClient
    {
        BaseAddress = new Uri("http://localhost:5025/")
    }
);

NOTE: Make sure you use the port number in your environment instead of 5085.

  • Edit BlazorFrontend/Pages/Weather.razor. Change:
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");

TO

forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");

Test Locally

Start the backend with:

cd WeatherApiBackend
dotnet run

Start the frontend with:

cd BlazorFrontend
dotnet watch

The Blazor app opens in your default browser. Click on the Weather tab.

Testing in an Orchestrated Docker Environment

Stop the apps in both the frontend and backend windows.

Create a text file WeatherApiBackend/Dockerfile and add to it this code for the backend WebAPI project:

# WeatherApiBackend/Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY ["WeatherApiBackend.csproj", "."]
RUN dotnet restore "WeatherApiBackend.csproj"
COPY . .
RUN dotnet build -c Release -o /app/build
RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false

FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final
WORKDIR /app
COPY --from=build /app/publish .
ENV ASPNETCORE_URLS=http://+:80
EXPOSE 80/tcp
EXPOSE 443/tcp
ENTRYPOINT ["dotnet", "WeatherApiBackend.dll"]

# docker build -t weatherapi .
# docker run -p 8080:80 weatherapi

Create a text file BlazorFrontend/Dockerfile and add to it this code for the frontend Blazor WASM project:

# BlazorFrontend/Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY ["BlazorFrontend.csproj", "./"]
RUN dotnet restore "BlazorFrontend.csproj"
COPY . .
RUN dotnet build "BlazorFrontend.csproj" -c Release -o /app/build
RUN dotnet publish "BlazorFrontend.csproj" -c Release -o /app/publish

FROM nginx:alpine AS final
WORKDIR /var/www/web
COPY --from=build /app/publish/wwwroot .
COPY ./nginx.conf /etc/nginx/nginx.conf
EXPOSE 80

# docker build -t blazorfrontend .
# docker run -p 8080:80 blazorfrontend

The frontend will be hosted in the Docker image by the nginx web server. This requires some basic configuration. Therefore, add a text file BlazorFrontend/nginx.conf with this content:

events { }
   http {
      include mime.types;
      types {
         application/wasm wasm;
       }
     server {
        listen 80;
        index index.html;
        location / {
           root /var/www/web;
           try_files $uri $uri/ /index.html =404;
        }
     }
}

Inside of the BlazorApiACR solution folder, add a text file named docker-compose.yml with this code:

services:
  webapi:
    build:
      context: ./WeatherApiBackend
      dockerfile: Dockerfile
    ports:
      - "5025:80"
    restart: always
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
    networks:
      - blazor-network
  wasm:
    build:
      context: ./BlazorFrontend
      dockerfile: Dockerfile
    depends_on:
      - webapi
    ports:
      - 8888:80
    networks:
      - blazor-network

networks:
  blazor-network:
    driver: bridge

# docker compose up --build
# docker compose down

NOTE: In the docker-compose.yml, change the port 5025 to match the port number for the WebAPI backend in your environment. 

The docker-compose.yml will build Docker images for both the backend and frontend projects and orchestrate them to work together in a virtual network. 

Make sure you start Docker Desktop. You will then run this command from inside of the BlazorApiACR solution folder:

docker compose up --build

Wait until activity in the terminal window settles down. Thereafter, visit http://localhost:8888 in your browser. Thiw is what the Weather tab in the Blazor app will look like:

Prepare Deployment to Azure Container Apps

We will be using  The Azure CLI tool az. 

a. Login to Azure using the az tool:

az login

b. Create Azure Resource Group:

az group create --name <RESOURCE_GROUP> --location <LOCATION>

az group create --name blazor-api-acr-RG --location "East US"

c. Create Azure Container Registry (ACR):

az acr create --resource-group <RESOURCE_GROUP> --name <ACR_NAME> --sku Basic

az acr create --resource-group blazor-api-acr-RG --name blapiacr --sku Basic

d. Login to Azure Container Registry:

az acr login --name <ACR_NAME>

az acr login --name blapiacr

Build and Push Backend WebAPI Image

a. Build backend WebAPI image:

docker build --platform linux/amd64 -t <ACR_NAME>.azurecr.io/backend:latest ./backend

docker build --platform linux/amd64 -t blapiacr.azurecr.io/weatherapi:latest ./WeatherApiBackend

b. Push backend WebAPI image to ACR:

docker push <ACR_NAME>.azurecr.io/backend:latest

docker push blapiacr.azurecr.io/weatherapi:latest

c. Register required providers:

az provider register --namespace Microsoft.App

az provider register --namespace Microsoft.OperationalInsights

d. Create an environment for Container Apps:

az containerapp env create --name <ENV_NAME> --resource-group <RESOURCE_GROUP> --location <LOCATION>

az containerapp env create --name blapi-container-app-env --resource-group blazor-api-acr-RG --location "East US"

NOTE: This takes a while to process, so be patient.

e. Get admin credentials (username and password) to access the registry.

az acr update -n <ACR_NAME>--admin-enabled true

az acr update -n blapiacr --admin-enabled true

f. Deploy backend app (public):

az containerapp create \
  --name backend-app \
  --resource-group <RESOURCE_GROUP> \
  --environment <ENV_NAME> \
  --image <ACR_NAME>.azurecr.io/backend:latest \
  --target-port 4000 \
  --ingress external \
  --registry-server <ACR_NAME>.azurecr.io

az containerapp create \
  --name backend-app \
  --resource-group blazor-api-acr-RG \
  --environment blapi-container-app-env \
  --image blapiacr.azurecr.io/weatherapi:latest \
  --target-port 80 \
  --ingress external \
  --registry-server blapiacr.azurecr.io

g Find backend URL

az containerapp show \
    --name backend-app \
    --resource-group <RESOURCE_GROUP> \
    --query properties.configuration.ingress.fqdn  
 
az containerapp show \
    --name backend-app \
    --resource-group blazor-api-acr-RG  \
    --query properties.configuration.ingress.fqdn 

I got the following response for the WebAPI backend:

This meants that the deployed backend can be accessed at:

https://backend-app.nicebush-967940f0.eastus.azurecontainerapps.io/weatherforecast

I pointed by browser to the above URL and experienced this response:


We now know that our WebAPI is deployed to Azure Container Apps and is alive and well. The next step is to modify our frontend app BaseAddress with the address of the backend.

Build and Push Frontend Blazor Image

a. Edit BlazorFrontend/Program.cs and and update the BaseAddress with the address of the deployed WebAPI backend. In my case I changed the statement to:

BaseAddress = new Uri("https://backend-app.nicebush-967940f0.eastus.azurecontainerapps.io/")

b. Build frontend Blazor image:

docker build - --platform linux/amd64 t <ACR_NAME>.azurecr.io/frontend:latest ./frontend

docker build --platform linux/amd64 -t blapiacr.azurecr.io/blazorclient:latest ./BlazorFrontend

c. Push frontend Blazor image to ACR:

docker push <ACR_NAME>.azurecr.io/frontend:latest

docker push blapiacr.azurecr.io/blazorclient:latest

d. Deploy frontend Blazor app (public):

az containerapp create \
  --name frontend-app \
  --resource-group <RESOURCE_GROUP> \
  --environment <ENV_NAME> \
  --image <ACR_NAME>.azurecr.io/frontend:latest \
  --target-port 80 \
  --ingress external \
  --registry-server <ACR_NAME>.azurecr.io

az containerapp create \
  --name frontend-app \
  --resource-group blazor-api-acr-RG \
  --environment blapi-container-app-env \
  --image blapiacr.azurecr.io/blazorclient:latest \
  --target-port 80 \
  --ingress external \
  --registry-server blapiacr.azurecr.io

e. Get the frontend app’s public URL:

az containerapp show --name frontend-app --resource-group <RESOURCE_GROUP> --query properties.configuration.ingress.fqdn

az containerapp show \
    --name frontend-app \
    --resource-group blazor-api-acr-RG \
    --query properties.configuration.ingress.fqdn

I got the following response for the Blazor frontend:

This meants that the deployed frontendcan be accessed at:

https://frontend-app.nicebush-967940f0.eastus.azurecontainerapps.io

I pointed by browser to the above URL and experienced this response:


Conclusion

In this article we learned how we can deploy a two project solution compring of a WebAPI backend and a Blazor frontend to Azure Container Apps. You can, similarly, deploy and combination of apps with a variety of technolopgies like Node, React, Java, etc....


No comments:

Post a Comment