Publish from Visual Studio to IIS with Web Deploy: Setup, Permissions, and the 5 Most Common Errors

FTP was how .NET apps got deployed for years, and it is still how sites get broken: a half-uploaded file, a lost web.config, a permissions fight. Microsoft Web Deploy (MSDeploy) replaces that with a sync engine that understands IIS – it uploads only what changed, applies web.config transforms, and can take the app offline during the swap. This is the setup for publishing from Visual Studio to a Windows VPS running IIS.

Install Web Deploy on the server

Download the Web Deploy installer (the current 4.x MSI, available from the official Microsoft download page) and choose Complete install. The Typical option installs only the client tool; Complete adds the handler, which is what lets remote machines publish:

# Confirm the handler is registered after install
& "$env:ProgramFiles\IIS\Microsoft Web Deploy V4\msdeploy.exe" -version

Enable remote management and create a deploy user

In IIS Manager, open the server node and go to Management Service. Enable remote management on the default port 8172 with HTTPS, then create an IIS Manager user (under IIS Manager Users) – this avoids handing out a Windows account. Finally, on the target site, open IIS Manager Permissions, add that user, and grant Content Management delegation so it can read and write the site folder but nothing else.

netsh advfirewall firewall add rule name="Web Deploy 8172" dir=in action=allow `
  protocol=TCP localport=8172

The management service endpoint is https://yourserver:8172/msdeploy.axd. Test it from your workstation before involving Visual Studio:

msdeploy.exe -verb:dump `
  -dest:computerName="https://yourserver:8172/msdeploy.axd",userName="webdeploy",password="***",authType="Basic",`
  -allowUntrusted

Publish profile in Visual Studio

  • Right-click the project, PublishNew profileWeb Deploy.
  • Server: https://yourserver:8172/msdeploy.axd; Site name: the IIS site name (e.g. contoso); user/password: the IIS Manager user.
  • Enable Remove additional files at destination for clean builds (it deletes orphaned files – disable it if you upload user content to the site folder).
  • Add a Web.Release.config with xdt:Transform entries to swap connection strings and app settings per environment.

Before the first real deploy, use Visual Studio’s Preview step in the publish dialog: it lists every file that will be added, updated or deleted on the server without touching anything. A five-second glance at the preview catches the classic mistakes – a missing appsettings.json, a stray bin folder, or a transform that dropped a connection string.

<!-- Web.Release.config -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <connectionStrings>
    <add name="Default" connectionString="Server=.;Database=app;User ID=app;Password=***"
         xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
  </connectionStrings>
</configuration>

The 5 errors everyone hits (and the fixes)

ErrorCauseFix
ERROR_USER_UNAUTHORIZEDIIS Manager user lacks delegation on the siteGrant Content Management in IIS Manager Permissions on the site node
ERROR_DESTINATION_NOT_REACHABLEPort 8172 blocked or Management Service stoppedOpen the firewall rule; verify the service is running and set to Automatic
ERROR_INSUFFICIENT_ACCESS_TO_SITE_FOLDERACLs on the web root block the deploy identityGive the IIS Manager user or app pool identity Modify on the site folder (icacls)
ERROR_FILE_IN_USE / locked filesw3wp.exe holds the old DLLsDrop an app_offline.htm into the site root before sync (Web Deploy removes it after)
500 on msdeploy.axdHandler not installedRe-run the Web Deploy installer and choose Complete; restart Management Service

The locked-file problem deserves extra attention: Web Deploy can take the app offline automatically, but the reliable pattern for a busy site is to place app_offline.htm in the root, publish, then delete it. IIS sees the file, unloads the app pool, and no process holds the files.

Using the same pipeline for CI/CD

The Visual Studio profile is just a wrapper around msdeploy.exe. Your build agent can do the identical sync from the command line:

msdeploy.exe -verb:sync -source:package="artifacts\site.zip" `
  -dest:auto,computerName="https://yourserver:8172/msdeploy.axd",userName="webdeploy",password="***",authType="Basic" `
  -setParam:name="IIS Web Application Name",value="contoso" `
  -allowUntrusted

Store the password in the build agent secret store, never in the repo. A dedicated IIS Manager user per site keeps one pipeline from deploying to another site, and the per-site delegation model means a compromised build agent can touch only its own folder.

Locking down the deploy endpoint

The msdeploy.axd endpoint accepts authentication from anywhere that can reach port 8172, so treat it like an admin interface. Restrict the firewall rule to your build agents or office IP range instead of all addresses, prefer IIS Manager users over local Administrator credentials, and rotate the password whenever a developer leaves the project:

netsh advfirewall firewall set rule name="Web Deploy 8172" new remoteip=203.0.113.10,198.51.100.0/24

For unattended syncs, add -enableRule:AppOffline to the msdeploy command: it drops app_offline.htm automatically before syncing and removes it afterwards, which eliminates the locked-file race without manual steps.

Before you wire up the pipeline

Web Deploy works best when the server is cleanly provisioned: dedicated app pool per site, known ACLs on the web root, and a firewall that only exposes 8172 to your build agents or office IPs. If you are still choosing where to host, the Windows VPS comparison table on our site compares providers by admin access and networking, and the feature checklist on our site lists the IIS-related capabilities worth confirming before you sign up.

Leave a Comment