Skip to content
Merged
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
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,18 @@ For more information, see [Azure CLI interception](https://docs.localstack.cloud

## Create an Azure Kubernetes Service (AKS) cluster

Before deploying any sample you must first provision an AKS cluster. Two interchangeable scripts are provided under [scripts/](scripts/); both produce an equivalent cluster and differ only in the type of managed identity assigned to the cluster:
Before deploying any sample you must first provision an AKS cluster. Four scripts under [scripts/](scripts/) create it; they all produce the `local-aks-test` cluster the samples expect and differ in how much they provision around it:

| Script | Cluster identity | When to use |
| ------ | ---------------- | ----------- |
| [scripts/01-system-assigned-managed-identity.sh](scripts/01-system-assigned-managed-identity.sh) | System-assigned managed identity | Simplest option. Azure creates and manages the identity lifecycle together with the cluster. |
| [scripts/01-user-assigned-managed-identity.sh](scripts/01-user-assigned-managed-identity.sh) | User-assigned managed identity | Use when you need a stable, pre-created identity that can be reused across resources and granted role assignments ahead of time. |
| [scripts/01-basic-cluster.sh](scripts/01-basic-cluster.sh) | System-assigned managed identity | Fastest and cheapest: no questions, one node that the cluster autoscaler can grow to three, Workload ID, Entra ID with Azure RBAC and the container registry, with the Azure Files and Azure Disk CSI drivers and the snapshot controller turned off. A good fit for demos; it runs every sample except the Azure Files and in-cluster PostgreSQL ones. |
| [scripts/01-configurable-cluster.sh](scripts/01-configurable-cluster.sh) | System-assigned or user-assigned, your choice | Asks which optional features to provision, or reads the answers from environment variables. Pressing Enter at every question provisions the same features as the two scripts below. |
| [scripts/01-system-assigned-managed-identity.sh](scripts/01-system-assigned-managed-identity.sh) | System-assigned managed identity | The full, production-like cluster. Azure creates and manages the identity lifecycle together with the cluster. |
| [scripts/01-user-assigned-managed-identity.sh](scripts/01-user-assigned-managed-identity.sh) | User-assigned managed identity | The full cluster, with a stable, pre-created identity that can be reused across resources and granted role assignments ahead of time. |

Both scripts are idempotent: they check whether each resource already exists before creating it, so they can be safely re-run. They provision the following building blocks:
[scripts/README.md](scripts/README.md) details what each script provisions, which samples need which feature, and the variables of the configurable script.

All four scripts are idempotent: they check whether each resource already exists before creating it, so they can be safely re-run. The two managed-identity scripts, and the configurable one with every default, provision the following building blocks:

- A resource group and a Log Analytics workspace (wired to the cluster through the monitoring add-on / Azure Monitor for containers).
- A dedicated virtual network (`10.0.0.0/8`) with three subnets: `SystemSubnet` (system node pool), `UserSubnet` (user node pool), and `AzureBastionSubnet`.
Expand All @@ -118,13 +122,13 @@ Both scripts are idempotent: they check whether each resource already exists bef

The `01-user-assigned-managed-identity.sh` script additionally creates a user-assigned managed identity, assigns it to the cluster (`--assign-identity`), and grants it the Contributor role on the node resource group and the Network Contributor role on the virtual network. The system-assigned variant grants Network Contributor on the virtual network to the auto-created cluster identity.

Both scripts finish by running `az aks get-credentials`, which merges the cluster context into your local `kubeconfig` so `kubectl` is ready to use.
All four scripts finish by running `az aks get-credentials`, which merges the cluster context into your local `kubeconfig` so `kubectl` is ready to use.

> Edit the variables at the top of the chosen script (`prefix`, `suffix`, `location`, node sizes, `aad_profile_admin_group_object_ids`, and so on) to match your environment before running it.
> Edit the variables at the top of the chosen script (`prefix`, `suffix`, `location`, node sizes, `aad_profile_admin_group_object_ids`, and so on) to match your environment before running it. The configurable and basic scripts take the Entra ID admin groups from `AAD_ADMIN_GROUP_OBJECT_IDS` instead. On Azure, `kubectl` needs [kubelogin](https://learn.microsoft.com/en-us/azure/aks/kubelogin-authentication) to sign in to a cluster with Entra ID (`az aks install-cli` installs it), and your account must be in one of its admin groups.

```bash
cd scripts
./01-system-assigned-managed-identity.sh # or ./01-user-assigned-managed-identity.sh
./01-basic-cluster.sh # or ./01-configurable-cluster.sh, ./01-system-assigned-managed-identity.sh, ./01-user-assigned-managed-identity.sh
```

The [scripts/](scripts/) folder also contains optional add-on installers you can run once the cluster is up, for example [02-install-prometheus.sh](scripts/02-install-prometheus.sh), [03-install-nginx-ingress-controller.sh](scripts/03-install-nginx-ingress-controller.sh), [04-install-gateway-api.sh](scripts/04-install-gateway-api.sh), [05-install-nginx-gateway-fabric.sh](scripts/05-install-nginx-gateway-fabric.sh), and [06-install-cert-manager.sh](scripts/06-install-cert-manager.sh).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ SECRET_RETRY_SLEEP=15
# Azure Database for PostgreSQL flexible server. Its own server, so this sample and
# web-app-postgresql-flexible-server never share data.
PG_SERVER_NAME="${PREFIX}-pgflex-appconfig-${SUFFIX}"
PG_VERSION='16'
PG_VERSION='18'
PG_SKU_TIER='Burstable'
PG_SKU_NAME='Standard_B1ms'
PG_STORAGE_SIZE_GB=32
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ if [[ $? != 0 ]]; then
--resource-group "$RESOURCE_GROUP_NAME" \
--location "$LOCATION" \
--sku "$ACR_SKU" \
--admin-enabled "true" \
--admin-enabled true \
--only-show-errors 1>/dev/null

if [ $? -eq 0 ]; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ SECRET_RETRY_SLEEP=15
# Azure Database for PostgreSQL flexible server. Its own server, so this sample and
# web-app-postgresql-flexible-server never share data.
PG_SERVER_NAME="${PREFIX}-pgflex-appconfig-${SUFFIX}"
PG_VERSION='16'
PG_VERSION='18'
PG_SKU_TIER='Burstable'
PG_SKU_NAME='Standard_B1ms'
PG_STORAGE_SIZE_GB=32
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ if [[ $? != 0 ]]; then
--resource-group "$RESOURCE_GROUP_NAME" \
--location "$LOCATION" \
--sku "$ACR_SKU" \
--admin-enabled "true" \
--admin-enabled true \
--only-show-errors 1>/dev/null

if [ $? -eq 0 ]; then
Expand Down
2 changes: 1 addition & 1 deletion samples/web-app-blob-storage/dotnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ cd scripts
| File | Description |
| ---- | ----------- |
| [`00-variables.sh`](scripts/00-variables.sh) | Defines the variables shared across the other scripts (resource names, image tag, storage account and container names, Kubernetes namespace, …). The other scripts load these values by sourcing this file. |
| [`01-deploy-resources.sh`](scripts/01-deploy-resources.sh) | Deploys the Azure resources used by this sample: the resource group, the [Azure Container Registry (ACR)](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-intro), the [Azure Blob Storage](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction) account, and the `activities` container. |
| [`01-deploy-resources.sh`](scripts/01-deploy-resources.sh) | Deploys the Azure resources used by this sample: the resource group, the [Azure Container Registry (ACR)](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-intro), the [Azure Blob Storage](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction) account, and the `activities` container with three sample activities (one blob each, uploaded only when missing). |
| [`02-build-docker-image.sh`](scripts/02-build-docker-image.sh) | Builds the Docker image for the web app from the [`src/`](src/) folder. |
| [`03-run-docker-container.sh`](scripts/03-run-docker-container.sh) | Runs the web app in a local Docker container (no Kubernetes) to validate that it starts and connects to the storage account as expected. |
| [`04-push-docker-image.sh`](scripts/04-push-docker-image.sh) | Tags and pushes the Docker image to the Azure Container Registry, on Azure or in the LocalStack emulator. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ if [[ $? != 0 ]]; then
--resource-group "$RESOURCE_GROUP_NAME" \
--location "$LOCATION" \
--sku "$ACR_SKU" \
--admin-enabled "true" \
--admin-enabled true \
--only-show-errors 1>/dev/null

if [ $? -eq 0 ]; then
Expand Down Expand Up @@ -122,6 +122,57 @@ else
echo "Blob container [$CONTAINER_NAME] already exists."
fi

# Upload the sample activities, so the app does not start with an empty list. Each activity is a blob named
# YYYY-MM-DD-HH-MM-SS-activity.txt holding the activity text, the format the app itself reads and writes; these
# are the activities the web-app-file-storage sample seeds. The names are fixed, so a re-run uploads only the
# missing ones.
SAMPLE_ACTIVITIES=(
"2026-01-05-09-00-00-activity.txt|Snorkelling at Coral Bay"
"2026-01-05-09-05-00-activity.txt|Sunset hike to the Cape lighthouse"
"2026-01-05-09-10-00-activity.txt|Cooking class: pasta from scratch"
)
SAMPLE_ACTIVITIES_DIR=$(mktemp -d)

for SAMPLE_ACTIVITY in "${SAMPLE_ACTIVITIES[@]}"; do
BLOB_NAME="${SAMPLE_ACTIVITY%%|*}"
ACTIVITY="${SAMPLE_ACTIVITY#*|}"

echo "Checking if blob [$BLOB_NAME] exists in blob container [$CONTAINER_NAME]..."
EXISTS=$(az storage blob exists \
--account-name $STORAGE_ACCOUNT_NAME \
--account-key $STORAGE_ACCOUNT_KEY \
--container-name $CONTAINER_NAME \
--name $BLOB_NAME \
--query exists \
--output tsv 2>/dev/null)

if [[ "$EXISTS" == "true" ]]; then
echo "Blob [$BLOB_NAME] already exists."
continue
fi

# The file has the blob's name, so az storage blob upload gives the blob the text/plain type of a .txt file
echo "Uploading sample activity [$ACTIVITY] as blob [$BLOB_NAME]..."
printf '%s' "$ACTIVITY" >"$SAMPLE_ACTIVITIES_DIR/$BLOB_NAME"
az storage blob upload \
--account-name $STORAGE_ACCOUNT_NAME \
--account-key $STORAGE_ACCOUNT_KEY \
--container-name $CONTAINER_NAME \
--name $BLOB_NAME \
--file "$SAMPLE_ACTIVITIES_DIR/$BLOB_NAME" \
--only-show-errors 1>/dev/null

if [ $? -eq 0 ]; then
echo "Blob [$BLOB_NAME] uploaded successfully."
else
echo "Failed to upload blob [$BLOB_NAME]."
rm -rf "$SAMPLE_ACTIVITIES_DIR"
exit 1
fi
done

rm -rf "$SAMPLE_ACTIVITIES_DIR"

# Retrieve the storage account connection string
echo "Retrieving storage account connection string for [$STORAGE_ACCOUNT_NAME]..."
AZURE_STORAGE_ACCOUNT_CONNECTION_STRING=$(az storage account show-connection-string \
Expand Down
2 changes: 1 addition & 1 deletion samples/web-app-blob-storage/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ cd scripts
| File | Description |
| ---- | ----------- |
| [`00-variables.sh`](scripts/00-variables.sh) | Defines the variables shared across the other scripts (resource names, image tag, storage account and container names, Kubernetes namespace, …). The other scripts load these values by sourcing this file. |
| [`01-deploy-resources.sh`](scripts/01-deploy-resources.sh) | Deploys the Azure resources used by this sample: the resource group, the [Azure Container Registry (ACR)](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-intro), the [Azure Blob Storage](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction) account, and the `activities` container. |
| [`01-deploy-resources.sh`](scripts/01-deploy-resources.sh) | Deploys the Azure resources used by this sample: the resource group, the [Azure Container Registry (ACR)](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-intro), the [Azure Blob Storage](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction) account, and the `activities` container with three sample activities (one blob each, uploaded only when missing). |
| [`02-build-docker-image.sh`](scripts/02-build-docker-image.sh) | Builds the Docker image for the web app from the [`src/`](src/) folder. |
| [`03-run-docker-container.sh`](scripts/03-run-docker-container.sh) | Runs the web app in a local Docker container (no Kubernetes) to validate that it starts and connects to the storage account as expected. |
| [`04-push-docker-image.sh`](scripts/04-push-docker-image.sh) | Tags and pushes the Docker image to the Azure Container Registry, on Azure or in the LocalStack emulator. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ if [[ $? != 0 ]]; then
--resource-group "$RESOURCE_GROUP_NAME" \
--location "$LOCATION" \
--sku "$ACR_SKU" \
--admin-enabled "true" \
--admin-enabled true \
--only-show-errors 1>/dev/null

if [ $? -eq 0 ]; then
Expand Down Expand Up @@ -122,6 +122,57 @@ else
echo "Blob container [$CONTAINER_NAME] already exists."
fi

# Upload the sample activities, so the app does not start with an empty list. Each activity is a blob named
# YYYY-MM-DD-HH-MM-SS-activity.txt holding the activity text, the format the app itself reads and writes; these
# are the activities the web-app-file-storage sample seeds. The names are fixed, so a re-run uploads only the
# missing ones.
SAMPLE_ACTIVITIES=(
"2026-01-05-09-00-00-activity.txt|Snorkelling at Coral Bay"
"2026-01-05-09-05-00-activity.txt|Sunset hike to the Cape lighthouse"
"2026-01-05-09-10-00-activity.txt|Cooking class: pasta from scratch"
)
SAMPLE_ACTIVITIES_DIR=$(mktemp -d)

for SAMPLE_ACTIVITY in "${SAMPLE_ACTIVITIES[@]}"; do
BLOB_NAME="${SAMPLE_ACTIVITY%%|*}"
ACTIVITY="${SAMPLE_ACTIVITY#*|}"

echo "Checking if blob [$BLOB_NAME] exists in blob container [$CONTAINER_NAME]..."
EXISTS=$(az storage blob exists \
--account-name $STORAGE_ACCOUNT_NAME \
--account-key $STORAGE_ACCOUNT_KEY \
--container-name $CONTAINER_NAME \
--name $BLOB_NAME \
--query exists \
--output tsv 2>/dev/null)

if [[ "$EXISTS" == "true" ]]; then
echo "Blob [$BLOB_NAME] already exists."
continue
fi

# The file has the blob's name, so az storage blob upload gives the blob the text/plain type of a .txt file
echo "Uploading sample activity [$ACTIVITY] as blob [$BLOB_NAME]..."
printf '%s' "$ACTIVITY" >"$SAMPLE_ACTIVITIES_DIR/$BLOB_NAME"
az storage blob upload \
--account-name $STORAGE_ACCOUNT_NAME \
--account-key $STORAGE_ACCOUNT_KEY \
--container-name $CONTAINER_NAME \
--name $BLOB_NAME \
--file "$SAMPLE_ACTIVITIES_DIR/$BLOB_NAME" \
--only-show-errors 1>/dev/null

if [ $? -eq 0 ]; then
echo "Blob [$BLOB_NAME] uploaded successfully."
else
echo "Failed to upload blob [$BLOB_NAME]."
rm -rf "$SAMPLE_ACTIVITIES_DIR"
exit 1
fi
done

rm -rf "$SAMPLE_ACTIVITIES_DIR"

# Retrieve the storage account connection string
echo "Retrieving storage account connection string for [$STORAGE_ACCOUNT_NAME]..."
AZURE_STORAGE_ACCOUNT_CONNECTION_STRING=$(az storage account show-connection-string \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ if [[ $? != 0 ]]; then
--resource-group "$RESOURCE_GROUP_NAME" \
--location "$LOCATION" \
--sku "$ACR_SKU" \
--admin-enabled "true" \
--admin-enabled true \
--only-show-errors 1>/dev/null

if [ $? -eq 0 ]; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ if [[ $? != 0 ]]; then
--resource-group "$RESOURCE_GROUP_NAME" \
--location "$LOCATION" \
--sku "$ACR_SKU" \
--admin-enabled "true" \
--admin-enabled true \
--only-show-errors 1>/dev/null

if [ $? -eq 0 ]; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ if [[ $? != 0 ]]; then
--resource-group "$RESOURCE_GROUP_NAME" \
--location "$LOCATION" \
--sku "$ACR_SKU" \
--admin-enabled "true" \
--admin-enabled true \
--only-show-errors 1>/dev/null

if [ $? -eq 0 ]; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ if [[ $? != 0 ]]; then
--resource-group "$RESOURCE_GROUP_NAME" \
--location "$LOCATION" \
--sku "$ACR_SKU" \
--admin-enabled "true" \
--admin-enabled true \
--only-show-errors 1>/dev/null

if [ $? -eq 0 ]; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ if [[ $? != 0 ]]; then
--resource-group "$RESOURCE_GROUP_NAME" \
--location "$LOCATION" \
--sku "$ACR_SKU" \
--admin-enabled "true" \
--admin-enabled true \
--only-show-errors 1>/dev/null

if [ $? -eq 0 ]; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ if [[ $? != 0 ]]; then
--resource-group "$RESOURCE_GROUP_NAME" \
--location "$LOCATION" \
--sku "$ACR_SKU" \
--admin-enabled "true" \
--admin-enabled true \
--only-show-errors 1>/dev/null

if [ $? -eq 0 ]; then
Expand Down
2 changes: 1 addition & 1 deletion samples/web-app-in-cluster-postgresql/dotnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

This sample demonstrates a ASP.NET Core Razor Pages single-page web application called *Vacation Planner* hosted on an [Azure Kubernetes Service (AKS)](https://learn.microsoft.com/en-us/azure/aks/what-is-aks) cluster in the cloud on Azure or locally in the LocalStack emulator for Azure. The app runs in a dedicated namespace and stores activity data in the `activities` table of the `PlannerDB` database on an **in-cluster PostgreSQL database** — a primary plus two streaming-replica pods deployed as a Kubernetes [StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/), rather than a managed service such as Azure Database for PostgreSQL flexible server.

The database runs entirely inside the cluster: PostgreSQL 16 pods are backed by Azure managed-disk `PersistentVolumeClaim`s, and they are exposed through three `ClusterIP` services — a headless service for stable per-pod DNS, a *primary* (write) endpoint targeting the pod-0 leader, and a *read* endpoint that round-robins across all replicas. The application connects to the primary (write) endpoint using a dedicated application user (`testuser`) rather than the `postgres` superuser, and the deployment seeds the `activities` table with a handful of sample plans so the app shows data on first load.
The database runs entirely inside the cluster: PostgreSQL 18 pods are backed by Azure managed-disk `PersistentVolumeClaim`s, and they are exposed through three `ClusterIP` services — a headless service for stable per-pod DNS, a *primary* (write) endpoint targeting the pod-0 leader, and a *read* endpoint that round-robins across all replicas. The application connects to the primary (write) endpoint using a dedicated application user (`testuser`) rather than the `postgres` superuser, and the deployment seeds the `activities` table with a handful of sample plans so the app shows data on first load.

Before installing the sample, make sure to create an [Azure Kubernetes Service (AKS)](https://learn.microsoft.com/en-us/azure/aks/what-is-aks) cluster by using one of the following scripts:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ if [[ $? != 0 ]]; then
--resource-group "$RESOURCE_GROUP_NAME" \
--location "$LOCATION" \
--sku "$ACR_SKU" \
--admin-enabled "true" \
--admin-enabled true \
--only-show-errors 1>/dev/null

if [ $? -eq 0 ]; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ spec:
fsGroup: 999 # makes the Azure disk PVC writable by postgres
initContainers:
- name: init-replica
image: postgres:16
image: postgres:18
command: ["/bin/bash", "/scripts/replica-init.sh"]
env:
- name: PGDATA
Expand All @@ -165,7 +165,7 @@ spec:
mountPath: /scripts
containers:
- name: postgres
image: postgres:16
image: postgres:18
ports:
- name: postgres
containerPort: 5432
Expand Down
Loading
Loading