ASP.NET MVC アプリケーションの WebDeploy パッケージを Azure Web Apps にデプロイする際に、Web.config に埋め込まれた Storage などの接続文字列を書き換えるために zip ファイルを展開する作業が非常に手間でした。
調べてみると、デプロイする際に Parameters.xml で Web.config の値を書き換えられることを知りました。この方法なら、zip ファイルを展開する必要はないですし、本番環境で使う接続文字列をソースコード管理に入れることなく、安心して開発できます。
前準備
ASP.NET MVC アプリケーションの Web.config に、Azure Storage の接続文字列を定義します。
#Web.conifg <configuration> <appSettings> <add key="MyStorage" value="UseDevelopmentStorage=true"/> </appSettings> </configuration>
プロジェクトの直下に Parameters.xml を追加し、書き換えルールを定義します。
#Parameters.xml <?xml version="1.0" encoding="utf-8" ?> <parameters> <parameter name="appSettings_MyStorage" description="Azure Storage の接続文字列"> <parameterEntry kind="XmlFile" defaultValue="UseDevelopmentStorage=true" scope="\\Web.config$" match="//appSettings/add[@key='MyStorage']/@value" /> </parameter> </parameters>
Visual Studio から WebDeploy パッケージを作成すると、WebApplication1.SetParameters.xml に書き換えの設定が追加されていることが分かります。
#WebApplication1.SetParameters.xml <?xml version="1.0" encoding="utf-8"?> <parameters> <setParameter name="IIS Web Application Name" value="Default Web Site/WebApplication1_deploy" /> <setParameter name="appSettings_MyStorage" value="UseDevelopmentStorage=true" /> </parameters>
この value の値を Azure Storage の接続文字列に変更すれば、Web.config を書き換えてデプロイすることができます。
コマンドからデプロイする
WebDeploy のコマンドを使って、デプロイします。「-setParamFile」の引数に SetParameters.xml のパスを渡します。ここでは、Azure Web Apps を 「deploytest」という名前で作成して、デプロイします。ユーザー名やパスワードは、Azure ポータルからダウンロードできる Publish プロファイルに記載されています。
#msdeploy.exe "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" -verb:sync -source:package="C:\Users\xxx\Desktop\Test\WebApplication1.zip" -dest:auto,ComputerName="https://deploytest.scm.azurewebsites.net:443/msdeploy.axd?site=deploytest",UserName='$deploytest',Password='xxx',AuthType='Basic',IncludeAcls='true' -enableRule:DoNotDeleteRule -setParam:"IIS Web Application Name"='deploytest' -setParamFile:"C:\Users\xxx\Desktop\Test\WebApplication1.SetParameters.xml"
C# のコードからデプロイする
コマンドはどうも苦手なので、やっぱり C# でコードを書いてデプロイしたいです。以前に帝国兵さんが書かれた記事とほぼ同じですが、SetParameters.xml を渡せるように改良しました。
コンソールアプリケーションなどを作成し、WebDeploy に必要なライブラリを NuGet からインストールしておきます。
引数には、Publish プロファイル、WebDeploy パッケージ、SetParameters.xml のパスを渡します。SetParameters.xml から読み取った値を DeploymentObject のパラメーターにセットしている部分がポイントです。
#WebAppsPublisherHelpler.cs public static class WebAppsPublisherHelpler { public static DeploymentChangeSummary Publish(string publishSettingsPath, string sourcePath, string parametersPath) { if (String.IsNullOrEmpty(publishSettingsPath)) throw new ArgumentNullException("publishSettingsPath"); if (String.IsNullOrEmpty(sourcePath)) throw new ArgumentNullException("sourcePath"); if (!File.Exists(publishSettingsPath)) throw new Exception(String.Format("{0}: Not found.", publishSettingsPath)); if (!File.Exists(sourcePath)) throw new Exception(String.Format("{0}: Not found.", sourcePath)); if (Path.GetExtension(sourcePath).Equals(".zip", StringComparison.InvariantCultureIgnoreCase) == false) throw new Exception("Extension supports only zip."); // PublishSettings var document = XElement.Load(publishSettingsPath); var profile = document.XPathSelectElement("//publishProfile[@publishMethod='MSDeploy']"); if (profile == null) { throw new Exception(String.Format("{0}: Not a valid publishing profile.", publishSettingsPath)); } var publishUrl = profile.SafeGetAttribute("publishUrl"); var userName = profile.SafeGetAttribute("userName"); var password = profile.SafeGetAttribute("userPWD"); var siteName = profile.SafeGetAttribute("msdeploySite"); var webDeployServer = string.Format(@"https://{0}/msdeploy.axd?site={1}", publishUrl, siteName); // Set up deployment var destinationOptions = new DeploymentBaseOptions{ ComputerName = webDeployServer, UserName = userName, Password = password, AuthenticationType = "basic", IncludeAcls = true, TraceLevel = TraceLevel.Info }; destinationOptions.Trace += (sender, e) => { Trace.TraceInformation(e.Message); }; var syncOptions = new DeploymentSyncOptions { DoNotDelete = true }; // Please change as you want DeploymentChangeSummary result; try { // Start deployment using (var deploy = DeploymentManager.CreateObject(DeploymentWellKnownProvider.Package, sourcePath, new DeploymentBaseOptions())) { // Apply package parameters foreach (var p in deploy.SyncParameters) { switch (p.Name) { case "IIS Web Application Name": p.Value = siteName; break; default: // SetParameters.xml if (!String.IsNullOrEmpty(parametersPath)) { var parameters = XElement.Load(parametersPath); var setParameter = parameters.XPathSelectElement(String.Format("//setParameter[@name='{0}']", p.Name)); if (setParameter != null) { p.Value = setParameter.SafeGetAttribute("value"); } } break; } } result = deploy.SyncTo(DeploymentWellKnownProvider.Auto, siteName, destinationOptions, syncOptions); } } catch (Exception) { throw; } return result; } } public static class MethodExtention { public static string SafeGetAttribute(this XElement node, string attribute, string defaultValue = null) { var attr = node.Attribute(attribute); return attr == null ? defaultValue : attr.Value; } }
まとめ
Cloud Services では、構成ファイル(cscfg)に Storage の接続文字列を設定してデプロイできますが、Web Apps でも Parameters.xml を使うと同じようにデプロイできます。業務系の Web アプリケーションであっても、Web Apps ファーストで積極的に使っていきたいと思います。