【Azure】【CLI版】まだWindows BATにパスワードを埋め込んでるの? Azure Logic Apps+Key Vaultで固定変数を外部化する方法

Azure

今回はAzureに関する記事です。
Azure のKey Vaultへ格納した変数を、インターネット経由でLogic Appsへアクセスし、
自宅や会社等、特定の場所から安全に参照する構成を解説していきます。

最初に、本記事で扱う用語は公式やAzureポータル上などで表記揺れがあるため
以下の太字部分で統一します。

  • Azure Key Vault:初出は正式名称、以降は「Key Vault」
  • Azure Logic Apps:初出は正式名称、以降は「Logic Apps」
  • キー コンテナー:Azure Portalの操作手順で、画面上の項目を指す場合だけ使用
  • ロジック アプリ:同じく画面上の項目を指す場合だけ使用

【Azure】まだWindows BATにパスワードを埋め込んでるの? Azure Logic Apps+Key Vaultで固定変数を外部化する方法

本記事のターゲット

以下に該当する方へおすすめするべく、本記事を執筆しています⇩

  • 使用しているツールにパスワード直書きなどしているものを見直したい
  • 複数のツールで毎回同じ固定変数を利用しており一元管理したい
  • 複数PCなどに同じツールを展開して、変数管理も一元化したい
  • 格納した変数を取り出す際に、Azure CLIなど専用のツールを利用したくない
  • 指定したネットワークからのみ取得できる固定変数置き場を作成したい
  • 専用ソフトやDBを導入せず、上記をフラットな感覚で実現したい

本構成は、送信元のグローバルIPアドレスを特定できることが前提です。
IPアドレスが変更された場合は、Logic Apps側の許可設定も更新する必要があります。

なお筆者の利用しているSoftBank光では、
グローバルIPが数年単位で変わらないこともあるため、今回は手動管理としています。

また本記事では、パスワードやAPIキー、各種設定値など、
プログラムから参照する比較的変更頻度の低い値を便宜上「固定変数」と表現しています。

全体概要図

自宅PCからLogicAppsを参照すると、引数で与えた文字列をキーにして、
LogicAppsがKeyVaultへ問い合わせをします。
戻り値としてKeyVaultへ格納されているSecretsが応答されます。

Cloud Shellで構築するための準備

今回はAzure Cloud Shellの Bash を使用します。対象サブスクリプションに「所有者」相当の権限があるユーザーで、Azureポータルへログインしてください。

リソース名はGUI版と同じものを使用します。

項目設定値
リソースグループAddache-Test
リージョンjapaneast(Japan East)
Key Vaultkey-testsecrets
Logic AppsLap-TestSecrets
Logic AppsのプランConsumption(従量課金)
Key Vaultの価格レベルStandard(標準)
実行履歴の保存期間7日

同じサブスクリプション・リソースグループに同名のリソースがある場合は、
そのリソースを更新します。

なお、Key VaultのStandardとLogic AppsのStandardは別の設定です。
今回は、Logic Appsを Consumption(従量課金) で作成します。

構築用ファイルを取得する

Cloud Shellを開き、Bashへ切り替えてから以下を実行します。

mkdir -p ~/azure-logicapps-keyvault-02
cd ~/azure-logicapps-keyvault-02
umask 077

BASE_URL='https://raw.githubusercontent.com/addache/addache-blog-assets/main/azure-logicapps-keyvault-02'

curl -fSLo setup.sh "$BASE_URL/setup.sh"
curl -fSLo settings.example.sh "$BASE_URL/settings.example.sh"
curl -fSLo logic-app-resource.template.json "$BASE_URL/logic-app-resource.template.json"

if [[ ! -f settings.local.sh ]]; then
  cp settings.example.sh settings.local.sh
fi

chmod 600 settings.local.sh
code settings.local.sh

取得するファイルは以下の3つです。

ファイル用途
setup.shリソース作成・権限設定などを行うスクリプト
settings.example.sh設定値のサンプル
logic-app-resource.template.jsonLogic Apps作成用のJSONテンプレート

☆GitHubでファイル一式を確認する☆

設定値を編集する

Cloud Shellのエディターで開いた settings.local.sh を編集します。

  1. Ctrl+S で保存
  2. Ctrl+Q でエディターを閉じる

赤文字の部分は、筆者側で用意しかねますので、編集して頂く必要があります。
順番に説明していきます。

SUBSCRIPTION_ID='REPLACE_WITH_SUBSCRIPTION_ID'
LOCATION='japaneast'
RG_NAME='Addache-Test'
KV_NAME='key-testsecrets'
LOGICAPP_NAME='Lap-TestSecrets'
ALLOWED_IP_CIDR='REPLACE_WITH_HOME_PUBLIC_IPV4/32'
RETENTION_DAYS=7

RDP_HOST='localhost'
RDP_USER='REPLACE_WITH_WINDOWS_USER'
RDP_PASS='REPLACE_WITH_WINDOWS_PASSWORD'
APP_ENABLED='ON'
COPY_FROM='FromFolder'
COPY_TO='ToFolder'
(1)SUBSCRIPTION_ID

構築先のサブスクリプションIDです。
Azureポータルの「サブスクリプション」で確認するか、
次のコマンドで一覧を表示し、出力されたSubscriptionIdのみ転記してください。
(サブスクリプション名は不要)

az account list \
  --query '[].{subscriptionName:name,subscriptionId:id}' \
  --output table
(2)ALLOWED_IP_CIDR

完成後にBATを実行する、Windows PC側のグローバルIPv4アドレスを指定します。末尾には /32 を付けます。

(書式の例)
203.0.113.10/32

そのまま使わず、ご自身のIPアドレスへ置き換えてください。
自宅のグローバルIPアドレスが分からない場合は以下のサイトで確認してください⇩

☆確認くん☆

(3)RDP_USER/RDP_PASS

接続先Windowsのユーザー名とパスワードです。
Azureへログインしているユーザーのパスワードではありません。

今回は手軽に試せるよう、シークレット値も設定ファイルへまとめています。
settings.local.sh にはパスワードが平文で残るため、
GitHubへの登録や画面の公開は避けてください。

値は単一引用符で囲みます。
値自体に単一引用符が含まれる場合は、'ab'\''cd' のようにBashの記法でエスケープしてください。

構築スクリプトを実行する

一発自動生成するスクリプト

コレを実行するとテストBATの章まで手順をスキップできます。
非常にシンプルな代わり、以下の要素に抵抗がない方だけ実行ください。

  • 構築している感が薄い
  • 筆者の提供するスクリプトを利用することに抵抗がある
    ※ネット上に転がっている長文コードは信用しないのが普通

(実行する場合)同じ作業フォルダで以下を実行します。

bash setup.sh

リソースの作成や権限反映に、数分かかる場合があります。

ひとつずつ、順番に作成していく場合

まずは設定した変数を読み込みします。

source ./settings.local.sh
(1)Key Vaultを作成する

対象サブスクリプションを選択し、必要なリソースプロバイダーを登録してから、
リソースグループとKey Vaultを作成します。

Key Vault作成部分のコマンドは以下です。

az keyvault create \
  --name "$KV_NAME" \
  --resource-group "$RG_NAME" \
  --location "$LOCATION" \
  --sku standard \
  --enable-rbac-authorization true \
  --public-network-access Enabled \
  --default-action Allow \
  --output none

GUI版と同じく、Key Vaultは標準プランを使用します。
アクセス許可モデルはAzure RBACを明示しています。

本記事ではVNetやプライベートエンドポイントを構成せず、パブリックネットワークへのアクセスを許可したうえで、RBACによって利用者を制御します。

(2)自分へKey Vault Administrator権限を付与する

GUI版で設定した「キー コンテナー管理者」を、Cloud Shellで操作している自分自身へ割り当てます。Azureリソースの「所有者」であっても、Key Vaultのシークレットを操作する権限は別途必要です。

まずは、先に作成したKeyVaultのリソースIDと、自身のユーザーIDを取得します。

KV_ID="$(az keyvault show \
  --name "$KV_NAME" \
  --resource-group "$RG_NAME" \
  --query id \
  --output tsv)"

USER_ID="$(az ad signed-in-user show \
  --query id \
  --output tsv)"

取得した値を確認します。
USER_ID はログイン中のユーザー、KV_ID は今回のKey VaultのリソースIDです。

printf 'KV_ID=%s\nUSER_ID=%s\n' "$KV_ID" "$USER_ID"

上記が正常に取得できているなら、権限の設定を行います。

az role assignment create \
  --assignee-object-id "$USER_ID" \
  --assignee-principal-type User \
  --role '00482a5a-887f-4fb3-b363-3b7fe8e74483' \
  --scope "$KV_ID" \
  --output none

※日本語の表示名と英語名の違いに影響されないよう、コマンドではロールIDを使っています。

日本語の表示名英語名
キー コンテナー管理者Key Vault Administrator
キー コンテナー シークレット ユーザーKey Vault Secrets User

Microsoft Learn:Key Vault関連のロール

権限付与の直後は、まだシークレットを操作できない場合があります。

(3)テスト用シークレットを登録する

GUI版と同じ6個のシークレットを登録します。

シークレット名設定する変数
RDP-HOSTRDP_HOST
RDP-USERRDP_USER
RDP-PASSRDP_PASS
APP-ENABLEDAPP_ENABLED
COPY-FROMCOPY_FROM
COPY-TOCOPY_TO

For文を利用し、一気に登録します。

for SECRET_NAME in \
  RDP-HOST RDP-USER RDP-PASS APP-ENABLED COPY-FROM COPY-TO
do
  VARIABLE_NAME="${SECRET_NAME//-/_}"

  if az keyvault secret set \
    --vault-name "$KV_NAME" \
    --name "$SECRET_NAME" \
    --value="${!VARIABLE_NAME}" \
    --output none
  then
    echo "登録完了: $SECRET_NAME"
  else
    echo "登録失敗: $SECRET_NAME"
    break
  fi
done

登録結果にシークレット値を表示しないよう、--output none を指定しています。

最後に、登録されたシークレットの名前だけを一覧表示します。
同じシークレットへ再登録すると、新しいバージョンが作成されます。

(4)JSONテンプレートの設定値を置き換える

ダウンロードしたJSONテンプレートには、ワークフローと周辺設定をまとめています。

以下を実行し、設定済みの変数を使って
リージョン/Key Vault名/許可する送信元IP/実行履歴の保存日数
を置き換えます。

mkdir -p .generated

sed \
  -e "s|__LOCATION__|$LOCATION|g" \
  -e "s|__KEY_VAULT_NAME__|$KV_NAME|g" \
  -e "s|__ALLOWED_IP_CIDR__|$ALLOWED_IP_CIDR|g" \
  -e "s|__RETENTION_DAYS__|$RETENTION_DAYS|g" \
  logic-app-resource.template.json \
  | jq '.properties.runtimeConfiguration.lifetime.count |= tonumber' \
  > .generated/logic-app-resource.json

元のテンプレートは残し、置換結果を .generated/logic-app-resource.json へ保存します。
最後の jq は、履歴日数をJSONの数値型へ変換する処理です。

続けて、JSONの構文を確認します。

jq empty .generated/logic-app-resource.json \
  && echo 'JSONの構文チェックが完了しました。'

エラーが出た場合は、設定値や置換コマンドを確認してから次へ進んでください。

(5)Logic AppsをConsumptionで作成する

作成先のリソースIDと管理APIのURLを変数へ設定します。

WORKFLOW_ID="/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG_NAME/providers/Microsoft.Logic/workflows/$LOGICAPP_NAME"

WORKFLOW_API="https://management.azure.com${WORKFLOW_ID}?api-version=2019-05-01"

次のコマンドで、置換済みJSONを投入します。

az rest \
  --method put \
  --url "$WORKFLOW_API" \
  --headers 'Content-Type=application/json' \
  --body @.generated/logic-app-resource.json \
  --output none

Consumptionのリソースである Microsoft.Logic/workflows を作成します。Standard用のApp Serviceプランは作成しません。

WORKFLOW_ID と WORKFLOW_API は、
後続の権限設定やHTTP URL取得でも使用するため、同じCloud Shellで続けて操作してください。

(6)Logic AppsへKey Vault Secrets User権限を付与する

作成したLogic AppsのマネージドIDを取得します。

PRINCIPAL_ID="$(az rest \
  --method get \
  --url "$WORKFLOW_API" \
  --query identity.principalId \
  --output tsv)"

取得結果を確認します。

printf 'PRINCIPAL_ID=%s\n' "$PRINCIPAL_ID"

ハイフンを含むIDが表示されたら、次のコマンドでKey Vaultの読み取り権限を付与します。

az role assignment create \
  --assignee-object-id "$PRINCIPAL_ID" \
  --assignee-principal-type ServicePrincipal \
  --role '4633458b-17de-408a-b874-0445c86b69e6' \
  --scope "$KV_ID" \
  --output none

GUI版で「キー コンテナー シークレット ユーザー」を選び、
Logic Appsをメンバーに追加する操作に対応します。

IDが空、または None・null の場合は権限付与へ進まず、

  • Logic Appsの作成が成功していること
  • マネージドIDが有効であること

を確認してください。作成直後なら、少し待ってID取得コマンドを再実行します。

権限付与後も反映に時間がかかる場合があるため、接続テストで権限エラーが出た場合は
数分待って再度試してください。

(7)IP制限と実行履歴を設定する

GUI版で設定していた以下の項目も、今回はJSONへ含めています。

項目設定内容
HTTPトリガーの送信元IP制限ALLOWED_IP_CIDR
実行履歴の保存期間RETENTION_DAYS(初期値7日)
HTTPアクションのSecure outputsON
Response-MyHomeのSecure inputsON

送信元IP制限と、秘密値を扱う処理の履歴非表示を組み合わせています。

HTTP URLを取得する

GUI版では画面からコピーしていた署名付きURLも、コマンドで取得します。

TRIGGER_NAME='When_an_HTTP_request_is_received'

CALLBACK_URL="$(az rest \
  --method post \
  --url "https://management.azure.com${WORKFLOW_ID}/triggers/${TRIGGER_NAME}/listCallbackUrl?api-version=2016-06-01" \
  --query value \
  --output tsv)"

取得したURLをTXTへ保存し、sed で % を %% に置き換えます。

umask 077
mkdir -p .generated

if [[ "$CALLBACK_URL" == https://* && "$CALLBACK_URL" == *sig=* ]]; then
  printf '%s\n' "$CALLBACK_URL" > .generated/callback-url.txt
  sed -i 's/%/%%/g' .generated/callback-url.txt
  chmod 600 .generated/callback-url.txt
  echo 'BAT貼り付け用のHTTP URLを保存しました。'
else
  echo 'HTTP URLを取得できていません。取得時のエラーを確認してください。'
fi

保存できたら、以下で表示します。

cat .generated/callback-url.txt

表示内容を、そのままBAT冒頭の URI へ貼り付けてください。変換済みなので、手動でさらに % を増やす必要はありません。

sed の行だけを繰り返すと二重変換になるため、
やり直す場合はURL取得⇨TXT保存から実行してください。
また、このTXTはBATソース用です。ブラウザーやcurlで直接呼び出す場合は、
未変換のURLを使用します。

URLには認証用の署名が含まれるため、ファイルや表示画面は公開しないでください。

「BAT貼り付け用のHTTP URLを保存しました。」と表示されたら、以下で表示して控えてください。

cat .generated/callback-url.txt

このURLには認証用の署名が含まれています。
URLを含む画面や、設定済みBATを公開しないでください。

構築結果を確認する

スクリプトの最後に、以下の設定を表示します。

表示項目確認する内容
resourceTypeMicrosoft.Logic/workflows
stateEnabled
identitySystemAssigned
allowedIPs設定したWindows PC側のIPアドレス
retention7(初期値の場合)

シークレット値や署名付きURLは、この確認結果には表示しません。

接続テストは、許可した送信元IPのWindows PCから行ってください。Cloud ShellからHTTP URLへアクセスすると、送信元IPが異なるため拒否される場合があります。

Windows BATを使った接続テスト

Key Vaultに登録した値を、WindowsのBATから取得して使ってみましょう。
用途別に3本のサンプルを用意しました。それぞれ単独で実行可能です。

(1)下準備

デスクトップへ以下のフォルダを作成してください。
[FromFolder]

ココに、サンプルBATを配置します。

このPATHじゃないと動かない、というものではありませんが、
以下の意図により指定いたします。

  • サンプル③(ファイルコピー)で実行結果の確認が出来るようにする
  • BATの置き場を記事内で統一する

フォルダを作成したら、以下をDLして格納してください。

サンプル①:RDP接続(01-rdp.bat)

サンプル②:ON/OFF制御(02-app-enabled.bat)

サンプル③:ファイルコピー(03-file-copy.bat)

各コードページの Download raw file から、.bat 形式で保存してください。
(そのままDLすれば即利用可能です)

DL後はBAT冒頭の

set "URI=REPLACE_WITH_LOGIC_APPS_SIGNED_URL"

を、先に控えたHTTP URLへ置き換えれば実行できます。
以下はHTTP URLを表示するCloudShellです。(少し前に記載したもの)

cat .generated/callback-url.txt

URL中の % は、BAT内では %% と記述してください。
共通的に以下部分へ含まれています。
なおブラウザ版と異なり、CLI版では最初から%→%%へ変換済みです。

(例)
変更前: sp=%2Ftriggers%2FWhen_an_HTTP_request_is_received%2F
変更後: sp=%%2Ftriggers%%2FWhen_an_HTTP_request_is_received%%2F

実際の署名付きURLを設定したBATは、GitHubなどへ公開しないでください。
必要なシークレットや設定上の注意点は、サンプルのREADMEにまとめています。

なお、筆者の作成したBATに警戒心があるときは
ChatGPTなどにコードを渡して、リスク監査を実施してもらってください。

・バックドアやウィルスなどの仕込みがないか
・不明な場所へのデータ送信が設定されていないか

それぞれのコードについて、簡単に解説します。

① RDP接続

シークレット:RDP-USER、RDP-PASS、RDP-HOST
に記述されている接続先/ユーザー名/パスワードをそれぞれ取得し、
リモートデスクトップを起動します。

画面には接続先とユーザー名を表示し、パスワードはマスクします。
※登録した資格情報は10秒後に削除されます

ただしWindows HomeはRDPの接続先になれず、自分自身への接続にも制約があります。
接続できない場合は、利用可能な別PCを指定してください。

※テスト可能なマシンをお持ちでない場合は、本検証はスキップしてください

② 処理のON/OFF制御

シークレット:APP-ENABLED を取得し、ON のときだけPC名を表示する hostname を実行します。
OFF の場合は実行をスキップします。

Key Vaultの値を変更して再実行すると、BATを書き換えずに動作を切り替えられます。
この設定は、ほかのBATには影響しません。

③ ファイルコピー

シークレット:COPY-FROM と COPY-TO を取得し、
デスクトップ上のフォルダ間でファイルをコピーします。

実行前にデスクトップへ FromFolder を手動で作成し、テスト用ファイルを置いてください。

記事の手順通りに進んでいれば、作成済のはずです。

コピー先の ToFolder は、存在しなければBATが作成し、存在していればそのまま使います。
同名ファイルは上書きされるため、テスト用のファイルで試しましょう。

本手順ではBATをFromFolderへまとめて配置してます。
ToFolderへBATがコピーされていれば成功です。

最後に

今回は変数をKeyVaultへ保存し、インターネット経由で取得し利用する手順を執筆しました。
10年前であれば

「PC内の特定の場所にスクリプトを固めて配置し、iniファイルなどへ変数をまとめる」
「Windows標準の環境変数設定をメンテナンスして、環境にあった動作をさせる」

あたりが定石だったと思いますが、モダンなパブリッククラウドを利用すれば

  • 「自宅で利用する変数までクラウドで管理できる」
  • 「BATだけ配布すれば自宅内にある、どのPCからでも即利用できる」
  • 「BATのファイルコピーとあわせてiniファイルなどのコピーや配置も不要、PC内でもBATの格納場所を選ばない」
  • 「Windowsの環境設定変数を、環境にあわせてメンテナンスする必要がない」
  • 「インターネット参照にも関わらず、グローバルIPによる最低限のセキュリティが担保できる(他の場所から利用できない)」

というメリットがあります。

Windowsの環境変数設定などの機能は、
よりセキュアな設定を行いたい場合は選択肢に入ってきます。

セキュリティと運用(操作性)はトレードオフなので、
「その環境でどこまでの安全確保をするか?」を検討し、
双方バランスの取れた機能を採用してください。

余談

AWSで実現する場合は
「Lambda + SSMパラメータストア or Secret Manager」のような構成になると思います。
コチラも機会があれば記事化したいと思います。

AzureのLogicAppsと直接対応するサービスは現時点(2026年11月現在)では存在しません。

M365 Power Automate が類似するサービスとなっており、完全にMicrosoftな世界観です。

また、LogicAppsへアクセスするためのHTTP URLに限って、
本記事内では各コードに埋め込むしかありませんが、こちらも

LogicAppsに依存せず、変数のようにインターネットから取得する

方法がありますので、そちらも機会を見て記事化します。

なお、本記事ではWindows BATをテストコードとして利用しています。
本当はPowerShellで提供したかったのですが、以下の理由でBATを採用しています。

Windows標準ではPowerShellをそのまま実行できないので、少々環境依存になる

本記事が皆さまのご参考になれば幸いです。

関連記事

【Azure】【マネコン操作】まだWindows BATにパスワードを埋め込んでるの? Azure Logic Apps+Key Vaultで固定変数を外部化する方法
今回はAzureに関する記事です。Azure のKey Vaultへ格納した変数を、インターネット経由でLogic Appsへアクセスし、自宅や会社等、特定の場所から安全に参照する構成を解説していきます。最初に、本記事で扱う用語は公式やAz…

コメント

タイトルとURLをコピーしました