Azure / Azure/arm-template-whatif

Regression: `what-if` fails when accessing ARM‑injected read‑only properties (`id`) on multiple resource types — works in validate & create

Open
#435 1 comment 1 reaction 0 assignees View on GitHub
Needs: Triage :mag:
Dominant language
HTML
Stars
101
Forks
21
Avg merge
3h 35m
Merged PRs (30d)
1

Description

# ❗ Regression: `what-if` fails when accessing ARM‑injected read‑only properties (`id`) on multiple resource types — works in validate & create

## Summary

`az deployment group what-if` fails when a Bicep template accesses **read‑only ARM‑injected properties** such as:

- `subnet.id` on inline subnet definitions
- `backendAddressPool.id` on Load Balancer backend pools

This is a **regression** — the same code worked until early April 2026.

`validate` and `create` both succeed. Only `what-if` fails.

The issue appears after the release of **Bicep v0.42.1**.

## Error message

Example for inline subnets:

```
InvalidTemplate - Deployment template language expression evaluation failed:
'The language expression property 'id' doesn't exist, available properties are 'name, properties'.'.
```

Example for Load Balancer backend pools:

```
The language expression property 'id' doesn't exist, available properties are 'name'.
```

## Expected behavior

- `what-if` should behave consistently with `validate` and `create`
- ARM‑injected read‑only properties (e.g., `id`) should be available during What‑If evaluation
- Accessing `subnet.id` or `backendAddressPool.id` should work as before

## Actual behavior

- `what-if` fails because it evaluates read‑only properties **before** ARM has generated them
- `validate` succeeds
- `create` succeeds
- Regression: this used to work reliably before April 2026
- Multiple resource types appear affected (not only VNets)
- Setting **any** value on **any** ARM‑injected read‑only property (not just `id`) makes the issue disappear — assigning even a dummy string prevents the What‑If failure

---

# 🔍 Minimal Repro 1 — Inline Subnets (VNet)

```bicep
param location string = resourceGroup().location

resource vnet 'Microsoft.Network/virtualNetworks@2025-05-01' = {
name: 's24-vnet-weu-d-module-test-sa'
location: location
properties: {
addressSpace: {
addressPrefixes: ['10.157.28.0/24']
}
subnets: [
{
name: 'test-subnet'
// Inline subnets do not expose "id" at compile time.
// ARM normally injects the "id" property at runtime.
//
// What-If now fails when evaluating subnet.id.
// IMPORTANT: If ANY id value is set here, What-If works again.
// id: 'dummy-value-to-make-whatif-work'
properties: {
properties: {
addressPrefix: '10.157.28.0/27'
privateEndpointNetworkPolicies: 'Disabled'
privateLinkServiceNetworkPolicies: 'Enabled'
serviceEndpoints: [
{
service: 'Microsoft.Storage'
}
]
}
}
]
}
}

resource storageAccount 'Microsoft.Storage/storageAccounts@2025-08-01' = {
name: 's24saweudmoduletestsa'
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
properties: {
networkAcls: {
defaultAction: 'Deny'

// ❌ What-If fails here:
virtualNetworkRules: map(vnet.properties.subnets, subnet => {
id: subnet.id
action: 'Allow'
})

// ✅ Workaround:
// virtualNetworkRules: map(vnet.properties.subnets, subnet => {
// id: '${vnet.id}/subnets/${subnet.name}'
// action: 'Allow'
// })

// Also works (no map()):
// virtualNetworkRules: [
// {
// id: vnet.properties.subnets[0].id
// action: 'Allow'
// }
// ]
}
}
}
```

# 🔍 Minimal Repro 2 — Load Balancer Backend Pools

The same issue occurs with Load Balancer backend pools.
If id is not manually constructed, What‑If fails.

```bicep
resource loadBalancer 'Microsoft.Network/loadBalancers@2025-05-01' = {
name: name
location: location
sku: {
name: skuName
tier: tier
}
properties: {
backendAddressPools: [
for pool in backendAddressPools: {
name: pool.name

// ❌ What-If fails unless "id" is manually constructed
id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', name, pool.name)
}
]

loadBalancingRules: [
for rule in loadBalancingRules: {
name: rule.name
properties: {
backendAddressPool: {
id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', name, rule.backendAddressPoolName)
}
}
}
]
}
}
```

If the id is omitted and the template tries to reference:

```bicep
loadBalancer.properties.backendAddressPools[i].id
```

then What‑If fails, even though:

- Validate works
- Create works
- ARM generates the ID at runtime

# 🧠 Additional Observations

#### Issue seems related to map() and similar functions

Using:

```bicep
map(vnet.properties.subnets, subnet => subnet.id)
```

fails in What‑If.

But:

```bicep
vnet.properties.subnets[0].id
```

works.

This suggests What‑If may be evaluating expressions differently inside loops/functions.

#### Multiple resource types affected

So far confirmed:

- Microsoft.Network/virtualNetworks/subnets
- Microsoft.Network/loadBalancers/backendAddressPools

Both rely on ARM‑injected read‑only properties (id) that What‑If no longer resolves.
It is likely other resource types with similar patterns are also affected.

# ▶️ Commands & results

| Command | Result |
| ------------------------------ | --------------------- |
| `az deployment group what-if` | ❌ Fails (id missing) |
| `az deployment group validate` | ✅ Succeeds |
| `az deployment group create` | ✅ Succeeds |

# 🧪 Environment

```bash
azure-cli 2.85.0
core 2.85.0
azure-mgmt-resource 24.0.0
Python 3.13.11
```

# 🛠️ Workarounds

#### Manually construct IDs

```bicep
'${vnet.id}/subnets/${subnet.name}'
```

or

```bicep
resourceId('Microsoft.Network/loadBalancers/backendAddressPools', name, pool.name)
```

#### Avoid map() and use direct indexing

```bicep
vnet.properties.subnets[0].id
```

works, but not practical.

# 📌 Impact

- Breaks any template that reads ARM‑injected read‑only properties
- Affects VNet, Load Balancer, and likely other resource types
- Causes CI/CD pipelines using What‑If to fail
- Regression in behavior compared to previous Bicep versions

# 🙏 Request

Could the team confirm whether:

- This is an intentional change in What‑If evaluation behavior
- ARM‑injected read‑only properties (e.g., id) should be available during What‑If
- The regression can be fixed so What‑If matches Validate/Create behavior again
- The issue affects additional resource types beyond VNets and Load Balancers

Happy to provide additional logs, traces, etc. if needed.

Contributor guide

No contributing guide indexed for this repository

Research direction

No source files or tests are named. Start by running the two minimal Bicep reproductions with `az deployment group what-if`, then compare the behavior with `validate` and `create`; done means What-If resolves ARM-injected `id` properties consistently and the regression is covered by a test.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure
Domain
cloud, infrastructure
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.