Contents
data/authors/Paul Logan.json

Using Web Deploy to get your apps onto IIS

Manual Deployment to local web server

My first procedure for getting the new bits onto the relevant domain IIS webserver were:

  • VS Code terminal: dotnet publish –configuration Release

  • Browse to \LegacyDatabaseMaintenanceApp\bin\Release\net7.0\publish

    • Sort files by date modified.
    • Copy the files that have just been updated.
  • Remote onto the IIS webserver and stop the application’s app pool.

  • Browse to the application folder on the web server and paste the files, overwriting those already there.

  • Start the AppPool for the app in IIS Manager.

Automated Deployment to local web server

I use Publish Profiles inside Visual Studio for some other apps I develop and really appreciate their ease of use - but more than that, they are the ethos of how us programmers operate. Automate as much as possible, improving efficiency and removing sources of (human) error.

Can we use them in a DotNet Core project within VS Code? Yes, but not completely using the .Net CLI. There is currently no CLI command to initially create the publish profile file required. It is done using the GUI in Visual Studio, or as I have done, using good old copy and paste from an existing project.

The first step is to create an empty file in a new folder following the naming convention, and then pasting in the XML from an existing publish profile.

Open a Command Prompt terminal in VS Code (my default PowerShell terminal requires me to use PowerShell command for creating a file.)

mkdir .\Properties\PublishProfiles
type nul > .\Properties\PublishProfiles\MyAppToLive.pubxml

Copy and paste, updating the path as required:

<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121. 
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <DeleteExistingFiles>False</DeleteExistingFiles>
    <ExcludeApp_Data>False</ExcludeApp_Data>
    <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
    <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
    <LastUsedPlatform>Any CPU</LastUsedPlatform>
    <!-- <PublishUrl>\\MyServer\Apps\MyApp</PublishUrl> -->
    <PublishDir>\\MyServer\Apps\MyApp</PublishDir>
    <WebPublishMethod>FileSystem</WebPublishMethod>
    <SiteUrlToLaunchAfterPublish />
    <TargetFramework>net8.0</TargetFramework>
    <ProjectGuid>1cdb1d35-5776-455e-a56a-d1ac4800fd2f</ProjectGuid>
    <SelfContained>false</SelfContained>
  </PropertyGroup>
</Project>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <AllowUntrustedCertificate>True</AllowUntrustedCertificate>
    <DeleteExistingFiles>False</DeleteExistingFiles>
    <DeployIisAppPath>ManningWebAPI</DeployIisAppPath>
    <EnableMsDeployAppOffline>True</EnableMsDeployAppOffline>
    <EnableMSDeployBackup>True</EnableMSDeployBackup>
    <ExcludeApp_Data>False</ExcludeApp_Data>
    <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
    <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
    <LastUsedPlatform>Any CPU</LastUsedPlatform>
    <MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
    <MsDeployServiceUrl>https://SERVER_NAME__or__IPaddress:8172/msdeploy.axd</MsDeployServiceUrl>
    <ProjectGuid>1cdb1d35-5776-455e-a56a-d1ac4800fd2f</ProjectGuid>
    <SelfContained>false</SelfContained>
    <SiteUrlToLaunchAfterPublish />
    <TargetFramework>net8.0</TargetFramework>
    <WebPublishMethod>MSDeploy</WebPublishMethod>
    <UserName>MyIisManagerUserName</UserName>
    <Password>MyIisManagerPassword</Password>
  </PropertyGroup>
</Project>
dotnet publish MyApp.csproj -p:PublishProfile=MyAppPublishToLive

The -p switch by convention looks in the .\Properties\PublishProfiles folder for a file with the same name as the right hand side of the equals. It also assumes a .pubxml file extension by convention.

One small tweak to the xml is needed if copying it from an older project - the PublishUrl tag needs changing to PublishDir.

Now, I can easily and safely publish to the web server.

Updating IIS Server

The final(?) hurdle - the IIS webserver I published to was not able to server the new app because it was not installed with the corresponding ASP.NET Core web hosting components:

IIS Server Error
The specified version of Microsoft.NetCore.App or Microsoft.AspNetCore.App was not found

The ASP.NET Core Runtime enables you to run existing web/server applications. On Windows, we recommend installing the Hosting Bundle, which includes the .NET Runtime and IIS support.

The download link is here.

After running the installer, executing the following commands in a command shell were enough to get the app running behind IIS:

All websites will be re-started
Issuing the following commands will restart the IIS web server, and therefore every site hosted on that server. Schedule this downtime.
  • net stop was /y
  • followed by net start w3svc

Install Web Deploy

Install web deploy onto the IIS web server (installation involves an IIS service restart).

Select the Custom install button….

MSDeploy custom install
MSDeploy custom install

…and ensure all the features are selected to be installed….

MSDeploy install all options
MSDeploy install all options

As a preliminary check, I used the following PowerShell command to test the functionality directly:

cd "C:\Program Files\IIS\Microsoft Web Deploy V3"
.\msdeploy.exe -verb:sync -source:backupManager -dest:backupManager=SiteName

Configuration section system.webServer/management/delegation is missing

If you don’t install all the features first time round, and then do a Repair to install the missing features, this error can occur.

THe solution is to run the installer and Remove the installation, then do a complete install.

ERROR_CANNOT_CREATE_BACKUP

MSDeploy cannot create backup
MSDeploy cannot create backup

IIS Manager => Root level => Configuration Editor

Enable backup feature in IIS
Enable backup feature in IIS
Testing MsDeploy from PowerShell
Testing MsDeploy from PowerShell

Could not complete the request to remote agent

dotnet publish -p:PublishProfile=ManningPublishToLive`

Could not complete the request to remote agent URL ‘http://YOUR_SERVER:8172/msdeploy.axd?site=YourSiteName’

Get the correct URL from “Configure Web Deploy Publishing” when you right-click the site in IIS and click deploy.

ERROR_CERTIFICATE_VALIDATION_FAILED

The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel The remote certificate is invalid according to the validation procedure Answer = True

ERROR_USER_UNAUTHORIZED

The remote server returned an error : (401)

Things I tried:

Configuring Remote Connections in IIS Manager

Root level of IIS manager => IIS Manager Users => Add User

IIS Manager Users
IIS Manager users are distinct from regular Windows users, and their credentials are specific to managing IIS-related tasks.

source Then, click the site under Sites => IIS Manager Permissions => Allow User => IIS Manager => select the user created above.

ERROR_INSUFFICIENT_ACCESS_TO_SITE_FOLDER

In Windows Services, the Web Management Service is running under the “Local Service” account. Web App folder => Right Click => Properties => Security => Advanced => Add “Local Service” account with Full Control

In App Pool for site, the Identity was set to a Windows account. In Advanced Settings, change the Identity, under Process Model, to Built-in account: ApplicationPoolIdentity.

Certificate Error

When porting this solution to a .Net Framework solution and publishing from Visual Studio, I received the following pop-up error:

Certificate error when publishing
Certificate error when publishing

Checking the Save this certificate checkbox and clicking Accept resulted in a successful site backup and publish.

Unable to perform the operation

Out of the blue, I was trying to publish one morning and I got the following error:

Unable to perform the operation

Microsoft.WebTools.Shared.Exceptions.WebToolsException: Build failed. Check the Output window for more details.

An error occurred when the request was processed on the remote computer. MSDEPLOY : error : Unable to perform the operation. Please contact your server administrator to check authorization and delegation settings.

Solution found here, an expired password for the WDeployConfigWriter account.

Start » Administrative tools » Computer Mangement » System Tools » Local Users And Groups » Users

There is a user called WDeployConfigWriter. Set to password never expired.

WDeployConfigWriter user login failure
WDeployConfigWriter user login failure
The WDeployConfigWriter user
The WDeployConfigWriter user

Changing the backup destination

I would rather have all of the backups from different sites going into the one backups folder, the default being backup folders at the same level as the web site folders.

This can be done per site within IIS, or scoped to the server level, with site specific settings taking precedence.

{SitePathParent}\PrePublishBackups{siteName}_snapshots

to

C:\WebApps\PrePublishBackups{siteName}_snapshots

Hiding the Credentials

I want other developers to benefit from this automation of the deployment process, so the publish profile needs to be checked into version control. However, storing credentials in version control is rightly frowned upon. Let’s move that IIS Manager username and password out of the main publish profile, and into a separate credentials XML file that will not be version controlled. The credentials will then be imported into the publish profile and time of deployment.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <AllowUntrustedCertificate>True</AllowUntrustedCertificate>
    <DeleteExistingFiles>False</DeleteExistingFiles>
    <DeployIisAppPath>ManningWebAPI</DeployIisAppPath>
    <EnableMsDeployAppOffline>True</EnableMsDeployAppOffline>
    <EnableMSDeployBackup>True</EnableMSDeployBackup>
    <ExcludeApp_Data>False</ExcludeApp_Data>
    <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
    <LastUsedPlatform>Any CPU</LastUsedPlatform>
    <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
    <MsDeployServiceUrl>https://SERVER_NAME__or__IPaddress:8172/msdeploy.axd</MsDeployServiceUrl>
    <MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
    <ProjectGuid>1cdb1d35-5776-455e-a56a-d1ac4800fd2f</ProjectGuid>
    <SelfContained>false</SelfContained>
    <SiteUrlToLaunchAfterPublish />
    <TargetFramework>net8.0</TargetFramework>
    <WebPublishMethod>MSDeploy</WebPublishMethod>
    <UserName>ImportedFromSecretFile</UserName>
    <Password>ImportedFromSecretFile</Password>
  </PropertyGroup>
  <Import Project="Local.pubxml.user.credentials" Condition="Exists('Local.pubxml.user.credentials')" />
</Project>

And the contents of the credentials file are:

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <UserName>MyIisManagerUserName</UserName>
    <Password>MyIisManagerPassword</Password>
  </PropertyGroup>
</Project>

Added Bonus

With the introduction of the Web Deploy deployment method, I am now able to publish individual files.

This is very handy when you have made a change to a View (.cshtml) or javascript file, for example a change of label text. Instead of publishing the whole app, with the associated brief downtime and session expiration, this feature overwrites the target file without an application pool restart.

Publish individual file to IIS
Publish individual file to IIS