Skip to content

jongio/azd-custom-parameters

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Azure Developer CLI — Custom Parameters

This repo demonstrates how to add custom prompts and parameters to the azd up flow. It covers three approaches, from simplest to most flexible.

What You'll See When Running azd up

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):

The Three Options

Option 1: Selection List — @allowed Decorator (Recommended)

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 string

How 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.

Option 2: Free-Text Prompt — No Default Value

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 string

How 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.

Option 3: Hooks — Full Custom Logic

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.

How It Works

  1. Hook script prompts the user with custom menus, validation, etc.
  2. Script stores values using azd env set VARIABLE_NAME value
  3. Values are persisted in .azure/<env>/.env
  4. main.parameters.json references them as ${VARIABLE_NAME}
  5. Bicep receives them as regular parameters during provisioning

The Flow

azure.yaml (hooks)  →  preprovision.ps1 / .sh  →  azd env set  →  .azure/<env>/.env
                                                                         ↓
main.bicep  ←  main.parameters.json references ${VARIABLE_NAME}  ←──────┘

Enable Hooks

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: true

The main.parameters.json file already maps the hook variable:

{
  "parameters": {
    "customTeamName": {
      "value": "${CUSTOM_TEAM_NAME}"
    }
  }
}

Quick Start

# 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 json

Testing All Variations

This section walks through how to test each option independently and in combination.

Prerequisites

Initial Setup (Run Once)

Initialize the project. This only needs to be done once — subsequent tests create new environments with azd env new.

azd init -e test-no-hooks

Test 1: Options 1 & 2 Only (No Hooks)

This 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 json

What you should see:

  1. ? Enter a new environment name: — built-in (skip if you used -e flag)
  2. ? Select an Azure Subscription to use: — built-in subscription picker
  3. ? Select an Azure location to use: — built-in location picker
  4. ? Select environmentType:Option 1 selection list (dev/staging/prod)
  5. ? 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-hooks

Test 2: All Three Options (With Hooks)

Enable 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: true

Step 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 json

What you should see:

  1. Hook runs first — custom team selection menu (1-5)
  2. Then the standard azd prompts (subscription, location)
  3. ? Select environmentType: — Option 1 selection list
  4. ? 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-hooks

Test 3: Pre-set Values (Skip Prompts)

Demonstrates 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 json

Approach 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-prompt

This 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-preset

Test 4: Hook Re-run Behavior

Verify 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-rerun

What 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-rerun

Test 5: Verify Environment Storage

Check 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/.env

Clean up:

azd env delete -e test-env

Test Summary

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

FAQ

Do hooks need both .ps1 and .sh files?

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

When should I use @allowed vs hooks?

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

Can I skip prompts in CI/CD?

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-prompt

Using environment variables (CI/CD pipelines):

export CUSTOM_TEAM_NAME="platform-engineering"
azd up --subscription <id> --location eastus --no-prompt

How do I set a default that shows in the prompt?

Use the @metadata decorator with azd.default:

@allowed(['dev', 'staging', 'prod'])
@metadata({
  azd: {
    default: 'dev'
  }
})
param environmentType string

This highlights dev as the default selection.


File Structure

├── 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)

References

About

Azure Developer CLI - Custom Parameters Demo (Bicep @Allowed, free-text, hooks)

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages