Configuration Files for DXP (PASS portal deployment method)
If you project is going to use DXP with the PASS portal deployment method, it is crucial to have the correct configuration files, so in this post we are going to explain all about it. Why do you need them? What files do you need? What is the order they are executed? Some tips to avoid issues when deploying to DXP and some examples. So, lets begin!
Introduction
By default, Each ASP.NET project will have the web.config file and two transformation files: Debug and Release. However, none of the transformation files are going to be applied when you deploy to a DXP environment such as integration, pre-production or production. You need to create new transformation files just for DXP.
Why you need them?
EPiServer DXP requires a set of configuration and transformation files with specific names in order to work properly. If you do not have it, the configurations for each environment will not be applied correctly causing deployment and security issues. Therefore, it is hugely important to add them to the solution with their correct names and that the transformations in each file are correctly written.
What files do you need?
In order to make to deploy to DXP you need 3 new transformation files:
- Web.Integration.config
- Web.Preproduction.config
- Web.production.config
Each file has a filename which represents the environment that it targets.
What is the order they are executed?
The main web.config file will use the transforms found in Web.Integration.config to get a final web.config file to deploy to the integration environment. But, from that point forward, the transformations are applied to the other two environments Pre-production and production are applied over the web.config file with the Web.Integration.config transformations already in place, not the original one from your project.
Tips
Check carefully that all configuration files have the Build Action property to Content and that they are part of the solution. If that is not the case, when you deploy you code, it is highly possible than the transforms are not going to be applied correctly.
If for some reason you are still having issue please check that the web project file (You can do that by unloading your web project from the solution for a moment) is configured properly. That means, that the web.config file is a dependency of the integration transformation file (It should also be a dependency of Debug and Release transformation files, but that is the default and it is not needed for DXP) and the preproduction and production transformation files do not have dependencies.
Examples
After taken consideration of all those aspects, you can start working on your transformation files. Usually the file Web.Integration.config is the largest one because it has all the main transformations required for DXP. This includes: remove connection strings, add redirect rules, add blobs and events providers, among other configurations. Below you can find and example of this transformation file.
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
</system.web>
<episerver.framework createDatabaseSchema="true" xdt:Transform="RemoveAttributes(configSource)">
<securityEntity xdt:Transform="Insert">
<providers>
<add name="SynchronizingProvider" type ="EPiServer.Security.SynchronizingRolesSecurityEntityProvider, EPiServer"/>
</providers>
</securityEntity>
<scanAssembly forceBinFolderScan="true" xdt:Transform="Insert" />
<virtualRoles addClaims="true" xdt:Transform="Insert">
<providers>
<add name="Administrators" type="EPiServer.Security.WindowsAdministratorsRole, EPiServer.Framework" />
<add name="Everyone" type="EPiServer.Security.EveryoneRole, EPiServer.Framework" />
<add name="Authenticated" type="EPiServer.Security.AuthenticatedRole, EPiServer.Framework" />
<add name="Anonymous" type="EPiServer.Security.AnonymousRole, EPiServer.Framework" />
<add name="CmsAdmins" type="EPiServer.Security.MappedRole, EPiServer.Framework" roles="WebAdmins, Administrators" mode="Any" />
<add name="CmsEditors" type="EPiServer.Security.MappedRole, EPiServer.Framework" roles="WebEditors" mode="Any" />
<add name="Creator" type="EPiServer.Security.CreatorRole, EPiServer" />
<add name="GoogleAnalyticsAdministrators" type="EPiServer.Security.MappedRole, EPiServer.Framework" roles="CmsAdmins, Administrators" mode="Any" />
<add name="Creator" type="EPiServer.Security.CreatorRole, EPiServer" />
<add name="PackagingAdmins" type="EPiServer.Security.MappedRole, EPiServer.Framework" roles="CmsAdmins, WebAdmins, Administrators" mode="Any" />
<add name="EPiBetaUsers" type="EPiServer.Security.MappedRole, EPiServer.Framework" roles="CmsAdmins, WebAdmins, Administrators" mode="Any" />
<add name="SearchAdmins" type="EPiServer.Security.MappedRole, EPiServer.Framework" roles="CmsAdmins, WebAdmins, Administrators" mode="Any" />
</providers>
</virtualRoles>
<geolocation defaultProvider="maxmind2" xdt:Transform="Insert">
<providers>
<add name="maxmind2" type="EPiServer.Personalization.MaxMindGeolocationProvider, EPiServer.Personalization.MaxMindGeolocation" databaseFileName="App_Data\GeoLite2-City.mmdb" locationsFileName="App_Data\GeoLite2-City-Locations-en.csv" />
</providers>
</geolocation>
<virtualPathProviders xdt:Transform="Insert">
<clear />
<add name="ProtectedAddons" virtualPath="~/episerver/" physicalPath="modules\_protected" type="EPiServer.Web.Hosting.VirtualPathNonUnifiedProvider, EPiServer.Framework.AspNet" />
</virtualPathProviders>
<appData basePath="App_Data" xdt:Transform="Insert" />
<blob xdt:Transform="Insert" defaultProvider="azureblobs">
<providers>
<add name="azureblobs" type="EPiServer.Azure.Blobs.AzureBlobProvider,EPiServer.Azure"
connectionStringName="EPiServerAzureBlobs" container="somemedia"/>
</providers>
</blob>
<event xdt:Transform="Insert" defaultProvider="azureevents">
<providers>
<add name="azureevents" type="EPiServer.Azure.Events.AzureEventProvider,EPiServer.Azure"
connectionStringName="EPiServerAzureEvents" topic="someevents"/>
</providers>
</event>
<geolocation defaultProvider="maxmind2">
<providers>
<add name="maxmind2" type="EPiServer.Personalization.MaxMindGeolocationProvider, EPiServer.Personalization.MaxMindGeolocation" databaseFileName="App_Data\GeoLocation\GeoLite2-City.mmdb" locationsFileName="App_Data\GeoLocation\GeoLite2-City-Locations-en.csv" />
</providers>
</geolocation>
</episerver.framework>
<connectionStrings xdt:Transform="Remove" />
<system.webServer>
<rewrite xdt:Transform="InsertIfMissing" />
<rewrite xdt:Transform="Replace">
<rules>
<rule name="Force HTTPS" stopProcessing="true">
<match url=".*" negate="false"/>
<conditions>
<add input="{HTTPS}" pattern="OFF" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" appendQueryString="false" redirectType="Permanent"/>
</rule>
<rule name="Force trailing slash" stopProcessing="false">
<match url="(.*[^/])$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_FILENAME}" pattern="(.*?)\.[a-zA-Z]+[a-zA-Z0-9]*$" negate="true" />
<add input="{REQUEST_METHOD}" matchType="Pattern" pattern="POST" ignoreCase="true" negate="true" />
<add input="{URL}" pattern="\robots.txt" negate="true" />
</conditions>
<action type="Redirect" redirectType="Permanent" url="{R:1}/" />
</rule>
</rules>
</rewrite>
</system.webServer>
<episerver.find xdt:Transform="Remove" />
</configuration>
The other transformation files now can only have the rules that applied to their specific environments. For instance, in production we can add more rules to improve security but keep in mind that for some sections in the transform file you will have to add the old rules found in the integration transformation file and the new ones that you want to add. Below you can find an example of a production transformation file.
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<system.webServer>
<rewrite xdt:Transform="InsertIfMissing" />
<rewrite xdt:Transform="Replace">
<rules>
<rule name="RedirectNonWwwToWww" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" pattern="^somedomain.com$" />
</conditions>
<action type="Redirect" url="https://www.somedomain.com/{R:0}" />
</rule>
<rule name="Force HTTPS" stopProcessing="true">
<match url=".*" negate="false"/>
<conditions>
<add input="{HTTPS}" pattern="OFF" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" appendQueryString="false" redirectType="Permanent"/>
</rule>
<rule name="Force trailing slash" stopProcessing="false">
<match url="(.*[^/])$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_FILENAME}" pattern="(.*?)\.[a-zA-Z]+[a-zA-Z0-9]*$" negate="true" />
<add input="{REQUEST_METHOD}" matchType="Pattern" pattern="POST" ignoreCase="true" negate="true" />
<add input="{URL}" pattern="\robots.txt" negate="true" />
</conditions>
<action type="Redirect" redirectType="Permanent" url="{R:1}/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Note
If you are using the Deployment API (the current best practice) to deploy to DXP, You will not need to set the transformation files Build Action to Content and you also do not need the dependencies from the web.config that were set up in the web project file. The reason is that you will be instead creating packages for the deployment from a CI/CD pipeline. Thanks Scott Reed for the correction.
And that is all. If you have any questions or suggestions please let me know in the comments. I hope this blog post can help someone and as always keep learning !!!
2 COMMENTS