This repo demonstrates how to add custom prompts and parameters to the azd up flow. It covers three approaches, from simplest to most flexible.
By default (without hooks), azd prompts for:
? Enter a new environment name: myenv
? Select an Azure Subscription to use: <subscription list>
? Select an Azure location to use: <location list>
? Select environmentType: ← OPTION 1: @allowed selection list
> dev
staging
prod
? Enter projectOwner: ← OPTION 2: free-text prompt
With hooks enabled, you also get:
========================================
Custom Pre-Provision Configuration ← OPTION 3: hook-driven prompt
========================================
Available teams:
1) platform-engineering
2) app-development
3) data-science
4) devops
5) Enter custom value
Select a team (1-5):
Best for: Adding a pick-list prompt with predefined choices.
In infra/main.bicep, use the @allowed decorator on a parameter:
@description('The environment type')
@allowed([
'dev'
'staging'
'prod'
])
param environmentType stringHow it works: When azd encounters a parameter with @allowed and no value provided, it displays an interactive selection list — exactly like the built-in subscription and location pickers. The user navigates with arrow keys and can type to filter.
No extra configuration needed. Just add the parameter to your Bicep file and azd handles the rest.
Best for: Collecting open-ended user input (names, emails, project IDs).
In infra/main.bicep, declare a parameter with no default:
@description('Who owns this project')
param projectOwner stringHow it works: When azd encounters a required parameter (no default, not in parameters.json), it prompts the user for free-text input. The user types a value and presses Enter.
Best for: Complex prompts, conditional logic, API calls, validation, or when you need a menu-style UI beyond what Bicep decorators support.
This approach uses a preprovision hook that runs a script before azd provision.
- Hook script prompts the user with custom menus, validation, etc.
- Script stores values using
azd env set VARIABLE_NAME value - Values are persisted in
.azure/<env>/.env main.parameters.jsonreferences them as${VARIABLE_NAME}- Bicep receives them as regular parameters during provisioning
azure.yaml (hooks) → preprovision.ps1 / .sh → azd env set → .azure/<env>/.env
↓
main.bicep ← main.parameters.json references ${VARIABLE_NAME} ←──────┘
Uncomment the hooks section in azure.yaml:
hooks:
preprovision:
windows:
shell: pwsh
run: hooks/preprovision.ps1
interactive: true
posix:
shell: sh
run: hooks/preprovision.sh
interactive: trueThe main.parameters.json file already maps the hook variable:
{
"parameters": {
"customTeamName": {
"value": "${CUSTOM_TEAM_NAME}"
}
}
}# 1. Initialize a new azd environment
azd init
# 2. Run the full flow (provision + deploy)
azd up
# 3. Verify your custom parameter values were applied
az group show -n rg-<your-env-name> --query tags -o jsonThis section walks through how to test each option independently and in combination.
- Azure Developer CLI (azd) installed
- Azure CLI (az) installed
- An Azure subscription
- Logged in:
azd auth login
Initialize the project. This only needs to be done once — subsequent tests create new environments with azd env new.
azd init -e test-no-hooksThis tests the Bicep-native parameter prompts. Hooks are commented out by default, so no changes needed.
# Run provisioning — observe the prompts
azd up -e test-no-hooks
# Verify your custom parameter values were applied
az group show -n rg-test-no-hooks --query tags -o jsonWhat you should see:
? Enter a new environment name:— built-in (skip if you used -e flag)? Select an Azure Subscription to use:— built-in subscription picker? Select an Azure location to use:— built-in location picker? Select environmentType:— Option 1 selection list (dev/staging/prod)? Enter projectOwner:— Option 2 free-text input
Verify the values were applied:
# Check resource group tags
az group show -n rg-test-no-hooks --query tags -o json
# Expected output:
# {
# "azd-env-name": "test-no-hooks",
# "environment-type": "dev", ← your selection
# "project-owner": "your-input", ← your text input
# "team-name": "unset" ← default (no hook ran)
# }Clean up:
azd down --force --purge -e test-no-hooksEnable the preprovision hooks, then test the full flow.
Step 1 — Enable hooks in azure.yaml:
Uncomment the hooks section so it looks like:
hooks:
preprovision:
windows:
shell: pwsh
run: hooks/preprovision.ps1
interactive: true
posix:
shell: sh
run: hooks/preprovision.sh
interactive: trueStep 2 — Create a new environment and run:
azd env new test-with-hooks
azd up -e test-with-hooks
# Verify your custom parameter values were applied
az group show -n rg-test-with-hooks --query tags -o jsonWhat you should see:
- Hook runs first — custom team selection menu (1-5)
- Then the standard azd prompts (subscription, location)
? Select environmentType:— Option 1 selection list? Enter projectOwner:— Option 2 free-text input
Verify:
az group show -n rg-test-with-hooks --query tags -o json
# Expected:
# {
# "azd-env-name": "test-with-hooks",
# "environment-type": "staging", ← your selection
# "project-owner": "jane", ← your text input
# "team-name": "platform-engineering" ← from hook prompt
# }Clean up:
azd down --force --purge -e test-with-hooksDemonstrates how to bypass all prompts by providing values upfront — useful for CI/CD.
There are two ways to pre-set Bicep parameter values:
Approach A — azd env config set (recommended for azd-managed parameters):
azd env new test-preset
# Pre-set the hook value
azd env set CUSTOM_TEAM_NAME "devops" -e test-preset
# Pre-set the Bicep parameters that would otherwise prompt
azd env config set infra.parameters.environmentType "prod" -e test-preset
azd env config set infra.parameters.projectOwner "ci-bot" -e test-preset
# Provision with flags to skip built-in prompts — zero interaction
azd provision -e test-preset --subscription <your-sub-id> --location eastus --no-prompt
# Verify your custom parameter values were applied
az group show -n rg-test-preset --query tags -o jsonApproach B — Environment variables:
You can also set Bicep parameters using environment variables with the AZURE_ENV_ prefix or matching the parameter name directly. This is useful in CI/CD pipelines where you set env vars rather than running azd commands:
# These environment variables map to Bicep parameters
export AZURE_ENV_NAME="test-preset"
export AZURE_LOCATION="eastus"
export CUSTOM_TEAM_NAME="devops"
azd provision --no-promptThis example uses azd env config set (Approach A) to demonstrate the azd-native way of managing parameter values, but either approach works.
What you should see:
- No hook prompt (CUSTOM_TEAM_NAME already set, hook shows current value and skips)
- No subscription/location prompts (provided via flags)
- No environmentType/projectOwner prompts (pre-set via
azd env config set)
Clean up:
azd down --force --purge -e test-presetVerify that the hook remembers previously set values.
azd env new test-rerun
# First run — hook prompts for team selection
azd provision -e test-rerun
# Verify your custom parameter values were applied
az group show -n rg-test-rerun --query tags -o json
# Second run — hook detects existing value and skips the prompt
azd provision -e test-rerunWhat you should see on second run:
Team name is already set to: platform-engineering
Pre-provision configuration complete.
The hook detects the existing value and skips the prompt entirely.
Clean up:
azd down --force --purge -e test-rerunCheck that azd stores custom values correctly between runs.
azd env new test-env
azd env set CUSTOM_TEAM_NAME "data-science" -e test-env
# Verify it's stored
azd env get-values -e test-env | grep CUSTOM_TEAM_NAME
# Output: CUSTOM_TEAM_NAME="data-science"
# Check the raw .env file
cat .azure/test-env/.envClean up:
azd env delete -e test-env| Test | Hooks | Prompts Expected | Validates |
|---|---|---|---|
| 1 | Off | environmentType (select), projectOwner (text) | Options 1 & 2 |
| 2 | On | Team menu (hook), environmentType, projectOwner | All 3 options |
| 3 | On | None (all pre-set) | CI/CD automation |
| 4 | On | "Keep this value?" on re-run | Hook idempotency |
| 5 | N/A | None (env only) | azd env storage |
No. It depends on who will use your template:
| Audience | What to provide |
|---|---|
| Public templates (azd gallery, open source) | Both .ps1 and .sh — use windows/posix blocks in azure.yaml |
| Windows-only teams | Just .ps1 with shell: pwsh |
| Linux/macOS-only teams | Just .sh with shell: sh |
| Cross-platform with PowerShell 7 | Single .ps1 with shell: pwsh (pwsh runs everywhere) |
The windows/posix blocks in azure.yaml let azd pick the right script per OS:
hooks:
preprovision:
windows:
shell: pwsh
run: hooks/preprovision.ps1
interactive: true
posix:
shell: sh
run: hooks/preprovision.sh
interactive: true| Scenario | Use |
|---|---|
| Pick from a fixed list of values | @allowed decorator |
| Collect a free-text value | Parameter with no default |
| Dynamic list (from API, file, etc.) | Hook |
| Conditional prompts (only ask if X) | Hook |
| Validation beyond type checking | Hook |
| Set parameter based on other parameters | Hook |
Yes. Provide all values upfront so azd doesn't prompt. Two approaches:
Using azd env config set (azd-native):
# Set hook values in the environment
azd env set CUSTOM_TEAM_NAME "platform-engineering"
# Set Bicep parameter values
azd env config set infra.parameters.environmentType "prod"
azd env config set infra.parameters.projectOwner "ci-bot"
# Provide built-in params via flags
azd up --subscription <id> --location eastus --no-promptUsing environment variables (CI/CD pipelines):
export CUSTOM_TEAM_NAME="platform-engineering"
azd up --subscription <id> --location eastus --no-promptUse the @metadata decorator with azd.default:
@allowed(['dev', 'staging', 'prod'])
@metadata({
azd: {
default: 'dev'
}
})
param environmentType stringThis highlights dev as the default selection.
├── azure.yaml # azd project config + hook definitions
├── infra/
│ ├── main.bicep # Parameters with @allowed and free-text prompts
│ ├── main.parameters.json # Maps azd env variables → Bicep parameters
│ └── resources.bicep # Deploys a storage account with custom tags
└── hooks/
├── preprovision.ps1 # Custom prompt script (Windows/PowerShell)
└── preprovision.sh # Custom prompt script (Linux/macOS/Bash)