Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

180 wiersze
7.2KB

  1. <#
  2. Runs ON the IIS server (invoked over SSH by scripts\deploy-iis.ps1). Not meant to be run
  3. by hand except for troubleshooting a stuck deploy.
  4. - Stops the site/app pool
  5. - Wipes RemoteDir and re-extracts the release zip into it (safe: the DB and error log
  6. live outside RemoteDir, at DbPath/ErrorLogDir, so a full wipe never touches them)
  7. - Points IIS at RemoteDir\public
  8. - Ensures the app pool identity has modify rights on the persistent data folder
  9. - Runs pending migrations (32-bit cscript - ACE OLEDB is x86-only)
  10. - Restarts the site/app pool
  11. #>
  12. param(
  13. [Parameter(Mandatory = $true)][string]$ZipPath,
  14. [Parameter(Mandatory = $true)][string]$RemoteDir,
  15. [Parameter(Mandatory = $true)][string]$SiteName,
  16. [Parameter(Mandatory = $true)][string]$AppPool,
  17. [Parameter(Mandatory = $true)][string]$DbPath,
  18. [string]$AdminSiteName = '',
  19. [string]$AdminAppPool = '',
  20. [switch]$RunMigrations = $true
  21. )
  22. $ErrorActionPreference = 'Stop'
  23. Import-Module WebAdministration
  24. # Stop-WebAppPool only requests a stop - the w3wp.exe worker process can keep running for a
  25. # few seconds afterwards (finishing in-flight requests), still holding the site's files open.
  26. # Wait for the pool to actually report Stopped, then force-kill any worker process that's
  27. # still lingering past the timeout so the wipe below doesn't hit "Access is denied".
  28. function Wait-AppPoolFullyStopped {
  29. param([string]$PoolName, [int]$TimeoutSec = 30)
  30. if([string]::IsNullOrWhiteSpace($PoolName)){ return }
  31. $deadline = (Get-Date).AddSeconds($TimeoutSec)
  32. while((Get-Date) -lt $deadline){
  33. if((Get-WebAppPoolState -Name $PoolName -ErrorAction SilentlyContinue).Value -eq 'Stopped'){
  34. return
  35. }
  36. Start-Sleep -Milliseconds 500
  37. }
  38. Write-Host "App pool $PoolName did not report Stopped within ${TimeoutSec}s - killing any lingering worker process"
  39. Get-CimInstance Win32_Process -Filter "Name = 'w3wp.exe'" -ErrorAction SilentlyContinue |
  40. Where-Object { $_.CommandLine -like ('*' + $PoolName + '*') } |
  41. ForEach-Object {
  42. try { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue } catch { }
  43. }
  44. }
  45. # Wipes are attempted a few times with a short pause - file handles (IIS or AV scanning
  46. # the just-stopped worker process's files) can take a moment to release even after the
  47. # worker process itself is confirmed gone.
  48. function Remove-DirectoryContentsWithRetry {
  49. param([string]$Path, [int]$MaxAttempts = 5, [int]$DelayMs = 2000)
  50. for($attempt = 1; $attempt -le $MaxAttempts; $attempt++){
  51. $failure = $null
  52. try {
  53. Remove-Item -Recurse -Force $Path -ErrorAction Stop
  54. return
  55. } catch {
  56. $failure = $_
  57. if($attempt -lt $MaxAttempts){
  58. Write-Host "Wipe attempt $attempt failed (files still locked?) - retrying in $($DelayMs)ms"
  59. Start-Sleep -Milliseconds $DelayMs
  60. }
  61. }
  62. }
  63. throw $failure
  64. }
  65. if(!(Get-Website -Name $SiteName -ErrorAction SilentlyContinue)){
  66. throw "IIS site '$SiteName' does not exist yet. Create the site and app pool once manually (or via a one-time setup script) before running this deploy."
  67. }
  68. if(!(Test-Path ('IIS:\AppPools\' + $AppPool))){
  69. throw "App pool '$AppPool' does not exist yet. Create it once manually before running this deploy."
  70. }
  71. Write-Host "Stopping IIS site $SiteName and app pool $AppPool"
  72. try { Stop-Website -Name $SiteName } catch { }
  73. try { Stop-WebAppPool -Name $AppPool } catch { }
  74. Wait-AppPoolFullyStopped -PoolName $AppPool
  75. # The admin site's physical path may already point inside RemoteDir (e.g. RemoteDir\public-admin)
  76. # from a previous deploy - if it's left running, IIS holds those files open and the wipe below
  77. # fails with "Access is denied". Stop it up front, alongside the public site, not after.
  78. if(![string]::IsNullOrWhiteSpace($AdminSiteName) -and (Get-Website -Name $AdminSiteName -ErrorAction SilentlyContinue)){
  79. Write-Host "Stopping IIS admin site $AdminSiteName"
  80. try { Stop-Website -Name $AdminSiteName } catch { }
  81. if(![string]::IsNullOrWhiteSpace($AdminAppPool)){
  82. try { Stop-WebAppPool -Name $AdminAppPool } catch { }
  83. Wait-AppPoolFullyStopped -PoolName $AdminAppPool
  84. }
  85. }
  86. if(Test-Path $RemoteDir){
  87. Write-Host "Wiping $RemoteDir"
  88. Remove-DirectoryContentsWithRetry -Path (Join-Path $RemoteDir '*')
  89. } else {
  90. New-Item -ItemType Directory -Force -Path $RemoteDir | Out-Null
  91. }
  92. Write-Host "Extracting $ZipPath -> $RemoteDir"
  93. Expand-Archive -Path $ZipPath -DestinationPath $RemoteDir -Force
  94. Remove-Item $ZipPath -ErrorAction SilentlyContinue
  95. $publicDir = Join-Path $RemoteDir 'public'
  96. if(!(Test-Path $publicDir)){
  97. throw "No public\ folder in the extracted release - deploy aborted, site left stopped for inspection."
  98. }
  99. # Persistent data folder (DB + error log) - lives outside RemoteDir so it survives the wipe
  100. # above. Create it and grant the app pool identity modify rights.
  101. $dataDir = Split-Path $DbPath -Parent
  102. if(!(Test-Path $dataDir)){
  103. New-Item -ItemType Directory -Force -Path $dataDir | Out-Null
  104. }
  105. $logDir = Join-Path $dataDir 'logs'
  106. if(!(Test-Path $logDir)){
  107. New-Item -ItemType Directory -Force -Path $logDir | Out-Null
  108. }
  109. icacls $dataDir /grant ("IIS AppPool\" + $AppPool + ":(OI)(CI)(M)") /T | Out-Null
  110. Set-ItemProperty ('IIS:\Sites\' + $SiteName) -Name physicalPath -Value $publicDir
  111. Set-ItemProperty ('IIS:\Sites\' + $SiteName) -Name applicationPool -Value $AppPool
  112. # This server has the 64-bit Access Database Engine (ACE OLEDB) installed, unlike the local
  113. # dev machine, which needs the 32-bit one - so the app pool stays 64-bit here.
  114. Set-ItemProperty ('IIS:\AppPools\' + $AppPool) -Name enable32BitAppOnWin64 -Value $false
  115. # --- Admin site ---
  116. $adminPublicDir = Join-Path $RemoteDir 'public-admin'
  117. if(![string]::IsNullOrWhiteSpace($AdminSiteName) -and (Test-Path $adminPublicDir)){
  118. if(!(Get-Website -Name $AdminSiteName -ErrorAction SilentlyContinue)){
  119. throw "IIS admin site '$AdminSiteName' does not exist yet. Create the site and app pool once manually before running this deploy."
  120. }
  121. Write-Host "Configuring admin site $AdminSiteName"
  122. if(![string]::IsNullOrWhiteSpace($AdminAppPool)){
  123. Set-ItemProperty ('IIS:\Sites\' + $AdminSiteName) -Name applicationPool -Value $AdminAppPool
  124. Set-ItemProperty ('IIS:\AppPools\' + $AdminAppPool) -Name enable32BitAppOnWin64 -Value $false
  125. icacls $dataDir /grant ("IIS AppPool\" + $AdminAppPool + ":(OI)(CI)(M)") /T | Out-Null
  126. }
  127. Set-ItemProperty ('IIS:\Sites\' + $AdminSiteName) -Name physicalPath -Value $adminPublicDir
  128. }
  129. if($RunMigrations){
  130. Write-Host 'Running pending migrations'
  131. Push-Location $RemoteDir
  132. try {
  133. & cscript.exe //nologo scripts\runMigrations.vbs up
  134. if($LASTEXITCODE -ne 0){ throw 'runMigrations.vbs up failed - see output above' }
  135. } finally {
  136. Pop-Location
  137. }
  138. }
  139. Write-Host "Starting IIS site $SiteName and app pool $AppPool"
  140. if((Get-WebAppPoolState -Name $AppPool).Value -eq 'Started'){
  141. Restart-WebAppPool -Name $AppPool
  142. } else {
  143. Start-WebAppPool -Name $AppPool
  144. }
  145. Start-Website $SiteName
  146. if(![string]::IsNullOrWhiteSpace($AdminSiteName)){
  147. if(![string]::IsNullOrWhiteSpace($AdminAppPool)){
  148. if((Get-WebAppPoolState -Name $AdminAppPool).Value -eq 'Started'){
  149. Restart-WebAppPool -Name $AdminAppPool
  150. } else {
  151. Start-WebAppPool -Name $AdminAppPool
  152. }
  153. }
  154. Start-Website $AdminSiteName
  155. }
  156. Write-Host 'Remote apply complete.'

Powered by TurnKey Linux.