IISExpress keeps its configuration in %userprofile%\documents\iisexpress\config\applicationhost.config
file and for newly created site it looks like this:

<site name="CodeVision.Web(1)" id="83">  
  <application path="/" applicationPool="Clr4IntegratedAppPool">
    <virtualDirectory path="/" physicalPath="C:\My\Playground\CodeVision\CodeVision\CodeVision.Web" />
  </application>
  <bindings>
    <binding protocol="http" bindingInformation="*:3500:localhost" />
  </bindings>
</site>  

I needed to add a couple of virtual folders to this configuration:

<site name="CodeVision.Web(1)" id="83">  
  <application path="/" applicationPool="Clr4IntegratedAppPool">
    <virtualDirectory path="/" physicalPath="C:\My\Playground\CodeVision\CodeVision\CodeVision.Web" />
  </application>
    <application path="/searchindex" applicationPool="Clr4IntegratedAppPool">
      <virtualDirectory path="/" physicalPath="C:\My\Playground\CodeVision\CodeVision\CodeVision.Tests\bin\Debug\Index" />
    </application>
  <application path="/searchcontent" applicationPool="Clr4IntegratedAppPool">
    <virtualDirectory path="/" physicalPath="C:\My\Playground\CodeVision\CodeVision\CodeVision.Tests\Content" />
  </application>
  <bindings>
    <binding protocol="http" bindingInformation="*:3500:localhost" />
  </bindings>
</site>  

There are several ways to make this or other similar changes.

We can simply make this change by hand in Notepad. This is easy to do for just one site but inconvinient, error prone and time consuming for other people who need to get the project running locally.

If it was regular IIS and not IISExpress, than Microsoft-provided Web Server IIS Administration Cmdlets would be my choice, but since we deal with IISExpress, it leaves us with AppCmd.exe.

AppCmd is a command line utility for managing both IIS and IISExpress. It is fairly straightforward to use from PowerShell. For example, to find all sites running on port 3500:

$appcmd = "c:\Program Files (x86)\IIS Express\appcmd.exe"
.$appcmd list site http://localhost:3500

This is all fine, but the problem is that I need not only execute commands, but interpret the results as well. In particular, here's what I wanted my script to do:

  1. Find site name on a given port
  2. Check if site already has custom virtual directories added.
  3. If not, grab site physical path and come up with two physical paths that I need for the virtual directories.
  4. Create virtual directories and set their application pools.

The problem is that AppCmd unlike PowerShell CmdLets returns result in strings and not objects. For example, for the above command the result is this string:

SITE "CodeVision.Web(1)" (id:83,bindings:http/*:3500:localhost,state:Unknown)  

This means we need to parse those strings out. I took a quick stab on it and it turned out to be not so bad with the help of Regex and -match CmdLet.

For example, to get site name on a given port:

$appcmd = "c:\Program Files (x86)\IIS Express\appcmd.exe"
$port = 3500
if((.$appcmd list site http://localhost:$port | Out-String) -match '(?<=SITE\s*")(.*?)(?=.\s*\(id:)' -eq $false)  
{    
    throw "No site on port " + $port
}
$siteName = $Matches[0]

I wrapped virtual directories creation in a little function:

function CreateVirtualDirectory($path, $physicalPath)  
{    
   .$appcmd add APP /site.name:$siteName /path:$path /physicalPath:$physicalPath
   .$appcmd set app /app.name:$siteName$path /applicationPool:"Clr4IntegratedAppPool"
}

See complete script here