Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
#Requires -Version 3.0
param($websiteNames, $jobname, $jobtype, $packOutput, $slotName = "")
$VerbosePreference = "continue"
$ErrorActionPreference = "continue"
# Helper Functions (based on or from
function Get-MSDeploy{
$installPath = $env:msdeployinstallpath
$keysToCheck = @('hklm:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\3','hklm:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\2','hklm:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\1')
foreach($keyToCheck in $keysToCheck){
if(Test-Path $keyToCheck){
$installPath = (Get-itemproperty $keyToCheck -Name InstallPath -ErrorAction SilentlyContinue | select -ExpandProperty InstallPath -ErrorAction SilentlyContinue)
throw "Unable to find msdeploy.exe, please install it and try again"
[string]$msdInstallLoc = (join-path $installPath 'msdeploy.exe')
"Found msdeploy.exe at [{0}]" -f $msdInstallLoc | Write-Verbose
function Execute-Command {
[Parameter(Mandatory = $true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[Parameter(Mandatory = $true,Position=1,ValueFromPipelineByPropertyName=$true)]
$psi = New-Object -TypeName System.Diagnostics.ProcessStartInfo
$psi.CreateNoWindow = $true
$psi.UseShellExecute = $false
$psi.RedirectStandardOutput = $true
$psi.FileName = $exePath
$psi.Arguments = $arguments
$process = New-Object -TypeName System.Diagnostics.Process
$process.StartInfo = $psi
# Register the event handler for error
$stdErrEvent = Register-ObjectEvent -InputObject $process -EventName 'ErrorDataReceived' -Action {
if (! [String]::IsNullOrEmpty($EventArgs.Data)) {
$EventArgs.Data | Write-Error
# Starting process.
$process.Start() | Out-Null
$process.BeginErrorReadLine() | Out-Null
$output = $process.StandardOutput.ReadToEnd()
$process.WaitForExit() | Out-Null
$output | Write-Output
# UnRegister the event handler for error
Unregister-Event -SourceIdentifier $stdErrEvent.Name | Out-Null
Write-Verbose "Published requested of $jobtype web job $jobname to the following website(s): $websiteNames"
$websiteNames.split(',') | % {
$websiteName = $_
#Write-Verbose "Restarting Azure Websites $websiteName to ensure no locks"
#Restart-AzureWebsite -Name $websiteName
Write-Verbose "Starting publish of $jobtype web job $jobname to $websiteName"
$website = if ([string]::IsNullOrWhiteSpace($slotName)) { Get-AzureWebsite -Name $websiteName } else { Get-AzureWebsite -Name $websiteName -Slot $slotName }
# If we have an array, we most likely have additional slots on this website. Throw an exception and leave.
if($website -is [System.Object[]]) {
throw [System.Exception] "Multiple websites returned for $websiteName; please specify a slot"
# Grab SCM url to use with MSDeploy; there should only be one
$msdeployurl = $website.EnabledHostNames | Where-Object {$_ -like "*.scm.*"}
if($msdeployurl -is [System.Object[]]) {
throw [System.Exception] "Multiple SCM urls returned for $websiteName; consult Kudu/Azure portal to clarify."
# MSDeploy variables to use in arguments below
$msdeployIisAppPath = $website.Name
$msdeployIisSubAppPath = "$msdeployIisAppPath/app_data/jobs/$jobtype/$jobname"
# The following is Azure specific; your mileage may vary
$msdeployComputerName = "https://$msdeployurl/msdeploy.axd"
$msdeployUserName = $website.PublishingUsername
$msdeployPassword = $website.PublishingPassword
# Build the msdeploy command, more info on this command here
$webrootOutputFolder = (get-item $packOutput).FullName
$publishArgs = @()
$publishArgs += "-source:contentPath='$webrootOutputFolder'"
$publishArgs += "-dest:contentPath='$msdeployIisSubAppPath',ComputerName='$msdeployComputerName',UserName='$msdeployUserName',Password='$msdeployPassword',IncludeAcls='False',AuthType='Basic'"
$publishArgs += '-verb:sync'
$publishArgs += '-enableLink:contentLibExtension'
$publishArgs += '-usechecksum'
$publishArgs += '-enablerule:AppOffline'
$publishArgs += '-retryAttempts:2'
$publishArgs += '-disablerule:BackupRule'
# Uncomment the following if you want to keep files on the server
#$publishArgs += '-enableRule:DoNotDeleteRule'
$msdeployPath = Get-MSDeploy
$msdeployArguments = $publishArgs -join ' '
$msdeployArgumentsLog = $msdeployArguments.Replace($msdeployPassword, "{PASSWORD_REMOVED}")
Write-Verbose "Executing MSDeploy command $msdeployPath $msdeployArgumentsLog"
Execute-Command -exePath $msdeployPath -arguments $msdeployArguments
Write-Verbose "Finished publish of $jobtype web job $jobname to $websiteName"
Write-Verbose "Finished requested publish of $jobtype web job $jobname to the following website(s): $websiteNames"[/powershell]
Then from VSO, I can add a new <em>Azure PowerShell</em> task setup similarly to this:
<a href=""><img src="" alt="VSO Azure Web Job" width="593" height="325" class="aligncenter size-full wp-image-22841" /></a>
The <em>Script Arguments</em> field is setup similarly to this:
[powershell]-websiteNames "website001,website002" -jobname "MyWebJob" -jobtype "continuous" -packOutput $(Build.SourcesDirectory)\$(system.teamProject)\artifacts\bin\$(BuildConfiguration)\Publish
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.