Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 13 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This repo contains the implementation that backs the [Deploy microservices with

## Introduction

Fabrikam inc has created a new operations team, and under its organization is a brownfield application called Drone Delivery. This application been running for a while in Azure Kubernetes Service (AKS), and while they are obtaining the benefits of containers to run microservices and Kubernetes to host them, it has been discovered that they are not making use of any of the advance features of AKS like custom service mesh or autoscaling among others.
Fabrikam inc has created a new operations team, and under its organization is a brownfield application called Drone Delivery. This application has been running for a while in Azure Kubernetes Service (AKS), and while they are obtaining the benefits of containers to run microservices and Kubernetes to host them, it has been discovered that they are not making use of any of the advanced features of AKS like custom service mesh or autoscaling among others.

The team has detected an opportunity to simplify and be more efficient at the DevOps level, and this is why they are now looking into Azure Container Apps to evaluate hosting Fabrikam Drone Delivery. This will allow them to publish and run containerized microservices at scale, faster than before, reducing the complexity, saving resources by using scale-to-zero, built-in autoscaling capability, and without losing all the container advantages they love.

Expand All @@ -21,7 +21,7 @@ This repository guides you through the process of running a single workload comp

For more information on how the Container Apps features are being used in this reference implementation, please take a look below:

- [HTTPS ingress, this allows to expose the Ingestion service to internet.](https://learn.microsoft.com/azure/container-apps/ingress-overview)
- [HTTPS ingress, this allows to expose the Ingestion service to the internet.](https://learn.microsoft.com/azure/container-apps/ingress-overview)
- [Internal service discovery, Delivery, DroneScheduler and Package services must be internally reachable by Workflow service](https://learn.microsoft.com/azure/container-apps/connect-apps)
- [Use user-assigned identities when authenticating into Azure KeyVault from Delivery and DroneScheduler services](https://learn.microsoft.com/azure/container-apps/managed-identity#add-a-user-assigned-identity)
- [Securely manage secrets for Package, Ingestion and Workflow services](https://learn.microsoft.com/azure/container-apps/manage-secrets)
Expand All @@ -37,7 +37,7 @@ Following the steps below will result in the creation of the following Azure res
| Object | Purpose |
| :------------------------------------- | :------------------------------------------------------ |
| An Azure Container App Environment | This is the managed Container App environment where Container Apps are deployed |
| Five Azure Container Apps | These are the Azure resources that represents the five Fabrikam microservices in the Azure Container App environment |
| Five Azure Container Apps | These are the Azure resources that represent the five Fabrikam microservices in the Azure Container App environment |
| An Azure Container Registry | This is the private container registry where all Fabrikam workload images are uploaded and later pulled from the different Azure Container Apps |
| An Azure Log Analytics Workspace | This is where all the Container Apps logs are sent, along with Azure Diagnostics on all services |
| An Azure Application Insights instance | All services are sending trace information to a shared Azure Application Insights instance |
Expand Down Expand Up @@ -93,9 +93,16 @@ Following the steps below will result in the creation of the following Azure res
1. Create a resource group for your deployment.

```bash
export PREREQS_DEPLOYMENT_NAME=workload-stamp-prereqs-${LOCATION}
az group create -n rg-shipping-dronedelivery-${LOCATION} -l ${LOCATION}
```

1. Deploy all the dependencies of the various microservices that comprise the workload.

az deployment sub create --name $PREREQS_DEPLOYMENT_NAME --location ${LOCATION} --template-file ./workload/workload-stamp-prereqs.bicep --parameters resourceGroupLocation=${LOCATION}
> None of these resources are for the application platform hosting the workload, but instead are tied directly to the drone delivery workload. For example, the per-microservice Key Vault, the per-microservice data stores, the message queue, logging sinks, etc. These same resources would exist no matter if the application platform was Azure Container Apps, Kubernetes, or App Service.

```bash
# [This takes about 22 minutes.]
az deployment group create -n workload-stamp -g rg-shipping-dronedelivery-${LOCATION} -f ./workload/workload-stamp.bicep
```

1. Get the user identities.
Expand All @@ -113,15 +120,6 @@ Following the steps below will result in the creation of the following Azure res
until az ad sp show --id $WORKFLOW_PRINCIPAL_ID &> /dev/null ; do echo "Waiting for Microsoft Entra ID propagation" && sleep 5; done
until az ad sp show --id $PACKAGE_ID_PRINCIPAL_ID &> /dev/null ; do echo "Waiting for Microsoft Entra ID propagation" && sleep 5; done
until az ad sp show --id $INGESTION_ID_PRINCIPAL_ID &> /dev/null ; do echo "Waiting for Microsoft Entra ID propagation" && sleep 5; done
``

1. Deploy all the dependencies of the various microservices that comprise the workload.

> None of these resources are for the application platform hosting the workload, but instead are tied directly to the drone delivery workload. For example, the per-microservice Key Vault, the per-microservice data stores, the message queue, logging sinks, etc. These same resources would exist no matter if the application platform was Azure Container Apps, Kubernetes, or App Service.

```bash
# [This takes about 18 minutes.]
az deployment group create -n workload-stamp -g rg-shipping-dronedelivery-${LOCATION} -f ./workload/workload-stamp.bicep -p droneSchedulerPrincipalId=$DRONESCHEDULER_PRINCIPAL_ID -p workflowPrincipalId=$WORKFLOW_PRINCIPAL_ID -p deliveryPrincipalId=$DELIVERY_PRINCIPAL_ID -p ingestionPrincipalId=$INGESTION_ID_PRINCIPAL_ID -p packagePrincipalId=$PACKAGE_ID_PRINCIPAL_ID
```

1. Build, tag, and host the five microservice container images in ACR.
Expand Down Expand Up @@ -192,7 +190,7 @@ Following the steps below will result in the creation of the following Azure res
echo -e "\nIngestion Config:\nINGESTION_NAMESPACE_NAME=${INGESTION_NAMESPACE_NAME}\nINGESTION_NAMESPACE_SAS_NAME=${INGESTION_NAMESPACE_SAS_NAME}\nINGESTION_NAMESPACE_SAS_KEY=${INGESTION_NAMESPACE_SAS_KEY}\nINGESTION_QUEUE_NAME=${INGESTION_QUEUE_NAME}"
```

If any of the config values were empty above, please stop and troubleshoot before proceeding.
If any of the configuration values above are empty, stop and troubleshoot before proceeding.

1. Deploy the Container Apps ARM template.

Expand Down Expand Up @@ -306,7 +304,6 @@ az containerapp revision restart -g rg-shipping-dronedelivery --app <containerap

```bash
az group delete -n rg-shipping-dronedelivery-${LOCATION} -y
az group delete -n rg-shipping-dronedelivery-${LOCATION}-acr -y
```

1. Purge deleted Key Vaults related to this deployment.
Expand Down
4 changes: 2 additions & 2 deletions acrpull-roleassignment.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ param containerAppName string
/*** EXISTING RESOURCE ***/

@description('Existing container registry')
resource existingContainerRegistry 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' existing = {
resource existingContainerRegistry 'Microsoft.ContainerRegistry/registries@2025-05-01-preview' existing = {
name: containerRegistryName
}

@description('Built-in ACR Pull role')
resource builtInAcrPullRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
resource builtInAcrPullRole 'Microsoft.Authorization/roleDefinitions@2022-05-01-preview' existing = {
scope: subscription()
name: '7f951dda-4ed3-4680-a7ca-43fe172d538d'
}
Expand Down
10 changes: 5 additions & 5 deletions container-http.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -64,25 +64,25 @@ var hasIngress = (containerPort == -1) ? false : true
/*** EXISTING RESOURCE ***/

@description('Resource group of the existing container registry')
resource containerRegistryResourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' existing = {
resource containerRegistryResourceGroup 'Microsoft.Resources/resourceGroups@2025-04-01' existing = {
scope: subscription()
name: split(containerRegistryResourceId, '/')[4]
}

@description('Existing container registry')
resource existingContainerRegistry 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' existing = {
resource existingContainerRegistry 'Microsoft.ContainerRegistry/registries@2025-05-01-preview' existing = {
scope: containerRegistryResourceGroup
name: split(containerRegistryResourceId, '/')[8]
}

@description('Resource group of the existing managed identity')
resource managedIdentityResourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' existing = {
resource managedIdentityResourceGroup 'Microsoft.Resources/resourceGroups@2025-04-01' existing = {
scope: subscription()
name: split(containerAppUserAssignedResourceId, '/')[4]
}

@description('Existing managed identity for this service')
resource existingManagedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = {
resource existingManagedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2025-01-31-preview' existing = {
scope: managedIdentityResourceGroup
name: split(containerAppUserAssignedResourceId, '/')[8]
}
Expand All @@ -100,7 +100,7 @@ module acrPull './acrpull-roleassignment.bicep' = {
}
}

resource containerApp 'Microsoft.App/containerApps@2022-11-01-preview' = {
resource containerApp 'Microsoft.App/containerApps@2025-02-02-preview' = {
name: containerAppName
location: location
dependsOn: [
Expand Down
6 changes: 3 additions & 3 deletions environment.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,20 @@ param logAnalyticsResourceId string
/*** EXISTING RESOURCES ***/

@description('Resource group of the provided log analytics workspace.')
resource laResourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' existing = {
resource laResourceGroup 'Microsoft.Resources/resourceGroups@2025-04-01' existing = {
name: split(logAnalyticsResourceId, '/')[4]
scope: subscription(split(logAnalyticsResourceId, '/')[2])
}

resource la 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
resource la 'Microsoft.OperationalInsights/workspaces@2025-02-01' existing = {
name: split(logAnalyticsResourceId, '/')[8]
scope: laResourceGroup
}

/*** RESOURCES ***/

@description('The Azure Container Apps Environment')
resource cae 'Microsoft.App/managedEnvironments@2022-11-01-preview' = {
resource cae 'Microsoft.App/managedEnvironments@2025-02-02-preview' = {
name: 'cae-shipping-dronedelivery'
location: location
properties: {
Expand Down
57 changes: 0 additions & 57 deletions workload/nested_workload-stamp-prereqs.bicep

This file was deleted.

4 changes: 2 additions & 2 deletions workload/nested_workload-stamp.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ param geoRedundancyLocation string
@description('Azure Container Registry name.')
param acrName string

resource acr 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' = {
resource acr 'Microsoft.ContainerRegistry/registries@2025-05-01-preview' = {
name: acrName
sku: {
name: 'Premium'
Expand Down Expand Up @@ -61,7 +61,7 @@ resource acr 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' = {
}
}

resource acrGeoRedundancyLocation 'Microsoft.ContainerRegistry/registries/replications@2023-01-01-preview' = {
resource acrGeoRedundancyLocation 'Microsoft.ContainerRegistry/registries/replications@2025-05-01-preview' = {
parent: acr
name: geoRedundancyLocation
location: geoRedundancyLocation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.10" />
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@

<ItemGroup>
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.9" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="MSTest.TestAdapter" Version="4.0.0" />
<PackageReference Include="MSTest.TestFramework" Version="4.0.0" />
<PackageReference Include="MSTest.TestAdapter" Version="4.0.1" />
<PackageReference Include="MSTest.TestFramework" Version="4.0.1" />
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@
<PackageReference Include="Azure.Identity" Version="1.17.0" />
<PackageReference Include="Azure.Messaging.EventHubs" Version="5.12.2" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.23.0" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.54.0" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.54.1" />
<PackageReference Include="Microsoft.Extensions.Logging.ApplicationInsights" Version="2.23.0" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="7.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="StackExchange.Redis" Version="2.9.25" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageReference Include="StackExchange.Redis" Version="2.9.32" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.6" />
</ItemGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,6 @@ public void ConfigureServices(IServiceCollection services)
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

// Configure AppInsights
var kubernetesEnricher = Configuration["KubernetesEnricher"];
if (kubernetesEnricher != null && "true".Equals(kubernetesEnricher))
{
services.AddApplicationInsightsKubernetesEnricher();
}
services.AddApplicationInsightsTelemetry(Configuration);

// Add health check
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="KubernetesClient" Version="17.0.14" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.9" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.10" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.3" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,15 @@
<ItemGroup>
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.4.0" />
<PackageReference Include="Azure.Identity" Version="1.17.0" />
<PackageReference Include="KubernetesClient" Version="17.0.14" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.23.0" />
<PackageReference Include="Microsoft.Extensions.Logging.ApplicationInsights" Version="2.23.0" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="7.0.2" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.54.0" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.54.1" />
<PackageReference Include="Microsoft.FeatureManagement.AspNetCore" Version="4.3.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.2" />
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.6" />
</ItemGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,6 @@ public void ConfigureServices(IServiceCollection services)
services.AddFeatureManagement();

// Configure AppInsights
var kubernetesEnricher = Configuration["KubernetesEnricher"];
if (kubernetesEnricher != null && "true".Equals(kubernetesEnricher))
{
services.AddApplicationInsightsKubernetesEnricher();
}
services.AddApplicationInsightsTelemetry(Configuration);

// Add framework services.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="KubernetesClient" Version="17.0.14" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.9" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.3" />
Expand Down
Loading