How to use Environment Variables in your plugins

by Sep 5, 2022

Environment Variables

Dynamics 365 / Power Apps solution can have Environment Variables. Often a Settings table (=entity) would be created to store configuration settings that differ between environments. We now can replace these with environment variables.

These environment variables can be added to a solution, be exported, and imported in another environment. If you do it correctly it even asks for the values for the environment variables when you import.

They can have distinct types, but the simplest one is Text. Other options are Decimal number, Two options and JSON. JSON is the most flexible one because you can store multiple settings, so you don’t need to create an environment variable for each one. It is also future proof because you can add more settings later.

There are also two more advanced data types, Data source and Secret (preview) which make it possible to store the values outside of Dataverse. With Data source you need to add a connector and Secret values are stored in Azure Key Vault.

The variables are stored in the tables environmentvariabledefinition and environmentvariablevalue, which can be secured using Dataverse Users and Roles.

You can use these environment variables in Client-side code (JavaScript), Plugins and in Power Automate but there is no out-of-the-box way to retrieve them. You need to query the tables to get the values.

new environment variable

Retrieving Environment Variables

So, there is not a straightforward way to retrieve them in plugins. Therefore, I added a method to my base plugin class to get the variables in a dictionary. You can also add this code in your specific plugin, instead of your base plugin.

I have added the methode GetEnvironmentVariables which will retrieve all the environment variables in the environment with the Current Values. It will pick the first Current Value if there are more than one. If there are no Current Values, then it will retrieve the Default Value. I don’t check for the status (active/Inactive), but this can be added easily if needed.

It does this by querying the table environmentvariabledefinition, which contains the name, type and default value. The current values are stored in a separated table named environmentvariablevalue. The code assumes the values are of the data type Text. I haven’t tested this code with other data types yet, but I assume JSON will also work.

The environment variables are retrieved on every execution of a plugin, so I have added a temporary cache which helps when the plugin is run multiple times under high load.

public class Plugin : IPlugin
{
    static IReadOnlyDictionary<string, string> EnvVariables;
    static readonly object Lock = new();
    
    protected static IReadOnlyDictionary<string, string> GetEnvironmentVariables(LocalPluginContext ctx)
    {
        ctx.Trace($"Entered GetEnvironmentVariables");
            
        // Singleton pattern to load environment variables less
        if (EnvVariables == null)
        {
            lock (Lock)
            {
                if (EnvVariables == null)
                {
                    ctx.Trace($"Load environment variables");
                    var envVariables = new Dictionary<string, string>();

                    var query = new QueryExpression("environmentvariabledefinition")
                    {
                        ColumnSet = new ColumnSet("statecode", "defaultvalue", "valueschema",
                          "schemaname", "environmentvariabledefinitionid", "type"),
                        LinkEntities =
                        {
                            new LinkEntity
                            {
                                JoinOperator = JoinOperator.LeftOuter,
                                LinkFromEntityName = "environmentvariabledefinition",
                                LinkFromAttributeName = "environmentvariabledefinitionid",
                                LinkToEntityName = "environmentvariablevalue",
                                LinkToAttributeName = "environmentvariabledefinitionid",
                                Columns = new ColumnSet("statecode", "value", "environmentvariablevalueid"),
                                EntityAlias = "v"
                            }
                        }
                    };

                    var results = ctx.SystemUserService.RetrieveMultiple(query);
                    if (results?.Entities.Count > 0)
                    {
                        foreach (var entity in results.Entities)
                        {
                            var schemaName = entity.GetAttributeValue<string>("schemaname");
                            var value = entity.GetAttributeValue<AliasedValue>("v.value")?.Value?.ToString();
                            var defaultValue = entity.GetAttributeValue<string>("defaultvalue");

                            ctx.Trace($"- schemaName:{schemaName}, value:{value}, defaultValue:{defaultValue}");
                            if (schemaName != null && !envVariables.ContainsKey(schemaName))
                                envVariables.Add(schemaName, string.IsNullOrEmpty(value) ? defaultValue : value);
                        }
                    }

                    EnvVariables = envVariables;
                }
            }
        }

        ctx.Trace($"Exiting GetEnvironmentVariables");
        return EnvVariables;
    }

    // Other code of the base Plugin...
}

How to use it in your plugins?

If you added the code above to your base plugin (or your specific plugin), you should be able to use the method GetEnvironmentVariables which will return a Dictionary with all the environment variables and their values in the environment.

You can then use the Dictionary to lookup the variable you need. See the example below:

ctx.Trace($"Try getting av_myvariable from environment variables");
if (!GetEnvironmentVariables(ctx).TryGetValue("av_myvariable", out string myVariable))
        throw new Exception("Couldn't read environmentvariable av_myvariable!");

Remy van Duijkeren

Remy van Duijkeren

Power Platform Advisor

Microsoft Power Platform Advisor with over 25 years of experience in IT with a focus on (marketing) automation and integration.

Helping organizations with scaling their business by automating processes on the Power Platform (Dynamics 365).

Expert in Power Platform, Dynamics 365 (Marketing & Sales) and Azure Integration Services. Giving advice and strategy consultancy.

Services:
– Strategy and tactics advise
– Automating and integrating

Subscribe to
The Daily Friction

A daily newsletter on automation and eliminating friction

Related Content

WordPress Blocks Post using the REST API

WordPress Blocks Post using the REST API

Sending email to my subscribers is done by an automation flow. In the last couple of days paragraphs in my emails started to disappear. It changed into one large blob of text which is very annoying to read. Sorry about this! The automation depends on the RSS-feed of...

read more
Form Data OnLoad

Form Data OnLoad

You want to call Form OnLoad multiple times? Don't! I was reading about a blogpost to trigger the Form OnLoad multiple times. This should happen after the data was saved and/or refreshed. Why would you want this? Usually, the Form OnLoad contains logic to change the...

read more
Who is the target audience for Power Pages

Who is the target audience for Power Pages

Power Pages is slowly getting better and better product. But then I look at the pricing page: https://powerpages.microsoft.com/en-us/pricing/ Who is the target audience for this product? If you need something 'quick and dirty' it is nice to have a website up and...

read more