Azure / bicep

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

Make Bicep more readable by removing brackets #2642

Closed rshariy closed 2 years ago

rshariy commented 3 years ago

TLDR: "You have became the very thing you swore to destroy!"

Currently Bicep provides no real advantages over ARM templates, when you need to define resources with multiple mandatory properties - it is still ARM template with endless brackets everywhere, which, tbh, is quite annoying. These brackets provide no benefits and in many cases they are required around a single line.

Bicep template for Application Gateway:

resource applicationGateway 'Microsoft.Network/applicationGateways@2020-06-01' = {
  name: applicationGatewayName_var
  location: location
  properties: {
    sku: {
      name: 'Standard_v2'
      tier: 'Standard_v2'
    }
    autoscaleConfiguration: {
      minCapacity: 1
      maxCapacity: 10
    }
    gatewayIPConfigurations: [
      {
        name: 'appGatewayIpConfig'
        properties: {
          subnet: {
            id: resourceId('Microsoft.Network/virtualNetworks/subnets', virtualNetwork.name, 'appGwSubnet')
          }
        }
      }
    ]
    frontendIPConfigurations: [
      {
        name: 'appGatewayFrontendIp'
        properties: {
          publicIPAddress: {
            id: appGwPublicIP.id
          }
        }
      }
    ]
    frontendPorts: [
      {
        name: 'appGatewayFrontendPort'
        properties: {
          port: 80
        }
      }
    ]
    backendAddressPools: [
      {
        name: 'appGatewayBackendPool'
        properties: {}
      }
    ]
    backendHttpSettingsCollection: [
      {
        name: 'appGatewayBackendHttpSettings'
        properties: {
          port: 80
          protocol: 'Http'
        }
      }
    ]
    httpListeners: [
      {
        name: 'appGatewayHttpListener'
        properties: {
          frontendIPConfiguration: {
            id: resourceId('Microsoft.Network/applicationGateways/frontendIPConfigurations', applicationGatewayName_var, 'appGatewayFrontendIp')
          }
          frontendPort: {
            id: resourceId('Microsoft.Network/applicationGateways/frontendPorts', applicationGatewayName_var, 'appGatewayFrontendPort')
          }
          protocol: 'Http'
        }
      }
    ]
    requestRoutingRules: [
      {
        name: 'temp'
        properties: {
          ruleType: 'Basic'
          httpListener: {
            id: resourceId('Microsoft.Network/applicationGateways/httpListeners', applicationGatewayName_var, 'appGatewayHttpListener')
          }
          backendAddressPool: {
            id: resourceId('Microsoft.Network/applicationGateways/backendAddressPools', applicationGatewayName_var, 'appGatewayBackendPool')
          }
          backendHttpSettings: {
            id: resourceId('Microsoft.Network/applicationGateways/backendHttpSettingsCollection', applicationGatewayName_var, 'appGatewayBackendHttpSettings')
          }
        }
      }
    ]
  }
}

I suggest to use indentions - make Bicep YAML like:

resource applicationGateway 'Microsoft.Network/applicationGateways@2020-06-01'
  name: applicationGatewayName_var
  location: location
  properties:
    sku:
      name: 'Standard_v2'
      tier: 'Standard_v2'
    autoscaleConfiguration:
      minCapacity: 1
      maxCapacity: 10
    gatewayIPConfigurations:
        name: 'appGatewayIpConfig'
        properties:
          subnet: 
            id: resourceId('Microsoft.Network/virtualNetworks/subnets', virtualNetwork.name, 'appGwSubnet')
    frontendIPConfigurations:
        name: 'appGatewayFrontendIp'
        properties:
          publicIPAddress:
            id: appGwPublicIP.id
    frontendPorts:
        name: 'appGatewayFrontendPort'
        properties:
          port: 80
    backendAddressPools:
        name: 'appGatewayBackendPool'
    backendHttpSettingsCollection:
        name: 'appGatewayBackendHttpSettings'
        properties:
          port: 80
          protocol: 'Http'
    httpListeners:
        name: 'appGatewayHttpListener'
        properties:
          frontendIPConfiguration:
            id: resourceId('Microsoft.Network/applicationGateways/frontendIPConfigurations', applicationGatewayName_var, 'appGatewayFrontendIp'
          frontendPort:
            id: resourceId('Microsoft.Network/applicationGateways/frontendPorts', applicationGatewayName_var, 'appGatewayFrontendPort')
          protocol: 'Http'
    requestRoutingRules:
        name: 'temp'
        properties:
          ruleType: 'Basic'
          httpListener:
            id: resourceId('Microsoft.Network/applicationGateways/httpListeners', applicationGatewayName_var, 'appGatewayHttpListener')
          backendAddressPool:
            id: resourceId('Microsoft.Network/applicationGateways/backendAddressPools', applicationGatewayName_var, 'appGatewayBackendPool')
          backendHttpSettings:
            id: resourceId('Microsoft.Network/applicationGateways/backendHttpSettingsCollection', applicationGatewayName_var, 'appGatewayBackendHttpSettings')

From my perspective it is way easier to read and create YAML files, rather than JSON.

Bicep should be simple, intuitive, easy to learn and not just another iteration of ARM templates.

shenglol commented 3 years ago

I agree that YAML is more readable than JSON, and Python programmers may also find this familiar. However, there are several concerns:

rshariy commented 3 years ago

I understand - it will be like going back to Bicep v0.1 and re-do everything. But it may be something that should be done: I just spent a few days trying to migrate our ARM templates to Bicep and ended up with "ARM templates v2".

First goal of Bicep defined as "build the best possible language for describing, validating, and deploying infrastructure to Azure".

Having "ARM templates v2" is not the best way to archive this goal.

alex-frankel commented 3 years ago

Currently Bicep provides no real advantages over ARM templates

While I understand your frustration, I don't think this is a fair statement. Bicep has (in my humble opinion) significantly improved resource referencing, dependency management, and overall type safety. As a result, we are seeing users author bicep much more successfully, much faster than they have with ARM templates. Have you walked through the tutorial? This shows off some of the net-new improvements that allow us to give you a much richer authoring experience.

At the same time, the syntax cleanliness improvements that we have made have reduced the number of characters between bicep and an equivalent template by 40-60%. That is a big improvement in both authoring and readability. We could get rid of brackets and be more YAML-like, but this would make bicep more whitespace sensitive (something we're actively trying to get away from). In our opinion, that will negatively affect the authoring experience, even if it makes the final document easier to read.

Finally, one of our goals with Bicep is to be a transparent abstraction, which means we expose all the power of ARM (and some of the baggage from ARM and ARM Templates) in Bicep. While sometimes this is not ideal, it is what allows us to have the broadest possible support of ARM operations. As a result, if an API is complex (like app gateway in particular), then it will be complex in bicep. This is why we have introduced modules and will eventually provide a module registry.

We hope that this combination of improvements is what makes the day-to-day with bicep a lot more pleasant than ARM templates.

afscrome commented 3 years ago

I also agree that bicep is a significant improvement over ARM. Whilst I've had limited experience with ARM, every time has been painful. when I first saw bicep, I was hopeful that it would be able to make working with azure resources tolerable. In reality, it's blown my expectations out of the water - and I've found working with bicep to be not just tolerable, but a pleasure. Huge thanks to everyone who's worked on bicep.

The biggest benefit I'm finding with bicep is that it's trivial to modularise your templates into several smaller files. Having several 50-100 line files is far more manageable than thousand line monolithic arm templates. That alone makes bicep orders of magnitude better than arm in my books. (I know this was technically possible in arm with linked templates, but that introduced a dependency hell problem of having to set up blob storage, keep things versioned in sync etc.)

The other huge benefit I find is the quality of the tooling (i.e. VS Code extension) means it is realistically possible to write templates from scratch. With auto completion you can very easily write a template from scratch without having to leave your editor or even look at the documentation for various resource types. I wouldn't dare attempt that with a raw ARM template.

That not to say bicep is perfect - however, almost every issue I've encountered would also exist in ARM (or be so painful to try in ARM that I'd never have even attempted it!). From my experience, bicep does everything ARM can do at least as well, if not better. (The one exception is lack of function support, but that is on the roadmap).

afscrome commented 3 years ago

One thing I notice with your example is that there are a lot of single element arrays - this is one of the worst case scenarios you can encounter in bicep for wasting newlines.

    backendHttpSettingsCollection: [
      {
       //...
      }
    ]

If/when the language is made less newline sensitive (#146), you could reduce this to:

    backendHttpSettingsCollection: [{
       //...
      }]

Bicep could potentially allow this to be reduced further by allowing implicit conversion of a object to an array containing that object (like PowerShell). That said, it does add complexity to the language, in a way which could be particularly painful for those who work infrequently with bicep. I'm not convinced if it's a worthwhile trade off to save 2 characters.

    backendHttpSettingsCollection: {
       //...
      }
rshariy commented 3 years ago

While I understand your frustration, I don't think this is a fair statement. Bicep has (in my humble opinion) significantly improved resource referencing, dependency management, and overall type safety.

Would not say it is frustration, more like missed expectations.

I really hoped Bicep would improve authoring of complex templates, like the Application Gateway template in my example, but it is still the same: same requirement to have all resources/rules specified in Bicep AppGW file or they will be removed on deployment, same issue with resource referencing etc..

It is totally understandable that different people with different experience have different preferences and ideas on what it should be, but I would focus on making Bicep simple even for complex templates.

It should be easy to reference resources specified just a few lines above.

Just an example of what I would do with recourse referencing:

    frontendPorts: [
      {
        name: 'appGatewayFrontendPort'
        properties: {
          port: 80
        }
      }
    ]

    frontendPort: appGatewayFrontendPort
alex-frankel commented 3 years ago

It should be easy to reference resources specified just a few lines above.

The issue is that these are not declared as resources in bicep/ARM, so they don't have symbolic name for the reference. FWIW, this is not something that would be resolved by moving to a more YAML-like syntax. The right fix here is an API revision for app gateway, however we do plan to add support for a this-like functionality as per #1852 which would help alleviate some of this pain. Can you take a look at that one and see if this would be closer to what you are looking for? It would let you do something like what is described in this comment: https://github.com/Azure/bicep/issues/565#issuecomment-825726751

I'll also note that App Gateway (and networking as a whole) have some uniquely poor API designs. We have not seen this type of issue for most other resource types in bicep.

matsest commented 3 years ago

Side note: The API design for App Gateway is an extreme in this case. The same goes for Terraform or any other IaC tool that interacts with the App Gateway API - lack of distinct child resource APIs and complex nested resources makes it very hard to model as IaC regardless of brackets, indents or whatnot. In my experience the resource API is limiting how this resource can be coded as declarative IaC.

Please consider upvoting this feedback: https://feedback.azure.com/forums/217313-networking/suggestions/39634588-add-rest-apis-and-sdk-to-manage-application-gatewa

There's also a (closed) issue regarding this in the rest-api specs repo: https://github.com/Azure/azure-rest-api-specs/issues/8252