An Azure Developer CLI template showing how to write hooks in Python, JavaScript, and TypeScript - multi-language hook support added in azd 1.23.15.
Before 1.23.15, hooks were limited to Bash and PowerShell. Now you can point directly at .py, .js, or .ts files and azd handles dependency management automatically.
- Azure Developer CLI 1.23.15+
- Python 3
- Node.js 18+
- An Azure subscription
azd init -t jongio/azd-hooks-languages
azd upYou'll see all three hooks fire during the workflow - Python validates before provisioning, JavaScript generates a report after, and TypeScript runs a health check at the end.
azure.yaml # Hook config - points to each script
hooks/
preprovision/
validate.py # Python validation hook
requirements.txt # colorama (auto-installed by azd)
postprovision/
report.js # JavaScript report hook
package.json # chalk (auto-installed by azd)
postup/
healthcheck.ts # TypeScript health check hook
package.json # chalk, tsx (auto-installed by azd)
infra/
main.bicep # Minimal Bicep - creates a resource group
Runs before azd provision. Validates that:
AZURE_ENV_NAMEandAZURE_LOCATIONare set- Environment name is 20 chars or less
- Environment name uses only letters, numbers, and hyphens
- Environment name doesn't start or end with a hyphen
Exits with code 1 on failure, which stops provisioning.
Runs after azd provision. Collects environment details (resource group, location, subscription) and writes a JSON report to .azure/reports/deploy-<env-name>.json. Uses path.basename to sanitize the filename.
Runs after azd up completes. Verifies all required environment values are populated and checks that the resource group actually exists in Azure via az group show. Uses typed interfaces (DeploymentConfig, CheckResult) and execFileSync for safe CLI calls.
In azure.yaml, each hook just points to the script file with a relative path:
hooks:
preprovision:
run: hooks/preprovision/validate.py
postprovision:
run: hooks/postprovision/report.js
postup:
run: hooks/postup/healthcheck.tsazd auto-detects the language from the file extension and handles the rest:
- Python (
.py): Creates a virtual environment and runspip install -r requirements.txtif present - JavaScript (
.js): Runsnpm installfrompackage.jsonif present - TypeScript (
.ts): Runs vianpx tsx- no compile step needed. Installspackage.jsondependencies first if present
Known issue in 1.23.15: The
run:+dir:syntax doesn't trigger language auto-detection. Use full relative paths (e.g.,run: hooks/preprovision/validate.py) instead ofdir: hooks/preprovision+run: validate.py.
Previously you had to wrap everything in a shell script:
# The old way - shell wrapper required
hooks:
preprovision:
shell: sh
run: |
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python validate.pyNow it's just:
hooks:
preprovision:
run: hooks/preprovision/validate.py