Comments (37)
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.
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.
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.
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:
-
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 thetargetScope
isresourceGroup
(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 whereresourceGroup
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). -
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. -
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 singleresource
declaration. Supplying a nested name (resA/resB
) will no longer work:Instead, the following syntax would have to be used.// this will no longer work - breaking change! resource resA 'My.Rp/a/b@2020-01-01 existing = { name: 'resA/resB' }
// 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 theresourceId
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.
- It's a breaking change - as child resources must make use of the
Conclusion
- 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' }
- 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.
- 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
- 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). - 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
- 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.
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)
- Scope:
-
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
- Scope:
-
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)
- Scope:
-
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
- Scope:
from bicep.
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.
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:
- A resourceId is composed of a 'parent scope', and a 'routing scope':
{parentScope}/{routingScope}
. - A resourceId is a
/
-separated list (not a URI), with each non-empty section referred to as a 'segment'. - The parent scope can be any of:
- The tenant scope (
/
). - The subscription scope (
/subscriptions/{subId}
). - The resourceGroup scope (
/subscriptions/{subId}/resourceGroups/{rgName}
). - Another resourceId (e.g. an extension resource). Note that the management group scope is a special case of this.
- The tenant scope (
- A resourceId is considered an extension resource if it has a parent scope which is also a resourceId.
- The routingScope MUST be of format
providers/{provider}/{type}/{name}(/{additionalType}/{additionalName})*
.{provider}
is the name of the RP (e.g.Microsoft.Compute
).{provider}/{type}
is the root type (e.g.Microsoft.Compute/virtualMachines
).{name}
is the root name.- 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:- Fully-qualified type:
Microsoft.Compute/virtualMachines/extensions
. - Fully-qualified name:
myVm/myExtension
.
- Fully-qualified type:
- 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
@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.
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:
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.
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.
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.
@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.
@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.
@bmoore-msft If you can do loop within loop you will not have to deal with it individually.
from bicep.
@slavizh - If I have a loop within a loop, then I have nesting don't I?
from bicep.
@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:
- loop that deploys parent resources.
- second loop that goes trough parent resources
- 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.
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.
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.
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.
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.
@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.
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.
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.
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
orwibble
).
Good point, edited my previous comment to clarify 'fully-qualified name' rather than just 'name'.
from bicep.
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.
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.
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?
from bicep.
Closing this as it has been implemented in #1800
from bicep.
Related Issues (20)
- `bicep local-deploy`: false warnings in the bicepconfig.json when referencing a local path
- Markdown visualization of description differences HOT 1
- Fun with Dick and Jane and completion with invalid recursive types (server crashes)
- Least privileged permissions bug? Application.ReadWrite.OwnedBy HOT 1
- ResourceDefinedTypes - Emit warning for type mismatches
- Do not steal focus from VS Code editor HOT 1
- ResourceDefinedTypes - Completions not working as expected with unmatched bracket
- Module output that uses a for loop with map/union is not rendered correctly as input to another module HOT 9
- AllowedSet for UDT discriminator
- KeyNotFoundException HOT 3
- Metadata backslash interpreted as escape character within string HOT 1
- Automatically add descriptions to bicepparams HOT 2
- Filepath references to extensions are resolved relative to the bicep file not relative to the bicepconfig.json HOT 5
- Conditional module scope is not respected HOT 2
- Enable support in `generate-params` and the "Decompile into bicepparams" for key vault references
- Bicep resource schema doesn't match documentation (or its own example) HOT 1
- Microsoft.Search/searchServices RegionNotMatched issue when template try to create instances at different locations
- readEnvironmentVariable() without a default value causes BCP338 linter error which is not configurable HOT 1
- Development-time validation of custom types HOT 4
- Local artifact file path is malformed HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from bicep.