Azure / bicep

Bicep is a declarative language for describing and deploying Azure resources
MIT License
3.21k stars 745 forks source link

Consider shortcut syntax for nested object keys/properties #704

Open divega opened 3 years ago

divega commented 3 years ago

In the examples I have seen so far it appears that when the right-hand side of a property is an object with a single key assigned, it is necessary to introduce nested curly brace blocks. E.g.:

resource diagsAccount 'Microsoft.Storage/storageAcconunts@2019-06-01' = {
    name: diagStorageAccountName
    location: location
    sku: {
        name: 'Premium_LRS'
    }
    kind: 'Storage'
}

It seems at first glance this is a common scenario for which it would be worth optimizing for terseness. One idea that came up in a brief discussion with the Bicep team was to support dot on the left-hand side for chaining of nested properties. Using this feature, the example above becomes:

resource diagsAccount 'Microsoft.Storage/storageAcconunts@2019-06-01' = {
    name: diagStorageAccountName
    location: location
    sku.name: 'Premium_LRS'
    kind: 'Storage'
}

Another example from (a fragment taken from docs/1vm-2nics-2subnets-1vnet/main.bicep) where this can have more impact:

resource nic1 'Microsoft.Network/networkInterfaces@2020-06-01' = {
  name: nic1Name
  location: location
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          subnet: {
            id: '${vnet.id}/subnets/${subnet1Name}'
          }
          privateIPAllocationMethod: 'Dynamic'
          publicIPAddress: {
            id: pip.id
          }
        }
      }
    ]
    networkSecurityGroup: {
      id: nsg.id
    }
  }
}

Becomes:

resource nic1 'Microsoft.Network/networkInterfaces@2020-06-01' = {
  name: nic1Name
  location: location
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          subnet.id: '${vnet.id}/subnets/${subnet1Name}'
          privateIPAllocationMethod: 'Dynamic'
          publicIPAddress.id: pip.id
        }
      }
    ]
    networkSecurityGroup.id: nsg.id
  }
}

cc @anthony-c-martin, @satyavel

anthony-c-martin commented 3 years ago

I like it!

We should also have a think about what the array equivalent looks like, and whether we can support multiple levels of nesting

majastrz commented 3 years ago

Yes, but we should not allow both styles on the same property. Otherwise, readability will suffer.

divega commented 3 years ago

Everyone, I appreciate very much the opportunity to brainstorm a bit with the team.

We should also have a think about what the array equivalent looks like

Do you mean shortcut syntax for defining an array containing a single element? If yes, my first thought is that Bicep could support something like this:

ipConfigurations[0]: {
  name: 'ipconfig1'
  properties: {
    subnet.id: '${vnet.id}/subnets/${subnet1Name}'
    privateIPAllocationMethod: 'Dynamic'
    publicIPAddress.id: pip.id
  }
}

However, I am not sure this is as compelling in terms of terseness (for the producer, there are savings in lines of code and indentation, but not so much in terms of keystrokes). If the right-hand side is an object as in this example, you still have one level of nesting for the curly braces of the object. Are arrays to which you need to populate with a single element as common as object to which you need to provide with a single key/value?

and whether we can support multiple levels of nesting

Hopefully yes :smile_cat:. Though on a quick scan over some of your examples I didn't find cases in which this would make a difference, I assume those exist.

Yes, but we should not allow both styles on the same property. Otherwise, readability will suffer.

@majastrz, do you mean something like this?

resource nic1 'Microsoft.Network/networkInterfaces@2020-06-01' = {
  name: nic1Name
  location: location
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties.subnet.id: '${vnet.id}/subnets/${subnet1Name}'
        properties: {
          privateIPAllocationMethod: 'Dynamic'
          publicIPAddress.id: pip.id
        }
      }
    ]
    networkSecurityGroup.id: nsg.id
  }
}

If yes, I think the main decision point here is whether you want to support usage of a key in an object to only partially specify the values under it. In this example properties is used twice, each time to provide the value for a different path of nested keys.

I am unsure that readability suffers a lot, but the mental model for the customer certainly changes.

BTW, hypothetically, you could combine this with a shortcut syntax for array elements as mentioned above, and enable something like this:

ipConfigurations[0]: {
   ...
}
ipConfigurations[1]: {
   ...
}
divega commented 3 years ago

One more thing I have noticed a lot while browsing some of your examples is that some resources tend to define a properties key with nested values (note: I was new to ARM when I wrote this, now I know it is a requirement). I can think of motivations for this design, but given the goals of Bicep, it seems to be a common source of syntax clutter that perhaps you could consider addressing. Assuming I am correct about this being a common idiom, my first intuition is to use flattening, e.g. special-case properties to allow resolving sub-keys under it as if they were defined directly on the parent object. Then this would be valid:

resource nic1 'Microsoft.Network/networkInterfaces@2020-06-01' = {
  name: nic1Name
  location: location
  ipConfigurations: [
    {
      name: 'ipconfig1'
      subnet.id: '${vnet.id}/subnets/${subnet1Name}'
      privateIPAllocationMethod: 'Dynamic'
      publicIPAddress.id: pip.id
    }
  ]
  networkSecurityGroup.id: nsg.id
}

If you think this (or some variation of this) is worth considering, I can create a separate issue.

askew commented 3 years ago

I raised this a while ago in #285. The discussion there got a bit hung up on the specifics on the way subnets are not child resources.

I still think this would be a great improvement to simplify a lot of bicep files.

crwsolutions commented 2 years ago

I would like this very much! It looks even tidier. I would like to see it as an alternative shortcut syntax. But why not keep both. I would say do not complicate things with arrays. What I would like is that this:

resource stg 'Microsoft.Storage/storageAccounts@2021-04-01' = {
  name: uniqueStorageName
  location: location
  sku: {
    name: storageSKU
  }
  kind: 'StorageV2'
  properties: {
    supportsHttpsTrafficOnly: true
    encryption: {
    file: {
       enabled: true
     }
   }
  }
}

Could also be written as this:

resource stg 'Microsoft.Storage/storageAccounts@2021-04-01' = {
  name: uniqueStorageName
  location: location
  sku.name: storageSKU
  kind: 'StorageV2'
  properties.supportsHttpsTrafficOnly: true
  properties.encryption.file.enabled: true
}

But also like this

resource stg 'Microsoft.Storage/storageAccounts@2021-04-01' = {
  name: uniqueStorageName
  location: location
  sku.name: storageSKU
  kind: 'StorageV2'
  properties: {
    supportsHttpsTrafficOnly: true
    encryption.file.enabled: true
  }
}
ghost commented 1 year ago

Hi divega, this issue has been marked as stale because it was labeled as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment. Thanks for contributing to bicep! :smile: :mechanical_arm:

divega commented 1 year ago

I haven't seen a question when this was marked as requiring feedback from author, so not sure why this would be marked as stale. I guess it's just a new rule being applied.