Web Config Transform Buildpack
Purpose of the web-config-transform-buildpack
Cloud Native Applications are expected to bring in configurations from external sources like environment variables, config server , etc. Please refer to Configuration in 12factor.net for more information.
In legacy ASP.Net applications, configuration settings are injected through web.config files, and in Console applications, configuration settings are injected through app.config. As per cloud native principles, configuration should stay out of build artifacts. In this recipe we will use a custom buildpack which provides a solution to this problem by using token replacement during cf push staging.
High level steps
- Identify environment dependent configurations and externalize
- Create app manifest
- Add [web|app] config transformations.
- Move config settings to Spring Cloud Config Server
- Create service for Spring Cloud Config Server
- Bind config service to app using manifest
- Push app by parameterized environment name
1. How the buildpack works
- Pulls all the configurations from environment variables and config server repo (if bounded).
- Config server environment is identified by the environment variable
ASPNETCORE_ENVIRONMENT, e.g.dev,prod, etc. - Apply xml transformation
The transformation file target is pulled from environment variable
XML_TRANSFORM_KEY. The pattern,[web|app].{XML_TRANSFORM_KEY}.config, is used to identify the file to transform. For e.g. if the value of environment variableXML_TRANSFORM_KEYisCF, then the transformation file, the buildpack looks for, isweb.CF.configfor web applications andapp.CF.configfor console applications. If theXML_TRANSFORM_KEYis not set, it looks forweb.Release.configorapp.Release.configby default. If the file doesn't exist, it skips transformation step and moves further. Note that the transformation filenames are case sensitive and should match the case used inXML_TRANSFORM_KEY. Lastly, the final transformed config file name for console applications is{applicationName}.exe.configinstead ofweb.configfor web applications. - Modify the transformed file with
appSettings:keyfor<AppSettings>section andconnectionStrings:namefor<ConnectionStrings>section - Modify the transformed file with tokens provided in the format
#{anykey}, e.g. A token named#{foo:bar}will replaced withmyfoovalueif an environment variable with keyfoo:baris set with valuemyfoovalueor the config server repoyamlcontains the info as below.
foo:
bar: myfoovalueNOTE: all transform xml attributes and tokens are case-sensitive
Execution steps in detail
- web.config (Before transformation)
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=LocalSQLServer;Initial Catalog=MyReleaseDB;User ID=xxxx;Password=xxxx" />
</connectionStrings>- web.CF.config (Transformation file)
<connectionStrings>
<add name="MyDB"
connectionString="#{connectionStrings:MyDB}"
xdt:Transform="SetAttributes"
xdt:Locator="Match(name)"/>
</connectionStrings>-
If
XML_TRANSFORM_KEYis set toCF -
web.config (after transformation)
<connectionStrings>
<add name="MyDB"
connectionString="#{connectionStrings:MyDB}"/>
</connectionStrings>- If
ASPNETCORE_ENVIRONMENTis set todevand the config server repoyamlis as below...
connectionStrings:
MyDB: "Data Source=11.11.11.11;Initial Catalog=mydb;User ID=xxxx;Password=xxxx"- web.config (after token replacement)
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=11.11.11.11;Initial Catalog=mydb;User ID=xxxx;Password=xxxx"/>
</connectionStrings>Note: For a console applications, the transformation file, in the above example, would have been called app.CF.config and the resulting transformed file would be the application exe name followed by the
.exe.configextension.
2. Create a Cloud Foundry app manifest
- Ensure your application has a Cloud Foundry manifest file. If your application is in Cloud Foundry already, you can create the manifest using the command
cf create-app-manifest [appname]. - Add a buildpack reference to the manifest (before the hwc buildpack) that will perform the token replacement on cf push action.
Note: Please refer to https://github.com/cloudfoundry-community/web-config-transform-buildpack/releases to pull the latest version as appropriate.
XXXrefers to the version of buildpack. - Add an environment variable to the manifest for each config item that will be used to replace the tokenized values. Below is a sample added referring to the connection string.
Note: Adding token replacements with Environment variables is only for experimental activities. Config settings should be externalized using git repositories and Spring Cloud Config Server.
applications:
- name: sampleapp
stack: windows
buildpacks:
- https://github.com/cloudfoundry-community/web-config-transform-buildpack/releases/download/vXXX/Web.Config.Transform.Buildpack-win-x64-XXX.zip
- hwc_buildpack
env:
"connectionStrings:MyDB": "Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;User ID=xxxx;Password=xxxx"Note
Only put configuration item keys and values in the manifest for testing purposes. Spring Cloud Config Server should be used for externalizing configuration settings (see further sections).
3. Add web config transformations
By default, all web apps and wcf apps created with Debug and Release configurations and corresponding web config transformation files (web.Debug.config, web.Release.config).
-
Add required transformations to
web.Release.config -
Build and push the application to Cloud Foundry to verify that your config settings are properly transformed
Note
For developer machine debugging, use
Debugconfiguration profile andweb.Debug.configfor transformation.
Sample web.Release.config with transformations
<?xml version="1.0" encoding="utf-8"?>
<!-- For Cloud Foundry -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<connectionStrings xdt:Transform="Replace">
<add name="MyDB" connectionString="#{connectionStrings:MyDB}" providerName="System.Data.SqlClient"/>
</connectionStrings>
<system.serviceModel>
<client xdt:Transform="Replace">
<endpoint
address="#{client:Default_IMyLogService:address}"
binding="#{client:Default_IMyLogService:binding}"
bindingConfiguration="#{client:Default_IMyLogService:bindingConfiguration}"
contract="ServiceProxy.IMyLogService" name="Default_IMyLogService" />
</client>
</system.serviceModel>
</configuration>4. Move config settings to Spring Cloud Config Server
A multi-environment, production-ready configuration can be achieved using share and environment specific transforms and using Spring Cloud Config Server - backed by a git repository data source.
- Create a network accessible git repository for each application
- Create <YOUR_APPLICATION>.yaml file to have common settings across all environments
- Create <YOUR_APPLICATION>-< YOUR_APP_ENVIRONMENT>.yaml for each unique environment
- Specify your environment with
ASPNETCORE_ENVIRONMENTenvironment variable in the manifest file created in step 2. e.g:ASPNETCORE_ENVIRONMENT=Production
Sample Config Server yml files
sampleapp.yaml
appSettings:
Setting1: "Common setting1"sampleapp-Development.yaml
connectionStrings:
MyDB: "Data Source=devserver;Initial Catalog=mydb;User ID=xxxx;Password=xxxx"sampleapp-Production.yaml
connectionStrings:
MyDB: "Data Source=prodserver;Initial Catalog=mydb;User ID=xxxx;Password=xxxx"5. Create service for Spring Cloud Config Server
-
Make sure you have config server available in your CF marketplace.
NOTE: To check if you have this server, run
cf marketplace. You should seep.config-serverorp-config-serverin this list. -
Create a JSON file for config server setup (ex: config-server.json)
{ "git" : { "uri": "https://github.com/YOUR_USERNAME/YOUR_CONFIG_REPO" } }NOTE: Ensure file is not BOM-encoded
-
Create config server using above configuration file.
cf create-service p-config-server standard my_configserver -c .\config-server.json
6. Bind config service to app using manifest
Bind the config server to your app once the config server service is created. Add your config server to the services section as seen below:
---
applications:
- name: sampleapp
stack: windows
buildpacks:
- https://github.com/cloudfoundry-community/web-config-transform-buildpack/releases/download/vXXX/Web.Config.Transform.Buildpack-win-x64-1.1.5.zip
- hwc_buildpack
env:
ASPNETCORE_ENVIRONMENT: ((env))
services:
- my_configserver7. Push app by parameterized environment name
Parameterizing your application environment gives ability to provide different value as per you deploy stage in CD pipelines. e.g: Development/QA/UAT/Production.
This can be achieved by replacing hardcoded value of ASPNETCORE_ENVIRONMENT=YOUR_DEPLOY_ENVIRONMENT with ASPNETCORE_ENVIRONMENT: ((env))
Now you can push your app with below command
cf push --var env=Production
You should be able to find if the environment value is actually passed in, by looking at logs.
================================================================================
=============== WebConfig Transform Buildpack execution started ================
================================================================================
-----> Using Environment: Production
-----> Config server binding found...
Special Behavior for appSettings and connectionStrings
This buildpack makes it possible to externalize appSettings and connectionString values in your web.config without using tokenized values. In this case simply include the values in your yaml config files on your Github repository ({YOUR-APP}.production.yml, YOUR-APP}.development.yml, etc.
sampleapp-Development.yaml
appSettings:
Setting1: "development setting"
connectionStrings:
MyDB: "Data Source=devserver;Initial Catalog=mydb;User ID=xxxx;Password=xxxx"sampleapp-Production.yaml
appSettings:
Setting1: "production setting"
connectionStrings:
MyDB: "Data Source=prodserver;Initial Catalog=mydb;User ID=xxxx;Password=xxxx"This buildpack can inject appSettings and connectionStrings values based on environment specific yaml config files even if replacement tokens are not present in web.Release.config file.
Sample Application & Walkthrough
A sample web application and walkthrough can be found here
Note
For any issues you face with the web-config-transform-buildpack, please raise an issue at https://github.com/cloudfoundry-community/web-config-transform-buildpack/issues.