adaptive.run TECH BLOG

Cloud can be tricky sometimes. Find out what scenarios we've ran into that are worth being mentioned and explained.

Fixing the DeploymentNotFound Error in Azure Bicep Modules

Level: 200
Publishing date: 14-Feb-2025
Author: Catalin Popa


Have you encountered a DeploymentNotFound error when using Azure Bicep, despite your code being syntactically correct? This issue can be frustrating, especially when working with Bicep modules that have dependencies.

In this guide, we’ll explore:

      • What causes the DeploymentNotFound error

      • How Azure Resource Manager (ARM) processes Bicep files

      • Why module dependencies create this issue

      • Effective fixes using conditional logic and ternary expressions

_______________________________________________________________________________

Understanding the DeploymentNotFound Error

Scenario: Failing Bicep Deployment

Let’s assume we have a Bicep template that includes three modules:

✔ ModuleA – No dependencies, always deployed.

✔ ModuleB – Depends on ModuleA, deployed conditionally (parDeployModuleB).

✔ ModuleC – Depends on ModuleB, but ModuleB may not always be deployed.

Bicep Code with Conditional Deployment

param parDeployModuleB bool = false

module modA 'ModuleA.bicep' = {
name: 'deploy-module-a'
}

module modB 'ModuleB.bicep' = if (parDeployModuleB) {
name: 'deploy-module-b-${uniqueString(modA.name)}'
params: {
parUseModuleAOutputs: modA.outputs.outModuleA
  }
}

module modC 'ModuleC.bicep' = {
name: 'deploy-module-c-${uniqueString(modB.name)}'
params: {
parUseModuleBOutputs: modB.outputs.outModuleB
  }

Why Does This Fail?

The problem occurs when parDeployModuleB is set to false. In this case:

❌ ModuleB is skipped, meaning it never exists in Azure.
❌ ModuleC still references ModuleB, trying to retrieve modB.outputs.outModuleB.
❌ Azure Resource Manager (ARM) throws a DeploymentNotFound error, as it cannot find the missing ModuleB deployment.

Error Message:

{
"error": {
"code": "DeploymentNotFound",
"message": "Deployment 'deploy-module-b-xyz123' could not be found."
}

How Azure Resource Manager (ARM) Processes Bicep Modules

Before execution, Azure Bicep is transpiled into JSON. To see how Bicep is converted, you can run:

az bicep build --file main.bicep

The JSON equivalent of ModuleC includes a reference() function:

"parameters": {
"parUseModuleBOutputs": {
"value": "[reference(resourceId('Microsoft.Resources/deployments', format('deploy-module-b-{0}', uniqueString('deploy-module-a'))), '2020-10-01').outputs.outModuleB.value]"
}

      • The reference() function is automatically used to retrieve ModuleB’s outputs.
      • However, if ModuleB is skipped, the reference fails, causing the DeploymentNotFound error.
_______________________________________________________________________________

How to Fix the DeploymentNotFound Error

There are two solutions to resolve this issue:

1. Always Deploy ModuleB
The simplest fix is to ensure that ModuleB is always deployed, even if it has no effect:

param parDeployModuleB bool = false

module modB 'ModuleB.bicep' = {
name: 'deploy-module-b-${uniqueString(modA.name)}'
params: {
parUseModuleAOutputs: modA.outputs.outModuleA
}

Why This Works:

✔ Ensures that ModuleB always exists, so ModuleC never encounters a missing reference.
✔ Avoids the need for additional conditions.

2. Use a Ternary Expression to Handle Missing ModuleB Outputs

A more flexible approach is to check if ModuleB is deployed before referencing it.

module modC 'ModuleC.bicep' = {
name: 'deploy-module-c-${uniqueString(modB.name)}'
params: {
parUseModuleBOutputs: parDeployModuleB ? modB.outputs.outModuleB : ''
  }

Why This Works:

✔ If parDeployModuleB = false, parUseModuleBOutputs gets an empty string ("") instead of an invalid reference.
✔ Prevents DeploymentNotFound errors while keeping the logic dynamic.
_______________________________________________________________________________

What About Adding a Condition to ModuleC?

You might think adding an if condition to ModuleC would fix the issue:

module modC 'ModuleC.bicep' = if (parDeployModuleB) {
name: 'deploy-module-c-${uniqueString(modB.name)}'
params: {
parUseModuleBOutputs: modB.outputs.outModuleB
  }

🚨 This does NOT work! 🚨

Even if ModuleC is conditionally skipped, Azure still validates all references before deployment. Since ModuleB may not exist, the validation still fails.

Final Fix: Combining Both Solutions

For maximum flexibility, use both solutions together:

param parDeployModuleB bool = false

module modA 'ModuleA.bicep' = {
name: 'deploy-module-a'
}

module modB 'ModuleB.bicep' = if (parDeployModuleB) {
name: 'deploy-module-b-${uniqueString(modA.name)}'
params: {
parUseModuleAOutputs: modA.outputs.outModuleA
  }
}

module modC 'ModuleC.bicep' = {
name: 'deploy-module-c-${uniqueString(modB.name)}'
params: {
parUseModuleBOutputs: parDeployModuleB ? modB.outputs.outModuleB : ''
  }

✔ ModuleC only references ModuleB if it exists
✔ ModuleB is conditionally deployed without causing errors

Conclusion

The DeploymentNotFound error in Azure Bicep modules occurs when a module is conditionally deployed but still referenced by another module.

Key Takeaways:

✔ Azure validates all references before deployment, even for skipped modules.
✔ Using a ternary (? :) expression prevents referencing missing modules.
✔ Ensuring a module is always deployed can also avoid errors.

By understanding how Bicep transpiles into JSON and how ARM processes dependencies, you can design robust and error-free deployments. 🚀

Mobirise
adaptive.run

Transform your business.
Run adaptive.

Contact

Phone: +40 73 523 0005
Email: hello@adaptive.run

Mobirise Website Builder
Mobirise Website Builder

© Copyright  2019-2025 adaptive.run- All Rights Reserved