Giter Site home page Giter Site logo

Comments (37)

MaxFrost avatar MaxFrost commented on September 27, 2024 4

For those who come later, current way to do child resources with bicep

resource hostVm 'Microsoft.Compute/virtualMachines@2019-03-01' ={
<stuff>
}
resource vmExtension1 'Microsoft.Compute/virtualMachines/extensions@2019-03-01' = {
  name: '${hostVm.id}/<extensionname>
  <stuff>
}

borrowed from https://github.com/Azure/bicep/blob/master/docs/examples/not-working-yet/301-nested-vms-in-virtual-network/main.bicep

from bicep.

anthony-c-martin avatar anthony-c-martin commented on September 27, 2024 2

100% agree this is something to improve; I really don't want users to have to think about concatting strings to form types or names.

It would be nice if there was some way syntactically to tie this into the concept of 'resource scopes' and 'defaults', since there are some similarities. Mockup from a while back here: https://github.com/Azure/bicep/blob/master/ideas/syntax/scoping.arm, though I'm definitely not sold on this syntax.

We also need to be careful that this feels as natural with extension resources.

from bicep.

anthony-c-martin avatar anthony-c-martin commented on September 27, 2024 2

What are your thoughts on a syntax like the following with an optional "parent" property (comments in-line in the code):

resource hostVm 'Microsoft.Compute/virtualMachines@2019-03-01' = {
  name: 'myVm'
}

resource vmExtension1 'Microsoft.Compute/virtualMachines/extensions@2019-03-01' = {
  parent: hostVm // Bicep can validate that hostVm is the correct type of parent resource here
  name: 'myExtension' // no formatting with '/' characters required - "name" is *just* the name of the child resource. We'll figure out the rest.
}

I touched on this idea here in the scopes spec: https://github.com/Azure/bicep/blob/master/docs/spec/resource-scopes.md#parent-child-syntax. It would provide a nice unification with deploying resources at different scopes, which is conceptually somewhat similar to deploying a resource under a parent resource.

from bicep.

anthony-c-martin avatar anthony-c-martin commented on September 27, 2024 2

Team Discussion - 02/01/21

'scope' vs 'parent' property

We discussed whether it would be possible to unambiguously use the pre-existing 'scope' property to add a reference to the parent resource, instead of introducing a new 'parent' property. For example:

resource resA 'My.Rp/a@2020-01-01' = {
  name: 'resA'
}

resource resB 'My.Rp/a/b@2020-01-01' = {
  // indicates that 'resA' is the parent of 'resB'
  scope: resA
  // the fully-qualified resource name ('resA/resB') need not be supplied
  name: 'resB'
}

Ambiguity

The main problem we saw trying to combine the two properties is that there are cases where it may be ambiguous or confusing to the end-user whether 'scope' is being used to annotate a child/parent relationship vs a nested child of an extension resource. For example:

  1. Consider a child resource declaration:

    resource resB 'My.Rp/a/b@2020-01-01' = {
      name: 'resA/resB'
    }

    Note that although we do not allow a 'scope' property supplied except in the case of extension resources, it is implicit from the targetScope of the file. For example, if the targetScope is resourceGroup (the default), this would be equivalent to the following:

    resource resB 'My.Rp/a/b@2020-01-01' = {
      scope: resourceGroup()
      name: 'resA/resB'
    }

    This is also coherent with the planned existing syntax where resourceGroup is a valid scope, and semantically fits with the future possibility of allowing resources to be deployed at mixed scopes within the same file (without modules).

  2. If we then swap out the scope for resB to declare it as an extension resource:

    resource resA 'My.Rp/a@2020-01-01' = {
      name: 'resA'
    }
    
    // child resource?
    resource resB 'My.Rp/a/b@2020-01-01' = {
      scope: resA
      name: 'resA/resB'
    }
    
    // extension resource?
    resource resC 'My.Rp/c@2020-01-01' = {
      scope: resA
      name: 'resC'
    }

    As you can see, resB & resC are very similar, but the proposed behavior would be very different - resB would be considered a child resource, and resC would be considered an extension resource.

    In the above example, the name value is a string literal and we can see clearly that there are a different number of / separators, but it is quite possible for this to be a deploy-time value not known to the Bicep compiler.

  3. A possible solution to the previous step that we discussed was to remove the ability to nest resource names, and instead force a single name per type. If a type has 1 segment, then treat the scope as an extension scope. If it has >1 segment, then treat the scope as a parent/child scope.

    resource resA 'My.Rp/a@2020-01-01 = {
      name: 'resA'
    }
    
    // child resource - because we've got a type with >1 type segments ('a' & 'b')
    resource resB 'My.Rp/a/b@2020-01-01' = {
      scope: resA
      name: 'resB'
    }
    
    // extension resource - because we've got a type with only a single type segment ('c')
    resource resC 'My.Rp/c@2020-01-01' = {
      scope: resA
      name: 'resC'
    }

    The disadvantages of this:

    • It's a breaking change - as child resources must make use of the existing syntax instead of being deployed in a single resource declaration. Supplying a nested name (resA/resB) will no longer work:
      // this will no longer work - breaking change!
      resource resA 'My.Rp/a/b@2020-01-01 existing = {
        name: 'resA/resB'
      }
      Instead, the following syntax would have to be used.
      // this resource is not deployed, and only exists to set the parent resource for `resB`
      resource resA 'My.Rp/a@2020-01-01 existing = {
        name: 'resA'
      }
      
      // this child resource is deployed
      resource resB 'My.Rp/a/b@2020-01-01' = {
        scope: resA
        name: 'resB'
      }
    • More lines of code to accomplish something which was previously possible to achieve in a single resource declaration block.
    • Confusion around what 'scope' really is, and deviation from how scope is defined in the ARM model. Typically, for tenant, subscription, resourceGroup, extension etc. resources, the scope is the portion of the resourceId prior to the final /providers/ segment, and overloading this to also consider a parent resource as setting the scope for a child resource breaks this mental model.

Conclusion

  1. We'll continue to allow a single-declaration child resource by supplying a name containing /:
    resource resA 'My.Rp/a/b@2020-01-01 existing = {
      name: 'resA/resB'
    }
  2. The 'scope' property may only be used to set the resourceId scope, and not to set the parent resource for a child. This is compatible with other uses of 'scope' - for tenant, subscription etc.
  3. We will introduce a 'parent' property to set the parent resource. If supplied, we will validate that the reference to the parent symbol is a compatible type (direct parent type, possibly different API version). This may be set to either a resource deployed in the file, or an existing resource.

Simplifying the resource type for a child resource

Should we simplify the resource type declaration in the scenario that the parent property has been assigned?

E.g.:

resource myVm 'Microsoft.Compute/virtualMachines@2019-03-01' = {
  name: 'myVm'
}

// Instead of 'Microsoft.Compute/virtualMachines/extensions@2019-03-01', simplify to just 'extensions@2019-03-01'
resource myExtension 'extensions@2019-03-01' = {
  parent: myVm
  name: 'myExtension'
}

Discussion

  1. Code completion would be negatively impacted, as we would not be able to offer 'extensions@2019-03-01' before the parent property had been set (which, generally would not be, if typing from left-to-right - the common case).
  2. We would like to support a nested resource syntax (#1363) which provides a more holistic solution, and would allow us to provide better completions:
    resource myVm 'Microsoft.Compute/virtualMachines@2019-03-01' = {
      name: 'myVm'
      resource myExtension 'extensions@2019-03-01' = {
        parent: myVm
        name: 'myExtension'
      }
    }

Conclusion

  1. We decided to stick with the full child type string for a top-level declaration, with the option to relax when #1363 is implemented. The proposed use of parent is compatible with this use case and allows easy conversion.

from bicep.

anthony-c-martin avatar anthony-c-martin commented on September 27, 2024 2

Disclaimer: This comment only holds if my assertions in the previous comment are correct, so feel free to correct me if I'm mistaken in any of those points.

Based on the above, I feel that it's misleading to mix up the terminology of 'scope' and 'parent' as they refer to different things. Some examples:

  • ResourceId: /subscriptions/{subId}/resourceGroups/{rgName}/providers/Microsoft.Compute/virtualMachines/myVm

    • Scope: /subscriptions/{subId}/resourceGroups/{rgName}
    • Fully-qualified Type: Microsoft.Compute/virtualMachines
    • Fully-qualified Name: myVm
    • Parent: N/A (this is a root resource, so it doesn't have a parent)
  • ResourceId: /subscriptions/{subId}/resourceGroups/{rgName}/providers/Microsoft.Compute/virtualMachines/myVm/extensions/myExtension

    • Scope: /subscriptions/{subId}/resourceGroups/{rgName}
    • Fully-qualified Type: Microsoft.Compute/virtualMachines/extensions
    • Fully-qualified Name: myVm/myExtension
    • Parent: /subscriptions/{subId}/resourceGroups/{rgName}/providers/Microsoft.Compute/virtualMachines/myVm
  • ResourceId: /subscriptions/{subId}/resourceGroups/{rgName}/providers/Microsoft.Compute/virtualMachines/myVm/providers/Microsoft.Insights/diagnosticSettings/mySettings

    • Scope: /subscriptions/{subId}/resourceGroups/{rgName}/providers/Microsoft.Compute/virtualMachines/myVm
    • Fully-qualified Type: Microsoft.Insights/diagnosticSettings
    • Fully-qualified Name: mySettings
    • Parent: N/A (this is a root resource, so it doesn't have a parent)
  • ResourceId: /subscriptions/{subId}/resourceGroups/{rgName}/providers/Microsoft.Compute/virtualMachines/myVm/providers/Microsoft.Insights/diagnosticSettings/mySettings/madeUpType/wibble

    • Scope: /subscriptions/{subId}/resourceGroups/{rgName}/providers/Microsoft.Compute/virtualMachines/myVm
    • Fully-qualified Type: Microsoft.Insights/diagnosticSettings/madeUpType
    • Fully-qualified Name: mySettings/wibble
    • Parent: /subscriptions/{subId}/resourceGroups/{rgName}/providers/Microsoft.Compute/virtualMachines/myVm/providers/Microsoft.Insights/diagnosticSettings/mySettings

from bicep.

MaxFrost avatar MaxFrost commented on September 27, 2024 1

Indeed, I actually agree with you here. If anything, I just want it to be easier to declare a child resource within bicep, without creating the resource nesting that earlier ARM templates created.

if I could use this as my decleration

resource hostVm/vmextension1 'Microsoft.Compute/virtualMachines/extensions@2019-03-01' = {
}

that would make me beyond happy.

from bicep.

anthony-c-martin avatar anthony-c-martin commented on September 27, 2024 1

I just want clarify to make sure we're on the same page - the below is my interpretation of ARM's semantic understanding of resourceIds, from my work in the ARM codebase:

  1. A resourceId is composed of a 'parent scope', and a 'routing scope': {parentScope}/{routingScope}.
  2. A resourceId is a /-separated list (not a URI), with each non-empty section referred to as a 'segment'.
  3. The parent scope can be any of:
    1. The tenant scope (/).
    2. The subscription scope (/subscriptions/{subId}).
    3. The resourceGroup scope (/subscriptions/{subId}/resourceGroups/{rgName}).
    4. Another resourceId (e.g. an extension resource). Note that the management group scope is a special case of this.
  4. A resourceId is considered an extension resource if it has a parent scope which is also a resourceId.
  5. The routingScope MUST be of format providers/{provider}/{type}/{name}(/{additionalType}/{additionalName})*.
    1. {provider} is the name of the RP (e.g. Microsoft.Compute).
    2. {provider}/{type} is the root type (e.g. Microsoft.Compute/virtualMachines).
    3. {name} is the root name.
    4. If additional types/names exist in the routing scope, they are treated as key-value pairs to generate the fully qualified type and fully-qualified name. For example, if the routing scope is providers/Microsoft.Compute/virtualMachines/myVm/extensions/myExtension, then:
      1. Fully-qualified type: Microsoft.Compute/virtualMachines/extensions.
      2. Fully-qualified name: myVm/myExtension.
    5. If there are multiple type segments, then the resource is considered a child (or sometimes 'nested') resource. Otherwise it is considered a root resource.

from bicep.

majastrz avatar majastrz commented on September 27, 2024 1

The names above are actually the fully-qualified names. Those are typically used with nested resources in templates, so it's fine in the context of Bicep. However, APIs would actually return the last segment of the fully-qualified names (myExtension or wibble).

from bicep.

miqm avatar miqm commented on September 27, 2024 1

As @anthony-c-martin wrote in #1363 we need to think how to make one-liner resource declarations to existing ones. I though about this recently when implementing keyVault references - due to ARM limitations we'll usually have key vault Id coming from parameter provided by user.

My thoughts were circling around as keyword, that we could use in:
idea 1:
using resource might be optional, therefore encosed in [ ]

param keyVaultId string
module secretModule './secretModule.bicep' = {
  name: 'theSecretModule'
  parameters: {
     secretParam: (keyVaultId as [resource] 'Microsoft.KeyVault/vaults@2019-06-01').getSecret('superSecret')
  }
}

idea 1a (using az.resourceId function to build existing ResourceId). This looks odd, as we need to put type twice - one for building resId, second to perform casting to type.

param keyVaultGroup string
param keyVaultName string
module secretModule './secretModule.bicep' = {
  name: 'theSecretModule'
  parameters: {
     secretParam: resourceId(keyVaultGroup, 'Microsoft.KeyVault/vaults', keyVaultName) as [resource] 'Microsoft.KeyVault/vaults@2019-06-01').getSecret('superSecret')
  }
}

idea 2:

param keyVaultId string as resource keyVault 'Microsoft.KeyVault/vaults@2019-06-01'

module secretModule './secretModule.bicep' = {
  name: 'theSecretModule'
  parameters: {
     secretParam: keyVault.getSecret('superSecret')
  }
}

idea 2a:
Similar to using is in C#, perhaps instead as in this syntax we could use is.

param keyVaultId string as 'Microsoft.KeyVault/vaults@2019-06-01' keyVault

module secretModule './secretModule.bicep' = {
  name: 'theSecretModule'
  parameters: {
     secretParam: keyVault.getSecret('superSecret')
  }
}

idea 3:

param keyVaultId string

resource keyVault 'Microsoft.KeyVault/vaults@2019-06-01' existing  = {
  id: keyVaultId  //name and scope usage would be forbidden when id is used and bicep would throw compilation error
}

module secretModule './secretModule.bicep' = {
  name: 'theSecretModule'
  parameters: {
     secretParam: keyVault.getSecret('superSecret')
  }
}

idea 4 - using decorator.
those might look odd since we'd declare resource object name in function argument, hence the addition of out keyword as in C#

@resource('Microsoft.KeyVault/vaults@2019-06-01', out keyVault) 
param keyVaultId string

module secretModule './secretModule.bicep' = {
  name: 'theSecretModule'
  parameters: {
     secretParam: keyVault.getSecret('superSecret')
  }
}
@resource('Microsoft.KeyVault/vaults@2019-06-01', resourceGroup(), out keyVault) 
param keyVaultName string

module secretModule './secretModule.bicep' = {
  name: 'theSecretModule'
  parameters: {
     secretParam: keyVault.getSecret('superSecret')
  }
}
param keyVaultGroup string

@resource('Microsoft.KeyVault/vaults@2019-06-01', resourceGroup(keyVaultGroup), out keyVault) 
param keyVaultId string

module secretModule './secretModule.bicep' = {
  name: 'theSecretModule'
  parameters: {
     secretParam: keyVault.getSecret('superSecret')
  }
}

In similar way we could use them in parent parameter when declaring child resource.

from bicep.

anthony-c-martin avatar anthony-c-martin commented on September 27, 2024 1

I would assume this issue is solving that problem in 0.4 release. Am I correct?

Correct! We have already implemented the two new mechanisms for deploying child resources - the 'nested' syntax & the 'parent property' syntax. This is available in the nightly 0.3 build, and will be released very soon. under an incremental 0.3 release (docs changes here: #1872).

So you'll have the option of either:

resource botService 'Microsoft.BotService/botServices@2020-06-02' = {
  name: botServiceName
  ...parent props...
  
  resource speechChannel 'channels' = {
    name: 'AlexaChannel'
    ...child props...
  }
}

Or:

resource botService 'Microsoft.BotService/botServices@2020-06-02' = {
  name: botServiceName
  ...parent props...
}

resource speechChannel 'Microsoft.BotService/botServices/channels@2020-06-02' = {
  parent: botService
  name: 'AlexaChannel'
  ...child props...
}

from bicep.

MaxFrost avatar MaxFrost commented on September 27, 2024

just encountered this in a test conversion. I feel a very common pattern for creating VMs in ARM is to create the parent VM resource, then a whole bunch of child resources for the various extensions that apply to the vm. With bicep, I have to define those outside of the VM resource using a syntax I haven't quite grokked yet.

from bicep.

slavizh avatar slavizh commented on September 27, 2024

Moving to resource within resource I think we are going back to the problems of ARM Templates where things become hard to read (code collapsing, horizontal scrolling, etc.). Yes, there could be benefits and improvements because of that but then reading that code becomes harder. At least for me. Also I do not think it is good idea to use the same API version for both the child resource and the parent resource. There are many cases which this will not be a problem but it could be a problem in many other cases just because either the API version for the child resource does not exist at all or when you change on parent resource it could be breaking on the child resource. Different Azure teams code their RPs differently so leaning into one direction you assume that all Azure Teams treat RP API versions in the same way which is not the case in real world and I do not think that will be solved soon.

from bicep.

slavizh avatar slavizh commented on September 27, 2024

although without resource nesting another problem will occur where you have multiple top level resources in loop and you want to create multiple child resources for each top level resource. Looking at the proposed loop syntax I do not see this happening without introducing resource within resource declaration which I really do not like. I would prefer better looping syntax that is not tied to the resource declaration.

from bicep.

bmoore-msft avatar bmoore-msft commented on September 27, 2024

I think if the scope property was fixed in ARM to work like it looks like it should... you could use the scope property and obviate the need for the parent property... then you'd have:

resource someExtension "extensions" = {
    scope: ${hostVM.id}
    name: foo
}

Note I intentionally removed the apiVersion because I'm longing for the day when those are easier to manage ;)

But the child syntax is simple and familiar once we fix the scope property in templates.

from bicep.

anthony-c-martin avatar anthony-c-martin commented on September 27, 2024

I think if the scope property was fixed in ARM to work like it looks like it should... you could use the scope property and obviate the need for the parent property... then you'd have:
...

That's a good point. I suggested a different property as I wasn't sure how we would disambiguate between a child resource and an extension resource. It sounds like you're suggesting we could use a partial type ('extensions') = child resource, and a full type ('Microsoft.Compute/virtualMachines/extensions') = extension resource?

from bicep.

bmoore-msft avatar bmoore-msft commented on September 27, 2024

Yep, scope should be a resourceId... so I could use in any scenario, child resource as you noted...

extension resource

resource extension 'Microsoft.Authorization/roleAssignments = {
    scope: ${someStorageAccount.id}

scope for a deployment or resource:

resource subnet-in-another-rg 'Microsoft.Network/virtualNetworks/subnets' = {
    scope: '/subscriptions/${someOtherSubId}/resourceGroups/${vnets-rg-name}'

tenant level resource

resource newSubscription 'Microsoft.Subscription/aliases' = {
    scope: '/'

I think we need a little bit of logic (or just more thinking) to distinguish scope in some ways (e.g. an extension vs. a deployment scope) but since the latter is finite I think it works.

from bicep.

slavizh avatar slavizh commented on September 27, 2024

@anthony-c-martin @bmoore-msft How would you solve the problem with loops when you loop on parent and child resource?

Otherwise the syntax is ok but looping becomes problem.

from bicep.

bmoore-msft avatar bmoore-msft commented on September 27, 2024

I think looping, when not nested, becomes the same as the parent - everything is treated as a "top level" resource, so you'd essentially define the loop twice (or n-times for each child). Not sure I've wrapped my head around an n*m scenario - off the top I think that becomes more complicated but still doable.

How does this look for a few options:

image

image

image

Option Notes
Child & Parent Full Definition Explicit & clear but verbose and may complicate some looping
Nested Children Terse syntax for name/type, allows code folding and logical organization. Indenting large property bodies can be disorienting
Scope Property Less complexity in type and name from full declaration. No nesting or logical organization.

Feel free to pile on...

from bicep.

alex-frankel avatar alex-frankel commented on September 27, 2024

FWIW, w/ the scope property example, I don't think we can do the abbreviated type because if you've only typed:

resource sourceControls 'sourceControls@2020-06-01' = {}

We will think the type is invalid until you type:

resource sourceControls 'sourceControls@2020-06-01' = {
  scope: webApp.id
}

which feels like a weird experience

from bicep.

anthony-c-martin avatar anthony-c-martin commented on September 27, 2024

Feel free to pile on...

One thing to point out is just that there are most likely always going to be scenarios where we need to support deploying a standalone child resource (e.g. nesting isn't possible), unless we can come up with a unifying syntax for child resources. So if we do pick the nested syntax as an option, we'll still have to support non-nested as well (similar to the current ARM Template behavior).

An important benefit to add for the "Nested Children" & "Scope Property" options are that they both enforce more structure, which allows the compiler to better understand the semantics of the deployment. One concrete example where this could help us make better decisions is #657.

from bicep.

slavizh avatar slavizh commented on September 27, 2024

@bmoore-msft nested resources looks ugly and hard to read and it basically brings the same problem with horizontal scrolling that we have in ARM Templates. So being able to do such loops only in nested resources does not simplify things in Bicep compared to ARM Templates. I think it will be better if nested resources are not supported at all in Bicep and you have to define them as top level resource but that is my personal opinion :)

from bicep.

bmoore-msft avatar bmoore-msft commented on September 27, 2024

@alex-frankel - I think we could identify the resource by just looking at all child types - it's fine that there's more than one match, we can either warn or know that without a namespace, a scope property would be required for complete validation... It's solvable, just depends on how fancy we want to get...

@anthony-c-martin - ack, we will always need the ability to deploy a child standalone... so going with the nested option will mean there is more than one way as today.

@slavizh - I do think there's bit of personal preference here... and providing the option helps with that... for me, nesting in JSON, JSON is a large part of the problem, though I do agree that the longer the definition gets that less non-JSON helps. But I think that's where folding starts to come in... I can easily remove all or part of the resource from view by collapsing it. With nesting I can do this easily with one gesture (think of SQL firewall rules where there may be many). If I cannot nest, I need to deal with each one individually.

from bicep.

slavizh avatar slavizh commented on September 27, 2024

@bmoore-msft If you can do loop within loop you will not have to deal with it individually.

from bicep.

bmoore-msft avatar bmoore-msft commented on September 27, 2024

@slavizh - If I have a loop within a loop, then I have nesting don't I?

from bicep.

slavizh avatar slavizh commented on September 27, 2024

@bmoore-msft technically yes but currently if you have loop within a loop each loop must have a resource to be deployed. If a loop works in a way that you do not have to deploy a resource you can have something like:

  1. loop that deploys parent resources.
  2. second loop that goes trough parent resources
  3. third loop inside the second loop that deploys the child resources.

Yes you loose some time for deploying it that way but certainly will make the loops more usable for different scenarios.

from bicep.

bmoore-msft avatar bmoore-msft commented on September 27, 2024

I'm not following... you're still nested in that scenario, yes?

Aside - I get the computer science problem, what's the scenario for it?

from bicep.

slavizh avatar slavizh commented on September 27, 2024

Let me give you some mock comparison with PowerShell.
If you we have the scenario of resource within resource creation we will have:

foreach($resource in $resources)
{
  New-AzResource -Resource $resource
  foreach($childResource in $resource.childResources)
  {
     New-AzChildResource -Resource $childResource
  }
}

the thing I am trying to explain will look like:

foreach($resource in $resources)
{
  New-AzResource -Resource $resource
}

foreach($resource in $resources)
{
  foreach($childResource in $resource.childResources)
  {
     New-AzChildResource -Resource $childResource
  }
}

Of course with PowerShell looks more cleaner as this is mockup code and does not show all cases out there. My bigger point is that loops should be general enough to be able to use them for transforming data but also if you want them to define child resources separately.

P.S. In the second example we can assume that both foreach loops are executed in parallel and on child resource creation you have dependency on the resource id of the parent so it gets created right after the parent.

from bicep.

bmoore-msft avatar bmoore-msft commented on September 27, 2024

Makes sense - but I think separate issues... One is nesting of resources the other is nesting of loops. My "simple" version of that would simply be something like

resource foo[] 'Microsoft.RP/parent' = {
  copies: 10
}
resource foo[] 'Microsoft.RP/parent' = {
  copies: 10
  resources: [
    resource bar[] 'child' = {
      copies: 2
    }
  ]
}

which would be equivalent to:

resource foo 'Microsoft.RP/parent' = {
  copies: 10
}
resource bar[] 'Microsoft.RP/parent/child' = {
  copies: 10 * 2
}

I can solve the looping issue without nesting and I can simplify looping with nesting.

from bicep.

josefehse avatar josefehse commented on September 27, 2024

For those who come later, current way to do child resources with bicep

resource hostVm 'Microsoft.Compute/virtualMachines@2019-03-01' ={
<stuff>
}
resource vmExtension1 'Microsoft.Compute/virtualMachines/extensions@2019-03-01' = {
  name: '${hostVm.id}/<extensionname>
  <stuff>
}

borrowed from https://github.com/Azure/bicep/blob/master/docs/examples/not-working-yet/301-nested-vms-in-virtual-network/main.bicep

Hello Max, in my tests, I had to use $hostVm.name instead of Id otherwise I'd get the infamous incorrect segment length error. The machine name fixed the issue.
Cheers.

from bicep.

shenglol avatar shenglol commented on September 27, 2024

@slavizh Loops will be implemented in 0.3, but to handle the 1:N parent-children relationship I think we will need to support double iteration in a list comprehension (which should be familiar to Python users):

resource[] parents 'ResourceProvider/parentResources' = [
  for index in range(0, 4): {
    name: 'parent-${index}'
  }
]

resource[] children 'ResourceProvider/childResources' = [
  for index in range(0, 2) for parent in parents: {
    parent: parent
    name: 'child-${index}'
  }
]

from bicep.

alex-frankel avatar alex-frankel commented on September 27, 2024

Wanted to add my latest thoughts on this. At the end of the day, I think we will end up supporting two styles of declaration as it will come down to personal preference. I think the current proposal of scope works for declaring child resources as top-level resources, but for nesting the child inside of the parent, I'd like to propose an alternative. This was inspired by @rynowak's work on his prototype:

resource parent 'microsoft.provider/parentType@2021-01-01' = {
  name: 'foo'
  
  resource child 'childType' = {
    name: 'bar'
  }

}

The main benefit is getting rid of the resources: [] array, but otherwise has the same benefits of the current strategy in ARM templates of nesting in the parent (no need to declare type of the parent). I think we were hesitant about nesting initially as it seemed like the syntax would get messy, but seeing it working made it clear that it worked pretty well and is easy to follow. Nested looping and conditions should also fit naturally.

from bicep.

bmoore-msft avatar bmoore-msft commented on September 27, 2024

This is a really good write-up it helped crystalize some things for me (I think 😉).

The first/main thing I'm thinking is that whatever we choose requires the current deployment engine to support it elegantly - e.g. if we only a used single property (e.g. scope), this would need to work in json templates the same way. Even if I separate the property (parent & scope) those properties may not be known at compile time and I can't determine how to generate a valid template for them, but because we mucked this up in json templates currently, it does work, bicep is just following suit. IOW, the current template engine handles this in a convoluted way, so bicep, being the transparent abstraction has to as well (albeit slightly simpler). Bicep can't do any better on it's own in this particular case. This may be the issue that we're struggling with more than the bicep representation of it… only so much lipstick can do.

That said, in the ARM API (which may be slightly different than the programming object model for a language), the way to think of scope is that it's a resourceId - or not so much the way to think about it, but it is resourceId, always and only. Whether it's a MG, Sub, RG, Resource (parent or extendable) it's still a resourceId. And… it's the resourceId that indicates the URI that precedes a resource definition when PUT. Introducing multiple properties for the nuances of the different types of resourceIds seems like it may be making things more complicated. So with parent+scope I have to think about the differences and know when to use each type, rather than "it's a resourceId". So I think with a clean slate we'd simply rely on a single property which is scope.

Re: auto complete - the only place I see any ambiguity for that is on standalone child resources, IOW when the resource declaration doesn't have the full namespace. If the child resource name is ambiguous (which isn't common) then we'd require the scope before helping with any other properties. Much like adding the namespace, I think it's fine to expect that until the shortcut of authoring is completed, or until user has provided enough info, auto-complete will be less helpful than when it is… Similar to how json needs type+apiVersion before being completely useful.

The scenario where I've authored something incorrectly (whether it's a child or an extension resource) is just an error - no different than today. Scope is a resourceId and if I've created an invalid resourceId, that will fail (also no different than today). We could improve the authoring experience based on what we know about certain types of resourceIds and that would help the user avoid errors without requiring the user to understand all the variations.

Now, I think the "real" problem is that how do I generate the correct json when a single property such as "scope" is not known at compile time [and has different behaviors]? Scope being simply a resourceId, this actually doesn't matter - except that the bicep cannot be translated into json because the json doesn't support it. So we either introduce a new property in bicep (that does not have an equivalence in ARM/json) or we can't simplify it. TBH, I don't think we should work around it in bicep - we should fix it in the template engine and then bicep [and all other deployments] will be more elegant. In the meantime, users simply author these in bicep the way users have to in the json.

from bicep.

anthony-c-martin avatar anthony-c-martin commented on September 27, 2024

The names above are actually the fully-qualified names. Those are typically used with nested resources in templates, so it's fine in the context of Bicep. However, APIs would actually return the last segment of the fully-qualified names (myExtension or wibble).

Good point, edited my previous comment to clarify 'fully-qualified name' rather than just 'name'.

from bicep.

miqm avatar miqm commented on September 27, 2024

Perhaps this might be a bit late (it popped to my mind this this morning), but what about following syntax:

resource myVm 'Microsoft.Compute/virtualMachines@2019-03-01' = existing {
  name: 'myVm'
}

// Instead of 'Microsoft.Compute/virtualMachines/extensions@2019-03-01', simplify to just 'extensions@2019-03-01'
resource myVm:myExtension 'extensions@2019-03-01' = {
  name: 'myExtension'
}

This will allow us to offer code completions when writing left2right as first thing would be to specify what parent is.

Pros:

  • We do not use scope
  • We do not introduce property that does not exist in ARM (parent)
  • Enables short type usage and completions, as parent is already specified

Cons:

  • Need to specify parent resource in bicep, but this could be mitigated by allowing still name: 'parent/child' syntax

from bicep.

bmoore-msft avatar bmoore-msft commented on September 27, 2024

OIC - the way I'm looking at it is via the concept of a resourceId and how it's exposed via the API. Not how the code base thinks of it…

All the scopes you mentioned (MG, Sub, RG) are resourceIds. When you do a GET, those resources have an id property and it's exactly that that I use when providing instruction or further precision to a PUT. What makes it well-formed for the template parser (or just valid from a user perspective) isn't really what I'm thinking about (for better/worse). In JSON templates, how those are formed are a bit opaque and simplified with language constructs like the scope property or the resourceId functions. I think we should build on that…

If I expand the scenarios you had for the different types of resourceIds into what I'm thinking for bicep, we could have:

// today's long form of a child resource declaration
resource msdeploy 'Microsoft.Web/sites/extensions@2020-06-01' = {  // today's long form
	name: test/msdeploy
}

// target a different resourceGroup
resource foo 'Microsoft.Web/sites@2020-06-01' = { 
    scope: resourceGroup('name')
    name: test
}

// child resource short format (nested or not doesn't matter) - since this is existing we could offer an auto complete list for the proper type
resource subnet 'subnets@2020-06-01' = { 
    scope: vnet.id
    name: defaultSubnet
}

// extension resource
resource ro 'Microsoft.Authorization/locks@2020-06-01' = { 
    scope: storage.id
    name: readOnlyLock
}

// target the tenant or another/different scope
resource newSub 'Microsoft.Subscriptions/alias@2020-06-01' = { 
    scope: /
    name: mySub
}

// creating an existing resource reference via params passed in to the template
resource existingSubnet 'subnets@2020-06-01' = existing {
   scope: resourceId(vnetRg, vnetName)
}

The odd thing out is sometimes I put the segment /providers/ in between things and sometimes I don't - which I think is largely a function of how ARM grew up. I think that's where we sometimes get the separation between "parent" and "scope" - but do think we can remove the need for the distinction for bicep users.

from bicep.

dschenzer avatar dschenzer commented on September 27, 2024

I'm currently trying to create a 'direct line speech' bot channel using the 'Microsoft.BotService/botServices/channels' child resource.
Where I currently get the 'incorrect segment lengths' error.

I would assume this issue is solving that problem in 0.4 release. Am I correct?

image

from bicep.

anthony-c-martin avatar anthony-c-martin commented on September 27, 2024

Closing this as it has been implemented in #1800

from bicep.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.