Beispiel #1
0
        public void EmitManagementGroupScope(SyntaxBase managementGroupNameProperty)
        {
            var managementGroupName  = converter.ConvertExpression(managementGroupNameProperty);
            var managementGroupScope = ExpressionConverter.GetManagementGroupScopeExpression(managementGroupName);
            var serialized           = ExpressionSerializer.SerializeExpression(managementGroupScope);

            writer.WriteValue(serialized);
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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);
        }
Beispiel #4
0
        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}");
            }
        }