Windows Azure

概要

スタートアップスクリプトはたいていの場合、ServiceDefinition.csdefで定義されます。
もしIISの設定を変更する必要があるなら、ロールのOnStartメソッドからスクリプトを実行する必要があります。この投稿は、configuration-driven定義を使用して設定するためのいくつかのサンプルを提供します。

本題

Windows Azureクラウドサービスで、ServiceDefinition.csdefで<Startup>エレメントを使用してロールスタート時に実行すべきスクリプトを指定します。
しかし、IISの設定に対して変更したりクエリを発行する場合には使用することができません。

Kevin Williamsが投稿したブログWindows Azure Roleアーキテクチャ)で説明されています。
スタートアップタスクが完了した後(Step7)まで、IISConfiguratorはIISウェブサイトを作成(Step8)しません。
IISに対して何か設定したくても、ServiceDefinition.csdefでは定義を設定することができません。IISConfiguratorが動作した後で処理する必要があります。

ロールのOnStartメソッド(Step9がトリガーで動作します)で処理します。通常、Process.Startコールを経由して処理します。

OnStartメソッドからプロセスとスクリプトを開始する定義を使用した単純なソリューションを書きました。フルコードは、ここからダウンロードできます。一部を抜粋して説明します。

このソリューションでは、ServiceConfiguration.cscfgで設定の定義をするためにスクリプトを指定しています。

<ConfigurationSettings>
  <Setting name="RoleStartupScripts" value="startup\test1.cmd!;startup\test2.cmd;Powershell.exe startup\test3.ps1" />
    ....
</ConfigurationSettings>

この例では、一つ目のファイル名の後ろに「!」マークを書いています。

これは次のスクリプトを実行する前に、一つ目のファイルのプロセスが完全に終了するのを待つ必要があることを定義しています。「!」マークが無い場合は、処理が完了するのを待たずに次のスクリプトを実行することができます。

これらのスクリプトは権限の昇格が必要ありません。権限の昇格が必要な場合、ServiceDefinition.csdefファイルで<Runtime executionContext="elevated" />を設定します。

次に、プロセスを自動実行させるためにRoleEntriPointを拡張する抽象クラスStartupScriptEntryPointクラスを書きました。

public abstract class StartupScriptRoleEntryPoint : RoleEntryPoint
{
    public override bool OnStart()
    {
        var startupScripts = CloudConfigurationManager.GetSetting("RoleStartupScripts");
        if (!String.IsNullOrEmpty(startupScripts))
        {
            var scriptList = startupScripts.Split(';');
            foreach (var script in scriptList)
            {
                bool waitForExit = false;

                string scriptCommandLine = script;
                if (script.EndsWith("!"))
                {
                    scriptCommandLine = script.Substring(0, script.Length - 1);
                    waitForExit = true;
                }

                var args = CmdLineToArgvW.SplitArgs(scriptCommandLine);

                var processStartInfo = new ProcessStartInfo()
                {
                    FileName = args[0],
                    Arguments = string.Join(" ", args.Skip(1).ToArray()),
                    WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory,
                    UseShellExecute = true,
                };
                var process = Process.Start(processStartInfo);
                if (waitForExit)
                {
                    process.WaitForExit();
                }
            }
        }
        return base.OnStart();
    }
}

デフォルトのRoleEntryPointの代わりにStartupScriptRoleEntryPointからWebRoleクラスを更新します。ほかにはコードの変更をする必要はありません。

public class WebRole : StartupScriptRoleEntryPoint
{
    public override bool OnStart()
    {
        // For information on handling configuration changes
        // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.

        return base.OnStart();
    }
}

意訳元のBlog

Running scripts from a Windows Azure role’s OnStart method