Skip to content

Instantly share code, notes, and snippets.

@CJHarmath
Last active October 14, 2021 02:59
Show Gist options
  • Save CJHarmath/b2af0f50700ce9fbdd8c5c3e582fd41b to your computer and use it in GitHub Desktop.
Save CJHarmath/b2af0f50700ce9fbdd8c5c3e582fd41b to your computer and use it in GitHub Desktop.
# Setup
Import-Module WebAdministration
# create 2 site root directories
$a = 'C:\inetpub\AspNetCoreSampleA'
$b = 'C:\inetpub\AspNetCoreSampleB'
$siteRoot = 'C:\inetpub\aspnetcoresample'
$siteName = 'AspNetCoreSample'
$poolName = "aspnetcore"
New-Item -Type Directory $a
New-Item -Type Directory $b
# create a symlink to targeting one side
New-Item -Type SymbolicLink -Path $siteRoot -Target $a
# point the site root to the symlink
Set-ItemProperty "IIS:\Sites\$siteName" -name physicalPath -value $siteRoot
# make sure it get's picked up
Restart-WebAppPool -Name $poolName
# this tells you the active side
Get-Item -Path $siteRoot | Select-Object -ExpandProperty target
# Flip the symlink
$current = (Get-Item -Path $siteRoot).Target
$newTarget = if ($current -eq $a) {$b} else {$a}
New-Item -Type SymbolicLink -Path $siteRoot -Target $newTarget -Force
# at this point w3wp.exe still locks the current target folder until it's getting recycled
# Deploy new version to the symlink which is now pointing to the other side which should have no locks
robocopy \\myshare\myapp $siteRoot /mir
# recycle app pool, so it picks up the new files
Restart-WebAppPool -Name $poolName
# bonus point: rollback is easy
$current = (Get-Item -Path $siteRoot).Target
$newTarget = if ($current -eq $a) {$b} else {$a}
New-Item -Type SymbolicLink -Path $siteRoot -Target $newTarget -Force
Restart-WebAppPool -Name $poolName
@CJHarmath
Copy link
Author

Here a paste from me copy pasting the first few lines.

c:\temp> $a = 'C:\inetpub\AspNetCoreSampleA'
c:\temp> $b = 'C:\inetpub\AspNetCoreSampleB'
c:\temp> $siteRoot = 'C:\inetpub\aspnetcoresample'
c:\temp> $siteName = 'AspNetCoreSample'
c:\temp> $poolName = "aspnetcore"
c:\temp> New-Item -Type Directory $a


    Directory: C:\inetpub


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----          1/5/2021   2:28 PM                AspNetCoreSampleA


c:\temp> New-Item -Type Directory $b


    Directory: C:\inetpub


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----          1/5/2021   2:28 PM                AspNetCoreSampleB


c:\temp> # create a symlink to targeting one side
c:\temp> New-Item -Type SymbolicLink -Path $siteRoot -Target $a


    Directory: C:\inetpub


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----l          1/5/2021   2:28 PM                aspnetcoresample


c:\temp> New-Item -Type SymbolicLink -Path $siteRoot -Target $b
New-Item : NewItemIOError
At line:1 char:1
+ New-Item -Type SymbolicLink -Path $siteRoot -Target $b
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceExists: (C:\inetpub\aspnetcoresample:String) [New-Item], IOException
    + FullyQualifiedErrorId : NewItemIOError,Microsoft.PowerShell.Commands.NewItemCommand

c:\temp> New-Item -Type SymbolicLink -Path $siteRoot -Target $b -Force


    Directory: C:\inetpub


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----l          1/5/2021   2:28 PM                aspnetcoresample


c:\temp>

Once you've created a folder or a symlink, yes you will see these errors.
To update the symlink, you need to use the -Force option.
To avoid trying to create the same folders, use if (-not Test-Path $a) { New-Item -Type Directory $a }
i.e.: first check if the folder is there and if not create it.

@joelmandell
Copy link

Okay thx for your reply. But why is the script creating the directories $a and $b?
It throws an exception creating symbolic link target $a because that is a folder.

@joelmandell
Copy link

Okay, I think I got it now. The site root is supposed to be a symbolic link that points to the newly created folder $a.
Anyway having a hard time understanding the concept. I will dig into this again.

@CJHarmath
Copy link
Author

keep on playing with it until it clicks.
Create 2 folders with 2 different files, then create a symlink pointing to the first, confirm. then update the symlink to the second folder then confirm that you now see the second folder contents.

I've added a diagram here showing that the IIS site root points to a symlink.
dotnet/aspnetcore#3793 (comment)

@alex-jitbit
Copy link

Thanks for the script, (and for the comment on the GH issue at MS repo) used it as an inspiration for our deploy script! I owe you.

PS. we used to switch IIS application path during deploys, but symlinks are so much better and in-flight requests are not being dropped. Thanks again. ❤ 🙌

@CJHarmath
Copy link
Author

Sure thing, glad it helped someone!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment