Azure Automation: Runbook の入力、出力、および Runbook の入れ子について


このポストは、8 月 12 日に投稿した Azure Automation: Runbook Input, Output, and Nested Runbooks の翻訳です。

Microsoft Azure の新しい機能である Azure Automation を使用すると、開発者、運用者、および IT 担当者は Runbook の作成と実行が可能となり、Azure のリソース上の複雑な反復作業を自動化できます。Azure Automation は PowerShell ワークフロー エンジンを使用して Runbook を実行します。つまり、Runbook は PowerShell ワークフローとして作成されるということです。詳しくは Runbook の概念 (英語) の記事を参照してください。

Azure Automation Runbook を作成する際には、システムを最大限に活用するためにベスト プラクティスを参考にしたいと思われるでしょう。Runbook を作成するには、PowerShell、PowerShell ワークフロー、および Azure Automation に関する知識が必要です。結局のところ、PowerShell に精通していれば Azure Automation Runbook の作成は造作なく行えます。

このブログ記事では、高品質な Runbook を作成するのに役立つ基本的な概念を説明します。このシリーズの他のブログ記事では、Runbook の作成および管理に関するその他の便利な面について説明します。この記事で取り上げる概念は以下のとおりです。

  • 入力パラメーターの定義
  • 出力型の定義
  • Runbook 内の子 Runbook の呼び出し

入力パラメーターの定義

ほとんどの Runbook は、実行前に渡される入力データを必要とします。そのため、必要となる入力パラメーターは Runbook のオーサリング中に定義するのが一般的です。入力パラメーターを定義する主な属性は次のとおりです。

  • 名前
  • 型 (.NET の型)
  • 必須かどうか
  • 既定値 (ある場合)

Azure Automation では、Runbook で使用する入力パラメーターでこれらの属性をサポートしています。PowerShell では、入力規則、エイリアス、パラメーター セットなど、入力パラメーターのより多くの属性がサポートされますが、現在 Azure Automation でサポートしているのは上記の項目のみです。

下記は、Runbook で定義されるパラメーターを表すコードの一部ですパラメーターについての詳細な解説は、スクリプト センターの Automation サブ カテゴリにあるパラメーターを使用した Runbook のサンプル (英語) を参照してください。

workflow Add-User
{
    param (
        [Parameter(Mandatory=$true)]
        [PSCredential]
        $Credential,

        [Parameter(Mandatory=$true)]
        [object]
        $FullName,

        [Parameter(Mandatory=$true)]
        [string]
        $Alias,

        [Parameter(Mandatory=$false)]
        [string]
        $Company="Contoso",

        [Parameter(Mandatory=$false)]
        [boolean]
        $HasAdminRights = $false            
    )
    # システムにユーザーを追加する処理を行う
}

Runbook 内で任意の Runbook をインラインで呼び出したい場合には、入力パラメーターに関する以下の点についてご留意ください。

  • 入力パラメーターが単純型または複合オブジェクト ([PSCredential] など) である場合は、単純型の値または複合オブジェクトを、必要に応じて入力値として直接渡せます。
  • [object] 型は、PowerShell ハッシュ テーブルとして渡すことができます。名前/値のペアが、オブジェクトのプロパティになります。次の例では、パラメーター “FullName” がハッシュ テーブルとして渡されています。
$name = @{"FirstName"="Joe";"MiddleName"="Bob";"LastName"="Smith"}
    Add-User -FullName $name -Alias "jsmith" -HasAdminRights $true -Credential $cred

Azure Automation Runbook では、パラメーター型 [switch] が便利なときがありますが、通常は [boolean] 型を使用したほうが結果の予測ができます。どちらの型が使用しやすいかとその理由についての詳細は、こちらのブログ記事 (英語) を参照してください。[switch] ではなく [boolean] を使用するのがベスト プラクティスです。その理由は、PowerShell ワークフローで使用する際にそのほうが複雑にならないためです。

PowerShell ワークフローでは、アクティビティまたは他のワークフロー (Runbook) を呼び出す際、パラメーターが位置ではなく名前で参照される必要があります。ワークフローで他の Runbook、アクティビティ、またはコマンドレットを呼び出す際には、念のため常に名前付きパラメーターを使用するとよいでしょう。

ベスト プラクティス: Runbook での入力パラメーターは、完全に明示的に宣言することをお勧めします。前述の Add-User の例を参考にして、常にパラメーターの型、名前、および必須であるかどうかを宣言に含めるようにします。パラメーターに既定値を設定している場合は、属性が必須に設定されているかどうかにかかわらず、PowerShell ではオプション パラメーターと見なされる点にご注意ください。必須の属性を省略した場合は、パラメーターは既定でオプションになりますが、この属性は常に明示的に宣言するのが最善の方法です。パラメーターの名前には、英字、数字、アンダースコアを使用します (ハイフンを使用すると特別な扱いが必要となるので、これを避けるためハイフンは使用しないでください)。

Azure Automation のポータルから Start Runbook ウィザードの UI を使用して Runbook を開始できるようにする場合は、入力パラメーターに関して以下の点にご留意ください。

  • このウィザードの UI では、数値、文字列、日付/時刻、スイッチ、ブール値、Azure Automation の資格情報アセット名、JSON 配列、または JSON オブジェクトで表わされる値を、Runbook パラメーターに入力できます。
  • Runbook のパラメーターに既定値が設定されている場合、この既定値は UI には表示されませんが、ウィザードでこのパラメーターを空欄のままにして Runbook を実行すると、既定値が使用されます (このような挙動については、今後のリリースでの改善を検討中です)。
  • Runbook のパラメーターが [array] または [object] 型をとる場合は、これを START Runbook ウィザードで JSON 形式 で渡す必要があります。
  • プロパティ バッグで入力される [object] 型のパラメーターは、この UI から JSON 文字列の形式で渡すことができます (例: {"StringParam":"Joe","IntParam":42,"BoolParam":true})。
  • [array] 型のパラメーターは、JSON 文字列の形式で入力できます (例: ["Joe",42,true])。
  • Runbook のパラメーターが PSCredential 型をとる場合は、Azure Automation の資格情報アセットの名前文字列を渡す必要があります。内部的には、その名前を持つ Azure Automation の資格情報アセットが取得され、Runbook に渡されます。

Runbook を PowerShell コンソールまたは Runbook 内から非同期に開始したい場合は、Start-AzureAutomationRunbook コマンドレット (英語) を使用します (このコマンドレットについては、後ほど詳しく説明します)。入力パラメーターについて、次の点にご留意ください (この後のコード例を併せてご覧ください)。

  • Start-AzureAutomationRunbook アクティビティによって開始される Runbook の入力パラメーターは、キー/値のペアとしてハッシュ テーブルに渡されます。キーがパラメーター名で、値はそのパラメーターの値です。
  • Start-AzureAutomationRunbook により開始される Runbook は、数値、文字列、日付/時刻、スイッチ、ブール値、Azure Automation の資格情報アセット名、JSON 配列、または JSON オブジェクトで表わされる入力パラメーターを持ちます。
  • 入力パラメーターが PSCredential 型の場合は、Azure Automation の資格情報アセットの名前文字列を渡す必要があります。内部的には、その名前を持つ資格情報アセットが取得され、Runbook に渡されます。
  • 入力パラメーターがスイッチ型の場合、値がブール値 ($true または $false) のハッシュ テーブルでパラメーターを宣言する必要があります。たとえば、HasAdminRights が [switch] パラメーターだった場合は、この後の例のように宣言します。ブール値のパラメーターの場合も、これと同様に宣言します。
  • 入力パラメーターがプロパティ バッグの場合、呼び出された Runbook で [object] 型に定義し、PowerShell ハッシュ テーブル オブジェクトを入力値として渡します。例として、以下に示す $name パラメーターをご覧ください。
  • 入力パラメーターが複合オブジェクト (例: [System.Diagnostics.Process]) の場合は、呼び出された Runbook で [object] 型に定義し、その複合オブジェクトを入力値として渡します。
    $name = @{"FirstName"="Joe";"MiddleName"="Bob";"LastName"="Smith"}
    $params = @{"Credential"="MyCred";"Alias"="jsmith";"FullName"=$name;"HasAdminRights"=$true}
    $job = Start-AzureAutomationRunbook `
                -Name "Add-User" `
                -Parameters $params `
                -AutomationAccountName $account

複合型を入力パラメーターに割り当てるときに留意すべきことは、Runbook が今後他の Runbook から呼び出されるかどうか、また呼び出される場合は、インライン呼び出しなのかまたは Start-AzureAutomationRunbook を使用して開始されるのかということです。インライン呼び出しのみと考えられる場合は、複合型の入力パラメーターを割り当てることができます。これは、Runbook をインラインで呼び出したときに、その複合型を直接渡すことができるためです。しかし、Runbook が前述の UI を使用して手動で開始される場合、または Start-AzureAutomationRunbook を使用して子 Runbook として開始される場合は、あらゆる複合型の型を [object] に設定する必要があります。これは、Web サービスを介して渡せるのが JSON 配列、または JSON ハッシュ テーブルのみだからです。Runbook の開始方法が明確に決まっていない場合は、複合入力パラメーターの型を [object] に設定するのがベスト プラクティスです。

出力型の定義

PowerShell は、機能およびコマンドレットで OutputType 属性 (英語) を使用した出力型の定義を長い間サポートしてきました。この属性は、実行時には影響を及ぼしませんが、設計時にオブジェクトの型を把握するツールとして提供されてきました。オブジェクトの型は、コマンドレットの実行を必要とせずに出力されます。

OutputType は PowerShell ワークフローで使用できます。出力のある Runbook には OutputType を含めるとよいでしょう。Runbook の最上部付近の、どのパラメーター宣言よりも上に OutputType 属性を配置します。Runbook での OutputType の使用例を次に示します。

workflow Get-UserNames
{
    [OutputType([string])]

    $names = @()
    # ここで名前を取得する処理を行う

    Write-Output $names
}

Azure Automation が進化し、Runbook 作成用のツールセットが拡張、強化されるのに伴い、コマンドレットおよび Runbook に OutputType 定義を含め、コントラクトを忠実に実行することが重要となってきています。

ベスト プラクティス: 出力のあるコマンドレットおよび Runbook は、常に OutputType を含むようにします。

Runbook 内の他の Runbook の呼び出し

あらゆる種類のコードを作成する場合に通じるベスト プラクティスの 1 つは、モジュラー化 (分離した、再利用可能なコード単位の作成) です。Azure Automation においては、コマンドレットおよび Runbook 内に自己完結型のタスクを配置し、これらのコマンドレットおよび Runbook を、その機能を必要とする Runbook 内で呼び出すということになります。したがって、親 Runbook で実行中のプロセスの一部として 1 つまたは複数の子 Runbook を呼び出すことは一般的な手法です。

Azure Automation で子 Runbook を呼び出す方法には、次の 2 つがあります。

なお、親 Runbook から呼び出される子 Runbook を表すために「入れ子」または「子」という一般的な言葉を使用しています。PowerShell では、同期的に開始、実行される操作を「呼び出し」という言葉で表します。また、非同期に開始、実行される操作については「開始」という言葉を使用しています。この先例に従い、同期的または非同期的な子 Runbook に対して、「呼び出し」および「開始」という言葉を使い分けます。

Runbook のインライン呼び出し

インラインで呼び出された Runbook は、親 Runbook と同一のジョブ内で動作します。つまり、親ワークフローは、子 Runbook が終了するのを待ってから (同期的に) 次の処理を続けます。また、子 Runbook によってスローされたあらゆる例外、および子 Runbook により生成されたすべてのストリーム出力は、親のジョブと関連付けられます。そのため、子 Runbook の実行と出力の追跡は、すべて親 Runbook に関連づけられたジョブを通して行われます。

インラインで呼び出される子 Runbook を持つ Runbook を開始するとき、その子 (およびすべての子孫の) Runbook は、実行開始前に親 Runbook にコンパイルされます。このコンパイル段階の間、親 Runbook は子 Runbook の名前について構文解析されます。この解析はすべての子孫 Runbook で再帰的に行われます。Runbook の完全なリストが取得されると、これらの Runbook 用のスクリプトは Storage から取得され、PowerShell ワークフロー エンジンに渡される単一のファイルにまとめられます。以上の理由から、Runbook のジョブが送信されるときには、親および子孫の Runbook は既に発行されている必要があります。発行されていない場合はコンパイル中に例外が発生します。現在のところ、発行の順番も重要です。まず子 Runbook を発行してから親を発行する必要があります。同様に、Azure Automation のオーサリング ページで親 Runbook のテストを行うときは、子 Runbook を先に発行してから親のテストを行う必要があります。もう 1 つの重要な点は、インラインで呼び出される子 Runbook の名前を渡すために変数を使用できないということです。つまり、親 Runbook 内では、子 Runbook を常に明示的に指定する必要があります。

次に示すのは、2 つの子 Runbook をインラインで呼び出す Runbook の例です。片方の子 Runbook は出力を返し、他方は出力を返しません。

workflow Process-VMs
{
    param (
        [Parameter(Mandatory=$true)]
        [string]
        $ScaleUnit
    )

    # オブジェクトを返す子 Runbook の呼び出し
    $vms = Get-VMs -scaleunit $ScaleUnit

    # オブジェクトを返さない子 Runbook の呼び出し
    Do-StuffToVMs -vm $vms
}

Start-AzureAutomationRunbook コマンドレットによる Runbook の開始

Start-AzureAutomationRunbook コマンドレット (英語) を使用して、別々のジョブで Runbook を開始できます。このコマンドレットは、Azure PowerShell モジュールの Azure Automation コマンドレット (英語) の 1 つで、Azure Automation にあらかじめインポートされています。Start-AzureAutomationRunbook を使用して子 Runbook を開始すると、この Runbook 用の新しいジョブが作成されます。

この方法で子 Runbook を開始すると、親 Runbook は子 Runbook が完了するのを待たずに (非同期的に) 処理を続けます。この方法は、親 Runbook から処理を分離して、その処理については関知しない場合に便利です。ただし、これはシステムでより多くのジョブを作成するとう代償を伴います。ジョブが多いということは、トラブルシューティングがより複雑になるということです。子 Runbook から出力を戻す必要がある場合には、さらに複雑なことになります。

この後に示すのは、Start-ChildRunbook (英語) という Runbook のコードです。これは、Start-AzureAutomationRunbook を使用して子 Runbook を開始します。また、ジョブが完了するのを待つかどうか、出力を取得するかどうかのオプションが用意されています。この Runbook は、Get-AzureAutomationJob (英語) および Get-AzureAutomationJobOutput (英語) といった他の Azure Automation コマンドレットも使用します。子 Runbook を開始する処理と、何らかの出力を返す処理のすべてをカプセル化しているので、ご利用の Runbook で子 Runbook を開始したい場合に便利に利用していただけます。この便利な Runbook (英語) は、スクリプト センターからダウンロードできます。お客様の Azure Automation アカウントにインポートして、幅広くご活用ください。

workflow Start-ChildRunbook
{
    [OutputType([object])]

    param (
        [Parameter(Mandatory=$true)]
        [string]
        $ChildRunbookName,

        [Parameter(Mandatory=$false)]
        [hashtable]
        $ChildRunbookInputParams,

        [Parameter(Mandatory=$true)]
        [string]
        $AzureConnectionName,

        [Parameter(Mandatory=$true)]
        [string]
        $AutomationAccountName,

        [Parameter(Mandatory=$false)]
        [boolean]
        $WaitForJobCompletion = $false,

        [Parameter(Mandatory=$false)]
        [boolean]
        $ReturnJobOutput = $false,

        [Parameter(Mandatory=$false)]
        [int]
        $JobPollingIntervalInSeconds = 10,

        [Parameter(Mandatory=$false)]
        [int]
        $JobPollingTimeoutInSeconds = 600
    )

   # パラメーターの値が適切であるかどうかを判定
   if(!$WaitForJobCompletion -and $ReturnJobOutput) {
       $msg = "The parameters WaitForJobCompletion and ReturnJobOutput must both "
       $msg += "be true if you want job output returned."
       throw ($msg)
   }

    # この Runbook で Azure コマンドレットを呼び出せるように Azure に接続する
    Connect-Azure -AzureConnectionName $AzureConnectionName

    InlineScript {
        # ワークフロー スコープのパラメーターをインライン スクリプト スコープに変換する
        $AutomationAccountName = $using:AutomationAccountName
        $AzureConnectionName = $using:AzureConnectionName
        $ChildRunbookInputParams = $using:ChildRunbookInputParams
        $ChildRunbookName = $using:ChildRunbookName
        $JobPollingIntervalInSeconds = $using:JobPollingIntervalInSeconds
        $JobPollingTimeoutInSeconds = $using:JobPollingTimeoutInSeconds
        $ReturnJobOutput = $using:ReturnJobOutput
        $WaitForJobCompletion = $using:WaitForJobCompletion

        # 処理の対象となる Azure サブスクリプションを指定する
        Select-AzureSubscription -SubscriptionName $AzureConnectionName

        if ($ChildRunbookInputParams -eq $null) { $ChildRunbookInputParams = @{} }

        # 子 Runbook を開始し、ジョブを取得
        $job = Start-AzureAutomationRunbook `
                    -Name $ChildRunbookName `
                    -Parameters $ChildRunbookInputParams `
                    -AutomationAccountName $AutomationAccountName `
                    -ErrorAction "Stop"

        # ジョブが存在するかどうか、ジョブの出力が必要かどうかを判定
        if ($job -eq $null) {
            # ジョブが作成されていない場合は例外をスローする
            throw ("No job was created for runbook: $ChildRunbookName.")
        }
        else {
            # ジョブが存在する

            # 開始された Runbook のジョブ ID を、追跡できるように記録する
            Write-Verbose "Started runbook: $ChildRunbookName. Job Id: $job.Id"

            if ($WaitForJobCompletion -eq $false) {
                # ジョブが完了するのを待たずに、すぐにジョブ ID を返す
                Write-Output $job.Id
            }
            elseif ($WaitForJobCompletion -eq $true) {
                # ジョブが完了するか、タイムアウトの制限に達するまで監視
                $maxDate = (Get-Date).AddSeconds($JobPollingTimeoutInSeconds)

                $doLoop = $true

                while($doLoop) {
                    Start-Sleep -s $JobPollingIntervalInSeconds

                    $job = Get-AzureAutomationJob `
                        -Id $job.Id `
                        -AutomationAccountName $AutomationAccountName

                    $status = $job.Status

                    $noTimeout = ($maxDate -ge (Get-Date))

                    if ($noTimeout -eq $false) {
                        $msg = "The job for runbook $ChildRunbookName did not "
                        $msg += "complete within the timeout limit of "
                        $msg += "$JobPollingTimeoutInSeconds seconds, so polling "
                        $msg += "for job completion was halted. The job will "
                        $msg += "continue running, but no job output will be returned."
                        throw ($msg)
                    }

                    $doLoop = (($status -ne "Completed") `
                              -and ($status -ne "Failed") `
                              -and ($status -ne "Suspended") `
                              -and ($status -ne "Stopped") `
                              -and $noTimeout)
                }

                if ($job.Status -eq "Completed") {
                    if ($ReturnJobOutput) {
                        # ジョブから出力を取得
                        $jobout = Get-AzureAutomationJobOutput `
                            -Id $job.Id `
                            -Stream Output `
                            -AutomationAccountName $AutomationAccountName

                        # Return the output string
                        Write-Output $jobout.Text
                    }
                    else {
                        # Return the job id
                        Write-Output $job.Id
                    }
                }
                else {
                    # The job did not complete successfully, so throw an exception
                    $msg = "The child runbook job did not complete successfully."
                    $msg += "  Job Status: $job.Status.  Runbook: $ChildRunbookName."
                    $msg += "  Job Id: $job.Id.  Job Exception: $job.Exception"
                    throw ($msg)
                }
            }
        }
    }
}

Start-AzureAutomationRunbook を使用して子 Runbook を開始すると、戻り値はジョブ オブジェクトになります。子 Runbook からデータを返したい場合は、ジョブを監視していつジョブが完了したのかを把握したうえで出力を抽出します。上記の Runbook の例でご覧いただけるように、Get-AzureAutomationJob および Get-AzureAutomationJobOutput コマンドレットを使用してジョブを監視して、子 Runbook ジョブから出力を取得します。なお、ジョブが想定した時間内に終了しない場合にループから抜けられるように、タイムアウト要素が含めてあります。また、数個の子ジョブを開始した場合に、システムはジョブが開始される順番を保証しないことにご注意ください。

インラインで呼び出した場合と、Start-AzureAutomationRunbook を使用して開始した場合の子 Runbook の比較

インライン呼び出し

  • メリット
    • 親および子 Runbook は同じジョブ内で実行されるのでシステム内のジョブ数は比較的少数であり、そのためジョブの追跡が容易です。
    • 親 Runbook は子 Runbook が終了するのを待ってから処理を続けます。また、親 Runbook は子 Runbook から返されるあらゆるデータを直接取得できます。
    • 子 Runbook からスローされた例外、および子 Runbook により生成されたストリーム出力は、親のジョブに関連付けられます。このため、調査対象となるジョブが 1 つだけなので、トラブルシューティングが容易です。
    • Runbook 入力パラメーターは、プリミティブ型から複合型まであらゆる型が使用できます。
    • 子 Runbook は親 Runbook と同じジョブで実行されているため、料金を課されるのは 1 つのジョブの実行時間だけです。
  • デメリット
    • 親 Runbook は子 Runbook の完了を待つ必要があるため、親 Runbook が処理を完了するまでにかかる全体の時間が増加することがあります。
    • 親 Runbook 内でインラインの子 Runbook の名前を渡すために、変数またはパラメーターを使用することができません。
    • 親 Runbook の発行前に、子 Runbook を発行する必要があります。
    • 親子両方の Runbook が、同一の Automation アカウントにある必要があります。

Start-AzureAutomationRunbook による開始

  • メリット
    • 親と子の Runbook が異なるジョブで実行されるため、親は複数のジョブを分離して並列して実行できます。
    • 親 Runbook は子 Runbook を待たないため、子 Runbook の実行中も処理を継続できます。
    • 親 Runbook でパラメーターまたは変数を使用して、子 Runbook の名前を渡して呼び出すことができます。
    • 同じサブスクリプションの異なる Automation アカウントに格納された Runbook を開始できます。また、Azure サブスクリプションが異なる場合でも、対象の Azure サブスクリプションへの Automation の接続アセットを指定すれば開始できます。
  • デメリット
    • 親および子の Runbook が異なるジョブで実行され、例外およびストリーム出力が別々に格納されるため、それぞれを追跡する必要があります。このためトラブルシューティングが難しくなる場合があります。
    • システムでより多くのジョブが作成されるため、ジョブが開始される前にキューで待機する時間が長くなる可能性があります。
    • 子 Runbook ジョブが返すデータの取得が簡単ではありません。
    • Start-AzureAutomationRunbook が Runbook を開始し、Runbook が出力の取得を完了するのを待つ場合、両方の Runbook の実行時間に対し課金されます。ただしこれは、[Start] を使用した場合には当てはまりません。
    • Start-AzureAutomationRunbook で Runbook を実行すると、すぐにジョブ ID を返します。
    • Runbook 入力パラメーターは、プリミティブ型、配列、およびオブジェクトのみ使用できます。これは、Web サービスを介した呼び出しによって、パラメーターがオブジェクトの JSON シリアル化を経る必要があるためです。

まとめ

Azure Automation は便利なツールです。これを使用すると、PowerShell ワークフロー エンジンが備える数多くの有益な機能を活用できる Runbook を作成することができます。Azure Automation および PowerShell ワークフローの内部的な働きをある程度理解し、ベスト プラクティスを学ぶことにより、高品質で信頼性が高く、保守性に優れた Runbook の作成が可能となります。

この記事では、入力パラメーターの定義、Runbook の出力の定義、そして子 Runbook の呼び出しに関するベスト プラクティスについて説明しました。この情報を最後までお読みになったところで、もう少しお時間をいただき、ぜひ Azure Automation のサンプル Runbook をお試しになってください。Runbook および Automation の機能について実感いただけると思います。お読みいただきありがとうございました。今回ご紹介した新しい知識を実践で活用していただくことで、お客様の Azure リソースの管理をよりリンプルに、予測可能に、そして信頼性のあるものにするお手伝いができれば幸いです。

Azure Automation をまだご利用でない方は、プレビュー版にサインアップしお試しください。また入門ガイドをご覧ください。

Comments (0)

Skip to main content