このページの本文へ

FIXER Tech Blog - Power Platform

Logic Appsをまとめて修正するならjqコマンドが一番便利

2025年01月10日 10時00分更新

文● 小野寺春樹/FIXER

  • この記事をはてなブックマークに追加
  • 本文印刷

 本記事はFIXERが提供する「cloud.config Tech Blog」に掲載された「Azure Logic Apps をまとめて修正するならjqコマンドが1番」を再編集したものです

はじめに

 この記事は、FIXER Rookies Advent Calendar 2024の13日目の記事です。

 さて、皆さんはたくさん Logic Apps を持っていますか?持っていますよね。

 Logic Appsと言えばノーコード・ローコードで書ける、謂わばScratch感覚(当社比)で作れちゃう代物です。

 このLogic Appsの設定を変更するときもボタンをポチポチしてあげると簡単にできますが、これが何十個もLogic Appsがあると考えたらさあ大変。設定変更だけで日が暮れます。しかしこのLogic Appsには実は裏技があり、コード自体はJSON形式で構成されているためこのJSONさえ取得できれば容易に自動化できます。

 そこで登場するのがjqコマンドです。今回はこのjqコマンドを使って同じ設定を使用している大量のLogic Appsを処理するスクリプトを組んでいきます。

動作環境

・PowerShell 7
・Azure CLI 2.66.0
・VS Code

jqコマンドって?

 まずは、jqコマンドについて簡単に説明したいと思います。まずはGaiXerに聞いてみましょう。

 分かりやすく完結に、そして例文まで出してくれるGaiXer君大好きです。

 はい、jqコマンドとは、JSONデータを効率的に処理できる便利なコマンドです。フィルター条件を設定すれば複雑な構造や深いネストも処理することができるためJSONを扱うときは重宝します。

スクリプトを組んでみた

 早速、このjqコマンドを使ってLogic Appsを書き換えるスクリプトを組んでいきましょう。

 そして追加で用意するのはこちら

・az logic workflow list
・az logic workflow show
・az logic workflow update

 全体の処理の流れとしては、listでLogic Appsの名前を一覧取得、listで各コードを取得、jqコマンドで設定値を変更、最後にupdateでコードを更新させるという感じです。

 では、実際にコードに書き起こしてみましょう。

# === 設定セクション ===

# テナント ID を設定(必要な場合)
$tenantId = "テナントID "

# サブスクリプション ID を設定
$subscriptionId = "サブスクリプションID"

# 置換するパスの指定
$before = "変更前の設定値"
$after = "変更後の設定値"

# 一時ファイルを保存するディレクトリ
$tempDir = "C:\Temp"

# === スクリプト開始 ===


# Azure にログイン
az login --tenant $tenantId --output none
az account set --subscription $subscriptionId
Write-Host "Azure へのログインが完了しました。"

# Logic App の一覧を取得
    # すべてのリソース グループを対象
    $logicApps = az logic workflow list --subscription $subscriptionId --query "[].{name:name, resourceGroup:resourceGroup}" -o tsv

# 各 Logic App を処理
$logicAppsLines = $logicApps -split "`n"
Write-Host "処理対象の Logic App 数: $($logicAppsLines.Count)"

#jq フィルターを保存(一度だけ保存)
$jqFilterContent = @'
def replace_paths:
  walk(
    if type == "object" and has("inputs") then
      if .inputs.path then
        .inputs.path |= sub($before; $after)
      elif .inputs.uri and .inputs.uri.path then
        .inputs.uri.path |= sub($before; $after)
      else
        .
      end
    else
      .
    end
  );
replace_paths
'@

 $jqFilterFile = Join-Path $tempDir "jqFilter.jq"
 if (-not (Test-Path $jqFilterFile)) {
     Set-Content -Path $jqFilterFile -Value $jqFilterContent -Encoding UTF8
 }

foreach ($line in $logicAppsLines) {
    if ([string]::IsNullOrWhiteSpace($line)) {
        continue
    }

    # タブで分割
    $fields = $line -split "`t"
    $logicAppName = $fields[0]
    $resourceGroupName = $fields[1]
        
    # Logic App の定義を取得   
    $definition = az logic workflow show --name "$logicAppName" --resource-group "$resourceGroupName" -o json

    # 定義をファイルに保存(変更前)
    $definitionFileBefore = Join-Path $tempDir "definition_before_$logicAppName.json"
    Write-Host "変更前の定義をファイルに保存しています: $definitionFileBefore"
    Set-Content -Path $definitionFileBefore -Value $definition -Encoding UTF8


    # jq エラー出力をキャプチャ
    $jqErrorFile = Join-Path $tempDir "jqError_$logicAppName.txt"

    # jq を実行し、出力を一時ファイルに保存
    $tempOutputFile = Join-Path $tempDir "temp_output_$logicAppName.json"
    & jq --arg oldPath "$before" --arg newPath "$after" -f $jqFilterFile $definitionFileBefore 2> $jqErrorFile > $tempOutputFile

    # エラーメッセージを読み込み
    $jqError = Get-Content $jqErrorFile -ErrorAction SilentlyContinue -Encoding UTF8
    Remove-Item $jqErrorFile -ErrorAction SilentlyContinue

    if (-not (Test-Path $tempOutputFile) -or $jqError) {
        Write-Host "パスの置換に失敗しました。エラーメッセージ:"
        Write-Host $jqError
        Write-Host "スキップします。"
        continue
    }

    # 置換後の定義を読み込み
    $updatedDefinition = Get-Content -Path $tempOutputFile -Encoding UTF8 -Raw

    # 更新された定義をファイルに保存(変更後)
    $updatedDefinitionFile = Join-Path $tempDir "definition_after_$logicAppName.json"
    Write-Host "変更後の定義をファイルに保存しています: $updatedDefinitionFile"
    Set-Content -Path $updatedDefinitionFile -Value $updatedDefinition -Encoding UTF8

    # 一時ファイルを削除
    Remove-Item $tempOutputFile -ErrorAction SilentlyContinue

    # Logic App の定義を更新
    az logic workflow update --name "$logicAppName" --resource-group "$resourceGroupName" --definition "@$updatedDefinitionFile" | Out-Null

    Write-Host "Logic App '$logicAppName' の処理が完了しました。"
}

 デバッグも含めてつらつらとコードを書きましたが、今回の処理の肝はここです。

# jq フィルターを保存(一度だけ保存)
$jqFilterContent = @'
def replace_paths:
  walk(
    if type == "object" and has("inputs") then
      if .inputs.path then
        .inputs.path |= sub($before; $after)
      elif .inputs.uri and .inputs.uri.path then
        .inputs.uri.path |= sub($before; $after)
      else
        .
      end
    else
      .
    end
  );
replace_paths
'@

 jqコマンドで変換するときにはフィルター条件が大事になりますが、これがその処理になります。

 上記の関数の流れとしては、$beforeに修正前の設定値、$afterに修正後の設定値で、JSONのオブジェクトと$beforeの値が一致すれば$afterの値に置き換わるというものです。

 また、Logic Appsの処理はinputsオブジェクトの中に書かれているので、inputsオブジェクトの中にあるものだけについて探索させるようにしています。

 そしてそのファイルを使ってjqコマンドを実行します。

jq --arg oldPath "$before" --arg newPath "$after" -f $jqFilterFile $definitionFileBefore 2> $jqErrorFile > $tempOutputFile


 $jqFilterFileがフィルター条件が格納されているファイルのパス、$definitionFileBeforeが修正前のLogic Appsが格納されているファイルのパスです。

 そういえば、処理の流れとしてフィルターと修正前jsonを一度ファイルに保存しているかというと、jqコマンドはフィルター条件を直接入れるとバグることがあり、その対策と処理の前後との比較がしやすいためです。切り戻しも修正前のコードを送信するだけで済むので楽です。
ついでに下に実行した結果も載せます。

修正前

修正後

 このように変更したい場所が変更出来ています。このコードはGaiXerに作成してもらいました。GaiXer大好き。

1つ、注意点があります。

 このスクリプトは注意点があり、JSONを保存するときは絶対utf-8エンコーディングを指定してください。


Set-Content -Path $definitionFileBefore -Value $definition -Encoding UTF8
 

 指定しないと、修正後のJSONに文字化けが発生します。

 なぜ、文字化けが発生するかというと、jqコマンドはutf-8で出力するのに対して、PowerShellは、デフォルトの文字コードはShift-JISなのでそこの相違で文字化けが発生します。

 しかし文字コードの指定をしないで保存した修正前のファイルを開くと、ちゃんとutf-8として表示されているんですよね(理由は分からなかったです)。

 おそらくVS Code側の認識の問題か、PowerShellの自動判断に誤りがあったと思われます。

 ことりあえずこの文字化けのせいで、私は延々と悩んでいました。
ということで、今回はLogic Appsの修正を一気に行う方法について簡単に解説しました。

 PowerShellでもJSONを扱うコマンド(ConvertFrom-Json etc..)がありますが、複雑な処理や大量のjsonを扱いたいときはjqコマンドの方が良いかなと個人的に思っています。皆もLogic Appsとjsonでもっと遊ぼう!

小野寺 春樹
2024卒の小野寺春樹です。 仙台高専出身で、高専ではC++をよく使いました。 趣味はゲーム

カテゴリートップへ

この連載の記事