Describes the Partner Center managed-application bundle and how it is built. Three files ship: the generated mainTemplate.json (never hand-edited; built from infra/main.bicep), the hand-written createUiDefinition.json (Portal wizard) and viewDefinition.json (managed-RG blade). It documents the image-distribution model — the customer deployment can't reach the publisher's private ACR, so images are pulled anonymously from a public publisher registry acrmikeoss.azurecr.io, flipping two bicep params (imagePullAuth:'anonymous' so registries[] stays empty and the AcrPull grant is skipped, createAcr:false) while self-host/dev keep managed-identity + a private ACR over the same modules. Includes the one-time publisher-ACR setup (Standard SKU + az acr update --anonymous-pull-enabled) and per-release image push. Note: this README is flagged stale in ARCHITECTURE-ANALYSIS §11 — it describes AI-key wizard fields the current createUiDefinition.json does not contain. Read to understand the marketplace bundle contents and the anonymous public-ACR image-pull design before touching the package.
Mike AI — Azure Marketplace package
This directory contains the artefacts uploaded to Partner Center to publish Mike as an Azure Marketplace Managed Application. The customer launches the offer from the Azure Portal, fills out the wizard, and the full stack is provisioned in their own subscription with no manual steps after the wizard.
Files in the bundle
| File | Source | Purpose |
|---|---|---|
mainTemplate.json |
generated from infra/main.bicep | ARM template the Portal deploys |
createUiDefinition.json |
hand-written | Portal wizard (Basics → Database → AI providers → Networking → Review) |
viewDefinition.json |
hand-written | Custom managed-resource-group blade with backend FQDN, KV name, etc. |
mainTemplate.json is generated — do not edit it by hand. Run
scripts/package-marketplace.sh to rebuild it from infra/main.bicep and
produce a mike-marketplace-<version>.zip ready to upload to Partner Center.
Building the package
# bash (Linux / macOS / Git-Bash) — needs python on PATH
./scripts/package-marketplace.sh # uses git short SHA as version
./scripts/package-marketplace.sh --version v1.2.0
./scripts/package-marketplace.sh --out dist/ # change output directory
# PowerShell 7+ (Windows / cross-platform) — no python required
./scripts/package-marketplace.ps1
./scripts/package-marketplace.ps1 -Version v1.2.0
./scripts/package-marketplace.ps1 -OutDir dist
Outputs:
marketplace/mainTemplate.json # generated, gitignored
dist/mike-marketplace-<version>.zip # upload this to Partner Center
The script:
- Verifies
azCLI is on PATH. - Runs
az bicep build --file infra/main.bicep --outfile marketplace/mainTemplate.json. - Validates
createUiDefinition.jsonandviewDefinition.jsonparse as JSON. - Zips the three required files into a flat archive (no nested folders — Partner Center rejects them).
Wizard → bicep parameter mapping
The wizard outputs in createUiDefinition.json map 1:1 to parameters in
infra/main.bicep. The three AI-key fields (anthropicApiKey, openaiApiKey,
openaiBaseUrl) are not consumed by the bicep yet — they are surfaced in
the UI so the future wrapper template (issue 016, deliverable 4) can write them
to Key Vault via a Microsoft.Resources/deploymentScripts resource.
Image distribution
The marketplace deployment runs in the customer's subscription, so it cannot
pull from the publisher's private ACR. The chosen approach is direct
anonymous pull from a public publisher ACR: a registry called
acrmikeoss.azurecr.io is created in the publisher subscription with
anonymous pull enabled, and customer Container Apps reference its images
(acrmikeoss.azurecr.io/backend:<version>,
acrmikeoss.azurecr.io/postgrest:v12.2.3) directly at startup. No customer
ACR is provisioned, no az acr import step runs at deploy time, and no
credentials are embedded in customer subscriptions.
The marketplace flow flips two bicep parameters away from their self-hosted
defaults via createUiDefinition.json:
imagePullAuth: 'anonymous'— Container Apps'registries[]is left empty (so the platform pulls anonymously); the managed-identity module skips theAcrPullrole assignment.createAcr: false— no customer-side ACR.
Self-hosted/dev deployments (deploy.ps1) keep the defaults
(imagePullAuth='managed-identity', private ACR with MI auth). Both code
paths share the same bicep modules.
See docs/issues/azure-migration/016-marketplace-listing.md
for the full reasoning, alternatives that were rejected (mirror via
deploymentScript, scope-map tokens, public Docker Hub), and the open
verification item against certification policy 300.4.7.
Publisher ACR — one-time setup
# Run once, in the publisher's own subscription. NOT the dev subscription.
# Anonymous pull requires Standard or Premium SKU and is enabled via a
# separate `az acr update` call (the flag does not exist on `az acr create`).
az group create --name rg-mike-publisher --location westeurope
az acr create \
--resource-group rg-mike-publisher \
--name acrmikeoss \
--sku Standard
az acr update \
--name acrmikeoss \
--anonymous-pull-enabled true
Per-release: push images to the publisher ACR
Both images live in acrmikeoss under tags that match the marketplace
package version. Run before package-marketplace.{ps1,sh}:
VERSION=v1.2.0 # match the --version you'll pass to package-marketplace
# Backend — built fresh each release from this repo
az acr build --registry acrmikeoss --image backend:${VERSION} ./backend
# PostgREST — mirror once per upstream version bump (idempotent with --force)
az acr import \
--name acrmikeoss \
--source docker.io/postgrest/postgrest:v12.2.3 \
--image postgrest:v12.2.3 \
--force
scripts/release-images.ps1 (and .sh) wraps both calls into a single
command — see scripts/release-images.ps1 -h.
Local validation
Before uploading, paste createUiDefinition.json into the Portal sandbox to
preview the wizard:
https://portal.azure.com/?feature.customportal=false#blade/Microsoft_Azure_CreateUIDef/SandboxBlade
Out of scope here
- Partner Center upload (manual today; automation tracked in issue 016 §5)
- Pricing / billing model — Mike v1 ships free / BYOL only