Azure Voting App - Container Offer
This directory contains an Azure Marketplace Container Offer (Kubernetes Application) based on the Azure Voting App Redis sample.
A Container Offer installs a Helm chart via the Microsoft.KubernetesConfiguration/extensions API onto an existing AKS cluster. The customer selects their cluster during the Marketplace purchase flow.
Directory Structure
container-offer/
├── app/ # Application source code
│ ├── Dockerfile # Container image build
│ └── azure-vote/
│ ├── main.py # Flask application
│ ├── config_file.cfg # Default config
│ ├── requirements.txt # Python dependencies
│ ├── static/default.css # Stylesheet
│ └── templates/index.html # HTML template
├── helm-chart/
│ └── azure-vote/ # Helm chart
│ ├── Chart.yaml
│ ├── values.yaml
│ └── templates/
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── service.yaml
│ └── secret.yaml
├── arm-template/
│ ├── mainTemplate.json # ARM template (extension only)
│ └── createUiDefinition.json # Marketplace UI definition
└── manifest.yaml # CNAB packaging instructions
How It Works
- Application – A Python Flask app that provides a simple two-option voting interface backed by Redis.
- Helm Chart – Packages the app for Kubernetes. Supports both in-cluster Redis and external Redis (Azure Cache for Redis) via
values.yaml. - ARM Template – Contains only the
Microsoft.KubernetesConfiguration/extensionsresource. This is what Marketplace deploys onto the customer’s AKS cluster. - CNAB Bundle – The Helm chart and container image are packaged into a CNAB bundle and pushed to an ACR for Marketplace publishing.
Before You Start: Replace Placeholders
Several files contain <YOUR_...> placeholders that must be replaced with your own values before packaging or deploying. See the root README for the full placeholder reference table.
Files in this directory that need updates:
| File | Placeholders |
|---|---|
arm-template/mainTemplate.json |
<YOUR_PUBLISHER_ID>, <YOUR_OFFER_ID>, <YOUR_PLAN_ID>, <YOUR_EXTENSION_NAME> |
manifest.yaml |
<YOUR_BUNDLE_NAME>, <YOUR_PUBLISHER_DISPLAY_NAME>, <YOUR_ACR_NAME> |
helm-chart/azure-vote/values.yaml |
<YOUR_ACR_NAME> |
porter.yaml |
<YOUR_BUNDLE_NAME>, <YOUR_ACR_NAME> |
test-install-extension/mainTemplate.json |
<YOUR_PUBLISHER_ID>, <YOUR_OFFER_ID>, <YOUR_PLAN_ID>, <YOUR_EXTENSION_NAME> |
Publishing Steps
1. Build and Push the Container Image
ACR_NAME="<your-acr>"
IMAGE_TAG="1.0.0"
# Build using ACR Tasks (no local Docker required)
az acr build -r $ACR_NAME -t azure-vote-front:${IMAGE_TAG} ./app/
2. Update Helm Chart Image Reference
Edit helm-chart/azure-vote/values.yaml and set image.repository to your ACR image path.
3. Grant Microsoft Access to Your ACR
The CPA tool copies the CNAB to a Microsoft-owned registry. Grant the Marketplace ingestion service principal pull access:
# Create the service principal if it doesn't exist
az ad sp show --id 32597670-3e15-4def-8851-614ff48c1efa || \
az ad sp create --id 32597670-3e15-4def-8851-614ff48c1efa
# Grant ACR pull access
SP_ID=$(az ad sp show --id 32597670-3e15-4def-8851-614ff48c1efa --query id -o tsv)
ACR_ID=$(az acr show --name $ACR_NAME --query id -o tsv)
az role assignment create --assignee $SP_ID --scope $ACR_ID --role acrpull
# Register the PartnerCenterIngestion resource provider
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
az provider register --namespace Microsoft.PartnerCenterIngestion \
--subscription $SUBSCRIPTION_ID --wait
4. Package and Publish the CNAB Bundle Using CPA
The Container Package App (CPA) tool validates artifacts, builds the CNAB, and pushes it to your ACR. Run it via Docker:
# Pull the CPA tool
docker pull mcr.microsoft.com/container-package-app:latest
# Run it, mounting the container-offer/ directory
docker run -it \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(pwd):/data \
--entrypoint "/bin/bash" \
mcr.microsoft.com/container-package-app:latest
Inside the container:
export REGISTRY_NAME=<your-acr-name>
az login
az acr login -n $REGISTRY_NAME
cd /data/container-offer
# Validate all artifacts
cpa verify
# Build and push the CNAB bundle to ACR
cpa buildbundle
Use
cpa buildbundle --forceto overwrite an existing tag. If this CNAB is already attached to a Partner Center offer, increment the version inmanifest.yamlinstead.
5. Update ARM Template
Edit arm-template/mainTemplate.json and update the variables section to match your published offer:
"variables": {
"plan-name": "<your-plan-id>",
"plan-publisher": "<your-publisher-id>",
"plan-offerID": "<your-offer-id>",
"releaseTrain": "stable",
"clusterExtensionTypeName": "<your-publisher-id>.<your-extension-name>"
}
| Variable | Where to find it |
|---|---|
plan-name |
Partner Center > Your offer > Plan overview > Plan ID |
plan-publisher |
Partner Center > Account settings > Publisher ID |
plan-offerID |
Partner Center > Your offer > Offer ID |
releaseTrain |
Typically stable; use preview if still in testing |
clusterExtensionTypeName |
Partner Center > Plan > Technical Configuration > Cluster Extension Type, or discover via az k8s-extension extension-types list-by-cluster |
Also update the configurationSettings in the extension resource if your application uses different Helm values than the sample voting app.
6. Create the Offer in Partner Center
- Go to Partner Center
- Create a new Azure Container offer
- Under Technical configuration, reference the CNAB tag pushed to your ACR
- Upload
mainTemplate.jsonandcreateUiDefinition.json - Configure pricing and publish