Configuring Azure Blob Storage and Azure CDN with Orchard CMS

Posted by Scott on November 12, 2022

Photo by Clint Patterson

I was setting up this very blog you're seeing, and wanted to try Orchard CMS. So far, I like it a lot.

For the media library - the part where I upload the images that go into the posts - I wanted to use an Azure Storage account, and it was a bit of a journey to get it set up.

The Orchard docs show how to configure everything in appsettings.json, but I wanted to set my configuration values in environment variables. to make sure I didn't check in any secrets to my source code.

And I wanted to put Azure CDN in front of all of the static files I serve. Why? Because I can. 🙂

Here's what I did.

Basic steps

  • Enable the Media features in Orchard's admin site
  • Set up Azure CDN endpoints for both your Azure Storage account, and the web site itself
  • Configure the environment variables to tell Orchard to use Azure Storage for the Media Library
  • Configure the Site CDN in Orchard's admin
  • Restart the site so it picks up the new configuration

Enable Media features

First, in order to enable storing the Media Library in Azure Blob Storage, you need to make sure three features are enabled in Orchard.

  • Media
  • Media Cache
  • Azure Media Storage

To find them, in the /admin section of your site, go to Configuration / Features, and search for Media.

Set up Azure CDN

I have a Microsoft CDN (classic) already running, so that's what I'm using. Azure Front Door seems like a great service for web sites with significant traffic, but this blog isn't one of them, so I'm sticking with a "regular" CDN service.

I wanted to put Azure CDN in front of two things:

  • the Azure Storage account that's holding my Media Library, i.e. all of the images on the site
  • the static files from the web server, i.e. CSS and JS and other stuff like that

That meant that I needed to create two separate endpoints:

  • scottarbeitstorage
    • Origin hostname of my Azure Storage account, scottarbeit.blob.core.windows.net
    • mapped to scottarbeitstorage.azureedge.net
  • scottarbeitweb

I also figured I'd create custom domain names for them. (Why not, right?)

First, I needed to create two CNAME's in my domain's DNS entries:

  • cdn.scottarbeit.com, mapped to scottarbeitstorage.azureedge.net
  • static.scottarbeit.com, mapped to scottarbeitweb.azureedge.net

Then I needed to add them as custom domains in the configuration of each endpoint. It's simple enough to add them if you have the CNAME's you're going to use ready to go.

I strongly recommend, of course, that you enable HTTPS, allow Azure to create and manage the certificates you need - which is really, really nice - and set a minimum TLS version of 1.2.

Here's the results for the first custom domain I set up:

Configuring environment variables for the Media Library

I need to provide the connection string for Azure Storage, the name of the container that I'm using for the Media Library, the URL of the storage CDN endpoint we set up above, and a few other values. I want to use environment variables to keep secrets out of my code.

By setting the CdnBaseUrl value, Orchard will emit requests for files in the Media Library - images in my case - to come from the CDN instead of directly from Azure Storage.

In appsettings.json (or appsettings.{Environment}.json), we'd have:

{
  "Logging": {
    ...
  },
  "OrchardCore": {
    "OrchardCore.Media": {
      "CdnBaseUrl": "https://cdn.mydomain.com",
      "AssetsRequestPath": "/static"  // Default value is "/media"
    },
    "OrchardCore.Media.Azure": {
      "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=(your storage account name);AccountKey=(your storage account key);EndpointSuffix=core.windows.net",
      "ContainerName": "static",  // Default value is "media"
      "BasePath": "",
      "CreateContainer": true
    }
  }
}

When translating a configuration hierarchy to environment variable names, substitute a double underscore __ to denote a level, and replace periods in configuration key names with a single underscore _. That becomes:

  • OrchardCore__OrchardCore_Media__CdnBaseUrl
  • OrchardCore__OrchardCore_Media__AssetsRequestPath
  • OrchardCore__OrchardCore_Media_Azure__ConnectionString
  • OrchardCore__OrchardCore_Media_Azure__ContainerName
  • OrchardCore__OrchardCore_Media_Azure__BasePath
  • OrchardCore__OrchardCore_Media_Azure__CreateContainer

How you set your environment variables depends on how you deploy, of course. I'm in Azure App Service, so I set them in the Configuration page there.

With those values set, Orchard will read them at startup, you'll be able to keep them out of appsettings.json, and keep secrets out of your source code.

Configure Orchard's Site CDN

The environment variables hold the CDN endpoint for the Media Library.

Now, I need to configure the CDN for the static files that are served from the web site itself, using the other CDN endpoint I created.

In the /admin site, go to Configuration / Settings / General, and click on the Resources tab. Then set the Site CDN (Content Delivery Network) base url to the static file CDN custom domain.

Success

With all of that done, I have this very blog that you're reading accelerated through the use of Azure Storage and CDN's.

You should be able to get your instance of Orchard configured like this with a little patience and a little bit of time.