Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding unused parameter with type expression fails deployment #11846

Closed
dmgonch opened this issue Sep 15, 2023 · 5 comments · Fixed by #11884
Closed

Adding unused parameter with type expression fails deployment #11846

dmgonch opened this issue Sep 15, 2023 · 5 comments · Fixed by #11884
Assignees
Milestone

Comments

@dmgonch
Copy link

dmgonch commented Sep 15, 2023

Bicep version
Bicep CLI version 0.21.1 (d4acbd2a9f)

Describe the bug
Running az deployment group create --what-if -x Ignore --subscription <SUB_ID> -g <RG_NAME> -f repro.bicep fails with:

InvalidTemplate - Deployment template validation failed: 'The template reference 'two' is not valid: could not find template resource or resource copy with this name. Please see https://aka.ms/arm-function-reference for usage details.'.

But if notUsedTypedParam is commented out, then the command above succeeds.

To Reproduce

  • Save 3 files below to a directory.
  • Run az deployment group create --what-if -x Ignore --subscription <SUB_ID> -g <RG_NAME> -f repro.bicep
  • Observe the failure described above.

repro.bicep

#disable-next-line no-unused-params
param notUsedTypedParam {
  Name: string
 }[] = []
 
 module one 'module-one.bicep' = {
   name: 'one'
   params: {
   }
 }
 
 module two 'module-two.bicep' = {
   name: 'two'
   params: {
    input: one.outputs.result
   }
 }
 
 var fakeOptions = concat(
   two.outputs.result
 )
 resource taskDeployment 'Microsoft.Resources/deployments@2020-10-01' = {
   name: 'name'
   properties: {
     mode: 'Incremental'
     template: {
       '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
       contentVersion: '1.0.0.0'
       resources: [
         {
           apiVersion: '2019-12-01'
           type: 'Microsoft.ManagedIdentity/userAssignedIdentities'
           name: 'example'
           properties: {
            fakeOptions: fakeOptions
           }
         }
       ]
     }
   }
 }

module-one.bicep

output result string = 'one'

module-two.bicep

#disable-next-line no-unused-params
param input string
output result array = ['a', 'b']
@jeskew
Copy link
Contributor

jeskew commented Sep 15, 2023

The behavior you ran into is the result of an intentional breaking change.

Some background: ARM recently introduced langauge version 2.0, which is required for templates that use aggregate parameter types. Bicep type expressions became GA in version 0.21.1, and any template that includes type expressions (like { Name: string }[]) will automatically be compiled to a JSON template with a top-level property of "languageVersion": "2.0".

The new ARM language version also included a small number of breaking changes, one of which was the removal of "outer" expression evaluation mode. "Outer" mode is the default in templates that do not specify a language version but is blocked in templates that use version 2.0 (in which case "Inner" mode is the default (and only permitted) evaluation mode). This change was not expected to impact any Bicep users, as module statements are always compiled to a nested deployment using "inner" evaluation mode.

What's happening in your deployment is that the taskDeployment resource is using the default expression evaluation mode, which would be "outer" if the template contains no type expressions or "inner" otherwise. The fakeOptions variable reference in that nested deployment can only be evaluated in "outer" mode, as it refers to the two module.

For consistent behavior, I would recommend updating taskDeployment to either be defined as a Bicep module or to set the "expressionEvaluationOptions" property to {"scope": "inner"}. This will in turn require that expressions that can only be evaluated in "outer" mode instead pass that data to the nested deployment explicitly as parameters.

@jeskew jeskew closed this as completed Sep 15, 2023
@jeskew jeskew reopened this Sep 15, 2023
@jeskew
Copy link
Contributor

jeskew commented Sep 15, 2023

Sorry, hit the wrong button there. Meant to leave this open for others to weigh in.

One thing we could do here is raise an error-level compiler diagnostic. The error message returned by ARM is not very clear in this instance, and we should be able to detect this scenario via static analysis rather than letting the runtime handle it. Adding a compile-time check might be as simple as checking any resource of type Microsoft.Resources/deployments for references to top-level declared symbols inside of the properties.template property.

@dmgonch
Copy link
Author

dmgonch commented Sep 15, 2023

If a compile-time check is possible/reasonable then this sounds definitely like a good idea.

BTW, adding expressionEvaluationOptions didn't seem to help - I got the same error. #jeskew Did I do it incorrectly?

resource taskDeployment 'Microsoft.Resources/deployments@2020-10-01' = {
  name: 'name'
  properties: {
    expressionEvaluationOptions: {
      scope: 'inner'
    }
    mode: 'Incremental'
    template: {
      '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
      contentVersion: '1.0.0.0'
      resources: [
<SNIP>

@jeskew
Copy link
Contributor

jeskew commented Sep 15, 2023

@dmgonch If the nested deployment resource still refers to the fakeOptions variable, you would get the same error. But at least with the evaluation mode set to "inner," you would get the same error regardless of whether you use a type expression.

To clear the error in "inner" mode, the fakeOptions reference has to be placed outside of the nested deployment's template property:

 resource taskDeployment 'Microsoft.Resources/deployments@2020-10-01' = {
   name: 'name'
   properties: {
     mode: 'Incremental'
     expressionEvaluationOptions: {
       scope: 'inner'
     }
     parameters: {
       options: {
         value: fakeOptions // <-- pass `fakeOptions` explicitly (as a parameter) to the nested deployment
       }
     }
     template: {
       '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
       contentVersion: '1.0.0.0'
       parameters: {
         options: {
           type: 'array'
         }
       }
       resources: [
         {
           apiVersion: '2019-12-01'
           type: 'Microsoft.ManagedIdentity/userAssignedIdentities'
           name: 'example'
           properties: {
            fakeOptions: '[parameters(\'options\')]' // <-- This expression will be evaulated as part of the nested deployment
           }
         }
       ]
     }
   }
 }

Alternatively, you could define taskDeployment as a module instead:

main.bicep

...
module taskDeployment 'taskDeployment.bicep' = {
  name: 'taskDeployment'
  params: {
    options: fakeOptions
  }
}

taskDeployment.bicep

param options array

resource example 'Microsoft.ManagedIdentity/userAssignedIdentities@2019-12-01' = {
  name: 'example'
  properties: {
    fakeOptions: options
  }
}

@dmgonch
Copy link
Author

dmgonch commented Sep 15, 2023

Confirmed that moving the resource to a separate .bicep file resolved that issue. Thank you very much for your help!

@jeskew jeskew self-assigned this Sep 21, 2023
@stephaniezyen stephaniezyen added this to the v0.22 milestone Sep 21, 2023
jeskew added a commit that referenced this issue Sep 22, 2023
…mbolic references to outer scope (#11884)

Resolves #11846 

This PR updates EmitLimitationCalculator to emit an error when a
resource of type `Microsoft.Resources/deployments` uses inner scoped
expression evaluation but refers to symbols defined in the outer scope
from within the `template` property.

This PR may be a **breaking change** for some templates, as the added
check may catch scenarios that would not result in a runtime deployment
failure, e.g., when a template has a symbol with the same name and type
defined in both contexts:

```bicep
var fizz = 'buzz'

resource nested 'Microsoft.Resources/deployments@2020-10-01' = {
  name: 'name'
  properties: {
    mode: 'Incremental'
    expressionEvaluationOptions: {
      scope: 'inner'
    }
    template: {
      '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
      contentVersion: '1.0.0.0'
      variables: {
        fizz: 'pop'
      }
      resources: [
        {
          apiVersion: '2022-09-01'
          type: 'Microsoft.Resources/tags'
          name: 'default'
          properties: {
            tags: {
              tag1: fizz // <-- Not an error, but surprise! Its value is 'pop', not 'buzz'
            }
          }
        }
      ]
    }
  }
}
```

In that case, the user *may* be using a Bicep symbolic reference to save
a few characters, but what the template will do (dereference the
inner-scoped symbol) does not line up with the communicated intent of
the syntax (i.e., to dereference the outer-scoped symbol), so I chose to
flag that as an error, too, as this usage would almost certainly
represent a *logical* error even if the compiled nested deployment would
not raise a runtime error.
###### Microsoft Reviewers: [Open in
CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/Azure/bicep/pull/11884)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
3 participants