azd hard-fails instead of re-downloading when managed bicep is the wrong architecture / non-runnable
- Dominant language
- Go
- Stars
- 569
- Forks
- 364
- Avg merge
- 2d 19h
- Merged PRs (30d)
- 136
Description
### Output from `azd version`
`azd version 1.27.1 (commit 257908961d465e98c1a6574eae9624a7954b21cf)`
### Describe the bug
On an **arm64** machine, if azd's managed bicep copy at `$AZD_CONFIG_DIR/bin/bicep` (`~/.azd/bin/bicep`) is the **wrong architecture** (e.g. an x86-64 binary left behind by an older/x64 install), `azd up` / `provision` hard-fails instead of recovering:
```
ERROR: deployment failed: step "provision" failed: initializing layer provision: failed to compile bicepparam template: ensuring bicep is installed: checking bicep version: exit code: 255, stdout: , stderr: qemu-x86_64: Could not open '/lib64/ld-linux-x86-64.so.2': No such file or directory
```
The native (arm64) azd binary runs fine — only the **managed bicep** is the wrong arch, so the OS tries to run it under `qemu-x86_64` binfmt emulation, which fails because the x64 glibc loader isn't installed.
### Root cause
In `cli/azd/pkg/tools/bicep/bicep.go`, `ensureInstalled` only downloads bicep when the file is **missing**:
```go
if errors.Is(err, os.ErrNotExist) {
... downloadBicep(...)
}
cli.path = bicepPath
ver, err := cli.version(ctx) // runs `bicep --version`
if err != nil {
return fmt.Errorf("checking bicep version: %w", err) // <-- hard fail
}
```
When a (broken) binary already exists, azd skips the download, runs `bicep --version`, that fails to execute, and azd returns the error rather than replacing the bad binary. There is no self-heal path for a non-runnable / wrong-arch / corrupted managed bicep.
Note azd intentionally does **not** fall back to a `bicep` on `PATH` (no `exec.LookPath`) — it manages its own version-pinned copy (`Version = 0.44.1`), overridable only via `AZD_BICEP_TOOL_PATH`. That's by design for reproducibility, but it means a corrupt managed copy is a dead end without manual intervention.
### Expected behavior
When the **existing** managed bicep fails its version check (can't execute — wrong arch, corrupted, or partial download), azd should re-download it **once** and retry, then fail only if the fresh copy still can't run. Freshly-downloaded binaries should not loop.
### To reproduce
1. On an arm64 Linux/WSL host, place an x86-64 bicep at `~/.azd/bin/bicep` (or have one left from a prior x64 azd install).
2. Run `azd up` (or `azd provision`).
3. Observe the `qemu-x86_64: Could not open '/lib64/ld-linux-x86-64.so.2'` failure.
### Environment
- arm64 WSL (Ubuntu, glibc). `uname -m` = `aarch64`.
- azd is native arm64; the stale `~/.azd/bin/bicep` was x86-64 (`file` reports `ELF 64-bit LSB pie executable, x86-64`).
- Deleting `~/.azd/bin/bicep` makes azd re-download the correct `bicep-linux-arm64` and resolves it (verified: `bicep --version` → `0.44.1`).
### Workarounds
- `rm ~/.azd/bin/bicep` and re-run (azd re-downloads the correct arch), **or**
- `export AZD_BICEP_TOOL_PATH="$(command -v bicep)"` to point azd at a working bicep on `PATH` (must be ≥ the pinned version).
### Proposed fix
In `ensureInstalled`, track whether we just downloaded; if the version check fails on a pre-existing binary, re-download once (via the existing `downloadBicep` atomic temp-file+rename path) and re-check. Add a unit test that seeds a non-runnable file and asserts a single re-download + success.
### Related
- #2593 (closed) — earlier arm64/M1 dev-container manifestation of a bicep-under-qemu failure.
Contributor guide
Assessment
This issue has not been assessed yet.