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
24 changes: 20 additions & 4 deletions .github/workflows/run-samples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,15 @@ jobs:
sudo apt-get install -y jq zip unixodbc-dev libsnappy-dev default-mysql-client postgresql-client
find . -name "*.sh" -exec chmod +x {} +

- name: Pin Azure CLI to 2.88.0
# SHORTCUT: runner image ubuntu22/20260810.260 bumped the preinstalled az to
# 2.89.0, whose `az mysql flexible-server firewall-rule create` payload the
# emulator answers with a 500 (AttributeError on a None `properties` in
# mysql_flexible/apis/firewall_rules.py). Remove this pin once the released
# emulator image parses the az 2.89 request shape. Both runner arches are
# jammy, so one version string covers amd64 and arm64.
run: sudo apt-get install -y --allow-downgrades azure-cli=2.88.0-1~jammy

- name: Install Terraform
uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
with:
Expand Down Expand Up @@ -179,16 +188,23 @@ jobs:
# Run the emulator in detached mode using the virtual environment.
# The readiness wait is generous because the architecture matrix roughly doubles
# the number of concurrent jobs, and the contention for Docker pulls and runner
# I/O pushed some starts past a 120s budget. A healthy emulator still returns as
# soon as it is ready, so a larger timeout costs nothing on the happy path.
# I/O pushed some starts past a 120s budget - and, on the slowest runners,
# occasionally past 300s as well. A healthy emulator still returns as soon as
# it is ready, so a larger timeout costs nothing on the happy path.
run: |
source .venv/bin/activate
python -m localstack_cli.cli.main start -d
python -m localstack_cli.cli.main wait -t 300
python -m localstack_cli.cli.main wait -t 600
env:
IMAGE_NAME: ${{ env.IMAGE_NAME }}:${{ env.DEFAULT_TAG }}
LOCALSTACK_AUTH_TOKEN: ${{ secrets.TEST_LOCALSTACK_AUTH_TOKEN }}
DOCKER_FLAGS: "-e MSSQL_ACCEPT_EULA=Y"
# GITHUB_API_TOKEN: the emulator's package installers query api.github.com
# (e.g. Azure Functions Core Tools releases when prebuilding the Functions
# image); anonymous calls share the runner egress IP's 60 req/h budget,
# which is routinely exhausted on GitHub-hosted runners. localstack-core's
# GitHubReleaseInstaller sends this token as a Bearer for the higher
# authenticated limit. The workflow token is ephemeral and contents:read.
DOCKER_FLAGS: "-e MSSQL_ACCEPT_EULA=Y -e GITHUB_API_TOKEN=${{ github.token }}"
LS_LOG: "DEBUG"
DISABLE_EVENTS: "1"
ACTIVATE_PRO: "1"
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

This repository contains comprehensive sample projects demonstrating how to develop and test Azure cloud applications locally using [LocalStack for Azure](https://localstack.cloud/). Each sample provides complete infrastructure-as-code templates, application code, and deployment instructions for seamless local development.

> [!NOTE]
> Azure Kubernetes Service (AKS) samples and tutorials live in a separate repository, [localstack-samples/aks-samples](https://github.com/localstack-samples/aks-samples). It covers cluster provisioning, application deployments backed by Azure data services, and standalone walkthroughs of individual AKS capabilities such as network policies, KEDA autoscaling, the Gateway API, and the Key Vault CSI driver. Everything there runs unchanged against both Azure and the emulator.

## Prerequisites

### Required Tools
Expand Down Expand Up @@ -38,6 +41,7 @@ This repository contains comprehensive sample projects demonstrating how to deve
| [Web App and MySQL Database ](./samples/web-app-mysql-flexible-server/python/README.md) | Azure Web App using MySQL Database |
| [Web App with Custom Docker Image](./samples/web-app-custom-image/python/README.md) | Azure Web App running a custom Docker image |
| [ACI and Blob Storage](./samples/aci-blob-storage/python/README.md) | Azure Container Instances with ACR, Key Vault, and Blob Storage |
| [Container Apps and Blob Storage](./samples/container-apps-blob-storage/python/README.md) | Azure Container Apps running a guestbook app from ACR with Blob Storage, secrets, revisions, replicas and scale rules |
| [Azure Service Bus with Spring Boot](./samples/servicebus/java/README.md) | Azure Service Bus used by a Spring Boot application |
| [URL Shortener](./samples/url-shortener/python/README.md) | URL shortener composing Web App, Functions, Storage, Key Vault, Service Bus and PostgreSQL |
| [Event Hubs Fraud Detection Pipeline](./samples/eventhubs/python/README.md) | Real-time payment stream processing with Event Hubs (AMQP, Kafka and HTTPS ingestion, Capture, Schema Registry), an Event Hubs-triggered Function App, Key Vault, Storage and a Web App dashboard |
Expand Down Expand Up @@ -78,6 +82,7 @@ container images Microsoft publishes for `amd64` alone, so there is no `arm64` i
| `function-app-*` | ✅ | ✅ | built from a multi-arch `python` / `node` / `dotnet` base |
| `web-app-custom-image` | ✅ | ✅ | the image the sample builds itself |
| `aci-blob-storage` | ✅ | ✅ | the image the sample builds itself |
| `container-apps-blob-storage` | ✅ | ✅ | the image the sample builds itself |
| `web-app-*` (code deployment) | ✅ | emulated | `mcr.microsoft.com/oryx/<platform>` |
| `eventhubs` | ✅ | emulated | deploys a dashboard web app (Oryx, as above) |
| `servicebus/java` | ✅ | emulated | `mcr.microsoft.com/azure-app-service/java` |
Expand Down
10 changes: 8 additions & 2 deletions run-samples.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ SAMPLES=(
"samples/web-app-postgresql-flexible-server/python|bash scripts/deploy.sh|bash scripts/validate.sh && bash scripts/call-web-app.sh"
"samples/web-app-custom-image/python|bash scripts/deploy.sh|bash scripts/validate.sh && bash scripts/call-web-app.sh"
"samples/aci-blob-storage/python|bash scripts/deploy.sh|bash scripts/validate.sh"
"samples/container-apps-blob-storage/python|bash scripts/deploy.sh|bash scripts/validate.sh"
"samples/url-shortener/python|bash scripts/deploy.sh|bash scripts/validate.sh && bash scripts/call-web-app.sh"
)

Expand All @@ -60,6 +61,7 @@ TERRAFORM_SAMPLES=(
"samples/web-app-mysql-flexible-server/python/terraform|bash deploy.sh"
"samples/web-app-postgresql-flexible-server/python/terraform|bash deploy.sh"
"samples/aci-blob-storage/python/terraform|bash deploy.sh"
"samples/container-apps-blob-storage/python/terraform|bash deploy.sh"
"samples/url-shortener/python/terraform|bash deploy.sh|bash ../scripts/validate.sh"
)

Expand All @@ -77,6 +79,7 @@ BICEP_SAMPLES=(
"samples/web-app-mysql-flexible-server/python/bicep|bash deploy.sh"
"samples/web-app-postgresql-flexible-server/python/bicep|bash deploy.sh"
"samples/aci-blob-storage/python/bicep|bash deploy.sh"
"samples/container-apps-blob-storage/python/bicep|bash deploy.sh"
"samples/url-shortener/python/bicep|bash deploy.sh|bash ../scripts/validate.sh"
)

Expand All @@ -95,8 +98,10 @@ TOTAL=${#ALL_SAMPLES[@]}
# - SQL Database is backed by mcr.microsoft.com/mssql/server — amd64-only.
# The samples below avoid all of those: Function Apps get an image built from a
# multi-arch base (arm64 support added in localstack-pro#8102), and the custom-image
# Web App and ACI samples run an image the sample itself builds. Their Cosmos DB,
# Service Bus, Storage and Front Door dependencies all publish arm64 manifests.
# Web App, ACI and Container Apps samples run an image the sample itself builds (the
# Container Apps k3d runtime images, rancher/k3s and k3d-proxy, are multi-arch).
# Their Cosmos DB, Service Bus, Storage and Front Door dependencies all publish
# arm64 manifests.
#
# "amd64-only" means *not native* — not "cannot run". The emulator never pins
# --platform, so on an arm64 host Docker pulls the amd64 manifest and runs it under
Expand All @@ -111,6 +116,7 @@ TOTAL=${#ALL_SAMPLES[@]}
# test_deploy_zip_without_basic_auth) are still marked @only_on_amd64.
ARM64_SAMPLE_DIRS=(
"samples/aci-blob-storage/python"
"samples/container-apps-blob-storage/python"
"samples/function-app-front-door/python"
"samples/function-app-managed-identity/python"
"samples/function-app-service-bus/dotnet"
Expand Down
134 changes: 134 additions & 0 deletions samples/container-apps-blob-storage/python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Guestbook on Azure Container Apps

A sample application demonstrating how to deploy a containerized Flask web app using three Azure services:

- **Azure Blob Storage** — Stores guestbook entries as a JSON blob
- **Azure Container Registry (ACR)** — Hosts the Docker container image
- **Azure Container Apps** — Runs the containerized application behind the managed environment's HTTP ingress

## Architecture

```mermaid
%%{init: {"flowchart": {"nodeSpacing": 60, "rankSpacing": 80}}}%%
flowchart TB
user((User))

subgraph env["Container Apps Managed Environment"]
ingress["HTTP ingress<br/>(public FQDN)"]
app["Container App: guestbook<br/>revisions v1 / v2, 1-3 replicas"]
end

acr["Container Registry (ACR)<br/>guestbook:v1"]
blob[("Blob Storage<br/>entries.json")]

user -->|"sign / read guestbook"| ingress
ingress -->|"routes to the latest revision"| app
app -.->|"pulls image"| acr
app -->|"reads/writes entries<br/>(secretref: storage-conn)"| blob

style env fill:#ffffff,stroke:#999999,color:#333333
```

- **Deployment flow:** The deploy script creates Storage first, then ACR, builds and pushes the container image, creates a Container Apps managed environment, and finally creates a container app that pulls from ACR. The storage connection string is stored as a Container Apps secret and injected into the container through a `secretref` environment variable.
- **At runtime:** The Flask app reads the storage connection string from its environment, connects to Blob Storage, and provides a web UI for signing and reading the guestbook. The revision that served each response is shown in the UI, so rolling out a new revision with `az containerapp update` is observable over HTTP.

## Prerequisites

- [LocalStack](https://docs.localstack.cloud/getting-started/installation/)
- [Docker](https://docs.docker.com/get-docker/)
- [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli)
- [lstk](https://github.com/localstack/lstk) (`brew install localstack/tap/lstk` or `npm install -g @localstack/lstk`)
- [Terraform](https://developer.hashicorp.com/terraform/downloads) (optional, for Terraform deployment)

## Quick Start

```bash
# Start the LocalStack Azure emulator
IMAGE_NAME=localstack/localstack-azure localstack start -d
localstack wait -t 60

# Route all Azure CLI calls to the LocalStack Azure emulator
lstk az start-interception

# Deploy all services
cd samples/container-apps-blob-storage/python
bash scripts/deploy.sh

# Validate the deployment (includes a live HTTP round trip and a revision rollout)
bash scripts/validate.sh
```

## Alternative Deployments

### Bicep

```bash
cd samples/container-apps-blob-storage/python
bash bicep/deploy.sh
```

### Terraform

```bash
cd samples/container-apps-blob-storage/python
bash terraform/deploy.sh
```

## Cleanup

```bash
# Removes all resources created by deploy.sh
bash scripts/cleanup.sh
```

## Application

The Guestbook is a Flask web application that lets visitors sign a guestbook. Entries are stored as a single JSON blob in Azure Blob Storage, so they survive replica restarts and revision switches.

### Endpoints

| Route | Method | Description |
|-------|--------|-------------|
| `/` | GET | View all guestbook entries |
| `/` | POST | Sign the guestbook |
| `/delete/<id>` | POST | Delete an entry |
| `/health` | GET | Health check (reports the serving revision) |

### Environment Variables

| Variable | Description |
|----------|-------------|
| `AZURE_STORAGE_CONNECTION_STRING` | Blob Storage connection string (injected via `secretref:storage-conn`) |
| `BLOB_CONTAINER_NAME` | Name of the blob container for entries |
| `APP_REVISION` | Revision label shown in the UI and `/health` (default: "v1") |

## Scripts

| Script | Description |
|--------|-------------|
| `scripts/deploy.sh` | Deploys Storage, ACR, the Container Apps environment, and the container app |
| `scripts/validate.sh` | Validates all resources, exercises secrets, revisions and replicas, and drives the live app over its ingress FQDN |
| `scripts/cleanup.sh` | Removes all resources created by deploy.sh |
| `bicep/deploy.sh` | Deploys all resources using a Bicep template |
| `terraform/deploy.sh` | Deploys all resources using Terraform |

## Container Apps Features Demonstrated

| Feature | Script |
|---------|--------|
| Managed environment create | deploy.sh |
| App create from a private registry (ACR) | deploy.sh |
| Secrets (`--secrets` + `secretref:` env var) | deploy.sh |
| External HTTP ingress + FQDN | deploy.sh |
| Multiple revisions mode + revision suffix | deploy.sh |
| Min / max replicas + HTTP scale rule | deploy.sh |
| Secret list / show | validate.sh |
| Revision list + rollout (`az containerapp update`) | validate.sh |
| Replica list | validate.sh |
| Live requests through the ingress FQDN | validate.sh |

## References

- [LocalStack for Azure Documentation](https://docs.localstack.cloud/azure/)
- [lstk CLI](https://docs.localstack.cloud/aws/developer-tools/running-localstack/lstk/)
- [lstk GitHub repository](https://github.com/localstack/lstk)
64 changes: 64 additions & 0 deletions samples/container-apps-blob-storage/python/bicep/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Bicep Deployment

This directory contains the Bicep template and a deployment script for provisioning Azure services in LocalStack for Azure. Refer to the [Container Apps Blob Storage](../README.md) guide for details about the sample application.

## Prerequisites

- [LocalStack for Azure](https://docs.localstack.cloud/azure/): Local Azure cloud emulator for development and testing
- [Docker](https://docs.docker.com/get-docker/): Container runtime required for LocalStack
- [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli): Azure command-line interface
- [Bicep extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-bicep): VS Code extension for Bicep language support
- [lstk CLI](https://docs.localstack.cloud/aws/developer-tools/running-localstack/lstk/): LocalStack command-line interface (proxies the Azure CLI via `lstk az`)
- [jq](https://jqlang.org/): JSON processor for scripting

### Installing lstk CLI

```bash
brew install localstack/tap/lstk # or: npm install -g @localstack/lstk
```

## Architecture Overview

The [deploy.sh](deploy.sh) script first builds and pushes the Docker image to ACR, then the [main.bicep](main.bicep) template creates the following Azure resources:

1. [Azure Storage Account](https://learn.microsoft.com/en-us/azure/storage/common/storage-account-overview): Provides blob storage for guestbook entries.
2. [Azure Container Registry](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-intro): Hosts the Docker container image.
3. [Azure Container Apps](https://learn.microsoft.com/en-us/azure/container-apps/overview): A managed environment and a container app with secrets, external HTTP ingress, and an HTTP scale rule.

The `Microsoft.App` resources pin api-version `2025-07-01`, the version the az CLI itself uses.

For more information on the sample application, see [Container Apps Blob Storage](../README.md).

## Configuration

Update the `main.bicepparam` file with your specific values:

```bicep
using 'main.bicep'

param prefix = 'local'
param suffix = 'test'
param imageName = 'guestbook'
param imageTag = 'v1'
```

## Deployment

```bash
cd samples/container-apps-blob-storage/python
bash bicep/deploy.sh
```

## Cleanup

```bash
bash scripts/cleanup.sh
```

## Related Documentation

- [Azure Bicep Documentation](https://docs.microsoft.com/en-us/azure/azure-resource-manager/bicep/)
- [Bicep Language Reference](https://docs.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions)
- [LocalStack for Azure Documentation](https://docs.localstack.cloud/azure/)
- [lstk CLI](https://docs.localstack.cloud/aws/developer-tools/running-localstack/lstk/)
- [lstk GitHub repository](https://github.com/localstack/lstk)
Loading