Skip to content

Instantly share code, notes, and snippets.

@rschiefer
Last active December 28, 2016 15:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rschiefer/463e7d82cde65b51d566dfe1d9c11c39 to your computer and use it in GitHub Desktop.
Save rschiefer/463e7d82cde65b51d566dfe1d9c11c39 to your computer and use it in GitHub Desktop.
Zero-Downtime MSDeploy Package Deployment Script
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