個人PCで複数の案件を回していると、いつの間にかこうなる。

pac auth list に複数のプロファイルが並ぶ。az account list にも複数のアカウントが載る。どれも自分がサインインしたものだが、どれが今アクティブなのかは、コマンドを打った瞬間には見えていない。

この状態で、スクリプトが1本走る。

--environment を書き忘れると、どこへ行くのか

Power Platform CLI の多くのコマンドは --environment で宛先を指定できる。書かなくても動く。

書かなかったとき、コマンドは今アクティブな認証プロファイルの既定環境へ向かう。

ここが分かれ目になる。複数テナントのアカウントを持っている人にとって、「今アクティブなプロファイル」は自分が最後に使ったものであって、今から触りたい環境とは限らない。

具体的にどうまずいか。既定でアクティブなのが別テナント(たとえば他社の業務で使っているアカウント)で、その既定環境が相手の実業務環境だったとする。そこへ --force-overwrite を含む import が飛ぶ。

宛先を書くことが、唯一の防御になる。

--environment を書けば安全か

書いても足りない。

--environment が守るのは宛先だけで、使われる資格情報はアクティブな認証プロファイルのままだからだ。宛先だけ正しくても、身元が別テナントのアカウントなら拒否される。

守るべき軸は2本ある。

何を決めるか守り方
宛先どの環境へ行くか--environment を必ず書く
身元誰として繋がるかその環境に対して正しいプロファイルを選ぶ

片方だけでは足りない。 宛先を書き忘れれば別の環境へ行き、身元を選び忘れれば拒否される。後者はまだ幸運で、拒否されれば気づく。

Azure CLI は、さらに別の身元を持っている

Dataverse(Power Platform のデータ基盤)の Web API を直接叩くとき、アクセストークンを Azure CLI から取ることがある。

$tok = (az account get-access-token --resource $OrgUrl --query accessToken -o tsv)

このトークンは Azure CLI 側でアクティブなアカウントのものだ。pac の認証プロファイルとは何の関係もない。

同じスクリプトの中に pac solution exportaz account get-access-token が混在していると、前半と後半で別の身元が使われることになる。両方のCLIに同じアカウントでサインインしていれば一致するが、複数テナントを持っていると一致している保証はどこにもない。

複数案件を個人PCで持つと、ツールが2つあり、それぞれが独立にサインイン状態を持つ。この不一致は、どちらの CLI からも警告されない。

手元の一覧を信じない。サーバーに聞く

pac auth list の表示は、手元の設定ファイルの内容でしかない。

手元の一覧は「そう書いてある」だけで、実際にどの身元でどこへ繋がるかは、繋いでみるまで分からない。

信用してよい計器はひとつだけだ。

pac org who --environment $OrgUrl

これは接続先の Org URL と、繋がっているユーザーのメールアドレスを返す。宛先と身元を、サーバーが答える形で確認できる唯一の手段になる。

スクリプトの冒頭でこれを1回叩き、期待する Org URL とユーザーの両方が一致しているかを見る。一致しなければ止める。

⚠ 終了コードを信じてはいけない

ここが最も危ない性質になる。

pac は失敗しても終了コード 0 を返すことがある。 pac solution exportpac org who で観測した挙動で、pac CLI のバージョンによって変わりうる。

$LASTEXITCODEif ($?) で分岐を書いていると、間違えたときにスクリプトが黙って先へ進む。宛先を間違えたことにも、身元が違うことにも気づかない。

判定は終了コードではなく、出力の中身で行う。

if ($out -match 'Forbidden|not a member of the organization|Could not connect|Unauthorized|Error:') {
    throw "失敗している(終了コードは 0 なので出力で判定した)"
}

エラーが返ることが失敗の証拠になるのは、そのツールがエラーを返してくれる場合だけだ。返さないツールでは、沈黙が成功の証拠にはならない

宣言に無い環境は止める

宛先と身元の対応を、スクリプトの外に表として持つ。

$PacAccountMap = @(
  @{ Host='<自分の開発環境1>'; User='<自分の開発アカウント>'; Note='開発の正本' }
  @{ Host='<自分の開発環境2>'; User='<自分の開発アカウント>'; Note='納品先の写し・リハーサル用' }
  # 触ってはいけない環境も、明示的に宣言して拒否する
  @{ Host='<他社業務の環境>';  User='<その案件のアカウント>'; Deny=$true; Note='案件スクリプトの宛先にしない' }
)

要点は2つある。

表に無い宛先は止める。 知らない環境に、勝手にどれかの身元で繋ぎにいかない。環境を増やしたら表に1行足す。足さないと動かない設計にしておけば、足し忘れが事故ではなく停止として現れる。

触ってはいけない環境も、書いておく。 書かなければ「知らない環境」として止まるので結果は同じだが、明示的に拒否として書いておくと、なぜ止まったのかがエラーメッセージで分かる。

宛先を repo / 自分の開発テナント / 顧客テナントの 3 層に分ける全体像は別記事にまとめてある。

プロファイルの切り替えは、グローバル状態を動かしている

pac auth select は、そのセッションだけの話ではない。次に開いたシェルにも残る。

ここから2つの規律が出てくる。

動かさないで済むなら動かさない。 今アクティブなプロファイルが既に正しい身元なら、切り替えない。

動かしたら戻す。責任は動かした側が持つ。 スクリプトの末尾に戻す処理を書くだけでは足りない。この種のスクリプトは途中で exit することが多く、末尾は踏まれない。

PowerShell なら PowerShell.Exiting イベントに戻し処理を登録しておくと、exit で抜けても戻る。

$back = [scriptblock]::Create("& pac auth select --index $prevIndex | Out-Null")
Register-EngineEvent -SourceIdentifier PowerShell.Exiting -SupportEvent -Action $back | Out-Null

切り替えた直後にこれを登録しておけば、戻しを呼び出し側の規律に任せなくて済む。

おまけ:PowerShell で戻り値が壊れる

ガードを関数にまとめるとき、実装中に踏んだ落とし穴がある。

PowerShell は、関数の中でパイプラインに出たものを全部戻り値にする。進捗表示に Write-Output を使うと、index を返すつもりが「文字列+index の配列」になる。

進捗は Write-Host で出す。PowerShell は表示と戻り値の出口が同じなので、ログを1行足すだけで戻り値が壊れる。

まとめると、確認すべきは4つ

複数テナントのアカウントが同じ端末に載っている状態で CLI を回すなら、これだけは押さえておきたい。

  1. --environment を必ず書く ── 書かないと、アクティブなプロファイルの既定環境へ行く
  2. 身元も選ぶ ── --environment は宛先しか守らない
  3. サーバーに聞いて確かめる ── pac org who --environment の出力だけが信用できる
  4. 終了コードを見ない ── 失敗しても 0 が返る。出力の中身で判定する

Azure CLI を併用しているなら、pac と az は別々のサインイン状態を持っていることを前提に組む。同じスクリプトの中で身元が入れ替わる。

この面倒くささは、複数案件を持つ代償

アカウントが1つしかなければ、こんなガードは要らない。宛先も身元も間違えようがない。

複数のテナントを行き来するようになった時点で、間違えられる状態を自分で作っている。CLI はそれを教えてくれない。--environment を書き忘れても動くし、失敗しても 0 を返す。

ガードは、この気づけなさを埋めるために置く。宛先と身元を毎回確かめる手間は消えないが、間違った環境を上書きしてから気づくよりは小さいコストで済む。


関連記事