Skip to main content

Web Transformation

The Web.config file contains settings and configurations for a web application. It may contain values that need to be kept secret and, therefore, should not be part of your source code. Since these values will be used at the time of deployment for the proper functioning of the application, there must be a way to provide or update the values during deployment. The Web.config transform allows developers to define how the Web.config file should change based on the build configuration. BOS, out of the box, supports Web.config transformation.

How does BOS do web.config transformation?

To understand it in a better way we should have a bit of knowledge of XML (eXtensible Markup Language) as the web.config file is XML. XML allows you to define a hierarchical structure with nested elements, and each element can have text content and attributes. We need to uniquely identify an element/node/attribute whose value we have to update at the time of deployment. Here we need to know a way to navigate through the elements and attributes in an XML document, allowing you to identify specific elements. We can do this via XPath.

XPath

XPath (XML Path Language) is a query language used for navigating and selecting nodes from XML documents. The node is selected by following a path or steps. Below are some expressions which are useful for selecting nodes.

ExpressionDescription
/Selects from the root node in the XML document
//Selects nodes at any level in the XML document
@Selects attribute
//@attributeSelects element with a specific attribute anywhere in the XML document
//@attribute="value"Selects any node that has the specified attribute value at any level in the XML document
//parent/childSelects all elements named "child" that are descendants of an element named "parent" at any level in the XML document.

Let’s understand these by taking an example.

<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="ClientId" value="19ee6b53-aaeb-4b44-ab99-c100f551f107"/>
<add key="Secret" value="c3b268862bb6af823702144a52b39a1c31dd5441b968d6b5efdb925d6ef5f66d"/>
</appSettings>
<connectionStrings>
<add name="MyDbContext" connectionString="Data Source=127.0.0.1;Initial Catalog=MyNewDatabase;User Id=my_pro_user;PWD=user_pro_pwd" providerName="System.Data.SqlClient"/>
<add name="MySecondDbContext" connectionString="Data Source=129.0.0.1;Initial Catalog=MyAnotherDatabase;User Id=another_user;PWD=another_pwd" providerName="System.Data.SqlClient"/>
</connectionStrings>
<configurationSettings>
<add name="tenantId">
<value>4cb65d2a2492ec05e7751b98f9f99d1832809676</value>
</add>
<add name="UserId">
<value>12bdf328-baec-4f02-bae2-f0d9e8d464fa</value>
</add>
<add name="UserSecret">
<value>3b749cb2390f0a5514995ee027ccb57b50b5c81e5580c54386c23fb23f530d16</value>
</add>
</configurationSettings>
</configuration>

Consider the above a Web.config file. Let's check how to select specific nodes. Below are some expressions and their description.

ExpressionDescription
/configuration/connectionStrings/add[@name='MySecondDbContext']/@connectionStringSelect connectionString attribute from the root node which is configuration having a child node connectionStrings which has node add having attribute name as “'MySecondDbContext'”
//connectionStrings/add[@name='MySecondDbContext']/@connectionStringThis will also select only a single element like in above expression but it will not look for a root element in this and directly select connectionString attribute from connectionStrings which has node add having attribute name as “'MySecondDbContext'” at any level.
//appSettings/add[@key="ClientId"]/@valueSelects value attribute of appSettings node which has node add having key attribute as “ClientId” at any level in the XML document
//configurationSettings/add[@name='tenantId']/valueSelects value node of configurationSettings node which has node add having name attribute as “'tenantId'” at any level in the XML document
//@attribute="value"Selects any node that has the specified attribute value at any level in the XML document
//parent/childSelects all elements named "child" that are descendants of an element named "parent" at any level in the XML document.

We have to keep in mind that the XPath that we are adding must be unique and select only a single node which we have to update. You should always avoid using an index to uniquely select a node and the structure and nodes might move up or down and hence the indexes will be updated. You can always check your expression to be sure that you have the correct XPath.

If you are using VisualStudio as your IDE then download XPath Tools which will help in validating the XPath of any element.

  1. Click on Extensions → Manage Extensions.Search for XPath Tools in the Manage Extensions popup. Click on download XPath Tools.
  2. Once installed, you can open your Web.config file then right click and select Open XPath Runner.
  3. It will open the XPath Tools - Runner tool window.
  4. Enter the XPath and it will show you the output as the selected node.
  5. If the XPath provides more than one result then you should not use that expression.
  6. If it shows no result then it means the XPath is wrong.

If you are using VisualStudio Code as your IDE then download XML Tools which will help in validating the XPath of any element.

  1. Click on Extensions and search for XML Tools in the Manage Extensions popup. Click on Install.
  2. Once installed, you can open your Web.config file then right click and open Command Palette (Ctrl + Shift + P).
  3. Search for XML Tools: Evaluate Path.
  4. Enter the XPath and it will show you the output as the selected node.
  5. If the XPath provides more than one result then you should not use that expression.
  6. If it shows no result then it means the XPath is wrong.

Using these concepts BOS provides a way of Web.config transformation. We need to make sure that the XPath that we are adding must be unique. We need to add a pipeline variable. To know how to add pipeline variables click here. The key will have a prefix wc_ to indicate whether this variable will be used in Web.config transformation or not. The value of the key will have two parts separated by %: the first part will be XPath to identify the element to be updated, and the second part will be either a reference variable or the actual value of the element.

Example

Consider this part of your Web.config:

<appSettings>
<add key="baseUrl" value="http://localhost:8774/"/>
<add key="ClientId" value="b2998407-8bea-4ad6-a697-fe26e6a06b4e"/>
<add key="ClientSecret" value="c3b268862bb6af823702144a52b39a1c31dd5441b968d6b5efdb925d6ef5f66d"/>
</appSettings>

If you have to update ClientId element attribute value with b2998407-8bea-4ad6-a697-fe26e6a06b4e, you need to create a pipeline variable with key wc_ClientId and the value will be

/appSettings/add[@name='ClientId']/@value%19ee6b53-aaeb-4b44-ab99-c100f551f107

Key: wc_ClientId
Value: /appSettings/add[@name='ClientId']/@value%19ee6b53-aaeb-4b44-ab99-c100f551f107

Now let's take an example of connection string node

 <connectionStrings>
<add name="MyDbContext" connectionString="Data Source=127.0.0.1;Initial Catalog=MyDatabase;User Id=my_user;PWD=user_pwd" providerName="System.Data.SqlClient" />
</connectionStrings>

If you have to update connectionString element attribute value with Data Source=127.0.0.1;Initial Catalog=MyNewDatabase;User Id=my_pro_user;PWD=user_pro_pwd, you need to create a pipeline variable with key wc_connectionstring and the value will be

/configurationSettings/configuration[@name='Configuration2']/value%BOS&098

Key: wc_connectionstring
Value: /connectionStrings/add[@name=MyDbContext]/@connectionString%Data Source=127.0.0.1;Initial Catalog=MyNewDatabase;User Id=my_pro_user;PWD=user_pro_pwd

Now let's take another example where you have a custom node and you want to update its value. Consider the following as a part of your web.config

<configurationSettings>
<configuration name="tenantId">
<value>4cb65d2a2492ec05e7751b98f9f99d1832809676</value>
</configuration>
<configuration name="clientId">
<value>12bdf328-baec-4f02-bae2-f0d9e8d464fa</value>
</configuration>
<configuration name="Secret">
<value>c3b268862bb6af823702144a52b39a1c31dd5441b968d6b5efdb925d6ef5f66d</value>
</configuration>
</configurationSettings>

If we have to update the value element text which is not an attribute but has the value inside it, you need to create a pipeline variable with key wc_configuration and the value will be /configurationSettings/configuration[@name=clientId]/value%19ee6b53-aaeb-4b44-ab99-c100f551f107

Key: wc_configuration
Value: /configurationSettings/configuration[@name=clientId]/value%19ee6b53-aaeb-4b44-ab99-c100f551f107

Note: If you have a value that you want to reuse in multiple places not just in Web.config transformation, then you can create another pipeline variable and use this variable as a reference variable in Web.config transformation variable. Let us understand that by an example. Let's consider ClientId. If you need to use the value of ClientId for multiple purposes then. You create a variable(app_client_id) and reference it in your Web.config transformation variables like

  1. Pipeline variable for ClientId:
    Key: app_client_id
    Value: 19ee6b53-aaeb-4b44-ab99-c100f551f107
  2. Pipeline variable for Web.config transformation of ClientId:
    Key: wc_client_id
    Value: /configurationSettings/configuration[@name=clientId]/value%${app_client_id}

There will be minor adjustments in the XPath and we are good to go. The whole point of the XPath is to select the element whose value you have to update.

Node Types

Node types refer to the different elements or sections within the Web.config file that define specific settings for the application. There are various node types in web.config file each serving a specific purpose. Understanding them is essential for transforming and working with Web.Config data.

Below are the list of Node types and their support in BOS console

Node TypesSupported in BOSDescription
AttributeYesRepresents an attribute of an element.
ElementYesRepresents an element (node) in the XML document
TextYesRepresents the text content of an element.
CDATANoRepresents a CDATA section.
CommentNoRepresents an XML comment
DocumentNoRepresents the entire XML document.
DocumentTypeNoRepresents the <!DOCTYPE…> node.
DocumentFragmentNoRepresents a temporary bag containing one or more nodes without any tree structure.
EntityReferenceNoRepresents the non-expanded entity reference text or or predefined entities in XML documents.
ProcessingInstructionNoRepresents a processing instruction, such as <?xml version="1.0"?>.
SignificantWhitespaceNoRepresents white space between markup in the XML document that is significant.
XmlDeclarationNoRepresents the XML declaration node <?xml version='1.0'...?>.