functions/MsDeploy.ps1
$msdeploy = "$env:ProgramFiles\IIS\Microsoft Web Deploy V3\msdeploy.exe" Import-Module process #region public function Test-MsDeploy($server, $credential) { $cmd = { write-host "Im user '$env:username' on server '$env:hostname'!" } if ($credential -eq $null -or $credential -eq "reset") { req cache $credential = get-credentialscached $server -reset:($credential -eq "reset") } # $url = get-msdeployComputername $server # $enc = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$($credential.username):$($credential.getnetworkcredential().password)")) # $h = @{ Authorization = "Basic $enc" } # Invoke-WebRequest $url -Headers $h -UseBasicParsing -ErrorAction stop # can upload files? $canUpload = $false try { echo "testing.." > "$env:Temp/_msdeploy.txt" Upload-MsDeployFile -server $server -source "$env:Temp\_msdeploy.txt" -targetPath c:\Temp\ $canUpload = $true } catch { } $canInvoke = $false try { #can execite commands? Invoke-MsDeployCommand -server $server ` -command $cmd ` -scriptMode -waitInterval 1000 -waitAttempts 600 -credential $credential $canInvoke = $true } catch { } return @{ CanUpload=$canUpload CanInvoke=$canInvoke } } function Copy-MsDeployApp { [CmdletBinding(SupportsShouldProcess=$true)] param( [Parameter(Mandatory=$true)] $source, [Parameter(Mandatory=$true)] $server, [Parameter(Mandatory=$true)] $site, [switch][bool]$deleteObsoleteItems, $authType, $credential ) $f = $source # TODO: run dotnet publish and determine source path from there $filename = [System.IO.Path]::GetFileName($f) $srcIsDir = ((test-path $f) -and ((get-item $f) -is [System.IO.DirectoryInfo])) -or ($filename -eq "*") if ($srcIsDir) { $fullTargetPath = $site $fullSourcePath = [System.IO.Path]::GetDirectoryName($f) } else { $targetpath = $f.Replace("\","/") if ($targetpath.startsWith("./")) { $targetpath = $targetpath.Substring(2)} $fullTargetPath = "$site/$targetpath" $fullSourcePath = $f } $fullSourcePath = (get-item $fullSourcePath).FullName $provider = "contentPath" $l = msdeploy -verb sync ` -source:(get-msdeploypath -provider $provider -path $fullSourcePath) ` -dest (get-msdeploypath -provider $provider -server $server -path $fullTargetPath -credential $credential -authType $authType) ` -skip:(get-skiprules $deleteObsoleteItems) ` -useChecksum ` -showOutput } function Copy-MsDeployFile { [CmdletBinding(SupportsShouldProcess=$true)] param( [Parameter(Mandatory=$true)] $server, [Parameter(Mandatory=$true)] $source, [Parameter(Mandatory=$true)] [Alias("destination")] $targetpath, [switch][bool]$deleteObsoleteItems, $credential ) foreach($f in @($source)) { $filename = [System.IO.Path]::GetFileName($f) $srcIsDir = [System.IO.Directory]::Exists($f) -or $filename -eq "*" $dstIsDir = $targetpath.EndsWith("\") -or $targetpath.EndsWith("/") if ($srcIsDir) { if (!$dstIsDir) { throw "cannot copy directory to file" } $fullTargetPath = $targetpath $fullSourcePath = [System.IO.Path]::GetDirectoryName($f) } else { if ($dstIsDir) { $fullTargetPath = [System.IO.Path]::Combine($targetpath, $filename) } else { $fullTargetPath = $targetpath } $fullSourcePath = $f } $fullSourcePath = (get-item $fullSourcePath).FullName $l = msdeploy -verb sync ` -source:(get-msdeploypath -provider contentPath -path $fullSourcePath) ` -dest (get-msdeploypath -provider contentPath -server $server -path $fullTargetPath -credential $credential) ` -skip:(get-skiprules $deleteObsoleteItems) ` -useChecksum ` -showOutput } } function Get-MsDeployFile { [CmdletBinding(SupportsShouldProcess=$true)] param($server, $source, [Parameter(Mandatory=$true)]$outputDir, [switch][bool]$deleteObsoleteItems, $credential) $path = $source if (!(test-path $outputdir)) { $null = mkdir $outputdir } #$provider = "file" $provider = "contentPath" if ($iisapp -ne $null) { $root = get-msdeployphysicalPath -server $server -iisapp $iisapp -credential $credential $path = [System.IO.Path]::Combine($root, $path) } $dstIsDir = $true#[System.IO.Directory]::Exists($f) -or $filename -eq "*" foreach($f in @($path)) { $srcIsDir = $path.EndsWith("\") -or $path.EndsWith("/") $filename = [System.IO.Path]::GetFileName($f) $targetPath = (get-item $outputdir).FullName if (!$srcIsDir -or $true) { $targetPath = (join-path (get-item $outputdir).FullName $filename) } $l = msdeploy -verb sync ` -source:(get-msdeploypath -provider $provider -server $server -path $f -credential $credential) ` -dest (get-msdeploypath -provider $provider -path $targetPath) ` -skip:(get-skiprules $deleteObsoleteItems) ` -useChecksum ` -showOutput write-output $targetPath } } function Invoke-MsDeployCommand($command, $server, $credential, [switch][bool]$scriptMode, $waitInterval = 60000, $waitAttempts = 10) { if ($scriptMode) { $tempDir = "c:\TEMP\" $guid = [system.guid]::newGuid() $null = mkdir "$env:Temp/$guid" $tmp = "$env:Temp/$guid/$guid.ps1" $tmpfile = "$tempDir\$guid.ps1" $command = "try { `$ErrorActionPreference = `"Stop`";`r`n" + $command + "`r`n } finally { rm '$tmpfile' }" $command | out-file $tmp -encoding UTF8 #$tmp = "$env:Temp/$guid/$guid.bat" #"powershell -NonInteractive -NoProfile -File $tempDir\$guid.ps1" | out-file $tmp -encoding UTF8 #$command = "$tempDir\$guid.bat" $command = "powershell -NonInteractive -NoProfile -File $tmpfile" Upload-MsDeployFile -server $server -source "$env:Temp\$guid\" -targetPath $tempDir } $additionalDestArgs=@() if ($waitInterval -ne $null) { $additionalDestArgs += "waitInterval=$waitInterval" } if ($waitAttempts -ne $null) { $additionalDestArgs += "waitAttempts=$waitAttempts" } #$command = "`'$command`'" $r = msdeploy -verb sync ` -source runCommand ` -dest (get-msdeploypath -provider runCommand -server $server -path $command -credential $credential -additionalArgs $additionalDestArgs) ` -skip:(get-skipRules) ` -showoutput ` -verbose $lastLines = $r | select -Last 2 $exitCode = $lastLines | % { if ($_ -match "code '([0-9A-Fx]+)'") { $Matches[1] } } if ($exitCode -ne "0x0") { throw "remote Command failed: exitcode='$exitCode'" } } function Get-MsDeployFiles($server, $path, $credential) { $l = msdeploy -verb dump ` -source:(get-msdeploypath -provider contentPath -server $server -path $path -credential $credential) return $l } function Invoke-MsDeploy { [CmdletBinding(SupportsShouldProcess=$true)] param ( $verb, $source, $dest, $skip, [switch][bool] $useChecksum, [switch][bool] $showOutput ) $a = @() if ($verb -ne $null) { $a += "-verb:$verb" } if ($source -ne $null) { $a += "-source:$source" } if ($dest -ne $null) { $a += "-dest:$dest" } if ($useChecksum) { $a += "-usechecksum" } if ($skip -eq $null) { # don't delete remote files by default $skip = get-skipRules } if ($skip -ne $null) { @($skip) | % { $a += "-skip:$_" } } # ignore invalid https certificates $a += "-allowUntrusted" $verbose = $true $r = invoke $msdeploy $a -passthru -showoutput:$showOutput -nothrow -Verbose:$verbose if ($LASTEXITCODE -ne 0) { if ($r -ne $null) { $r | % { write-warning $_ } } throw "msdeploy failed with EXITCODE=$lastExitCode" } return $r } #endregion #region private function get-skipRules([switch][bool]$deleteObsoleteItems = $false) { $skip = @() if (!$deleteObsoleteItems) { $skip += @("skipaction=Delete,objectname=dirPath", "skipaction=Delete,objectname=filePath") } return $skip } function get-msdeployComputername($server,$port=$null) { if ($server.startsWith("http")) { return $server } if ($port -ne $null) { $server = "$($server):$port" } return "http://$($server)/MSDEPLOYAGENTSERVICE" } function get-msdeploypath($provider, $path, $server, $additionalArgs, $credential, $authType, $site) { $a = @() $a += "$provider=`"$path`"" if ($server -ne $null) { if ($credential -eq $null -and (gmo cache -erroraction Ignore)) { $credential = Get-CredentialsCached "$server" } if ($credential -eq $null) { throw "missing credentials" } $a += "computername=" + (get-msdeployComputername $server) } if ($credential -ne $null) { if ($credential -is "String") { $a += $credential } else { $username = $credential.username $password = $credential.GetNetworkCredential().Password $a += "username=$username" $a += "password=$password" if ($authType -ne $null) { $a += "AuthType='$authType'" } } } if ($site -ne $null) { $a += "site=$site" } if ($additionalArgs -ne $null) { $a += @($additionalArgs) } return [string]::Join(",", $a) } function get-msdeployphysicalPath($server, $iisapp, $credential) { $r = msdeploy -verb dump -source (get-msdeploypath -provider appHostConfig -path "$iisapp/" -server $server -credential $credential) -credential $credential $found = $false foreach($line in @($r)) { if ($found) { return $line } if ($line.Contains("application[@path='/']/virtualDirectory[@path='/']")) { $found = $true # the next line will contains physical path value continue } } throw "Could not determine physical path" } function add-ext($f, $suffix) { $filename = [System.IO.Path]::GetFileNameWithoutExtension($f) $ext = [System.IO.Path]::GetExtension($f) $newname = "$filename$suffix$ext" $dir = split-path -parent $f return join-path $dir $newname } #endregion #region aliases new-alias msdeploy Invoke-MsDeploy -force new-alias Download-MsDeployFile Get-MsDeployFile -force new-alias Upload-MsDeployFile Copy-MsDeployFile -force new-alias Upload-MsDeployApp Copy-MsDeployApp -force new-alias List-MsDeployFiles Get-MsDeployFiles -force #endregion |