-
Notifications
You must be signed in to change notification settings - Fork 745
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
Set or skip an object property based on a condition #387
Comments
@marcre - we're pretty sure null with the ternary operator would fulfill this, so wondering if we are missing something. |
Yes and no - depends on how we handle NULL.. Does null mean we omit the value when we send to the RP, or do we send the null on. If we don't send the null, then we don't need undefined. However, with PUTCH RPs like compute you may need to be able to explicitly set something to null to make sure it is cleared if previously set. |
Agree with that. We would like the distinction between null and undefined/omitted properties not to exist, but the reality disagrees. |
agree as well. Not all RPs react on null equally. |
I have had to rely on the Happy to share a sample of this! I'd say that having an easy way of defining if you want a property to be omitted, set to null, or set to an object would be very useful. |
+1, this would be super useful in so many use-cases. for example, like in Javascript
|
+1 really like this to be supported. My scenarios is - I have 3 different set of nsg rules, which contains 90% same rules. I'd like to be able to define all rules in one array and use bool parameters to control which rules to be included when instantiated. |
Would love if this existed. I'm trying to define the 'analytical TTL' on a Cosmos container and it only accepts an 'int' but in the .NET SDK it's a nullable int, so 'undefined' would be the only way I could conditionally set an analytical storage ttl. Or it could be done w/ spread ( #1560 ) Or per the ugly way: |
Looks like AppGateway uses a number of resource specific top-level properties (zones, identity etc). I cannot see any way to conditionally include these (for my use-case, the identity object). Looks like you cannot use a variable for the entire resource body |
@khowling does using the ternary operator work? var shouldUseIdentity = true
resource appGw 'Microsoft.Network/applicationGateways@2021-02-01' = {
name: 'foo'
identity: shouldUseIdentity ? {
type: 'UserAssigned'
userAssignedIdentities: {
'${userAssignedMI}': {}
}
} : null
} |
@alex-frankel Unfortunately not, that produces:
if I replace
|
I ran into this issue trying to create a bicep file that creates an Azure function with a consumption plan in the test environment and a standard plan (for always on) in production.
It fails the value for |
I encountered this today when trying to conditionally add vnet rules to a storage account depending on a |
Maybe |
Thanks Alex. I'm new to Bicep and your little snip helped me out. Only thing I changed was the null for {} Here is what I ended up using when one probe used statusCodes, but the second probe did not need "Use probe matching conditions" checked:
|
Another use case where this is causing trouble: I'm making a template for an App Gateway. In the backendHttpSettingsCollection, there are two attributes, hostname and pickHostNameFromBackendAddress. If pickHostNameFromBackendAddress is true, you don't need hostname, otherwise you do. I tried to do this conditionally based on my parameters:
The problem is that even having "hostname: null" in your deployment causes an error if pickHostNameFromBackendAddress is true:
I have two backendHttpSettingsCollections, one with pickHostNameFromBackendAddress false, the other with it true. So I need to be able to conditionally include or exclude the hostname attribute, not just conditionally assign a value or a null to it. |
We need an 'undefined' value that can be put in.. it's familiar to people who know any JavaScript |
What we do now as a workaround (awaiting a proper alternative) is using modules and conditional deployment. We have several VM images that needs to deploy, but only some of the have plans. We now need one module for VmWithPlan, and one VmWithoutPlan. It works, but It means we need to repeat our code in two files which we don't want. Please keep me posted regarding a fix. |
This is the same problem as with properties - why we need to omit or introudce undefined if we can just have null? :) Perhaps there might be cases when we want to have null in array but conditionally put. In fact, the |
Lambdas open up a lot of options that weren't available when this issue was opened. Conditional properties on objects can also be handled with I do wonder though if lambdas are any more approachable for a non-developer audience than |
I think we should also consider case with upcoming strong typing for parameters. When we develop modules we expect Other question with conditional properties would be whether we show them in object completions or not. And if we do (I think we should) we should make user to handle the case when property is not set due to the condition. |
I know Ansible/Jinja have the omit filter to ignore properties. Something like that perhaps?
That is, if 'mode' is set, then use it, but otherwise the property is ignored as though it were not being defined. |
Maybe this is just obvious for everyone in this thread, but for those who don't get that (Like me) and have a problem to solve, I can report that passing json('null') works to omit a value for a property.
In this case, null, '', {} in direct form or via a variable/parameter did not work. But json('null') did. Hope this helps anyone |
It does not work with all resource types. |
Yep, agree with @jikuja . You are stating a single case but putting null on property acts different not only on per RP bases but sometimes on per property base for the same resource. |
I'm having a similar problem too. I am trying to enter the rules of the firewall, but there are cases where there are items that do not need to be entered among each parameter for each rule. It seems that there is no error when the template is distributed without even entering the key value, but if null or [''] values are entered, an error is output because there is no value for the key value. I wish there was a workaround. |
I would love this feature to exist but fwiw I used the following workaround in case it helps someone. Context: Conditional site config for function apps. Added an extra param to my module:
Then added the following to my resource call:
|
This does break intellisense on the properties, but yes this is definitely a thing |
Another use case for this. I like re-deployable templates for our customers. It is now not possible to redeploy templates that have VMs and example: // VM deployment bicep script
// Other params are removed for simplicity
param isInitialDeployment bool = true
// Create vm
resource vm {
properties: {
osProfile: {
computerName: 'myVM'
adminUsername: 'install'
adminPassword: adminPassword
// When re-deploying vm, you get 'Changing property customdata is not allowed'. exception due the fact that null is not the same as do not set.
customData: isInitialDeployment ? base64(customData) : null // this should be fixable by providing undefined / other syntax
}
} |
The workaround we ended up using for this is setting custom data to an include for cloud init for Linux in a remote file, either on a web server or blob storage. The custom data in the template won't ever change while the web server can present specific data based on properties of the accessing client or pull down a script to determine what to do based on other properties of the client like registering it with a configuration management tool like Ansible. If you need to have a no-op just make the default contents of the script exit early. For windows you just use an additional run command or startup script to parse and execute the remote web file, and this is one of the places where AWS has done it better than Azure because the user data flow is the same for Windows and Linux. |
So many hacky workarounds for this.. |
Another example for this is with |
Yet another great usecase for this:
Additionally, you cannot for expressions inside of a union, so in my scenario I just cant conditionally set this. |
yes would like to have undifined for virtualNetworkSubnetId in Web/sites too, because null does not seem to work. |
This should have been resolved long time ago |
Would be handy indeed - in my case, I have to pass an empty array in my parameter object. I tried using empty() but unless I specify that [] for delegations in my first parameter, it fails as the property is missing. Template:
parameters file:
|
@lansalot - There are two better options for you in this particular scenario. The older approach would be to use contains() instead of empty():
|
Oh fantastic, didn't know about subnet.?delegations, that's a sweet option :) |
… on lambdas (#13658) Adds the spread operator `...` as well as various new functions + indexes on lambdas: 1. Spread operator - usage is as follows: * In an object: ```bicep var objA = { bar: 'bar' } var objB = { foo: 'foo', ...objA } // equivalent to { foo: 'foo', bar: 'bar' } ``` * In an array: ```bicep var arrA = [ 2, 3 ] var arrB = [ 1, ...arrA, 4 ] // equivalent to [ 1, 2, 3, 4 ] ``` 1. New functions + usage: * `objectKeys`: Returns the keys of an object parameter: ```bicep var example = objectKeys({ a: 1, b: 2 }) // returns [ 'a', 'b' ] ``` * `mapValues`: Create an object from an input object, using a custom lambda to map values: ```bicep var example = mapValues({ foo: 'foo' }, val => toUpper(val)) // returns { foo: 'FOO' } ``` * `groupBy`: Create an object with array values from an array, using a grouping condition: ```bicep var example = groupBy(['foo', 'bar', 'baz'], x => substring(x, 0, 1)) // returns { f: [ 'foo' ], b: [ 'bar', 'baz' ] ``` * `shallowMerge`: Perform a shallow merge of input object parameters: ```bicep var example = shallowMerge([{ foo: 'foo' }, { bar: 'bar' }]) // returns { foo: 'foo', bar: 'bar' } ``` 1. Optional indices on lambdas + usage: * `map`: ```bicep var example = map(['a', 'b'], (x, i) => { index: i, val: x }) // returns [ { index: 0, val: 'a' }, { index: 1 val: 'b' } ] ``` * `reduce`: ```bicep var example = reduce([ 2, 3, 7 ], (cur, next, i) => (i % 2 == 0) ? cur + next : cur) // returns 9 ``` * `filter`: ```bicep var example = filter([ 'foo', 'bar', 'baz' ], (val, i) => i < 2 && substring(val, 0, 1) == 'b') // returns [ 'bar' ] ``` Closes #13560 Closes #9244 Closes #1560 Addresses some of the issues described under the following: #2082, #1853, #387 ###### Microsoft Reviewers: [Open in CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/Azure/bicep/pull/13658)
@anthony-c-martin as mentioned during the call it might be good to explore that you have the undefined syntax available in Bicep only and when compiled to ARM spread syntax to be used for it. I did not log the other feedback around intellisense when you use spread as I saw that you already logged that. I would definitely use spread syntax instead of union especially if intelisense is available. If undefined is also available will just make that case easier to use and a little bit more readable the code. |
Is your feature request related to a problem? Please describe.
When conditionally enabling ipv6 in a template, there are cases where some properties need to be either set or omitted from a resource declaration. Currently in Bicep, this requires creating two separate object literals and choosing between them via the ternary operator.
Describe the solution you'd like
Can we create a simpler way of doing this that doesn't involve declaring two separate objects? Should we consider introducing a concept of
undefined
that is separate fromnull
?The text was updated successfully, but these errors were encountered: