Using Bicep Functions in C#, if you really want to...

This week, I discovered that it is possible to use the functions that are in Bicep (and ARM) templates in C# code. Why would you want to do this? Well, for the most part, you wouldn’t. Basic functions like concatenate, split etc., already have equivalents in C# code that would produce the same results. However, I had a specific scenario where it did prove useful.

In my scenario, I needed to generate a unique string identifier for some resource naming. I also needed to create this string in both a Bicep template and some C# code, at the same time, independently. Usually, I would generate this string in one language and have it passed into the other as a parameter, but this wouldn’t work here. I needed a string generation or hashing process that would work in both scenarios. Given Bicep is the more limited feature set, I looked here first. Bicep does have a Base64 function which I could have used to take a string and hash it, but this string is long, and I needed a short string. I could have just truncated it, but then I increase the chance it is not unique. The only other option in Bicep is the uniqueString function, which generates a 13-character unique string based on some seed values (in my case, subscription Id, resource group name and an app name). This generated string fits my needs, but uniqueString is a Bicep-only function, and there are no published docs on what this uses to create the string.

It turns out that Microsoft does publish a NuGet Package which has all the code for every Bicep Function, the Azure.Deployments.Expression pacakge. Using this, it was possible to use the uniqueString function in C#.

How to use the Azure.Deployments.Expression Package

The first thing we need to do is install the package in our project, either using the NuGet package explorer or the Package Manager CLI.

Install-Package Azure.Deployments.Expression -Version 1.0.635

We also need the Newtonsoft Json package to generate some of the structures used.

We first need to build an array of parameters we want to pass into the function. The C# code is designed to be generic to run any ARM function, so you don’t get much guidance on what the params are, but you can refer to the Bicep/ARM docs for the function. For unique string we are passing in a set of seed values, in my case, the sub Id, resource group name and app name. So the array looks like this:

var parameters = new FunctionArgument[]
{
    new FunctionArgument(new JValue("subscription")),
    new FunctionArgument(new JValue("resourcegroup")),
    new FunctionArgument(new JValue("managedapp")),

};

We then need to instantiate an instance of the “ExpressionBuiltInFunctions” class and then use the “EvaluateFunction” method to call it. We pass in the name of the function we want to run, the parameters and a default evaluation context.

var funcs = new ExpressionBuiltInFunctions();
var prefix = funcs.EvaluateFunction("uniqueString", parameters, new ExpressionEvaluationContext()).ToString();
Console.WriteLine(prefix)

Here is the complete code:

using Azure.Deployments.Expression.Expressions;
using Newtonsoft.Json.Linq;

var funcs = new ExpressionBuiltInFunctions();

var parameters = new FunctionArgument[]
{
    new FunctionArgument(new JValue("subscription")),
    new FunctionArgument(new JValue("resourcegroup")),
    new FunctionArgument(new JValue("managedapp")),

};

var prefix = funcs.EvaluateFunction("uniqueString", parameters, new ExpressionEvaluationContext()).ToString();
Console.WriteLine(prefix)

You can use the same code to call any other Bicep/ARM functions, providing the correct parameters for that function.

As mentioned, there is limited use for this. Most Bicep functions can already be done with native C# capabilities, but if you find a function in Bicep that you need to emulate in C# directly, then now you can.