Azure / bicep

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

scope: managementGroup() acts differently depending on targetScope #7637

Open slavizh opened 2 years ago

slavizh commented 2 years ago

Bicep version Bicep CLI version 0.8.9 (33a85174de)

Describe the bug I have bicep template that can be deployed at both subscription scope and management group scope. In the template I set the target scope to subscription.

I have the following code:

resource remediatePolicyAssignments 'Microsoft.Authorization/policyAssignments@2021-06-01' existing = [for remediationTask in remediationTasks: if (empty(union(defaultRemediationTask, remediationTask).scope) && empty(managementGroupName)) {
  name: remediationTask.policyAssignmentName
}]

resource remediatePolicyAssignmentsManagementGroup 'Microsoft.Authorization/policyAssignments@2021-06-01' existing = [for remediationTask in remediationTasks: if (!empty(union(defaultRemediationTask, remediationTask).scope) && !empty(managementGroupName)) {
  name: remediationTask.policyAssignmentName
  scope: empty(union(defaultRemediationTask, remediationTask).scope) ? managementGroup(managementGroupName) : managementGroup(remediationTask.scope)
}]

...
policyAssignmentId: empty(union(defaultRemediationTask, remediationTask).scope) && empty(managementGroupName) ? remediatePolicyAssignments[i].id : remediatePolicyAssignmentsManagementGroup[i].id

when I set the target scope to subscription the ARM code is:

        "policyAssignmentId": "[if(and(empty(union(variables('defaultRemediationTask'), parameters('remediationTasks')[copyIndex()]).scope), empty(parameters('managementGroupName'))), subscriptionResourceId('Microsoft.Authorization/policyAssignments', parameters('remediationTasks')[copyIndex()].policyAssignmentName), subscriptionResourceId('Microsoft.Authorization/policyAssignments', parameters('remediationTasks')[copyIndex()].policyAssignmentName))]",

when I set the target scope to managementGroup the ARM code is:

        "policyAssignmentId": "[if(and(empty(union(variables('defaultRemediationTask'), parameters('remediationTasks')[copyIndex()]).scope), empty(parameters('managementGroupName'))), extensionResourceId(managementGroup().id, 'Microsoft.Authorization/policyAssignments', parameters('remediationTasks')[copyIndex()].policyAssignmentName), extensionResourceId(managementGroup().id, 'Microsoft.Authorization/policyAssignments', parameters('remediationTasks')[copyIndex()].policyAssignmentName))]",

The logic of where resources will be referenced and deployed should be in the bicep code rather bicep generating different code based on the scope. There are certain set of resources that can be deployed at both subscription and management group scopes and the code for those in 90% is the same so it does not makes sense to create different templates just for the scope. That increases the support and development hours as you will have to maintain two templates instead of one.

To Reproduce above

Additional context Add any other context about the problem here.

anthony-c-martin commented 2 years ago

I think #788 covers this - there are a few small differences in the codegen required at different scopes (as you've found), so currently I wouldn't recommend declaring a Bicep file at one scope and deploying it to another - in fact, AzureCLI will block you from even doing this.

The biggest problem I'm aware of is that there's no ability to reference the 'scope of the deployment', which is why we're forced to codegen different expressions for resourceIds at different scopes (the issue you've run into).

slavizh commented 2 years ago

@anthony-c-martin I have no choice as I was doing that successfully in ARM and I have converted now everything to Bicep. I will have to use the workaround of using the ARM functions directly rather using existing. We do not use AzureCLI thus this is allowed via PowerShell so I think best is to provide support or for sure not intentionally try to block it. Yes the scope thing was my proposal - https://github.com/Azure/bicep/issues/4698

anthony-c-martin commented 2 years ago

We'll discuss this in the next triage. IMO it would be worth us prioritizing adding 'scope' to the deployment() object in the deployment engine, so that we have the option to improve (and simplify) Bicep's codegen. This is tracked under Microsoft internal work item https://msazure.visualstudio.com/One/_workitems/edit/8736388.

slavizh commented 2 years ago

@anthony-c-martin In case you need I can provide the example template but in private to see the real-world scenario.

slavizh commented 8 months ago

@anthony-c-martin I guess it can be closed as it can be workarounded with:

resource remediatePolicyAssignments 'Microsoft.Authorization/policyAssignments@2023-04-01' existing = [for remediationTask in remediationTasks: if (empty(union(defaultRemediationTask, remediationTask).scope) && empty(managementGroupName)) {
  name: remediationTask.policyAssignmentName
}]

resource remediatePolicyAssignmentsManagementGroup 'Microsoft.Authorization/policyAssignments@2023-04-01' existing = [for remediationTask in remediationTasks: if (empty(union(defaultRemediationTask, remediationTask).scope) && !empty(managementGroupName)) {
  name: remediationTask.policyAssignmentName
  scope: managementGroup(managementGroupName)
}]

resource remediatePolicyAssignmentsOtherManagementGroup 'Microsoft.Authorization/policyAssignments@2023-04-01' existing = [for remediationTask in remediationTasks: if (!empty(union(defaultRemediationTask, remediationTask).scope) && !empty(managementGroupName)) {
  name: remediationTask.policyAssignmentName
  scope: managementGroup(union(defaultRemediationTask, remediationTask).scope!)
}]

Although the original posted syntax should not be allowed as it returns false resource ID for management group resource.