Configuration Guide#
HASTE environments are configured with the Azure Developer CLI. Every setting is
supplied with azd env set <NAME> <value> before azd up; the values are
read by infra/main.bicepparam at provision time and
by the deploy hooks in deploy/. Defaults live in
main.bicepparam, so an unset variable falls back to a sensible value.
This guide documents each configuration mode. For the end-to-end workflow, see
setup/README.md and deployment.md.
Contents#
Core settings#
Variable |
Default |
Purpose |
|---|---|---|
|
|
Prefix for all resource names. Generic default; override per deployment. |
|
|
Per-environment suffix; keeps names unique. |
|
|
Azure region. |
|
— |
APIM publisher email (required). |
|
|
APIM publisher name. |
|
env RG |
Resource group holding bring-your-own shared resources (Batch, ACR). |
|
— |
Shared Azure Container Registry name (without |
Resource names are HASTE_RESOURCE_PREFIX + HASTE_RANDOM_SUFFIX based (not
azd’s resourceToken) so what-if stays clean against existing deployments.
Project detail performance#
The API and queue workers use the following optional runtime settings. Blob executor and project cache settings are validated at process startup. Metadata map and artifact worker limits are validated when their associated operation is first used; invalid settings may therefore fail a later request or job.
Function App setting |
Default |
Allowed |
Purpose |
|---|---|---|---|
|
16 |
1–64 |
Process-wide blocking Blob I/O budget. |
|
8 |
1–64 |
Per-map limit within the process budget. |
|
8 |
1–64 |
Per-artifact limit within the process budget. |
|
15 |
0–300 |
Process-local response freshness; browsers must revalidate ( |
|
64 |
1–512 |
Maximum cached project response variants per worker. |
These controls have code defaults and are not yet exposed as Bicep parameters. Treat IaC parameterization of non-default values as a separate deployment change. Load-test worker-count overrides with concurrent requests before production rollout.
Batch (create vs. bring-your-own)#
HASTE runs GPU workloads on Azure Batch. Both the Batch account and the GPU pool can either be created in the environment resource group or reused from a shared resource group.
Variable |
Default |
Values |
Purpose |
|---|---|---|---|
|
|
|
Create a Batch account in the env RG, or reference a shared one. |
|
— |
account name |
Required when the account mode is |
|
|
|
Create the GPU pool, or reference an existing one. |
|
— |
pool resource id |
Required when the pool mode is |
Additional pool parameters (batchPoolVmSize, batchPoolMaxNodes,
batchPoolSubnetName) have defaults in infra/main.bicep
and can be overridden by adding them to main.bicepparam.
Common combinations:
Self-contained fork —
HASTE_BATCH_ACCOUNT_MODE=CreateandHASTE_BATCH_POOL_MODE=Createcreate a Batch account and a GPU pool in the environment resource group.Shared account, own pool —
HASTE_BATCH_ACCOUNT_MODE=ExistingwithHASTE_BATCH_POOL_MODE=Createcreates only a new named pool on the shared account, in the shared resource group, via a cross-RG-scoped sub-module. It never modifies the account or sibling pools.Shared account and pool — both set to
Existingreference the account and pool for app-settings wiring only; nothing is written to the shared resource group.
When the pool is created on a shared account, review what-if against both
the environment resource group and HASTE_SHARED_RESOURCE_GROUP, and make sure
the deploy identity has pool-write on the shared account.
hastegeo wheel pinning#
The Function Apps install hastegeo from a published wheel, but
api/*/requirements.txt commit -e ../../hastelib as the default install source
so docker-compose and local Function builds work straight from the checked-out
tree with no wheel publish.
That relative path does not resolve inside a deployment package — only
api/<app> is uploaded — so the line must be rewritten to the published wheel
before packaging. CI does this in
deploy_apps.sh;
azd does it with a pair of hooks:
Hook |
Script |
Purpose |
|---|---|---|
|
Resolve the wheel URL and pin it into the |
|
|
Restore the editable source from a verbatim backup. |
Variable |
Default |
Purpose |
|---|---|---|
|
(blank) |
Exact |
Four things worth knowing:
The hooks are declared per service in
azure.yaml, not at the root. Root-level hooks fire around the matchingazdcommand, so a rootprepackageruns forazd packagebut is skipped byazd deploy’s internal packaging step — which would ship the unresolvable editable path and fail the Oryx build.test_hastegeo_pin_hooks_are_service_scopedguards the placement.titilerhas nohastegeodependency and needs neither hook.The pin fails the deploy on error, deliberately. A mis-resolved wheel produces an app that deploys “successfully” and only breaks when a task runs.
Resolution requires
pythonand an authenticatedghCLI, because the resolver lists release assets viagh release view.
Email sender domain#
The email backend (Azure Communication Services) is provisioned in-IaC, so its connection string is a deploy-time output rather than a manually pasted secret.
Variable |
Default |
Values |
Purpose |
|---|---|---|---|
|
|
|
Sender-domain strategy. |
|
— |
domain |
Required when the type is |
AzureManaged provisions an
azurecomm.netsender domain with no DNS step — the default, best for forks and quick environments.Custom provisions a custom sender domain. DNS verification (TXT/SPF/DKIM records) lives in the customer’s DNS zone and is an out-of-band, one-time step.
No Key Vault is introduced; the connection string is wired from a listKeys()
output to the Function App settings.
Front Door#
Azure Front Door and its WAF are a feature-flagged module, deployed only when enabled.
Variable |
Default |
Purpose |
|---|---|---|
|
|
Provision Front Door + WAF in front of the app. |
Development mode#
Development mode is a dev-only switch. It must never be true in production.
Variable |
Default |
Purpose |
|---|---|---|
|
|
Anonymous Function auth + auto-provisioning of users as administrators. |
When true, the API uses anonymous auth and GetUserById auto-creates any
unknown caller as an administrator — convenient for local and throwaway
environments, unacceptable for production. When false (the default and the
production setting), Functions are key-protected (the postdeploy hook injects the
host key into the APIM backends) and unknown users are rejected until an admin
adds them.
The Docker Compose UI image pre-fills administrators in the SWA emulator’s
mock-login form. The source staticwebapp.config.json used for production is
unchanged.
Data publishing#
Enables the Published Datasets feature and the Publish action on model results. The Local target (an immutable copy in the app’s storage) is on by default; the Planetary Computer target is off until you configure it. For how the feature is used, see Publishing datasets.
Feature flags#
Variable |
Default |
Purpose |
|---|---|---|
|
|
Master switch for the Published Datasets section and the Publish action. |
|
|
Register/expose the Planetary Computer publishing target. |
|
|
Render a damage-classification COG and register the Explorer render/mosaic/tile config on Planetary Computer publish. |
Planetary Computer target#
Required to publish to a Microsoft Planetary Computer Pro GeoCatalog. The GeoCatalog is external to this template — you provision and own it (see the out-of-app setup).
Variable |
Default |
Purpose |
|---|---|---|
|
— |
GeoCatalog base URL (no trailing slash). Required for the PC target. |
|
— |
Explorer base URL used to build published-dataset links. |
|
— |
GeoCatalog ingestion-source name. Only for private publish containers; public containers need none. |
|
|
Prefix for STAC collection ids (one collection per project/event). |
|
|
STAC license id applied to published collections/items. |
|
— |
Object id of the GeoCatalog managed identity to grant Storage Blob Data Reader on HASTE storage (asset ingestion). Empty = skip the role grant. |
Publish storage and attribution#
HASTE copies published assets into a network-reachable container the GeoCatalog ingests from,
and records the operating organization as the STAC processor provider.
Variable |
Default |
Purpose |
|---|---|---|
|
— |
Storage account URL the GeoCatalog ingests published assets from. |
|
— |
Blob container (on the publish storage account) HASTE copies published PC assets into. |
The publish-store settings are an all-or-nothing pair
HASTE_PUBLISH_STORAGE_ACCOUNT_URL and HASTE_PUBLISH_BLOB_CONTAINER are used only when
both are set. If either is empty, HASTE silently falls back to referencing assets in
place from the primary artifact store — which a firewalled GeoCatalog cannot ingest, so
publishing appears configured but fails at ingestion. Set both, or neither.
| HASTE_PUBLISHING_ORGANIZATION_NAME | — | Organization operating this deployment, recorded as the STAC processor provider. Empty = omit. |
| HASTE_PUBLISHING_ORGANIZATION_URL | — | URL companion to HASTE_PUBLISHING_ORGANIZATION_NAME. |
Organization attribution and PC URLs are treated as secrets in CI
On the GitHub Actions deploy path the organization values and the GeoCatalog/publish-storage
URLs are read from environment secrets (masked in logs), while the non-sensitive feature
flags and collection prefix are environment variables. The Bicep/azd path reads them all
as HASTE_* settings.
Ingestion size limits#
These caps bound assembled uploads, remote imagery downloads, and the input files used to generate an assessment report during publishing. GitHub Environment configuration variables are the source of truth for Actions. They are not secrets. Editing a variable does not update Azure automatically.
Setting |
Default |
Read by |
What it bounds |
|---|---|---|---|
|
5 GiB |
api |
Assembled chunked upload ( |
|
8 GiB |
queues |
Each remote imagery download in a new Batch task |
|
512 MiB |
api |
Combined downloaded footprints and inference GPKG inputs for a published assessment |
Each key is written only to the app that reads it. Setting one on the wrong app is how you end up “tuning” a limit that never takes effect.
HASTE_MAX_IMAGERY_DOWNLOAD_BYTESis not enforced in any Function App. The remote fetch runs inside the imageryprep Batch container (hastegeo.workflows.prepare_imagery). The queues app reads the setting and forwards it to the Batch task as an environment setting (imagery.py); without that hand-off the container would always use the 8 GiB code default no matter what the app setting said.
Changing a limit#
First deploy the API and queues code containing
GetEffectiveLimitsand a hastegeo wheel containing the configurable assessment cap and Batch download cap forwarding. The update workflow does not deploy code or upgrade wheels.Open Settings > Environments > target environment > Environment variables. Set the desired variables from the table, for example
HASTE_MAX_IMAGERY_DOWNLOAD_BYTES=30GiB. Keep these names at environment scope; repository or organization values with the same names can otherwise be inherited when an environment variable is removed.Run Update Size Limits for that environment with
dry_run=true. Review the resolved byte counts and targets. This requires no Azure login or writes.Run it again with
dry_run=false. Review the previous and desired values, settings read-back, and HTTP sampling result in the run output and summary.
The environment needs the same AZURE_CLIENT_ID, AZURE_TENANT_ID,
AZURE_SUBSCRIPTION_ID, RESOURCE_PREFIX, and RESOURCE_SUFFIX secrets used
by application deployment. The identity needs app-settings read/write, restart,
and function-key read access on the target apps. Retain environment approval
rules and restrict who can edit these configuration variables.
Both Update Size Limits and Deploy Azure Applications read the same variables. There are no one-off size inputs to drift from deployment settings. The workflows share an environment concurrency group; coordinate local or portal changes separately. The lightweight workflow updates both apps without building or publishing code. A component deploy updates only its target app.
The shared parser accepts integer
bytes or integer sizes: 30GiB, 30GB, 512MiB. Binary units use 1024; SI units
use 1000. 030GiB means 30 GiB, not octal. Decimals, negatives, overflow-sized
values, and values outside 1 MiB to 1 TiB fail before any settings are written.
That range is a validation boundary, not a capacity recommendation: assessment
inputs expand in memory and must fit the Function App’s memory and concurrency
budget. Imagery must fit the Batch node’s disk and processing budget.
Reset and rollback#
Missing or blank variables restore the documented defaults on the next apply; they do not leave previous overrides unchanged. To reset one cap, set its variable to the table’s default explicitly and run Update Size Limits. All three desired values are applied, so review the dry run first.
To roll back, restore the previous values in GitHub and rerun the workflow. Settings changes can recycle workers; schedule them around active processing. Cross-app writes are not transactional: if the second app fails, the first may already have changed. Use the recorded previous values and per-app output to choose between retrying the desired configuration and restoring it. No automatic rollback or task cancellation is performed. Existing Batch tasks retain the cap captured when they were submitted, even after a rollback.
Infrastructure redeployment and local azd#
ARM/Bicep replaces the app-settings collection. These caps are therefore explicit parameters in main.bicep, wired to the consuming apps in functions.bicep. Never rely on an undeclared Azure setting surviving provisioning.
Local azd does not read GitHub variables. Before azd provision or azd up,
copy the environment’s resolved decimal byte counts into the same-named
azd variables. For example, for 30 GiB downloads and default remaining caps:
azd env set HASTE_MAX_UPLOAD_BYTES 5368709120
azd env set HASTE_MAX_IMAGERY_DOWNLOAD_BYTES 32212254720
azd env set PUBLISH_ASSESSMENT_MAX_TOTAL_BYTES 536870912
main.bicepparam maps these names to the bounded integer parameters. It accepts decimal bytes, not suffixes. Missing or blank values use defaults. Provisioning with stale or missing local values will replace the tuned settings, so synchronize before provisioning. See ADR-0006.
Verification scope#
The workflow reads back stored settings, requests app restarts, and samples
GET /api/GetEffectiveLimits on both apps using function-key authentication.
It fails if keys or hosts cannot be resolved or the responses do not converge.
Failures may mean old code, connectivity, authentication, or stale workers;
inspect the failure before attempting another deployment.
Matching HTTP responses are not fleet-wide verification. Several replies
can come from the same instance. Flex Consumption scales queue triggers on
instances separate from HTTP triggers, and the imagery download runs in Batch.
For download enforcement verification, submit a new imagery job, inspect its
HASTE_MAX_IMAGERY_DOWNLOAD_BYTES task environment setting, and exercise an
appropriately bounded download in that container. An HTTP check alone cannot
prove this. Existing tasks are not changed by the workflow.
To check by hand:
KEY=$(az functionapp keys list -n <funcapp> -g <rg> --query functionKeys.default -o tsv)
HOST=$(az functionapp show -n <funcapp> -g <rg> --query defaultHostName -o tsv)
curl -fsS -H "x-functions-key: $KEY" "https://$HOST/api/GetEffectiveLimits"
Do not log or share function keys. The returned instanceId identifies only
the HTTP instance that answered; unrelated caps may show defaults on that app.
Adding another tunable knob#
Update the shared resolver, both workflows’ variable mappings, each script’s app targeting, the Bicep parameters, and the appropriate worker diagnostic. Extend the configuration tests and document the default, consuming process, resource budget, and verification scope.
First-admin bootstrap#
Production uses DEVELOPMENT_MODE=false, so users are managed explicitly and are
not auto-provisioned as administrators. That creates a bootstrap problem: an
admin is needed to add the first users, but a fresh environment has none. The
postdeploy hooks solve it by seeding a first admin.
Variable |
Default |
Purpose |
|---|---|---|
|
signed-in user |
Email of the first administrator. |
Interactive deploys — if
HASTE_FIRST_ADMIN_EMAILis unset, the seed uses the signed-in deployer’s email (deploy/seed-storage-defaults.ps1), writes it intousers_acl.jsonas an administrator, and invites it to the Static Web App (deploy/invite-user.ps1).Non-interactive / CI / service-principal deploys — there is no signed-in user, so set
HASTE_FIRST_ADMIN_EMAIL. Without it, the first-admin seed is skipped and the environment has no administrator.
Both hooks are idempotent (skip-if-exists / dedup). After the first admin signs in, subsequent users are added through the app’s admin user-management flow — the first-admin seed is a one-time bootstrap, not the ongoing mechanism.
Cleaning up an environment#
Deleting an environment does not immediately free its name. APIM, Front Door, and
Batch resources are soft-deleted and retained by Azure for a day or two, so
you cannot reuse the same HASTE_RESOURCE_PREFIX + HASTE_RANDOM_SUFFIX
combination right away.
To tear down and reuse a name sooner:
# Delete the environment's resources.
azd down --force --purge
# APIM must be purged from its soft-deleted state before the name is free.
az apim deletedservice list -o table
az apim deletedservice purge --service-name <name-from-the-list> --location <region>
azd down --purge purges soft-deletable resources it manages; a shared Batch
pool created on a shared account is additive, so remove it explicitly if it
is no longer needed. Otherwise, choose a new suffix and let Azure auto-purge the
old resources.