|
| 1 | +--- |
| 2 | +title: Add custom prompts to your azd workflow |
| 3 | +description: Learn how to add custom prompts and parameters to the Azure Developer CLI (azd) provisioning flow using Bicep decorators and hooks. |
| 4 | +author: alexwolfmsft |
| 5 | +ms.author: alexwolf |
| 6 | +ms.date: 04/08/2026 |
| 7 | +ms.service: azure-dev-cli |
| 8 | +ms.topic: how-to |
| 9 | +ms.custom: devx-track-azdevcli |
| 10 | +ai-usage: ai-generated |
| 11 | +--- |
| 12 | + |
| 13 | +# Add custom prompts to your workflow |
| 14 | + |
| 15 | +By default, `azd up` prompts for three things: environment name, subscription, and location. However, real projects often need extra input, such as an environment type, project owner, or team name. This article describes three approaches for adding custom prompts, from simplest to most flexible. You can mix and match all three approaches in the same template. |
| 16 | + |
| 17 | +## Option 1: Selection list with the @allowed decorator |
| 18 | + |
| 19 | +The simplest way to add a custom prompt is to use the `@allowed` decorator on a Bicep parameter. When `azd` encounters a parameter with `@allowed` values and no value is provided, it displays an interactive selection list, similar to the built-in subscription and location pickers. |
| 20 | + |
| 21 | +Add the decorated parameter to your `infra/main.bicep` file: |
| 22 | + |
| 23 | +```bicep |
| 24 | +@description('The environment type') |
| 25 | +@allowed([ |
| 26 | + 'dev' |
| 27 | + 'staging' |
| 28 | + 'prod' |
| 29 | +]) |
| 30 | +param environmentType string |
| 31 | +``` |
| 32 | + |
| 33 | +When you run `azd up`, `azd` automatically presents a pick list for this parameter. The user can navigate by using arrow keys and type to filter, just like the subscription picker. |
| 34 | + |
| 35 | +No extra configuration is needed. Add the parameter to your Bicep file and `azd` handles the rest. |
| 36 | + |
| 37 | +> [!TIP] |
| 38 | +> Use the `@metadata` decorator with `azd.default` to highlight a default selection: |
| 39 | +> |
| 40 | +> ```bicep |
| 41 | +> @allowed(['dev', 'staging', 'prod']) |
| 42 | +> @metadata({ |
| 43 | +> azd: { |
| 44 | +> default: 'dev' |
| 45 | +> } |
| 46 | +> }) |
| 47 | +> param environmentType string |
| 48 | +> ``` |
| 49 | +> |
| 50 | +> For more decorator options, see [Work with Azure Developer CLI metadata for Bicep input parameters](metadata.md). |
| 51 | +
|
| 52 | +## Option 2: Free-text input |
| 53 | +
|
| 54 | +If you need open-ended input instead of a fixed list, declare a parameter with no default value. When `azd` encounters a required parameter that has no default and no value in `main.parameters.json`, it prompts the user for free-text input. |
| 55 | +
|
| 56 | +Add the parameter to your `infra/main.bicep` file: |
| 57 | +
|
| 58 | +```bicep |
| 59 | +@description('Who owns this project') |
| 60 | +param projectOwner string |
| 61 | +``` |
| 62 | +
|
| 63 | +When you run `azd up`, the user types a value and presses Enter. |
| 64 | + |
| 65 | +## Option 3: Hooks for custom logic |
| 66 | + |
| 67 | +Bicep decorators cover most scenarios, but sometimes you need more flexibility, such as dynamic lists from an API, conditional prompts, validation, or a custom menu-style UI. In these cases, use a `preprovision` hook to run a custom script before `azd provision`. |
| 68 | + |
| 69 | +### Understand the flow |
| 70 | + |
| 71 | +The hook approach works through the following chain: |
| 72 | + |
| 73 | +1. A hook script runs and prompts the user with custom menus, validation, or other logic. |
| 74 | +1. The script stores values using `azd env set VARIABLE_NAME value`. |
| 75 | +1. Values persist in `.azure/<env>/.env`. |
| 76 | +1. `main.parameters.json` references the values as `${VARIABLE_NAME}`. |
| 77 | +1. Bicep receives them as regular parameters during provisioning. |
| 78 | + |
| 79 | +### Create the hook script |
| 80 | + |
| 81 | +Create a hook script that prompts the user and stores the result. The following examples include an idempotency guard that skips the prompt on re-runs if a value is already set, and a `default` case that handles invalid input. |
| 82 | + |
| 83 | +#### [PowerShell](#tab/powershell) |
| 84 | + |
| 85 | +Create a `hooks/preprovision.ps1` file: |
| 86 | + |
| 87 | +```powershell |
| 88 | +Write-Host "========================================" |
| 89 | +Write-Host " Custom Pre-Provision Configuration" |
| 90 | +Write-Host "========================================" |
| 91 | +
|
| 92 | +# Skip prompt if value is already set |
| 93 | +$existingTeam = $null |
| 94 | +$output = azd env get-value CUSTOM_TEAM_NAME 2>&1 |
| 95 | +if ($LASTEXITCODE -eq 0 -and $output) { |
| 96 | + $existingTeam = $output.Trim() |
| 97 | +} |
| 98 | +
|
| 99 | +if ($existingTeam) { |
| 100 | + Write-Host "Team name is already set to: $existingTeam" |
| 101 | + exit 0 |
| 102 | +} |
| 103 | +
|
| 104 | +Write-Host "Available teams:" |
| 105 | +Write-Host " 1) platform-engineering" |
| 106 | +Write-Host " 2) app-development" |
| 107 | +Write-Host " 3) data-science" |
| 108 | +Write-Host " 4) devops" |
| 109 | +Write-Host " 5) Enter custom value" |
| 110 | +
|
| 111 | +$choice = Read-Host "Select a team (1-5)" |
| 112 | +$teamName = switch ($choice) { |
| 113 | + "1" { "platform-engineering" } |
| 114 | + "2" { "app-development" } |
| 115 | + "3" { "data-science" } |
| 116 | + "4" { "devops" } |
| 117 | + "5" { Read-Host "Enter custom team name" } |
| 118 | + default { |
| 119 | + Write-Error "Invalid selection: $choice" |
| 120 | + exit 1 |
| 121 | + } |
| 122 | +} |
| 123 | +
|
| 124 | +# Store in azd environment |
| 125 | +azd env set CUSTOM_TEAM_NAME $teamName |
| 126 | +Write-Host "Team name set to: $teamName" |
| 127 | +``` |
| 128 | + |
| 129 | +#### [Bash](#tab/bash) |
| 130 | + |
| 131 | +Create a `hooks/preprovision.sh` file: |
| 132 | + |
| 133 | +```bash |
| 134 | +#!/bin/sh |
| 135 | +set -e |
| 136 | + |
| 137 | +echo "========================================" |
| 138 | +echo " Custom Pre-Provision Configuration" |
| 139 | +echo "========================================" |
| 140 | + |
| 141 | +# Skip prompt if value is already set |
| 142 | +EXISTING_TEAM="" |
| 143 | +if azd env get-value CUSTOM_TEAM_NAME >/dev/null 2>&1; then |
| 144 | + EXISTING_TEAM=$(azd env get-value CUSTOM_TEAM_NAME 2>/dev/null) |
| 145 | +fi |
| 146 | + |
| 147 | +if [ -n "$EXISTING_TEAM" ]; then |
| 148 | + echo "Team name is already set to: $EXISTING_TEAM" |
| 149 | + exit 0 |
| 150 | +fi |
| 151 | + |
| 152 | +echo "Available teams:" |
| 153 | +echo " 1) platform-engineering" |
| 154 | +echo " 2) app-development" |
| 155 | +echo " 3) data-science" |
| 156 | +echo " 4) devops" |
| 157 | +echo " 5) Enter custom value" |
| 158 | + |
| 159 | +printf "Select a team (1-5): " |
| 160 | +read -r CHOICE |
| 161 | + |
| 162 | +case $CHOICE in |
| 163 | + 1) TEAM_NAME="platform-engineering" ;; |
| 164 | + 2) TEAM_NAME="app-development" ;; |
| 165 | + 3) TEAM_NAME="data-science" ;; |
| 166 | + 4) TEAM_NAME="devops" ;; |
| 167 | + 5) |
| 168 | + printf "Enter custom team name: " |
| 169 | + read -r TEAM_NAME |
| 170 | + ;; |
| 171 | + *) |
| 172 | + echo "Invalid selection: $CHOICE" >&2 |
| 173 | + exit 1 |
| 174 | + ;; |
| 175 | +esac |
| 176 | + |
| 177 | +# Store in azd environment |
| 178 | +azd env set CUSTOM_TEAM_NAME "$TEAM_NAME" |
| 179 | +echo "Team name set to: $TEAM_NAME" |
| 180 | +``` |
| 181 | + |
| 182 | +--- |
| 183 | + |
| 184 | +### Map the variable to a Bicep parameter |
| 185 | + |
| 186 | +In your `main.parameters.json` file, reference the environment variable: |
| 187 | + |
| 188 | +```json |
| 189 | +{ |
| 190 | + "parameters": { |
| 191 | + "customTeamName": { |
| 192 | + "value": "${CUSTOM_TEAM_NAME}" |
| 193 | + } |
| 194 | + } |
| 195 | +} |
| 196 | +``` |
| 197 | + |
| 198 | +In your `main.bicep` file, declare the parameter with a default so `azd` doesn't also prompt for it: |
| 199 | + |
| 200 | +```bicep |
| 201 | +@description('Team name - set by preprovision hook') |
| 202 | +param customTeamName string = '' |
| 203 | +``` |
| 204 | + |
| 205 | +### Enable the hook |
| 206 | + |
| 207 | +Add the hook configuration to your `azure.yaml`: |
| 208 | + |
| 209 | +```yaml |
| 210 | +hooks: |
| 211 | + preprovision: |
| 212 | + windows: |
| 213 | + shell: pwsh |
| 214 | + run: hooks/preprovision.ps1 |
| 215 | + interactive: true |
| 216 | + posix: |
| 217 | + shell: sh |
| 218 | + run: hooks/preprovision.sh |
| 219 | + interactive: true |
| 220 | +``` |
| 221 | +
|
| 222 | +The `interactive: true` setting binds the script to the console so it can read user input. For more information on hook configuration, see [Customize your Azure Developer CLI workflows using command and event hooks](azd-extensibility.md). |
| 223 | + |
| 224 | +## Choose the right approach |
| 225 | + |
| 226 | +| Scenario | Approach | |
| 227 | +|---|---| |
| 228 | +| Pick from a fixed list of values. | `@allowed` decorator (Option 1) | |
| 229 | +| Collect free-text input. | Parameter with no default (Option 2) | |
| 230 | +| Dynamic lists, API calls, or conditional logic. | Hook script (Option 3) | |
| 231 | +| Skip all prompts in CI/CD. | Pre-set values (see following section) | |
| 232 | + |
| 233 | +## Skip prompts in CI/CD |
| 234 | + |
| 235 | +All three options support non-interactive mode. Pre-set values before running `azd` and pass `--no-prompt`: |
| 236 | + |
| 237 | +```bash |
| 238 | +# Set hook values |
| 239 | +azd env set CUSTOM_TEAM_NAME "platform-engineering" |
| 240 | +
|
| 241 | +# Set Bicep parameter values |
| 242 | +azd env config set infra.parameters.environmentType "prod" |
| 243 | +azd env config set infra.parameters.projectOwner "ci-bot" |
| 244 | +
|
| 245 | +# Run with no prompts |
| 246 | +azd up --subscription <subscription-id> --location eastus --no-prompt |
| 247 | +``` |
| 248 | + |
| 249 | +If a required value is missing, `azd` reports exactly which parameter is missing and shows the `azd env config set` command needed to fix it. |
| 250 | + |
| 251 | +## Related content |
| 252 | + |
| 253 | +- [Work with Azure Developer CLI metadata for Bicep input parameters](metadata.md) |
| 254 | +- [Customize your Azure Developer CLI workflows using command and event hooks](azd-extensibility.md) |
| 255 | +- [Work with Azure Developer CLI environment variables](manage-environment-variables.md) |
| 256 | +- [Bicep parameters and decorators](/azure/azure-resource-manager/bicep/parameters) |
| 257 | +- [Sample repo: azd-custom-parameters](https://github.com/jongio/azd-custom-parameters) |
0 commit comments