Skip to content

Commit

Permalink
Use managementGroup() function for MG-scope codegen (#4874)
Browse files Browse the repository at this point in the history
* Use managementGroup() function for MG-scope codegen

* Update test baselines

* Fix up scope tests

Co-authored-by: Bicep Automation <bicep@noreply.github.com>
  • Loading branch information
anthony-c-martin and Bicep Automation committed Oct 15, 2021
1 parent 2dd4020 commit 223b8d2
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 17 deletions.
4 changes: 2 additions & 2 deletions docs/examples/101/mg-policy/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"_generator": {
"name": "bicep",
"version": "dev",
"templateHash": "2022767932279852077"
"templateHash": "4645215035624560254"
}
},
"parameters": {
Expand Down Expand Up @@ -63,7 +63,7 @@
"policyDefinitionId": "[extensionResourceId(variables('mgScope'), 'Microsoft.Authorization/policyDefinitions', variables('policyDefinitionName'))]"
},
"dependsOn": [
"[format('Microsoft.Authorization/policyDefinitions/{0}', variables('policyDefinitionName'))]"
"[extensionResourceId(managementGroup().id, 'Microsoft.Authorization/policyDefinitions', variables('policyDefinitionName'))]"
]
}
]
Expand Down
47 changes: 46 additions & 1 deletion src/Bicep.Core.IntegrationTests/ScenarioTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2749,5 +2749,50 @@ public void Test_Issue4565()
var evaluated = TemplateEvaluator.Evaluate(result.Template);
evaluated.Should().HaveValueAtPath("$.outputs['test'].value", "1234", "the evaluated output should be of type string");
}
}

// https://github.com/Azure/bicep/issues/1228
[TestMethod]
public void Test_Issue1228()
{
var result = CompilationHelper.Compile(@"
targetScope = 'managementGroup'
resource policy01 'Microsoft.Authorization/policyDefinitions@2020-09-01' = {
name: 'Allowed locations'
properties: {
policyType: 'Custom'
mode: 'All'
policyRule: {
if: {
field: 'location'
notIn: [
'westeurope'
]
}
then: {
effect: 'Deny'
}
}
}
}
resource initiative 'Microsoft.Authorization/policySetDefinitions@2020-09-01' = {
name: 'Default initiative'
properties: {
policyDefinitions: [
{
policyDefinitionId: policy01.id
// policyDefinitionId: '/providers/Microsoft.Management/managementGroups/MYMANAGEMENTGROUP/providers/${policy01.id}'
}
]
}
}
");

result.Template.Should().HaveValueAtPath("$.resources[?(@.name == 'Default initiative')].properties.policyDefinitions[0].policyDefinitionId", "[extensionResourceId(managementGroup().id, 'Microsoft.Authorization/policyDefinitions', 'Allowed locations')]");

var evaluated = TemplateEvaluator.Evaluate(result.Template);
evaluated.Should().HaveValueAtPath("$.resources[?(@.name == 'Default initiative')].properties.policyDefinitions[0].policyDefinitionId", "/providers/Microsoft.Management/managementGroups/3fc9f36e-8699-43af-b038-1c103980942f/providers/Microsoft.Authorization/policyDefinitions/Allowed locations");
}
}
}
6 changes: 3 additions & 3 deletions src/Bicep.Core.IntegrationTests/ScopeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class ScopeTests
[DataRow("tenant", "managementGroup('abc')", "managementGroup", ExpectedTenantSchema, "[reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', 'abc'), 'Microsoft.Resources/deployments', 'myMod'), '2020-06-01').outputs.hello.value]", "[extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', 'abc'), 'Microsoft.Resources/deployments', 'myMod')]")]
[DataRow("tenant", "subscription('abc')", "subscription", ExpectedTenantSchema, "[reference(subscriptionResourceId('abc', 'Microsoft.Resources/deployments', 'myMod'), '2020-06-01').outputs.hello.value]", "[subscriptionResourceId('abc', 'Microsoft.Resources/deployments', 'myMod')]")]
[DataRow("tenant", "resourceGroup('abc', 'def')", "resourceGroup", ExpectedTenantSchema, "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', 'abc', 'def'), 'Microsoft.Resources/deployments', 'myMod'), '2020-06-01').outputs.hello.value]", "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', 'abc', 'def'), 'Microsoft.Resources/deployments', 'myMod')]")]
[DataRow("managementGroup", "managementGroup()", "managementGroup", ExpectedMgSchema, "[reference(format('Microsoft.Resources/deployments/{0}', 'myMod'), '2020-06-01').outputs.hello.value]", "[format('Microsoft.Resources/deployments/{0}', 'myMod')]")]
[DataRow("managementGroup", "managementGroup()", "managementGroup", ExpectedMgSchema, "[reference(extensionResourceId(managementGroup().id, 'Microsoft.Resources/deployments', 'myMod'), '2020-06-01').outputs.hello.value]", "[extensionResourceId(managementGroup().id, 'Microsoft.Resources/deployments', 'myMod')]")]
[DataRow("managementGroup", "subscription('abc')", "subscription", ExpectedMgSchema, "[reference(subscriptionResourceId('abc', 'Microsoft.Resources/deployments', 'myMod'), '2020-06-01').outputs.hello.value]", "[subscriptionResourceId('abc', 'Microsoft.Resources/deployments', 'myMod')]")]
[DataRow("managementGroup", "resourceGroup('abc', 'def')", "resourceGroup", ExpectedMgSchema, "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', 'abc', 'def'), 'Microsoft.Resources/deployments', 'myMod'), '2020-06-01').outputs.hello.value]", "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', 'abc', 'def'), 'Microsoft.Resources/deployments', 'myMod')]")]
[DataRow("subscription", "subscription()", "subscription", ExpectedSubSchema, "[reference(subscriptionResourceId('Microsoft.Resources/deployments', 'myMod'), '2020-06-01').outputs.hello.value]", "[subscriptionResourceId('Microsoft.Resources/deployments', 'myMod')]")]
Expand Down Expand Up @@ -74,7 +74,7 @@ public void Emitter_should_generate_correct_module_output_scope_strings(string t
}

[DataRow("tenant", "[tenantResourceId('My.Rp/myResource', 'resourceA')]", "[tenantResourceId('Microsoft.Resources/deployments', 'myMod')]")]
[DataRow("managementGroup", "[format('My.Rp/myResource/{0}', 'resourceA')]", "[format('Microsoft.Resources/deployments/{0}', 'myMod')]")]
[DataRow("managementGroup", "[extensionResourceId(managementGroup().id, 'My.Rp/myResource', 'resourceA')]", "[extensionResourceId(managementGroup().id, 'Microsoft.Resources/deployments', 'myMod')]")]
[DataRow("subscription", "[subscriptionResourceId('My.Rp/myResource', 'resourceA')]", "[subscriptionResourceId('Microsoft.Resources/deployments', 'myMod')]")]
[DataRow("resourceGroup", "[resourceId('My.Rp/myResource', 'resourceA')]", "[resourceId('Microsoft.Resources/deployments', 'myMod')]")]
[DataTestMethod]
Expand Down Expand Up @@ -145,7 +145,7 @@ public void Emitter_should_generate_correct_extension_scope_property_and_correct
}

[DataRow("tenant", "[format('My.Rp/myResource/{0}', 'resourceA')]", "[reference(tenantResourceId('My.Rp/myResource', 'resourceA'), '2020-01-01').myProp]")]
[DataRow("managementGroup", "[format('My.Rp/myResource/{0}', 'resourceA')]", "[reference(format('My.Rp/myResource/{0}', 'resourceA'), '2020-01-01').myProp]")]
[DataRow("managementGroup", "[format('My.Rp/myResource/{0}', 'resourceA')]", "[reference(extensionResourceId(managementGroup().id, 'My.Rp/myResource', 'resourceA'), '2020-01-01').myProp]")]
[DataRow("subscription", "[format('My.Rp/myResource/{0}', 'resourceA')]", "[reference(subscriptionResourceId('My.Rp/myResource', 'resourceA'), '2020-01-01').myProp]")]
[DataRow("resourceGroup", "[format('My.Rp/myResource/{0}', 'resourceA')]", "[reference(resourceId('My.Rp/myResource', 'resourceA'), '2020-01-01').myProp]")]
[DataTestMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"_generator": {
"name": "bicep",
"version": "dev",
"templateHash": "4677180672072707203"
"templateHash": "2821996608423621848"
}
},
"parameters": {
Expand Down Expand Up @@ -43,7 +43,7 @@
"roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c"
},
"dependsOn": [
"[format('Microsoft.Authorization/roleAssignments/{0}', guid('owner', parameters('ownerPrincipalId')))]"
"[extensionResourceId(managementGroup().id, 'Microsoft.Authorization/roleAssignments', guid('owner', parameters('ownerPrincipalId')))]"
]
},
{
Expand All @@ -59,8 +59,8 @@
"roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c"
},
"dependsOn": [
"[format('Microsoft.Authorization/roleAssignments/{0}', guid('contributor', parameters('contributorPrincipals')[0]))]",
"[format('Microsoft.Authorization/roleAssignments/{0}', guid('owner', parameters('ownerPrincipalId')))]"
"[extensionResourceId(managementGroup().id, 'Microsoft.Authorization/roleAssignments', guid('contributor', parameters('contributorPrincipals')[0]))]",
"[extensionResourceId(managementGroup().id, 'Microsoft.Authorization/roleAssignments', guid('owner', parameters('ownerPrincipalId')))]"
]
},
{
Expand Down
8 changes: 8 additions & 0 deletions src/Bicep.Core/Emit/ExpressionConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,14 @@ public LanguageExpression GenerateManagementGroupResourceId(SyntaxBase managemen
}
}

/// <summary>
/// Generates a management group id, using the managementGroup() function. Only suitable for use if the template being generated is targeting the management group scope.
/// </summary>
public static LanguageExpression GenerateCurrentManagementGroupId()
=> AppendProperties(
CreateFunction("managementGroup"),
new JTokenExpression("id"));

private static FunctionExpression CreateFunction(string name, params LanguageExpression[] parameters)
=> CreateFunction(name, parameters as IEnumerable<LanguageExpression>);

Expand Down
16 changes: 9 additions & 7 deletions src/Bicep.Core/Emit/ScopeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,17 +249,19 @@ public static LanguageExpression FormatFullyQualifiedResourceId(EmitterContext c
// We've got to DIY it, unfortunately. The resourceId() function behaves differently when used at different scopes, so is unsuitable here.
return ExpressionConverter.GenerateExtensionResourceId(scope, fullyQualifiedType, nameSegments);
case ResourceScope.ManagementGroup:
LanguageExpression mgScope;
if (scopeData.ManagementGroupNameProperty != null)
{
var managementGroupScope = converter.GenerateManagementGroupResourceId(scopeData.ManagementGroupNameProperty, true);

return ExpressionConverter.GenerateExtensionResourceId(managementGroupScope, fullyQualifiedType, nameSegments);
mgScope = converter.GenerateManagementGroupResourceId(scopeData.ManagementGroupNameProperty, true);
}
else
{
// use managementGroup().id to format the scope. This will only work at management group scope,
// but we only permit referencing a parameter-less management group function at this scope.
mgScope = ExpressionConverter.GenerateCurrentManagementGroupId();
}

// We need to do things slightly differently for Management Groups, because there is no IL to output for "Give me a fully-qualified resource id at the current scope",
// and we don't even have a mechanism for reliably getting the current scope (e.g. something like 'deployment().scope'). There are plans to add a managementGroupResourceId function,
// but until we have it, we should generate unqualified resource Ids. There should not be a risk of collision, because we do not allow mixing of resource scopes in a single bicep file.
return ExpressionConverter.GenerateUnqualifiedResourceId(fullyQualifiedType, nameSegments);
return ExpressionConverter.GenerateExtensionResourceId(mgScope, fullyQualifiedType, nameSegments);
case ResourceScope.Resource:
if (scopeData.ResourceScope is not { } resource)
{
Expand Down

0 comments on commit 223b8d2

Please sign in to comment.