public void EmitManagementGroupScope(SyntaxBase managementGroupNameProperty) { var managementGroupName = converter.ConvertExpression(managementGroupNameProperty); var managementGroupScope = ExpressionConverter.GetManagementGroupScopeExpression(managementGroupName); var serialized = ExpressionSerializer.SerializeExpression(managementGroupScope); writer.WriteValue(serialized); }
public void EmitLanguageExpression(SyntaxBase syntax) { var symbol = context.SemanticModel.GetSymbolInfo(syntax); if (symbol is VariableSymbol variableSymbol && context.VariablesToInline.Contains(variableSymbol)) { EmitExpression(variableSymbol.Value); return; } LanguageExpression converted = converter.ConvertExpression(syntax); if (converted is JTokenExpression valueExpression && valueExpression.Value.Type == JTokenType.Integer) { // the converted expression is an integer literal JToken value = valueExpression.Value; // for integer literals the expression will look like "[42]" or "[-12]" // while it's still a valid template expression that works in ARM, it looks weird // and is also not recognized by the template language service in VS code // let's serialize it as a proper integer instead writer.WriteValue(value); return; } // strings literals and other expressions must be processed with the serializer to ensure correct conversion and escaping var serialized = ExpressionSerializer.SerializeExpression(converted); writer.WriteValue(serialized); }
public void EmitLanguageExpression(SyntaxBase syntax) { var symbol = context.SemanticModel.GetSymbolInfo(syntax); if (symbol is VariableSymbol variableSymbol && context.VariablesToInline.Contains(variableSymbol)) { EmitExpression(variableSymbol.Value); return; } if (syntax is FunctionCallSyntax functionCall && symbol is FunctionSymbol functionSymbol && string.Equals(functionSymbol.Name, "any", LanguageConstants.IdentifierComparison)) { // the outermost function in the current syntax node is the "any" function // we should emit its argument directly // otherwise, they'd get wrapped in a json() template function call in the converted expression // we have checks for function parameter count mismatch, which should prevent an exception from being thrown EmitExpression(functionCall.Arguments.Single().Expression); return; } LanguageExpression converted = converter.ConvertExpression(syntax); if (converted is JTokenExpression valueExpression && valueExpression.Value.Type == JTokenType.Integer) { // the converted expression is an integer literal JToken value = valueExpression.Value; // for integer literals the expression will look like "[42]" or "[-12]" // while it's still a valid template expression that works in ARM, it looks weird // and is also not recognized by the template language service in VS code // let's serialize it as a proper integer instead writer.WriteValue(value); return; } // strings literals and other expressions must be processed with the serializer to ensure correct conversion and escaping var serialized = ExpressionSerializer.SerializeExpression(converted); writer.WriteValue(serialized); }
public static LanguageExpression FormatCrossScopeResourceId(ExpressionConverter expressionConverter, ScopeData scopeData, string fullyQualifiedType, IEnumerable <LanguageExpression> nameSegments) { var arguments = new List <LanguageExpression>(); switch (scopeData.RequestedScope) { case ResourceScopeType.TenantScope: arguments.Add(new JTokenExpression(fullyQualifiedType)); arguments.AddRange(nameSegments); return(new FunctionExpression("tenantResourceId", arguments.ToArray(), new LanguageExpression[0])); case ResourceScopeType.SubscriptionScope: if (scopeData.SubscriptionIdProperty != null) { arguments.Add(expressionConverter.ConvertExpression(scopeData.SubscriptionIdProperty)); } arguments.Add(new JTokenExpression(fullyQualifiedType)); arguments.AddRange(nameSegments); return(new FunctionExpression("subscriptionResourceId", arguments.ToArray(), new LanguageExpression[0])); case ResourceScopeType.ResourceGroupScope: // We avoid using the 'resourceId' function at all here, because its behavior differs depending on the scope that it is called FROM. LanguageExpression scope; if (scopeData.SubscriptionIdProperty == null) { if (scopeData.ResourceGroupProperty == null) { scope = new FunctionExpression("resourceGroup", new LanguageExpression[0], new LanguageExpression[] { new JTokenExpression("id") }); } else { var subscriptionId = new FunctionExpression("subscription", new LanguageExpression[0], new LanguageExpression[] { new JTokenExpression("subscriptionId") }); var resourceGroup = expressionConverter.ConvertExpression(scopeData.ResourceGroupProperty); scope = ExpressionConverter.GenerateResourceGroupScope(subscriptionId, resourceGroup); } } else { if (scopeData.ResourceGroupProperty == null) { throw new NotImplementedException($"Cannot format resourceId with non-null subscription and null resourceGroup"); } var subscriptionId = expressionConverter.ConvertExpression(scopeData.SubscriptionIdProperty); var resourceGroup = expressionConverter.ConvertExpression(scopeData.ResourceGroupProperty); scope = ExpressionConverter.GenerateResourceGroupScope(subscriptionId, resourceGroup); } // We've got to DIY it, unfortunately. The resourceId() function behaves differently when used at different scopes, so is unsuitable here. return(ExpressionConverter.GenerateScopedResourceId(scope, fullyQualifiedType, nameSegments)); case ResourceScopeType.ManagementGroupScope: if (scopeData.ManagementGroupNameProperty != null) { var managementGroupName = expressionConverter.ConvertExpression(scopeData.ManagementGroupNameProperty); var managementGroupScope = ExpressionConverter.GetManagementGroupScopeExpression(managementGroupName); return(ExpressionConverter.GenerateScopedResourceId(managementGroupScope, fullyQualifiedType, nameSegments)); } // 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)); default: throw new NotImplementedException($"Cannot format resourceId for scope {scopeData.RequestedScope}"); } }