Last active
December 28, 2016 15:04
-
-
Save rschiefer/463e7d82cde65b51d566dfe1d9c11c39 to your computer and use it in GitHub Desktop.
Zero-Downtime MSDeploy Package Deployment Script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Param( | |
[Parameter(Mandatory = $true, Position = 0)] [string] $configName, | |
[Parameter(Mandatory = $true, Position = 1)] [string] $TFSUri, | |
[Parameter(Mandatory = $true, Position = 2)] [string] $environmentName, | |
[Parameter(Mandatory = $true, Position = 3)] [string] $username, | |
[Parameter(Mandatory = $true, Position = 4)] [string] $password, | |
[Parameter(Mandatory = $false, Position = 5)] [bool] $defaultStopWebsiteForDeployment = $true, | |
[Parameter(Mandatory = $false, Position = 6)] [int] $defaultStopWebsiteForDeploymentWaitSeconds = 22 | |
) | |
$Error.Clear(); | |
$lastexitcode = $null | |
[void][Reflection.Assembly]::LoadWithPartialName('System.Management.Automation') | |
import-module WebAdministration | |
function CheckForErrors{ | |
if ($lastexitcode -lt 0 -or $Error.Count -gt 0 -and $lastexitcode -ne $null) | |
{ | |
Write-Output "lastexitcode = $lastexitcode" | |
Write-Output "error count = " $Error.Count | |
Write-Output $Error | |
$Error.Clear() | |
cd "$root" | |
exit -1 | |
} | |
} | |
trap { | |
Write-Output "trapped error" | |
Write-Output $_ | |
cd "$root" | |
exit -1 | |
} | |
$root = (Get-Location).Path | |
$p = ConvertTo-SecureString $password -AsPlainText -Force | |
$creds = New-Object –TypeName "System.Management.Automation.PSCredential" –ArgumentList $username, $p | |
Write-Host $tfsServer | |
$path = "$/DeployConfigurations/$($configName).json" | |
$deployConfigJson = Invoke-RestMethod -Method Get -Credential $creds -Uri "$TFSUri/_apis/tfvc/items/$($path)?api-version=2.0" | |
#$deployConfigJson.testBuilds | |
$failed = $false | |
$builds = $deployConfigJson.builds | |
foreach($build in $builds){ | |
$environment = $build.environments | Where-Object {$_.name -eq $environmentName} | Select-Object -First 1 | |
Write-Host "Environment = $environment" | |
if ($environment -ne $null) | |
{ | |
$buildName = $build.name | |
Write-Host "Build = $buildName" | |
$teamProject = $build.teamProject | |
Write-Host "Team Project = $teamProject" | |
$setParamFilename = "sparam.$environmentName.xml" | |
Write-Verbose "setParamFilename = $setParamFilename" | |
$command = Get-ChildItem "MSDeployPackages\$teamProject-$buildName\*.cmd" | Select-Object -First 1 | |
cd $command.Directory.FullName | |
Write-Host $command | |
#WebDeploy | |
if (Get-Content $command | Select-String 'IIS Web Deploy technology') | |
{ | |
Write-Host "Deploying WebDeploy command - $command" | |
$websiteName = "" | |
$stopWebsiteForDeployment = $defaultStopWebsiteForDeployment | |
$stopWebsiteForDeploymentWaitSeconds = $defaultStopWebsiteForDeploymentWaitSeconds | |
if ($build.stopWebsiteForDeployment -eq 'false') | |
{ | |
$stopWebsiteForDeployment = $false | |
} | |
if ([string]::IsNullOrEmpty($build.stopWebsiteForDeploymentWaitSeconds) -eq $false) | |
{ | |
$stopWebsiteForDeploymentWaitSeconds = $build.stopWebsiteForDeploymentWaitSeconds | |
} | |
foreach($server in $environment.servers) | |
{ | |
$session = $null | |
if ($stopWebsiteForDeployment -eq $true) | |
{ | |
# Get website name from param file | |
if ($Matches -ne $null) { | |
$Matches.Clear() | |
} | |
$webAppName = (Select-Xml -Path $setParamFilename -XPath "//*[@name='IIS Web Application Name']/@value[1]")[-1].Node.Value | |
$webAppName -match "[^\\/]+" | |
$websiteName = $Matches[0] | |
Write-Verbose "websiteName = $websiteName" | |
Write-Verbose "webAppName = $webAppName" | |
# Remove node from load balancer | |
$monitorFilePath = "IIS:\Sites\$($websiteName)\monitor.htm" | |
$monitorFileDisabledName = 'monitor.disabled.htm' | |
$monitorFileDisabledPath = "IIS:\Sites\$($websiteName)\$monitorFileDisabledName" | |
Invoke-Command -ComputerName $server { | |
Param($monitorFilePath, $monitorFileDisabledName, $stopWebsiteForDeploymentWaitSeconds); | |
Get-Website > $null | |
Write-Host "test path = $(Test-Path $monitorFilePath)" | |
if (Test-Path $monitorFilePath) { | |
$monitorFile = Get-Item $monitorFilePath | |
$monitorFile | Rename-Item -NewName $monitorFileDisabledName | |
Write-Host "Disabled monitor.htm file." | |
# Wait for traffic to dissipate | |
Write-Host "Waiting $stopWebsiteForDeploymentWaitSeconds seconds for load balancer to update..." | |
Start-Sleep -Seconds $stopWebsiteForDeploymentWaitSeconds | |
} | |
} -ArgumentList $monitorFilePath, $monitorFileDisabledName, $stopWebsiteForDeploymentWaitSeconds | |
# Stop site | |
Write-Host "Stopping Website '$websiteName' on '$server'" | |
Invoke-Command -ComputerName $server { Param($websiteName); stop-website -Name "$websiteName" } -ArgumentList $websiteName | |
# Re-enable monitor file after site is stopped so it will be added to the load balancer when the site is started | |
Invoke-Command -ComputerName $server { | |
Param($monitorFilePath); | |
Get-Website > $null | |
Write-Host "test path = $(Test-Path $monitorFilePath)" | |
if (Test-Path $monitorFilePath) { | |
$monitorFile = Get-Item $monitorFilePath | |
$monitorFile | Rename-Item -NewName 'monitor.htm' | |
Write-Host "Re-enabled monitor.htm file." | |
} | |
} -ArgumentList $monitorFileDisabledPath | |
} | |
$newCommand = "$command.edited.cmd" | |
(Get-Content $command) | ForEach-Object { $_ -replace 'set RootPath=%~dp0', 'set RootPath=' } | Set-Content $newCommand | |
#& $newCommand /M:$server /T | |
& $newCommand /M:$server /Y -setParamFile:$setParamFilename $build.additionalMSDeployArgs -verbose 2>&1 | |
if ($stopWebsiteForDeployment -eq $true) | |
{ | |
Write-Host "Starting Website '$websiteName' on '$server'" | |
Invoke-Command -ComputerName $server { Param($websiteName); start-website -Name "$websiteName" } -ArgumentList $websiteName | |
Write-Host "Waiting $stopWebsiteForDeploymentWaitSeconds seconds for website to start..." | |
Start-Sleep -Seconds $stopWebsiteForDeploymentWaitSeconds | |
} | |
} | |
} | |
#AppDeploy | |
elseif (Get-Content $command | Select-String 'appName=') | |
{ | |
Write-Host "Deploying AppDeploy command - $command" | |
foreach($server in $environment.servers){ | |
if ($server -in $serverstack.servers){ | |
& $command $server -setParamFile:$setParamFilename $build.additionalMSDeployArgs 2>&1 | |
} | |
} | |
} | |
#SqlDeploy | |
elseif ($command.name -like '*.sqlDeploy.cmd') | |
{ | |
Write-Host "Deploying SqlDeploy command - $command" | |
& $command /Y -setParamFile:$setParamFilename $build.additionalMSDeployArgs 2>&1 | |
} | |
CheckForErrors | |
cd "$root" | |
} | |
CheckForErrors | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment