How To

Configure an IIS Virtual Directory on Windows Server 2025

You have files outside the IIS site root, and you want them served under the same hostname without copying anything. An IIS virtual directory does exactly that: it maps a URL path such as /reports to a folder on another drive or a share on the NAS. The local folder case takes two minutes. The network share case is where most people end up staring at a 500.19 page, so that is where we spent our lab time.

Original content from computingforgeeks.com - post 34479

We start with the question most readers arrive with, virtual directory vs application, and then you create one in IIS Manager and with New-WebVirtualDirectory in PowerShell. After that we convert one into an application (and fix the 403.18 that conversion left behind), sort out the folder permissions behind a 401.3, and point a virtual directory at a UNC path with every 500.19 we hit before it worked. I ran every PowerShell and appcmd step on Windows Server 2025 (IIS 10.0) in October 2026, with a Samba share on an Ubuntu box called nas01 standing in for the file server, and it works end to end.

IIS virtual directory vs application

A virtual directory only maps a URL path to a physical folder, and it runs inside the application that owns it, in the same worker process and the same application pool. An application is a unit IIS can isolate, so it gets its own application pool, its own root and its own web.config boundary. In the Microsoft model for IIS, a site contains applications and every application contains at least one virtual directory, its root. What you are really deciding is whether the folder you publish needs its own process and pool. For static files, downloads or reports it does not, so a virtual directory is the right tool and an application only adds work.

QuestionVirtual directoryApplication
What it mapsA URL path to a folder or UNC shareA URL path to a folder, plus a code boundary
Application poolInherits the pool of the parent applicationCan run in its own pool
Worker processShares the w3wp.exe of the parentSeparate w3wp.exe when the pool differs
Where IIS stores itA virtualDirectory element inside the parent applicationIts own application element with a root virtualDirectory
Use it forStatic content, file downloads, a share on the NASA separate ASP.NET app, a different runtime, crash isolation

You can see the difference in applicationHost.config. On web01 we created /reports and /downloads as virtual directories and then converted /api into an application (Step 4 below), so printing the Default Web Site block shows both kinds side by side.

Select-String -Path "$env:windir\system32\inetsrv\config\applicationHost.config" -Pattern '<site name="Default Web Site"' -Context 0,16

The two virtual directories sit inside the root application path="/", while /api got its own application element with applicationPool="ApiPool" and a root virtual directory of its own. We cut the output after the closing </site> tag.

> inetsrv\config\applicationHost.config:162:            <site name="Default Web Site" id="1">
  inetsrv\config\applicationHost.config:163:                <application path="/">
  inetsrv\config\applicationHost.config:164:                    <virtualDirectory path="/" physicalPath="%SystemDrive%\inetpub\wwwroot" />
  inetsrv\config\applicationHost.config:165:                    <virtualDirectory path="/reports" physicalPath="C:\data\reports" />
  inetsrv\config\applicationHost.config:166:                    <virtualDirectory path="/downloads" physicalPath="C:\data\downloads" />
  inetsrv\config\applicationHost.config:167:                    <virtualDirectory path="/api" physicalPath="C:\data\api" />
  inetsrv\config\applicationHost.config:168:                </application>
  inetsrv\config\applicationHost.config:169:                <application path="/api" applicationPool="ApiPool">
  inetsrv\config\applicationHost.config:170:                    <virtualDirectory path="/" physicalPath="C:\data\api" />
  inetsrv\config\applicationHost.config:171:                </application>
  inetsrv\config\applicationHost.config:172:                <bindings>
  inetsrv\config\applicationHost.config:173:                    <binding protocol="http" bindingInformation="*:80:" />
  inetsrv\config\applicationHost.config:174:                </bindings>
  inetsrv\config\applicationHost.config:175:            </site>

Look at line 167. Converting /api left the original virtual directory entry behind in the root application, so the same path is now defined twice, once as a virtual directory of the root and once as an application. That leftover is not cosmetic, because on our server it made every request to /api/ fail with a 403.18 until we deleted it (Step 4 shows the fix). A separate site, the third level, is only needed when the content needs its own hostname or port binding, and we cover that in the guide on configuring the default site in IIS.

Prerequisites

Our lab was two VMs on the same LAN, and you only need the second one if you want to follow the UNC share part in Step 6.

  • web01: Windows Server 2025 Datacenter, Desktop Experience, workgroup
  • 4 vCPU and 6 GB RAM on web01
  • nas01: Ubuntu 26.04 with Samba, share webcontent
  • An elevated Windows PowerShell prompt as local Administrator
  • TCP 445 from web01 to the file server

If you test from a workstation instead of on the server itself, open port 80 first with the steps in our Windows Server firewall guide. We tested on the server with curl.exe on purpose, because IIS shows its detailed error pages only to local requests by default. Those details (error code, config file, logon user) are what make the errors later in this guide readable, so run your own checks on the server too.

Step 1: Install IIS and the management console

Skip this step if IIS Manager already opens on your server. On a fresh Windows Server 2025 install, the Web Server role and IIS Manager come from one command, and -IncludeManagementTools is the part that pulls in the console and the WebAdministration module we use later. The longer walkthrough, with the role services explained, is in our guide to installing IIS on Windows Server.

Install-WindowsFeature -Name Web-Server -IncludeManagementTools

Be patient here, because the role install is slow on Server 2025. On our 4 vCPU VM it took 686 seconds, and it sat silent for several minutes before the servicing stack started committing packages, so do not kill it when the progress bar stalls. It finished without asking for a reboot.

Success Restart Needed Exit Code Feature Result
------- -------------- --------- --------------
True    No             Success   {Common HTTP Features, Default Document, Directory Browsing, Request Filtering...}

Confirm the role and the console, then read the IIS version from the registry:

Get-WindowsFeature Web-Server, Web-Mgmt-Console
Get-ItemProperty HKLM:\SOFTWARE\Microsoft\InetStp | Select-Object MajorVersion, MinorVersion, VersionString

Both features should read Installed. Windows Server 2025 still reports major version 10, the same IIS major version that Server 2019 and 2022 carry, so the configuration model below is the one you may already know.

Display Name                       Name             Install State
------------                       ----             -------------
[X] Web Server (IIS)               Web-Server           Installed
        [X] IIS Management Console Web-Mgmt-Console     Installed

MajorVersion  : 10
MinorVersion  : 0
VersionString : Version 10.0

With the role in place, the Default Web Site already answers on port 80 from C:\inetpub\wwwroot, and that is the site we hang every virtual directory on below.

Step 2: Create a virtual directory in IIS Manager

IIS Manager will not create the physical folder for you, so make it first and drop a test page in it. We use C:\data\reports throughout. Any path outside C:\inetpub\wwwroot shows the point of a virtual directory better than a subfolder of the site root would, because a subfolder is served anyway without any mapping.

New-Item -ItemType Directory -Path C:\data\reports -Force
Set-Content C:\data\reports\index.html '<h1>reports on web01</h1>'

Open IIS Manager from Server Manager (Tools, then Internet Information Services (IIS) Manager) or run inetmgr. In the Connections pane, expand the server and Sites, right-click Default Web Site and choose Add Virtual Directory…. You will find the same entry in the Actions pane when you open View Virtual Directories for the site. The two screenshots in this step come from the original version of this guide on an older Windows Server build, so the alias there is geeksvirtual instead of our reports.

IIS Manager Virtual Directories page with Add Virtual Directory in the context menu and the Actions pane

The dialog asks you for an alias and a physical path. The … button next to the path opens a folder browser if you would rather click than type, which also saves you from a typo in the path.

IIS Add Virtual Directory dialog with the Alias field, Physical path browse button and Connect as button

Fill the dialog in this order:

  1. Alias: reports, the path in the URL
  2. Physical path: C:\data\reports
  3. Connect as: leave on pass-through for a local folder
  4. Test Settings: checks access to the path
  5. OK: saves to applicationHost.config, no restart

The Connect as button only matters when the physical path is a UNC share such as \\nas01\webcontent, which Step 6 covers in detail. The new entry appears under Default Web Site with a folder-and-shortcut icon, and that icon is how you tell a virtual directory from a plain subfolder in the tree. Check it from the server:

curl.exe -s -i http://localhost/reports/

A 200 with our test page means IIS mapped /reports to the folder and found index.html through the default document list.

HTTP/1.1 200 OK
Content-Type: text/html
Last-Modified: Thu, 29 Oct 2026 20:48:28 GMT
Accept-Ranges: bytes
ETag: W/"55feed9e667dd1:0"
Server: Microsoft-IIS/10.0
Date: Thu, 29 Oct 2026 20:48:31 GMT
Content-Length: 27

<h1>reports on web01</h1>

If you get a 404 here instead, compare the alias with the URL first and then confirm index.html exists in the folder, because those two cover almost every miss on a local path.

Step 3: Create a virtual directory with PowerShell

For anything you do more than once, or on more than one server, New-WebVirtualDirectory from the WebAdministration module is the better route, because it is scriptable and leaves no room for a typo in a dialog. The module arrived with the management tools in Step 1, so you only need to load it in an elevated prompt. Pick one route for /reports: if you already created it in IIS Manager, skip the create command below or give it another alias, because the same name fails the second time.

Import-Module WebAdministration

Now create the virtual directory. -Site names the site, -Name is the alias and -PhysicalPath is the folder, which must already exist.

New-WebVirtualDirectory -Site 'Default Web Site' -Name 'reports' -PhysicalPath 'C:\data\reports'

The cmdlet echoes the new entry back to you, alias and path.

Name    PhysicalPath
----    ------------
reports C:\data\reports

Two errors will bite a script that skips its checks. If the folder does not exist, the cmdlet refuses with Parameter 'PhysicalPath' should point to existing path. and creates nothing, so your script should create folders before virtual directories. If you already made reports in the GUI in Step 2, running the same command again fails with the error below.

New-WebVirtualDirectory : Destination element already exists, please use "force" parameter to override.

Adding -Force overwrites the existing entry. That is handy in a deployment script you run again and again, and risky when the existing entry carries share credentials, because you replace an entry you have not read first. To place a virtual directory under an application instead of the site root, the cmdlet also takes -Application, as listed in the New-WebVirtualDirectory reference. List what the site has now:

Get-WebVirtualDirectory -Site 'Default Web Site'

Only the virtual directories you added are listed. The root of the site (/) is a virtual directory too, but this cmdlet leaves it out.

Name             Physical Path
----             -------------
reports          C:\data\reports

On older scripts and on Server Core boxes you will also see appcmd.exe. It does the same job, and its list vdir view does include the root, which makes it the quicker way to see everything at once.

& "$env:windir\system32\inetsrv\appcmd.exe" list vdir

At this point web01 had the root of the site and /reports, each with its physical path.

VDIR "Default Web Site/" (physicalPath:%SystemDrive%\inetpub\wwwroot)
VDIR "Default Web Site/reports" (physicalPath:C:\data\reports)

We then added a second virtual directory with appcmd. The trailing slash in /app.name:"Default Web Site/" names the root application of the site, which is the identifier appcmd uses in its own help example.

New-Item -ItemType Directory C:\data\downloads -Force
& "$env:windir\system32\inetsrv\appcmd.exe" add vdir /app.name:"Default Web Site/" /path:/downloads /physicalPath:C:\data\downloads

The add confirms in one line.

VDIR object "Default Web Site/downloads" added

Both tools write to the same applicationHost.config, so you can create with one and inspect or remove with the other without any conflict.

Step 4: Convert a virtual directory to an application

When the folder holds code that needs its own runtime settings, or must not take down the main site when it crashes, you promote it to an application in a dedicated pool. In IIS Manager that is a right-click on the virtual directory and Convert to Application. In PowerShell we created /api as a virtual directory first, then a pool, and then converted it.

New-Item -ItemType Directory C:\data\api -Force
Set-Content C:\data\api\index.html 'api'
New-WebVirtualDirectory -Site 'Default Web Site' -Name 'api' -PhysicalPath 'C:\data\api'
New-WebAppPool -Name 'ApiPool'
ConvertTo-WebApplication -PSPath 'IIS:\Sites\Default Web Site\api' -ApplicationPool 'ApiPool'

The last command returns the new application with its pool, which looks like the job is done.

Name             Application pool   Protocols    Physical Path
----             ----------------   ---------    -------------
api              ApiPool            http         C:\data\api

It is not done. Requesting http://localhost/api/ right after the conversion returned a 403.18, and the detailed page said the request could not run in the pool configured for it.

HTTP Error 403.18 - Forbidden
The specified request cannot be processed in the application pool that is configured for this resource on the Web server.
Error Code = 0x00000000
Physical Path = C:\data\api\
Notification = BeginRequest

We reproduced it a second time with a fresh /svc folder and a SvcPool, which returned 200 as a virtual directory and 403.18 straight after ConvertTo-WebApplication. The cause is the leftover entry from the comparison section above. appcmd list vdir shows the path twice, as a virtual directory of the root (Default Web Site/api) and as the root of the new application (Default Web Site/api/). Even appcmd refuses to delete the first one, with Must use exact identifer for VDIR object with verb DELETE., so remove it from the configuration directly and request the page again.

Remove-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -Filter "system.applicationHost/sites/site[@name='Default Web Site']/application[@path='/']" -Name '.' -AtElement @{path='/api'}
curl.exe -s -o NUL -w '%{http_code}' http://localhost/api/

The -AtElement filter only matches the virtualDirectory entry with path="/api" inside the root application, so the new application and its own root are left alone. The status code that comes back is now a plain success.

200

If you know up front that a folder needs to be an application, skip the conversion and create it with New-WebApplication instead. That writes only the application element and its root, so there is no duplicate to clean up, and our second test folder returned 200 on the first request. The pool must exist before you point an application at it, so the block below creates SvcPool first (we had made it for the /svc test).

New-WebAppPool -Name 'SvcPool'
New-Item -ItemType Directory C:\data\svc2 -Force
Set-Content C:\data\svc2\index.html 'svc2'
New-WebApplication -Site 'Default Web Site' -Name 'svc2' -PhysicalPath 'C:\data\svc2' -ApplicationPool 'SvcPool'

You get the application in SvcPool, and applicationHost.config gains a single <application path="/svc2" applicationPool="SvcPool"> block with nothing left over in the root.

Name             Application pool   Protocols    Physical Path
----             ----------------   ---------    -------------
svc2             SvcPool            http         C:\data\svc2

Is the extra pool worth it? Only when the code in that folder needs its own runtime settings, its own identity on the file system, or protection from crashes elsewhere in the site. For static content an application adds a worker process and a second config boundary for you to look after, while a plain virtual directory does the same job for free.

Step 5: Set NTFS permissions on the folder

A virtual directory on a folder IIS cannot read fails at one of two stages, and the error tells you which stage. Two accounts touch every anonymous request, and Microsoft lists missing read for IIS_IUSRS on the folder as a cause of the 500.19 below:

  • IIS_IUSRS: the pool identity, reads web.config first
  • IUSR: the anonymous user, reads the content
  • Both need read and execute on the folder

To reproduce both failures, we locked a new folder down to Administrators and SYSTEM and published it as /private. You can run the same four lines on a test server if you want to see the errors for yourself before you meet them in production.

New-Item -ItemType Directory C:\data\private -Force
Set-Content C:\data\private\index.html 'private'
icacls C:\data\private /inheritance:r /grant:r 'BUILTIN\Administrators:(OI)(CI)F' 'NT AUTHORITY\SYSTEM:(OI)(CI)F'
New-WebVirtualDirectory -Site 'Default Web Site' -Name 'private' -PhysicalPath 'C:\data\private'

Requesting http://localhost/private/ with the folder in that state returned an HTTP 500, and these are the fields from the detailed error page. Logon User reads “Not yet determined” because the request died at BeginRequest, before authentication even ran, so no user account is involved yet.

HTTP Error 500.19 - Internal Server Error
Error Code = 0x80070005
Config Error = Cannot read configuration file due to insufficient permissions
Logon User = Not yet determined
Logon Method = Not yet determined
Physical Path = C:\data\private\
Module = IIS Web Core
Notification = BeginRequest

Next we granted IUSR read on index.html alone, which is the half fix people often try first. The request moved one stage further and failed at AuthenticateRequest instead, now as the anonymous user, because IUSR still had no right to the folder itself. Notice that the 500.19 cleared before we touched IIS_IUSRS at all, so read the code as a stage marker (configuration or authentication), not as proof of exactly which account is missing.

HTTP Error 401.3 - Unauthorized
Error Code = 0x80070005
Logon User = Anonymous
Logon Method = Anonymous
Physical Path = C:\data\private\
Module = IIS Web Core
Notification = AuthenticateRequest

What works is read and execute for both groups on the folder, inherited by everything inside it. Grant it once on the folder rather than per file, so that files dropped in later are readable too and you do not chase a fresh 401.3 every time someone adds a page.

icacls C:\data\private /grant 'BUILTIN\IIS_IUSRS:(OI)(CI)(RX)' 'IUSR:(OI)(CI)(RX)'
icacls C:\data\private

After this the same request returned 200. The ACL that works carries both entries with the (OI)(CI) inheritance flags, so new files and subfolders pick them up.

C:\data\private BUILTIN\Administrators:(F)
                NT AUTHORITY\IUSR:(OI)(CI)(RX)
                BUILTIN\IIS_IUSRS:(OI)(CI)(RX)
                NT AUTHORITY\SYSTEM:(OI)(CI)(F)
                BUILTIN\Administrators:(OI)(CI)(F)

Successfully processed 1 files; Failed processing 0 files

New folders under C:\ inherit read and execute for the built-in Users group (icacls C:\data\reports showed BUILTIN\Users:(I)(OI)(CI)(RX)), which is why /reports worked in Step 2 without any of this. You will meet these errors when content lives in a folder someone hardened, or on a data disk with custom ACLs. If the folder needs write access for uploads, grant Modify to the specific pool identity (for example IIS AppPool\ApiPool) rather than to IIS_IUSRS, so one pool cannot write into the content of another.

Step 6: Point a virtual directory at a UNC share

Most readers of this page are fighting with this case, and it is where pass-through authentication lets you down on a server that is not in a domain. Our share is \\nas01\webcontent, a read-only Samba share that only the user svc-iis may open. On a Windows file server you would build the same thing as a share with Read for that user plus NTFS read on the folder. If you want to copy our file server, these are the commands we ran on nas01, an Ubuntu VM. smbpasswd -a asks for the password, and you will reuse it on web01 later.

sudo apt install -y samba
sudo useradd -M -s /usr/sbin/nologin svc-iis
sudo smbpasswd -a svc-iis
sudo mkdir -p /srv/webcontent
echo '<h1>docs served from nas01</h1>' | sudo tee /srv/webcontent/index.html
sudo chown -R svc-iis:svc-iis /srv/webcontent
sudo chmod -R 750 /srv/webcontent

Then add the share at the end of the Samba configuration:

sudo vim /etc/samba/smb.conf

The share is read-only and open to svc-iis alone:

[webcontent]
   path = /srv/webcontent
   read only = yes
   valid users = svc-iis
   browseable = yes

Restart Samba and let testparm print the share back, which also catches a typo in the file:

sudo systemctl restart smbd
testparm -s 2>/dev/null | grep -A4 '\[webcontent\]'

Samba drops read only = yes and browseable = yes from the printout because both are its defaults, so only the path and the user remain.

[webcontent]
	path = /srv/webcontent
	valid users = svc-iis

web01 also has to resolve the name nas01. Our lab had no DNS server, so we added a hosts entry on web01; use your own DNS record if you have one, and replace the example address with the IP of your file server.

Add-Content C:\Windows\System32\drivers\etc\hosts "`r`n10.0.1.51 nas01"

Before touching IIS, check that web01 can reach SMB on the file server at all. Every later error looks like a permission problem even when it is a network one, so rule the network out first.

Test-NetConnection nas01 -Port 445

The line that matters is TcpTestSucceeded, and you want it to read True.

ComputerName RemotePort TcpTestSucceeded
------------ ---------- ----------------
nas01               445             True

A False here is a firewall or routing problem between the two machines, and no IIS setting below will fix it.

Create the virtual directory with -Force

Our first attempt without -Force failed with the same Parameter 'PhysicalPath' should point to existing path. error from Step 3, even though the share exists. The cmdlet checks the path as the account running PowerShell, and that account had no credentials for nas01. IIS will use its own credentials for the share anyway, so add -Force to skip the check.

New-WebVirtualDirectory -Site 'Default Web Site' -Name 'docs' -PhysicalPath '\\nas01\webcontent' -Force

The entry is created with the UNC path, even though nothing has proven yet that IIS can read it.

Name PhysicalPath
---- ------------
docs \\nas01\webcontent

From here on, every check is a request to http://localhost/docs/ on web01, and each error below is what that request returned at that stage.

Error: 500.19 0x80070005 with pass-through authentication

With no credentials set, the virtual directory uses pass-through authentication, meaning IIS opens the share as the application pool identity. On a workgroup server that identity has nothing nas01 will accept, so http://localhost/docs/ failed before IIS read a single byte of content.

HTTP Error 500.19 - Internal Server Error
Error Code = 0x80070005
Config Error = Cannot read configuration file due to insufficient permissions
Config File = \\?\UNC\nas01\webcontent\web.config
Logon User = Not yet determined
Logon Method = Not yet determined
Physical Path = \\nas01\webcontent\
Module = IIS Web Core
Notification = BeginRequest

Read the Config File line. There is no web.config on the share at all, and IIS failed while checking for one, which is the same first read that blocked /private in Step 5, only across the network this time. The Microsoft page on HTTP error 500.19 gives the same advice we arrived at: stop relying on pass-through and set a specific account for the share.

Error: 500.19 0x8007052e “Can not log on locally”

You set the specific account with the userName and password attributes of the virtual directory, which is what the Connect as button writes. We set the share user and its correct share password; replace Lab-Share-2026! here and in the New-LocalUser command below with the password you gave smbpasswd.

Set-ItemProperty 'IIS:\Sites\Default Web Site\docs' -Name userName -Value 'svc-iis'
Set-ItemProperty 'IIS:\Sites\Default Web Site\docs' -Name password -Value 'Lab-Share-2026!'

It still failed, now with a different code and a config file on the local disk instead of the share.

HTTP Error 500.19 - Internal Server Error
Error Code = 0x8007052e
Config Error = Can not log on locally to \\nas01\webcontent as user svc-iis with virtual directory password
Config File = \\?\C:\inetpub\temp\apppools\DefaultAppPool\DefaultAppPool.config
Physical Path = \\nas01\webcontent\

This snag cost us the most time. IIS does not hand the user name and password straight to the file server. It first logs the account on locally on web01 (the logonMethod of a virtual directory defaults to ClearText), and svc-iis only existed on nas01, so that local logon failed before SMB was ever tried. In a domain you would point userName at a domain account, but we tested the workgroup case only. In a workgroup you need a mirrored account:

  • A local user on web01 named svc-iis
  • The same password as on the file server
  • Read on the share and the folder behind it

Our guide to adding a local user on Windows Server covers the GUI way. In PowerShell it is one line, and you should give it a password that never expires, or the content goes dark the day it does.

New-LocalUser -Name 'svc-iis' -Password (ConvertTo-SecureString 'Lab-Share-2026!' -AsPlainText -Force) -PasswordNeverExpires -AccountNeverExpires -Description 'IIS UNC content account'

We left the account in the default Users group only, and that was enough, because IIS just logs it on and then opens the share with it. It is not a login for people, so keep it out of Remote Desktop Users and Administrators. One more thing about the commands in this step: a password typed inline lands in your PSReadLine history file, so on a real server clear that history afterwards or enter the password through Read-Host -AsSecureString instead.

Error: 500.19 0x8007052e “Cannot read configuration file”

Before getting it right, we created the local user with a password that did not match the share, to see what drift between the two machines looks like. The local logon now succeeded and the SMB logon to nas01 did not, so the error moved back to the web.config path on the share while keeping the 0x8007052e code (bad user name or password).

HTTP Error 500.19 - Internal Server Error
Error Code = 0x8007052e
Config Error = Cannot read configuration file
Config File = \\?\UNC\nas01\webcontent\web.config
Physical Path = \\nas01\webcontent\

Remember that one for the day someone rotates the share password on the NAS and forgets web01. With the local password matching the share password, the request finally went through.

curl.exe -s -i http://localhost/docs/

IIS now serves the page straight from the Samba share, with a 200 and the file contents from nas01.

HTTP/1.1 200 OK
Content-Type: text/html
Last-Modified: Thu, 29 Oct 2026 20:34:16 GMT
Accept-Ranges: bytes
ETag: "17918dee467dd1:0"
Server: Microsoft-IIS/10.0
Date: Thu, 29 Oct 2026 20:50:45 GMT
Content-Length: 32

<h1>docs served from nas01</h1>

IIS stores the password encrypted in applicationHost.config, not in clear text. The saved entry looks like this, with the ciphertext shortened by us.

<virtualDirectory path="/docs" physicalPath="\\nas01\webcontent" userName="svc-iis" password="[enc:IISCngProvider:8XYRjGOWtgiD...:enc]" />

In IIS Manager the same two attributes live behind the Basic Settings… link of the virtual directory in the Actions pane. Click Connect as…, choose Specific user and click Set… to enter the account, then use Test Settings… in that dialog, which checks authentication and authorization against the path. A passing test does not replace a real request, though, so finish with the curl.exe check above every time you change the credentials or the share password.

Step 7: Remove a virtual directory

Removing a virtual directory deletes the mapping only. The folder or share and its files stay where they are, which is what you want, but it also means cleaning up the content is a separate job. In PowerShell you name the application the virtual directory belongs to, and / is the site root.

Remove-WebVirtualDirectory -Site 'Default Web Site' -Application '/' -Name 'private'

On /private the cmdlet stopped for a confirmation prompt before removing anything, so answer Y when you run it by hand. In a PowerShell session started with -NonInteractive, which is how our remote runner launched it, the prompt cannot be shown. The call then fails without removing anything, even with -Confirm:$false added, and the cmdlet has no -Force or -Recurse switch to get past the prompt.

remove-item : Windows PowerShell is in NonInteractive mode. Read and Prompt functionality is not available.

For unattended scripts we used appcmd instead, because it never prompts. We removed /downloads that way and checked that the files were still on disk.

& "$env:windir\system32\inetsrv\appcmd.exe" delete vdir "Default Web Site/downloads"
Test-Path C:\data\downloads\index.html

The mapping is gone and the content is untouched.

VDIR object "Default Web Site/downloads" deleted
True

The same Test-Path check returned True for C:\data\private\index.html after the PowerShell removal. Delete the folder yourself once nothing else points at it, and check appcmd list vdir first, because two virtual directories can share one physical path.

IIS virtual directory errors we hit, in one table

Every row below came from web01 during this lab. The Notification column is the fastest way to place an error. A 500.19 at BeginRequest means IIS could not even read configuration, the 403.18 at BeginRequest is a pool mismatch, and AuthenticateRequest means configuration was fine and the request user was refused.

ErrorCode and stageCause in our labFix
Parameter 'PhysicalPath' should point to existing path.PowerShell, before IISFolder missing, or a UNC path the shell user cannot readCreate the folder first; -Force for a share
Destination element already existsPowerShell, before IISAlias already created in the GUIPick another alias or use -Force
403.18 Forbidden, cannot be processed in the application pool0x00000000, BeginRequestStale virtual directory left behind by ConvertTo-WebApplicationRemove the root-level entry with Remove-WebConfigurationProperty
500.19 Cannot read configuration file due to insufficient permissions0x80070005, BeginRequestFolder ACL blocks the config read, or pass-through on a shareRX for IIS_IUSRS, or Connect as a specific user on a share
401.3 Unauthorized0x80070005, AuthenticateRequestIUSR has no read on the folderRX for IUSR on the folder, inherited
500.19 Can not log on locally … with virtual directory password0x8007052e, BeginRequestConnect as user does not exist on the IIS serverMirrored local account, or a domain account
500.19 Cannot read configuration file0x8007052e, BeginRequestLocal and share passwords differSet the same password on both machines
Windows PowerShell is in NonInteractive modePowerShell, before IISRemove-WebVirtualDirectory prompting in a non-interactive sessionappcmd delete vdir

If a share-backed virtual directory breaks months later with no config change, check the password row first, because a password rotated on only one side is the most likely cause and the error looks like a permissions fault. Every virtual directory also inherits HTTPS from its parent site, and our guide to adding an SSL certificate to IIS covers that binding. When you want to go deeper on NTFS and share permissions than one guide can, the Windows Server administration books we recommend are a good next stop.

Keep reading

PHP2Shell on End-of-Life PHP 8.1, 8.0 and 7.4 (WHMCS, ionCube) Php PHP2Shell on End-of-Life PHP 8.1, 8.0 and 7.4 (WHMCS, ionCube) Create a Bootable Windows 11 USB and Install Windows 11 Windows Create a Bootable Windows 11 USB and Install Windows 11 Set Up a Samba Share on Windows 11 and Access It from Linux Networking Set Up a Samba Share on Windows 11 and Access It from Linux CapRover Docker Install on Debian 13: Deploy Your First App Containers CapRover Docker Install on Debian 13: Deploy Your First App Configure NTP Server on Windows Server 2025 / 2022 (w32tm Guide) Windows Configure NTP Server on Windows Server 2025 / 2022 (w32tm Guide) Windows Server Backup: Schedule, Restore and Bare-Metal Recovery Windows Windows Server Backup: Schedule, Restore and Bare-Metal Recovery

Leave a Comment

Press ESC to close