Skip to content

Instantly share code, notes, and snippets.

@J-Swift
Created July 8, 2022 13:55
Show Gist options
  • Select an option

  • Save J-Swift/524fda8672122be6f1371cf4f5534683 to your computer and use it in GitHub Desktop.

Select an option

Save J-Swift/524fda8672122be6f1371cf4f5534683 to your computer and use it in GitHub Desktop.
Capistrano-style IIS deployment helper
param(
[switch] $Rollback
)
################################################################################
# Config
################################################################################
$WebsiteName = "FIXME: IIS WEBSITE NAME"
$WaitForWebsiteTimeoutSeconds = 60
$NumReleasesToKeep = 5
$ReleasesDir = (Join-Path "." releases)
################################################################################
# Helpers
################################################################################
function WriteInfo {
param([Parameter(Mandatory = $true)][string] $Msg)
Write-Host $Msg
}
function WriteWarn {
param([Parameter(Mandatory = $true)][string] $Msg)
Write-Host -ForegroundColor Yellow $Msg
}
function WriteError {
param([Parameter(Mandatory = $true)][string] $Msg)
Write-Host -ForegroundColor Red $Msg
}
function FailWithMessage {
param([Parameter(Mandatory = $true)][string] $Msg)
WriteError $Msg
[System.Console]::ReadKey()
exit 1
}
function WaitForState() {
param([Parameter(Mandatory = $true)][string] $State)
$Timeout = (Get-Date).AddSeconds($WaitForWebsiteTimeoutSeconds)
while ($State -ne (Get-IISSite -name $WebsiteName).State) {
if ((Get-Date) -gt $Timeout) {
return $false
}
Start-Sleep -Seconds 1
}
return $true
}
function LoadIISModule() {
Import-Module IISAdministration
if (-not $?) {
FailWithMessage "Error while loading IISAdministration module. Exiting."
}
}
function UpdateCurrentTo() {
param([Parameter(Mandatory = $true)][string] $TargetPath)
$CurrentPath = (Join-Path "." "current")
if (Test-Path $CurrentPath) {
Remove-Item $CurrentPath
}
New-Item -ItemType SymbolicLink -Path $CurrentPath -Target $TargetPath | Out-Null
WriteInfo " Updated $CurrentPath to point at [$TargetPath]"
}
function GetRollbackTarget() {
$CurrentTarget = (Get-ChildItem -Filter "current").Target
$Releases = (Get-ChildItem $ReleasesDir | Sort-Object -Descending)
$ReturnNext = $false
foreach ($Release in $Releases) {
if ($ReturnNext) {
return $Release
}
if ($Release.ToString() -eq $CurrentTarget.ToString()) {
$ReturnNext = $true
}
}
return $null
}
function RemoveOldReleases() {
$ReleasesToDelete = (Get-ChildItem $ReleasesDir -exclude ".revision" | Sort-Object -Descending | Select-Object -Skip $NumReleasesToKeep)
$CurrentTarget = (Get-ChildItem -Filter "current").Target
foreach ($Release in $ReleasesToDelete) {
if ($Release.ToString() -eq $CurrentTarget.ToString()) {
WriteInfo " Skipping current release [$Release]"
} else {
WriteInfo " Removing old release at [$Release]"
Remove-Item $Release -Force -Recurse
}
}
}
function CheckIfRunningAsAdmin() {
# https://megamorf.gitlab.io/2020/05/26/check-if-powershell-is-running-as-administrator/
[Security.Principal.WindowsIdentity]::GetCurrent().Groups -contains 'S-1-5-32-544'
}
################################################################################
# Main
################################################################################
if (-not ("Core" -eq $PSVersionTable.PSEdition)) {
FailWithMessage "This script requires Powershell Core. Exiting."
}
if (-not (CheckIfRunningAsAdmin)) {
FailWithMessage "This script requires being run as Admin. Exiting."
}
if (-not (Test-Path $ReleasesDir)) {
FailWithMessage "No releases found in [$ReleasesDir]. Exiting."
}
$NewestRelease = Get-ChildItem $ReleasesDir | Sort-Object -Descending | Select-Object -First 1
if ($null -eq $NewestRelease) {
FailWithMessage "No releases found in [$ReleasesDir]. Exiting."
}
$NewestReleasePath = $NewestRelease
function Main() {
WriteInfo " > Loading IISAdministration PS module"
LoadIISModule
WriteInfo " > Checking if website is stopped"
if ("Stopped" -ne (Get-IISSite -name $WebsiteName).State) {
WriteWarn "You must stop the website named [$WebsiteName] in IIS.`n`nWaiting..."
if (-not (WaitForState "Stopped")) {
FailWithMessage "`nTimed out waiting for [$WebsiteName] to be stopped. Exiting."
}
}
WriteInfo " Stopped"
WriteInfo " > Updating current link"
if ($Rollback) {
$Target = GetRollbackTarget
if ($null -eq $Target) {
WriteInfo " No target for rollback"
} else {
WriteInfo " Rolling back to [$Target]"
UpdateCurrentTo $Target
}
} else {
UpdateCurrentTo $NewestReleasePath
WriteInfo " > Checking for old releases"
RemoveOldReleases
}
WriteWarn "Release updated. You must start the website named [$WebsiteName] in IIS.`n`nWaiting..."
if ("Started" -ne (Get-IISSite -name $WebsiteName).State) {
if (-not (WaitForState "Started")) {
FailWithMessage "`nTimed out waiting for [$WebsiteName] to be started. Exiting."
}
}
WriteInfo " Started"
WriteInfo "`nDone!"
[System.Console]::ReadKey()
}
Main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment