MSDeployは、 Web配置ツール (Web Deploy) によるアプリケーションパッケージの展開を可能にします。
IIS マネージャー用の Web 配置ツールの概要.aspx)
このMSDeployを使えば、ASP.NET MVCアプリをIISホストへファイル展開、同期することが容易になるため、非常に強力で利用すべき機能です。 MSDeployには、よくコマンドラインでの利用構文が紹介されますが、PowerShellもサポートしています。 そこで、今回は、 PowerShellによるMSDeployの実行について見てみましょう。
MS-DOSコマンドでのコマンドライン構文
ここに記述があります。
Web 配置のコマンド ラインの構文.aspx)
Msdeploy.exeコマンド ラインの主要な要素は、動詞 ("操作" とも呼ばれます)、同期元、同期先 (任意指定)、および操作設定 (任意指定) です。 動詞と同期元は必須です。同期先は、動詞によって必要な場合と必要でない場合があります。任意指定の操作設定では、コマンドの実行方法を変更できます。
PowerShellでのWeb 配置の使用
PowreShellでの利用も簡単で、変更点はこれだけです。 Web配置のコマンドのverb、source、およびdestの各引数の後ろのコロン (:) を等号記号 (=) に変更します。
# cmd コマンド例: msdeploy -verb:sync -source:metakey=/lm/w3svc/1 -dest:metakey=/lm/w3svc/2 -verbose # PowerShell コマンド例: .\msdeploy.exe -verb=sync -source=metakey=/lm/w3svc/1 -dest=metakey=/lm/w3svc/2 -verbose もう少し本格的なコマンドで見てみましょう。 # cmd コマンド例: "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" -verb:sync -source:package="C:\パッケージパス\パッケージ.zip" -dest:auto,computerName="http://対象ホストIP/MSDeployAgentService",userName="配置管理者ユーザー",password="配置管理者パスワード",includeAcls="False" -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension -setParamFile:"C:\パラメーターxmlパス/パラメータ.xml" # PowerShell コマンド例: $packagepath = "C:\パッケージパス" $parameterxml = "C:\パラメータxml" $hostip = "対象ホストIPAddress" $user = "配置管理者ユーザー" $pass = "配置管理者パスワード" ."C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" -verb=sync -source=package="$packagepath" "-dest=auto,computerName=""http://$hostip/MSDeployAgentService"",userName=""$user"",password=""$pass"",includeAcls=""False""" -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension -setParamFile:"$parameterxml"
PowerShellでのWeb 配置のコード
PowerShellでの展開のメリットは、PowerShellでの制御が可能である事です。 では実際にPowerShellで展開する方法を考えてみます。
System.Diagnotic.Process での配置
展開には、 msdeploy.exeつまり外部コマンドを利用することになります。 そこで、 まずは外部コマンドの制御が自由に扱えるSystem.Diagnotic.Processを使ってみましょう。 このやり方は、StandardOutputなどの制御も楽なんですが、パッケージ展開が止まってしまってます。 まだ原因を探っていませんがどうもほげりました。
$msdeploy = "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" $user = "配置管理者ユーザー" $Password = "配置管理者パスワード" foreach ($deploygroup in $deploygroups) { # define arguments of msdeploy [string[]]$arguments = @( "-verb:sync", "-source:package=$zip", "-dest:auto,computerName=`"http://$deploygroup/MSDeployAgentService`",userName=$user,password=$Password,includeAcls=`"False`"", "-disableLink:AppPoolExtension", "-disableLink:ContentExtension", "-disableLink:CertificateExtension", "-setParam:`"IIS Web Application Name`"=`"W3C1hogehoge`"") # Start Process "running msdeploy to $deploygroup" | Out-LogHost -logfile $log -showdata # Deploy内容が存在した際に 実行されにゃいお (更新があった場合にのみ走らないので却下です) $processinfo = New-Object System.Diagnostics.ProcessStartInfo $processinfo.FileName = $msdeploy $processinfo.RedirectStandardError = $true $processinfo.RedirectStandardOutput = $true $processinfo.UseShellExecute = $false $processinfo.Arguments = $arguments $process = New-Object System.Diagnostics.Process $process.StartInfo = $processinfo $process.Start() > $null $process.WaitForExit() $output = @() $output = $process.StandardError.ReadToEnd() $output += $process.StandardOutput.ReadToEnd() $output | Out-LogHost -logfile $log -hidedata }
Start-Process での配置
ならばしょうがないと、Start-Processを利用してみましょう。このやり方が面倒な点は、-RedirectStandardOutputがAppend出来ないので、一旦外部ファイルに逃がす必要がある点です。 また、記述にある通りただのforeachをぶんまわすのでは対象ホストが1-3個程度ならいいのですが、10~となるとパッケージの大きさによってはとっても時間がかかります。
$msdeploy = "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" $user = "配置管理者ユーザー" $Password = "配置管理者パスワード" foreach ($deploygroup in $deploygroups) { # define arguments of msdeploy [string[]]$arguments = @( "-verb:sync", "-source:package=$zip", "-dest:auto,computerName=`"http://$deploygroup/MSDeployAgentService`",userName=$user,password=$Password,includeAcls=`"False`"", "-disableLink:AppPoolExtension", "-disableLink:ContentExtension", "-disableLink:CertificateExtension", "-setParam:`"IIS Web Application Name`"=`"W3C1hogehoge`"") # Start Process "running msdeploy to $deploygroup" | Out-LogHost -logfile $log -showdata # foreach が sequencial で一向に終わらにゃいお Start-Process -FilePath $msdeploy -ArgumentList $arguments -Wait -RedirectStandardOutput $tmplog -RedirectStandardError $tmperrorlog -NoNewWindow Get-Content -Path $tmplog -Encoding Default | Out-File -FilePath $log -Encoding utf8 -Append Get-Content -Path $tmperrorlog -Encoding Default | Out-File -FilePath $log -Encoding utf8 -Append if ($tmplog) {Remove-Item -Path $tmplog -Force} if ($tmperrorlog) {Remove-Item -Path $tmperrorlog -Force} }
Start-Process を workflow で並列実行
workflowを使って5本並列で実行しましょう。workflowを使えば、foreach -parallelを使って並列にmsdeploy実行できます。
StandatdOutputを、ログに取り込む場合は、workflowの外部で読み取ってください。ファイル読み取りGet-Contentと書き込みOut-Fileが競合することを避けるためです。
workflowを使うことで、10 - 50程度の台数へ一斉配置する際でも大きく効率化されます。
更に高速化することも考えていますが、サクッと並列実行を実装可能な点ではworkflow便利。
# - msdeploy workflow -# workflow Invoke-msdeployParallel{ param( [parameter( position = 0, mandatory)] [string[]] $deploygroups, [parameter( position = 1, mandatory)] [string] $msdeploy, [parameter( position = 2, mandatory)] [string] $zip, [parameter( position = 3, mandatory)] [string] $user, [parameter( position = 4, mandatory)] [string] $Password, [parameter( position = 5, mandatory)] [string] $log, [parameter( position = 6, mandatory)] [string] $logfolder ) foreach -parallel ($deploygroup in $deploygroups) { # setup tmplog $logfolder = $workflow:logfolder $log = $workflow:log $ipstring = "$deploygroup".Replace(".","") $tmplog = Join-Path -Path $logfolder -ChildPath $("tmp" + $ipstring +".log") $tmperrorlog = Join-Path -Path $logfolder -ChildPath $("tmperror" + $ipstring +".log") # define arguments of msdeploy [string[]]$arguments = @( "-verb:sync", "-source:package=$workflow:zip", "-dest:auto,computerName=`"http://$deploygroup/MSDeployAgentService`",userName=$($workflow:user),password=$($workflow:Password),includeAcls=`"False`"", "-disableLink:AppPoolExtension", "-disableLink:ContentExtension", "-disableLink:CertificateExtension", "-setParam:`"IIS Web Application Name`"=`"W3C1hogehoge`"") # Start Process $msdeploy = $workflow:msdeploy Write-Warning -Message "[$(Get-Date)][message][""running msdeploy to $deploygroup""]" "[$(Get-Date)][message][""running msdeploy to $deploygroup""]" | Out-File -FilePath $tmplog -Encoding utf8 -Append Start-Process -FilePath $msdeploy -ArgumentList $arguments -Wait -RedirectStandardOutput $tmplog -RedirectStandardError $tmperrorlog -NoNewWindow } }
あとは、これを呼び出し実行するだけです。
$msdeploy = "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" $user = "配置管理者ユーザー" $Password = "配置管理者パスワード"
古いログを消すにはこうします。
Get-ChildItem -Path $logfolder -Filter "tmp*" | Remove-Item -Force
MSDeployを実行するにはこうします。
Invoke-msdeployParallel -deploygroups $deploygroups -msdeploy $msdeploy -zip $zip -user $user -Password $Password -log $log -logfolder $logfolder
ログを取得するにはこうします。
$result = @() foreach ($deploygroup in $deploygroups) { # setup tmplog $logfolder = $logfolder $log = $log $ipstring = "$deploygroup".Replace(".","") $tmplog = Join-Path -Path $logfolder -ChildPath $("tmp" + $ipstring +".log") $tmperrorlog = Join-Path -Path $logfolder -ChildPath $("tmperror" + $ipstring +".log") $result += "[$((Get-Item $tmplog).LastWriteTime)][message][Result of MSDeploy for {$deploygroup}]" $result += Get-Content -Path $tmplog -Encoding Default -Raw $result += Get-Content -Path $tmperrorlog -Encoding Default -Raw }
まとめ
速度を求める場合は、 PowerShellではなくC#で実行コードを書いて置くべきでしょう。 しかし、PowerShellで記述することで、自動化の一部に容易に組み込めるメリットもあります。 このような外部コマンドとの連携 + 自動化はPowerShellを使っていて頻繁に利用したくなるので、ぜひ参考になれば幸いです。