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
@joelmandell
Copy link

joelmandell commented Jan 5, 2021

Is this possible to do on the same IIS Site root path and instead do it on same site but on differet webapp-folders?
I.E:
Default Web Site\Production folder
Default Web Site\Staging.

@joelmandell
Copy link

joelmandell commented Jan 5, 2021

I get errors. I replace the content of the variables according to my testenvironment, but I do get IOExceptions.

bild

@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