Set web.config transform in Asp.NET Core - asp.net-core

I've just came across with problem of web.config transformation in asp.net core.
There are two files: base web.config and web.prod-zone-a.config. My aim is to use transformation inside web.prod-zone-a.config when publishing my project.
I have the following "prod-zone-a" configuration settings in .csproj:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'prod-zone-a|AnyCPU' ">
<IntermediateOutputPath>obj\Debug\netcoreapp1.1</IntermediateOutputPath>
<DebugSymbols>true</DebugSymbols>
<Optimize>false</Optimize>
<DefineConstants>TRACE;DEBUG;NETCOREAPP1_1</DefineConstants>
<Configuration>prod-zone-a</Configuration>
</PropertyGroup>
web.prod-zone-a.config looks like:
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<aspNetCore>
<environmentVariables xdt:Transform="Replace">
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="prod-zone-a" />
</environmentVariables>
</aspNetCore>
</system.webServer>
I tried to run publish by two commands:
dotnet msbuild /t:Publish /p:OutputPath=c:\delivery /p:Configuration=prod-zone-a
and
dotnet publish --configuration prod-zone-a --output c:\delivery
But no transformation applies to web.config on output - just the default value.
Do I miss something in configuration or command executing?

This worked for me:
Add web.release.config file to the project root.
In Visual Studio 2017, Publish using Web Deploy (make sure it is set to Release). Settings will automatically be picked up.
Sample transformation:
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<system.webServer>
<aspNetCore>
<environmentVariables>
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="PRODUCTION" xdt:Locator="Match(name)" xdt:Transform="SetAttributes" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</configuration>
Update: If you want to remove web.config.release file and others on publish, simply edit your .csproj file and add something like this:
<ItemGroup>
<Content Remove="appsettings.Development.json" />
<Content Remove="web.release.config" />
</ItemGroup>
<ItemGroup>
<None Include="appsettings.Development.json" />
<None Include="web.release.config" />
</ItemGroup>

There is a well-documented tool on github for xdt-transformations.
Also it doesn't depend on command, both of dotnet publish and dotnet msbuild works fine

With the latest version of dotnet cli (2.1.400 or greater), you can just set this msbuild property $(EnvironmentName) and publish tooling will take care of adding ASPNETCORE_ENVIRONMENT environmentVariable to the web.config with the specified environment name.
Also, XDT support is available starting 2.2.100-preview1.
Sample: https://github.com/vijayrkn/webconfigtransform/blob/master/README.md

IIS Web Deploy ASP.NET Core (2.1) in Visual Studio 2017 (VS2017)
First do this: (ref:https://github.com/nil4/dotnet-transform-xdt#-use-with-msbuildcsproj-tooling)
Install package - dotnet add package DotNet.Xdt --version 2.1.0
Modify .csproj - add package - refer github
Modify .csproj - add transform code (ApplyXdtConfigTransform) at the end - refer github
Add web.DEV_Server.config transfor file by right-clicking on DEV_Server.pubxml
Added following to web.DEV_Server.config
<environmentVariable xdt:Locator="Match(name)" name="ASPNETCORE_ENVIRONMENT" value="Development" xdt:Transform="SetAttributes" />
Modify DEV_Server.pubxml to modify following setting value.
<LastUsedBuildConfiguration>DEV_Server</LastUsedBuildConfiguration>
Validate Connection & Publish
Deploy still uploads other config files, not sure how to stop that.

Following on from user1820686's answer above:
The github page misses out some of the steps required to add this for MSBuild/csproj tooling:
You need to open a command prompt in your project directory and run
dotnet add myProj.csproj package Microsoft.DotNet.Xdt.Tools --version 2.0.0
Then you need to open the csproj file and add
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
<DotNetCliToolReference Include="Microsoft.Dotnet.Xdt.Tools" Version="2.0.0" />
<!-- ... other package references ... -->
</ItemGroup>

may be i don't clear question. For mine case web.config override all settings in web.Release.config file.
Fix for me, i just add reference for transformation xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform" to configuration file.
so, .config file should start:
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
After some time, the best solutions is using dotnet-transform-xdt tool

This is now supported by dotnet publish from SDK version 2.2 with a whole bunch of options.
https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/transform-webconfig?view=aspnetcore-2.2
I think in the example from the question, it would then work when published as
dotnet publish --configuration prod-zone-a

This worked for me with the 1. & 2. above:
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<location>
<system.webServer>
<httpErrors existingResponse="PassThrough"
xdt:Locator="Match(existingResponse)"
xdt:Transform="InsertIfMissing" />
</system.webServer>
</location>
</configuration>

Related

Cannot start website because of <handlers> in web.config

I have taken over the development of a software product that we currently deploy on an IIS server. Initially it was running with ASP.net core 2.0 but since I would like to use EF, I have configured the software to use 2.1.
The problem is, when I deploy a new version of our software, I cannot run it successfully. I tried to track down the error and i recognized that the web.config of the successfully running version and the new published version differ from each other.
Here's the version that runs just fine:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<aspNetCore processPath="dotnet" arguments=".\myProject.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
</system.webServer>
</configuration>
<!--ProjectGuid: 2b991c10-ec9f-493d-97b9-ee1f96458510-->
And this does not work:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\myProject.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
</system.webServer>
</configuration>
<!--ProjectGuid: 2b991c10-ec9f-493d-97b9-ee1f96458510-->
Obviously, it's just the handler that makes the difference. When I manually remove it, the software runs fine. How can i make sure that in future publishing processes via VB 2019, this handler is not added to the web.config? Is there any setting that I can make to prevent this error?
For ignoring the web.config during publishing, you could try <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled> in *.csproj like
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup>
<PropertyGroup>
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
</ItemGroup>
</Project>
For more information, you could refer web.config file
Unfortunately I cannot reconstruct that initial error message but I solved the problem in the meantime.
The problem was a <handler> tag that was added to the web.config during the publishing. We use a nested website structure with one main website and 4 sub-sites. In this configuration, the handler in the web.config must be removed according to documentation from Microsoft. My former colleague wrote a small xdt-transform script a while ago that was supposed to remove the tag when publishing the software. It always worked fine but it stopped working when I updated the SDK to 2.1. So the handler-tag was added to the web.config and that's why I could not access the website in the first place.
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document->Transform">
<system.webServer>
<handlers xdt:Transform="Remove" />
</system.webServer>
</configuration>
I figured out that the generated web.config had an additional <language>-tag before the <system.webServer>-tag. Adding the language-tag to the remove script made the script work again. Now that problem was solved and I could at least connect to the website.
But after connecting I received a HTTP Error 502.5. This was due to an old 2.0.7 version of the runtime that was installed on the server. After updating to 2.2 the website finally was rendered correctly.

How to set ASPNETCORE_ENVIRONMENT during publishing?

I have several WebDeploy publish profiles that deploy my .NET Core web project to various places (Dev, QA, Stage on IIS). For the application to know where it's running, I need to set the ASPNETCORE_ENVIRONMENT environment variable.
Is it possible to set the ASPNETCORE_ENVIRONMENT environment variable as a part of publishing the application?
P.S. This question doesn't solve anything for me because it shows how to achieve it manually. I would like it to be automatic as a part of deploy a publish profile.
You can Just Update your webconfig file with below text in section.
<configuration>
<system.webServer>
<aspNetCore .....>
<environmentVariables>
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</configuration>
Or you can follow below step after hosting your application in IIS.
Step 1 :
Click on configuration editor as shown in below image
Step 2 :
now select system.webserver/asp.netcore and in other dropdown select applicationHost.config shown in below image.
Step 3 :
Now select enviromentVariable and enter value.
I hope it may help you.
From https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-3.1#set-the-environment
For Windows IIS deployments: Include the property in the publish profile (.pubxml) or project file. This approach sets the environment in web.config when the project is published.
<PropertyGroup>
<EnvironmentName>Development</EnvironmentName>
</PropertyGroup>
I accidentally found a simple answer to the OP's question.
If an environment variable named environmentName is set when you called dotnet publish it will incorporate it into your web.config.
Azure App Service - What modified my web.config?
After a long search, reading and experimenting, i solved by simply adding the enviroment variable ASPNETCORE_ENVIRONMENT in my .pubxml
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ASPNETCORE_ENVIRONMENT>Test</ASPNETCORE_ENVIRONMENT>
</PropertyGroup>
<PropertyGroup>
<WebPublishMethod>MSDeploy</WebPublishMethod>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
.....
</PropertyGroup>
</Project>
I use it to select my react app's build environment, on publish, my .csproj looks like:
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build:$(ASPNETCORE_ENVIRONMENT.ToLower())" />
...
</Target>
Finally my react app's package.json:
"scripts": {
"start": "env-cmd -f .env.development react-scripts start",
"build:production": "env-cmd -f .env.production react-scripts build",
"build:staging": "env-cmd -f .env.staging react-scripts build",
"build:test": "env-cmd -f .env.test react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"mock:api": "json-server --watch db.json --port 4000"
},
I intentionally removed "build": "react-scripts build" line, so environment is always supplied.
EDIT:
I also added PropertyGroup for setting EnvironmentName in .csproj file, so on publishing, my web.config will set the ASPNETCORE_ENVIRONMENT as environment variable, as defined in my .pubxml, dynamically:
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build:$(ASPNETCORE_ENVIRONMENT.ToLower())" />
<PropertyGroup>
<EnvironmentName>$(ASPNETCORE_ENVIRONMENT)</EnvironmentName>
</PropertyGroup>
</Target>
For publishing the web.config with specific environment, you could try Transform web.config.
For your requirement, you could try Profile.
Create multiple web.{profile}.config with different ASPNETCORE_ENVIRONMENT environment variable.
eg.web.Dev.config
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<location>
<system.webServer>
<aspNetCore>
<environmentVariables xdt:Transform="InsertIfMissing">
<environmentVariable name="ASPNETCORE_ENVIRONMENT"
value="Dev"
xdt:Locator="Match(name)"
xdt:Transform="InsertIfMissing" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</location>
</configuration>
eg. web.Release.config
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<location>
<system.webServer>
<aspNetCore>
<environmentVariables xdt:Transform="InsertIfMissing">
<environmentVariable name="ASPNETCORE_ENVIRONMENT"
value="Release"
xdt:Locator="Match(name)"
xdt:Transform="InsertIfMissing" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</location>
</configuration>
Create your project profile with the specific name like Dev and Release, you could create the web.{profile}.config with your existing profile name.
After publishing, it will copy the specific content from web.{profile}.config to the web.config
Currently, it will exsit web.config, web.Dev.config and web.Release.config in your publihshed folder, if you prefer only web.config, change your Project.csproj like below to exclude the unwantted files.
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<Configurations>Debug;Release;Dev</Configurations>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<Content Update="web.Dev.config" CopyToPublishDirectory="Never" />
<Content Update="web.Release.config" CopyToPublishDirectory="Never" />
</ItemGroup>
</Project>

Passing parameters to Web.config transformation in MsBuild script

I have transformation file:
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<runtime>
<asm:assemblyBinding xmlns:asm="urn:schemas-microsoft-com:asm.v1">
<asm:dependentAssembly xdt:Locator="Condition(asm:assemblyIdentity/#name='Company.Product')">
<asm:codeBase xdt:Transform="SetAttributes(version,href)" version="1.2.3.4" href="FILE://D:/Path/To/Company.Product.dll" />
<asm:bindingRedirect xdt:Transform="SetAttributes(newVersion)" newVersion="1.2.3.4" />
</asm:dependentAssembly>
</asm:assemblyBinding>
</runtime>
</configuration>
It is possible run task TransformXml (or something similar) with version as parameter?
My buildscript must recognize version of some assembly and set this version as attribute in web.config file.

Msbuild just produces a package but does not deploy it when using a publish profile. Why?

MSBuild really seems to like me.
Recently I am trying out the different possibilities to build and deploy from command line.
However I am experiencing some seemingly strange behaviour when I pass a publish profile to MSBuild.
Here is an example of what I just did:
I deploy to a local IIS for the moment with a command such as this:
msbuild D:\PathToFile\DeployDBVariation01\DeployDBVariation01\DeployDBVariation01.csproj
/p:Configuration=Release;
Platform=AnyCpu;
DeployOnBuild=true;
DeployTarget=MSDeployPublish;
MSDeployServiceURL="localhost";
DeployIisAppPath="DeployApp/DeployThis";
MSDeployPublishMethod=InProc;
Username=thisIsNotActuallyMyUsername;
password=guesswhat;
AllowUntrustedCertificate=true
And this works! After that it is successfully deployed and I can call it in a browser.
However, since Visual Studio gives us the comfort of using publishing profiles I wanted to try that in conjunction with MSBuild over command line and tried the following command:
msbuild D:\PathToFile\DeployDBVariation01\DeployDBVariation01\DeployDBVariation01.csproj
/p:DeployOnBuild=true;
AllowUntrustedCertificate=true;
PublishProfile=ReleaseLocal
ReleaseLocal is a profile I created in Visual Studio and it looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>MSDeploy</WebPublishMethod>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<ExcludeApp_Data>False</ExcludeApp_Data>
<MSDeployServiceURL>localhost</MSDeployServiceURL>
<DeployIisAppPath>DeployApp/DeployThis</DeployIisAppPath>
<RemoteSitePhysicalPath />
<SkipExtraFilesOnServer>True</SkipExtraFilesOnServer>
<MSDeployPublishMethod>InProc</MSDeployPublishMethod>
<EnableMSDeployBackup>False</EnableMSDeployBackup>
<UserName />
<_SavePWD>False</_SavePWD>
<PublishDatabaseSettings>
<Objects xmlns="">
<ObjectGroup Name="DefaultConnection" Order="1" Enabled="False">
<Destination Path="Data Source=.\SQLEXPRESS;Initial Catalog=HorstDataProductive;User ID=sa;Password=GuessWhat" />
<Object Type="DbDacFx">
<PreSource Path="Data Source=.\SQLEXPRESS;Initial Catalog=HorstData;User ID=sa;Password=GuessWhat" includeData="False" />
<Source Path="$(IntermediateOutputPath)AutoScripts\DefaultConnection_IncrementalSchemaOnly.dacpac" dacpacAction="Deploy" />
</Object>
<UpdateFrom Type="Web.Config">
<Source MatchValue="Data Source=.\SQLEXPRESS;Initial Catalog=HorstData;User ID=sa;Password=GuessWhat" MatchAttributes="$(UpdateFromConnectionStringAttributes)" />
</UpdateFrom>
</ObjectGroup>
</Objects>
</PublishDatabaseSettings>
</PropertyGroup>
<ItemGroup>
<MSDeployParameterValue Include="$(DeployParameterPrefix)DefaultConnection-Web.config Connection String">
<ParameterValue>Data Source=.\SQLEXPRESS;Initial Catalog=HorstDataProductive;User ID=sa;Password=GuessWhat</ParameterValue>
</MSDeployParameterValue>
</ItemGroup>
</Project>
As you can see I have some additional connection string replacement there that I want to test.
So I execute that last MSBuild-command that I have shown you and it executes without any errors. Instead it says that the web deployment task was successful and that a package has been created in a certain path. Now that is actually the right package. When I import that manually in my IIS it is the result I expect, also the connection string replacement has been done.
But I do not understand why it actually is just creating the package but not deploying it in one run, like in my first command.
Can someone explain?
(Or even better, what do I have to do to make it also deploy that package immediately)
You need to specify the VS version.
http://www.asp.net/mvc/tutorials/deployment/visual-studio-web-deployment/command-line-deployment
msbuild /P:DeployOnBuild=True /P:VisualStudioVersion=11.0 /P:PublishProfile=Dev.pubxml
Don't forget to allow untrusted certs if you're using a 'fake' internal certificate
Looks like you're missing the deploy target.. it's got all the necessary info, but doesn't know what you want it to do. Try this;
msbuild D:\PathToFile\DeployDBVariation01\DeployDBVariation01\DeployDBVariation01.csproj
/p:DeployOnBuild=true;DeployTarget=MSDeployPublish;AllowUntrustedCertificate=true;PublishProfile=ReleaseLocal

Why is msbuild duplicating entries in web.config transformations?

I am using web.config transformations to insert entries into the web.config for certain build configurations.
E.g. my Web.Test.config has this entry:
<elmah>
<errorMail from="me#me.com" to="me#me.com" async="false" smtpPort="25" smtpServer="mail" subject="test.senegal.co.uk Exception" xdt:Transform="Insert" />
</elmah>
This works absolutely fine building from visual studio.
However when creating a deployment package using msbuild, the entry is duplicated in the web.config. This obviously causes an exception.
Any ideas?
UPDATE
My "master" config is Web.Master.config not Web.config. The web.config file gets overwritten on build in visual studio. I think it must have something to do with this.
What I think is happening is msbuild is transforming web.config rather than using the Web.Master.config.
The question is how to tell it to use the right master.
I added /p:TransformWebConfigEnabled=false to the msbuild parameters, as my web.config was already being transformed in a BeforeBuild target like so:
<Target Name="BeforeBuild">
<TransformXml Source="$(MSBuildProjectDirectory)\Web.Master.config" Transform="$(MSBuildProjectDirectory)\Web.$(Configuration).config" Destination="$(MSBuildProjectDirectory)\Web.config" />
</Target>
In my case duplication was caused by xdt:Transform="Insert". If remove it from Web..config and leave the rest it will work properly, e.g.:
<!-- doesn't work -->
<add name="EventLog" type="System.Diagnostics.EventLogTraceListener" initializeData="My"
xdt:Transform="Insert" />
vs.
<!-- works -->
<add name="EventLog" type="System.Diagnostics.EventLogTraceListener" initializeData="My"
/>