Josef Ottosson's Picture

Josef Ottosson

23 posts

ASP.net core appsettings transformation with environment variables on unix

TL;DR Replace dots with underscore and colons with double underscores.

Consider the following IOptions<MetroSettings> implementation

public class MetroSettings  
{
    public RealTimeDepartures RealTimeDepartures { get; set; }
}

public class RealTimeDepartures  
{
    public string ApiKey { get; set; }
    public string BaseUrl { get; set; }
}

In my Startup I have the following code:

...
var builder = new ConfigurationBuilder()  
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", true, true)
        .AddEnvironmentVariables("MyNamespace.MyPrefix.");
Configuration = builder.Build();  
...

And my appsettings.json looks like this:

{
    "Metro": {
        "RealTimeDepartures": {
            "ApiKey": "OVERRIDE ME WITH A SYSTEM ENVIRONMENT VARIABLE",
            "BaseUrl": "http://example.com"
        }
    }
}

I don't like having API keys and other sensitive data in config files, I prefer environment variables.

In ASP.Net core it's pretty straight forward to transform the appsettings file with environment variables.

On windows you just run this in your terminal:

set MyNamespace.MyPrefix.Metro:RealTimeDepartures:ApiKey=MySecretApiKeyHere  

and your good to go, at least in a windows environment.

I, however, wanted to run my application on a Ubuntu server and I ran into some problems.

When I ran the following command

export MyNamespace.MyPrefix.Metro:RealTimeDepartures:ApiKey=TESTING  

I got the following error:

export: MyNamespace.Metro:RealTimeDepartures:ApiKey=TESTING': not a valid identifier  

Apparently you are not allowed to have dots or colons in the environment variable name.

So, what to do?

Luckily I found this issue on github (open source ftw!) and I was able to make it work by doing the following:

  • Change my prefix from MyNamespace.MyPrefix. to MyNamespace_MyPrefix_ in the Startup file.
  • Replace the colons with double underscores instead

Before:

MyNamespace.MyPrefix.Metro:RealTimeDepartures:ApiKey=TESTING  

After:

MyNamespace_MyPrefix_Metro__RealTimeDepartures__ApiKey=TESTING  
  • Setting the correct environment variables in windows and ubuntu.

Windows:

set MyNamespace_MyPrefix_Metro__RealTimeDepartures__ApiKey=MySecretApiKeyHere  

Ubuntu:

export MyNamespace_MyPrefix_Metro__RealTimeDepartures__ApiKey=MySecretApiKeyHere  

(I actually ended up adding the variable directly in the /etc/environment file)

Easy way of changing the Episerver login backgrounds/logo

I've created a NuGet package that gives you(the developer) an easy way to change the background images and the logo(Digital Experience Cloud) on the login screen.

It's heavily inspired by this blog post by Jon D Jones but I've tweaked it a little and also created a package.

Currently Episerver has three different background images, so you can also have up to three custom background images. If you only add one, all of Episervers background images will be replaced with the same one.

The background images can be named anything, it doesn't matter, the only thing that matters is the file format.
The following formats are supported for the background images:

  • jpg
  • jpeg
  • gif
  • png

The logo needs to be in the svg format and have the following name: epi_login_logo.svg

Note: You can choose to only replace the background images or just the logo, you don't need to replace both.

How to easily change the Episerver login background

Episerver 10.0.1 and above is supported.

  1. Install-Package JOS.Epi.LoginScreen
  2. Either place your images in the Static/img/login folder OR specify your own location in appsettings using the key JOS.LoginScreen.ImageFolder. The value should be the relative folder path.
    Example: my\custom\path\here
  3. Profit.
Examples

Default login screen.
Default login screen

Custom background.
Here I've placed a png file in static\img\login
Custom background

Custom background and logo.
Here I've also added a custom logo by placing a custom svg file named epi_login_logo.svg in static\img\login
Custom background and logo

Source code

Code available here

Custom EPiServer key value property with predefined keys support

I've created a custom property with support for predefined keys. You can check out the source code on GitHub and you can install it by simply running Install-Package JOS.PropertyKeyValueList in the Package manager console.

Before I started creating the property I did some googling and I found this blogpost by Peter Löfman where he had done something quite similar to what I wanted to achieve so I used his blog post to get started.

The property has two "modes", ReadOnlyKeys or Normal.

ReadOnlyKeys

Add the property like this to try it out.

[BackingType(typeof(PropertyKeyValueList))]
[KeyValue(typeof(ReadOnlyKeysProvider))]
public virtual IEnumerable<KeyValueItem> ReadOnly { get; set; }  

ReadOnlyMode is specified by passing in an IReadOnlyKeysProvider to the constructor of the KeyValueAttribute.

IReadOnlyKeysProvider

public interface IReadOnlyKeysProvider  
{
    List<string> GetKeys();
}

My implementation in this example looks like this:
ReadOnlyKeysProvider

public class ReadOnlyKeysProvider : IReadOnlyKeysProvider  
{
    public List<string> GetKeys()
    {
        return new List<string>()
        {
            "Arsenal",
            "Real Madrid",
            "Barcelona",
            "Skara FC",
            "Axvalls IF",
            "Juventus",
            "IFK Göteborg",
            "Djurgårdens IF",
            "AIK"
        };
    }
}

When specifying a IReadOnlyKeysProvider the property will render in ReadOnlyKeysMode and look like this:
Property rendered in readOnlyKeysMode The editor will only be able to edit the "value" portion of the property, not the predefined keys. It's not possible to add new items in this mode.

Normal

Add the property like this to try it out.

[BackingType(typeof(PropertyKeyValueList))]
[KeyValue]
public virtual IEnumerable<KeyValueItem> NotReadOnly { get; set; }  

The property will look like this in edit mode:
Property rendered in normal mode Currently it's possible to add as many items as you like but Im thinking of adding an optional MaxLimit.

Some notes

Im no DOJO expert but in all examples I've found online regarding custom EPiServer properties it looks like this.value should be available in the postCreate method. this.value is null for me in postCreate for some weird reason, but if I add the below code(wrapping it in a setTimeout block), it works? I've submitted a ticket to the developer support but if anyone have any idea of why it's null I would gladly accept your help :)

Ugly workaround for null value in postCreate

postCreate: function () {  
    this.readOnlyKeysMode = this.readOnlyKeys !== undefined && this.readOnlyKeys.length > 0;
    //This is a really ugly fix for a (possible) episerver bug? 
    //this.value is null in postCreate but if we wait 200ms, this.value suddenly isn't null anymore...?
    var that = this;
    setTimeout(function () {
        if (that.readOnlyKeysMode) {
            domStyle.set(that.kvlAddButton, {
                "display": "none"
            });
            array.forEach(that.readOnlyKeys, that._renderReadOnlyMode, that);
        } else {
            array.forEach(that.value, that._renderNormalMode, that);
        }
    }, 200);
}

UPDATE After consulting the support, Episerver changed some logic in a recent UI release, so instead of getting the value in postCreate, you can retrieve it in _setValueAttr. I will update my code asap to get rid of the ugly timeout.