Skip to content
Draft
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ This repository contains comprehensive sample projects demonstrating how to deve
| [Web App and CosmosDB for NoSQL API ](./samples/web-app-cosmosdb-nosql-api/python/README.md) | Azure Web App using CosmosDB for NoSQL API |
| [Web App and Managed Identities](./samples/web-app-managed-identity/python/README.md) | Azure Web App using Managed Identities |
| [Web App and SQL Database ](./samples/web-app-sql-database/python/README.md) | Azure Web App using SQL 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 |
| [Azure Service Bus with Spring Boot](./samples/servicebus/java/README.md) | Azure Service Bus used by a Spring Boot application |

Expand Down
1 change: 1 addition & 0 deletions run-samples.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ SAMPLES=(
"samples/web-app-cosmosdb-mongodb-api/python|bash scripts/deploy.sh|bash scripts/validate.sh && bash scripts/call-web-app.sh"
"samples/web-app-managed-identity/python|bash scripts/user-assigned.sh|bash scripts/validate.sh && bash scripts/call-web-app.sh"
"samples/web-app-sql-database/python|bash scripts/deploy.sh|bash scripts/validate.sh && bash scripts/get-web-app-url.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"
)

Expand Down
59 changes: 59 additions & 0 deletions samples/web-app-custom-image/python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Azure Web App With Custom Docker Image

This sample demonstrates a Python Flask web application hosted on an Azure Web App using a custom Docker image. The deployment builds the image from the local `src/Dockerfile`, creates an Azure Container Registry resource, and configures a Linux Web App to run the custom image in the LocalStack Azure emulator.

## Architecture

The sample creates the following Azure resources:

1. **Azure Resource Group**: Logical container for all resources in the sample.
2. **Azure Container Registry**: Stores the custom Docker image metadata and credentials.
3. **Azure App Service Plan**: Linux plan used by the Web App.
4. **Azure Web App**: Runs the Flask application from the custom image.

## Prerequisites

- Docker
- Azure CLI
- azlocal CLI
- jq
- LocalStack for Azure

## Deploy

Start LocalStack for Azure and configure Azure CLI interception as described in the repository root README. Then run:

```bash
cd samples/web-app-custom-image/python
bash scripts/deploy.sh
```

The script builds the Docker image from `src/`, creates the App Service resources, and configures the Web App to use the custom image.

## Validate

```bash
bash scripts/validate.sh
```

## Invoke The App

```bash
bash scripts/call-web-app.sh
```

The app exposes:

- `/` for the HTML page
- `/api/status` for a JSON health response

## Local Docker Run

You can run the same image directly with Docker:

```bash
cd src
docker build -t vacation-planner-webapp:v1 .
docker run --rm -p 8080:80 vacation-planner-webapp:v1
curl http://127.0.0.1:8080/api/status
```
33 changes: 33 additions & 0 deletions samples/web-app-custom-image/python/scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Web App Custom Image Scripts

These scripts deploy and validate a Python Flask application running on Azure Web App for Containers.

## Deploy

```bash
bash scripts/deploy.sh
```

The deployment script creates:

- Resource group
- Azure Container Registry with admin credentials enabled
- Custom Docker image built from `src/Dockerfile`
- Linux App Service Plan
- Web App configured to use the custom image

If pushing to the emulated registry is unavailable in the current LocalStack environment, the script falls back to the local Docker image tag.

## Validate

```bash
bash scripts/validate.sh
```

## Call The Web App

```bash
bash scripts/call-web-app.sh
```

The call script first uses the LocalStack proxy endpoint and then, when available, calls the Docker host port mapped to the emulated Web App container.
36 changes: 36 additions & 0 deletions samples/web-app-custom-image/python/scripts/call-web-app.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/bash
set -euo pipefail

PREFIX='local'
SUFFIX='test'
RESOURCE_GROUP_NAME="${PREFIX}-custom-image-rg"
WEB_APP_NAME="${PREFIX}-custom-image-webapp-${SUFFIX}"

get_docker_container_name_by_prefix() {
local app_prefix="$1"
docker ps --format "{{.Names}}" | grep "^${app_prefix}" | head -1
}

get_docker_container_port_mapping() {
local container_name="$1"
local container_port="$2"
docker inspect -f "{{(index (index .NetworkSettings.Ports \"${container_port}/tcp\") 0).HostPort}}" "$container_name"
}

APP_HOST_NAME=$(az webapp show \
--name "$WEB_APP_NAME" \
--resource-group "$RESOURCE_GROUP_NAME" \
--query "defaultHostName" \
--output tsv \
--only-show-errors)

if [ -z "$APP_HOST_NAME" ]; then
echo "Failed to retrieve Web App hostname."
exit 1
fi

echo "Web App hostname: $APP_HOST_NAME"

echo "Calling Web App using $APP_HOST_NAME..."
curl -fsS "http://$APP_HOST_NAME/api/status"
echo ""
90 changes: 90 additions & 0 deletions samples/web-app-custom-image/python/scripts/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/bin/bash
set -euo pipefail

# Variables
PREFIX='local'
SUFFIX='test'
LOCATION='westeurope'
RESOURCE_GROUP_NAME="${PREFIX}-custom-image-rg"
ACR_NAME="${PREFIX}customimageacr"
APP_SERVICE_PLAN_NAME="${PREFIX}-custom-image-plan-${SUFFIX}"
APP_SERVICE_PLAN_SKU="B1"
WEB_APP_NAME="${PREFIX}-custom-image-webapp-${SUFFIX}"
IMAGE_NAME="custom-image-webapp"
IMAGE_TAG="v1"
CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)"

cd "$CURRENT_DIR" || exit

echo "Creating resource group [$RESOURCE_GROUP_NAME]..."
az group create \
--name "$RESOURCE_GROUP_NAME" \
--location "$LOCATION"

echo "Creating Azure Container Registry [$ACR_NAME]..."
az acr create \
--name "$ACR_NAME" \
--resource-group "$RESOURCE_GROUP_NAME" \
--location "$LOCATION" \
--sku Basic \
--admin-enabled true

az acr login --name $ACR_NAME

LOGIN_SERVER=$(az acr show \
--name "$ACR_NAME" \
--resource-group "$RESOURCE_GROUP_NAME" \
--query "loginServer" \
--output tsv \
--only-show-errors)

FULL_IMAGE="${LOGIN_SERVER}/${IMAGE_NAME}:${IMAGE_TAG}"
LOCAL_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}"

echo "Building custom Docker image [$LOCAL_IMAGE]..."
docker build -t "$LOCAL_IMAGE" ../src/
docker tag "$LOCAL_IMAGE" "$FULL_IMAGE"

echo "Pushing image [$FULL_IMAGE] to ACR..."
docker push "$FULL_IMAGE"
WEBAPP_IMAGE="$FULL_IMAGE"


echo "Creating Linux App Service Plan [$APP_SERVICE_PLAN_NAME]..."
az appservice plan create \
--resource-group "$RESOURCE_GROUP_NAME" \
--name "$APP_SERVICE_PLAN_NAME" \
--location "$LOCATION" \
--sku "$APP_SERVICE_PLAN_SKU" \
--is-linux

echo "Creating Web App [$WEB_APP_NAME] from custom image [$WEBAPP_IMAGE]..."

az webapp create \
--resource-group "$RESOURCE_GROUP_NAME" \
--plan "$APP_SERVICE_PLAN_NAME" \
--name "$WEB_APP_NAME" \
--container-image-name "$WEBAPP_IMAGE"

echo "Setting Web App container settings..."
az webapp config appsettings set \
--name "$WEB_APP_NAME" \
--resource-group "$RESOURCE_GROUP_NAME" \
--settings \
WEBSITE_PORT="80" \
WEBSITES_PORT="80" \
APP_NAME="Custom Image" \
IMAGE_NAME="$WEBAPP_IMAGE"

echo "Listing resources in resource group [$RESOURCE_GROUP_NAME]..."
az resource list --resource-group "$RESOURCE_GROUP_NAME" --output table

echo ""
echo "Deployment complete."
echo "Resource Group: $RESOURCE_GROUP_NAME"
echo "App Service Plan: $APP_SERVICE_PLAN_NAME"
echo "Web App: $WEB_APP_NAME"
echo "ACR: $ACR_NAME ($LOGIN_SERVER)"
echo "Image: $WEBAPP_IMAGE"
echo ""
echo "Run 'bash scripts/validate.sh' to verify the deployment."
45 changes: 45 additions & 0 deletions samples/web-app-custom-image/python/scripts/validate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/bin/bash
set -euo pipefail

PREFIX='local'
SUFFIX='test'
RESOURCE_GROUP_NAME="${PREFIX}-custom-image-rg"
ACR_NAME="${PREFIX}customimageacr"
APP_SERVICE_PLAN_NAME="${PREFIX}-custom-image-plan-${SUFFIX}"
WEB_APP_NAME="${PREFIX}-custom-image-webapp-${SUFFIX}"

echo -e "[$RESOURCE_GROUP_NAME] resource group:\n"
az group show \
--name "$RESOURCE_GROUP_NAME" \
--output table

echo -e "\n[$APP_SERVICE_PLAN_NAME] App Service Plan:\n"
az appservice plan show \
--name "$APP_SERVICE_PLAN_NAME" \
--resource-group "$RESOURCE_GROUP_NAME" \
--output table

echo -e "\n[$ACR_NAME] Azure Container Registry:\n"
az acr show \
--name "$ACR_NAME" \
--resource-group "$RESOURCE_GROUP_NAME" \
--output table

echo -e "\n[$WEB_APP_NAME] Web App:\n"
az webapp show \
--name "$WEB_APP_NAME" \
--resource-group "$RESOURCE_GROUP_NAME" \
--query "{name:name, state:state, defaultHostName:defaultHostName, kind:kind}" \
--output table

echo -e "\n[$WEB_APP_NAME] app settings:\n"
az webapp config appsettings list \
--name "$WEB_APP_NAME" \
--resource-group "$RESOURCE_GROUP_NAME" \
--query "[?name=='IMAGE_NAME' || name=='WEBSITE_PORT' || name=='WEBSITES_PORT'].[name,value]" \
--output table

echo -e "\nResources in [$RESOURCE_GROUP_NAME]:\n"
az resource list \
--resource-group "$RESOURCE_GROUP_NAME" \
--output table
4 changes: 4 additions & 0 deletions samples/web-app-custom-image/python/src/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.git
__pycache__
*.pyc
*.zip
13 changes: 13 additions & 0 deletions samples/web-app-custom-image/python/src/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

ENV PORT=80
EXPOSE 80

CMD ["python", "-m", "flask", "run", "--host=0.0.0.0", "--port=80"]
33 changes: 33 additions & 0 deletions samples/web-app-custom-image/python/src/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import os
import socket

from flask import Flask, jsonify, render_template


app = Flask(__name__)


@app.route("/")
def index():
return render_template(
"index.html",
app_name=os.environ.get("APP_NAME", "Custom Image Web App"),
image_name=os.environ.get("IMAGE_NAME", "vacation-planner-webapp:v1"),
hostname=socket.gethostname(),
)


@app.route("/api/status")
def status():
return jsonify(
{
"status": "ok",
"app": os.environ.get("APP_NAME", "Custom Image Web App"),
"image": os.environ.get("IMAGE_NAME", "vacation-planner-webapp:v1"),
"hostname": socket.gethostname(),
}
)


if __name__ == "__main__":
app.run(host="0.0.0.0", port=int(os.environ.get("PORT", "80")))
1 change: 1 addition & 0 deletions samples/web-app-custom-image/python/src/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Flask==3.1.0
Loading
Loading