Azure Synapse から Salesforce へのコピーで発生する「invalid_client_id」エラーの原因と解決策

Azure Synapse Analytics から Salesforce(Service Cloud)へデータを書き込むとき、テスト接続は成功するのにスケジュール実行だけ「invalid_client_id」エラーで失敗することがあります。本記事では、この症状が起きる理由と、実務で使える具体的なチェックポイント・設計パターンを詳しく解説します。

目次

Azure Synapse から Salesforce にコピーすると「invalid_client_id」が出る状況とは

今回取り上げるケースは、次のような状況です。

  • Azure Synapse のパイプラインで Salesforce(Service Cloud)へ書き込む Copy アクティビティを実行している
  • 認証方式は OAuth 2.0 クライアント資格情報フロー(Client Credentials Flow)
  • テスト接続やデバッグ実行は成功する
  • しかし、スケジュール実行/トリガー実行だけ次のエラーで失敗する
ErrorCode=SalesforceOAuth2ClientCredentialFailure
{"error":"invalid_client_id","error_description":"client identifier invalid"}

「Client ID が間違っている」と言われているように見えますが、テスト接続は通っているため、単純なコピペミスではないことが多く、原因の切り分けに時間がかかりがちです。

実はこのパターンは、Synapse 側の実行モードの違い(Git 連携 / Live、テスト / トリガー、IR の違いなど)と、Salesforce 側の Connected App 設定が複雑に絡んで発生します。

なぜテスト接続は成功するのに、トリガー実行だけ失敗するのか

まず、Azure Synapse の「接続テスト」と「スケジュール/トリガー実行」は、内部的に次のような差があります。

項目テスト接続 / デバッグ実行スケジュール / トリガー実行
設定の参照元Git 連携中であれば Git(開発ブランチ) の Linked Service 定義Publish 済みの Live 環境 の Linked Service 定義
シークレットの扱いUI 上の入力欄に保持している値をそのまま使用することが多いLive 環境の JSON 定義に埋め込まれた値(または Key Vault 参照)を使用
実行 IDユーザーの操作に紐づく一時的なデバッグ実行トリガー定義に基づく正式なパイプライン実行
Integration RuntimeLinked Service のデフォルト IR(または UI 上の選択)パイプライン / アクティビティに定義された IR(別設定になっている場合あり)

この差により、次のようなことが起こります。

  • テスト接続は フォームに入力したそのままの Client ID / Secret / URL を使って成功
  • トリガー実行では Publish 済み Live の JSON 定義を読み込むが、そこに
    • 空白やタイプミスが残っている
    • Secret がそもそも保存されていない(Git 連携時の「Secret 未同期」)
    などの問題があり、結果として Salesforce 側から invalid_client_id が返される

つまり、「テスト接続で成功しているから設定は正しい」ではなく、「テスト接続とトリガー実行で、実際に使われている設定が違う」というのが根本原因であることが多いのです。

主な原因と対処方法の一覧

まずは全体像を整理するために、よくある原因とチェックポイントを一覧でまとめます。

主な原因確認ポイント・解決策ポイント
① URL や Client ID / Secret に余計な空白・タイプミスSalesforce エンドポイント(https://login.salesforce.com / https://test.salesforce.com / カスタムドメイン)の前後に半角・全角スペースが含まれていないか確認 Client ID・Client Secret を Salesforce の Connected App 画面から再コピーし、貼り付けし直すテスト接続は UI 入力値をダイレクトに使うため、たまたま空白が無視されて成功することもある。
② トリガー実行時だけ資格情報が見つからない(Git 連携時の「Secret 未同期」)Client Secret を Azure Key Vault に格納し、Linked Service から Key Vault 参照に切り替える Synapse ワークスペースの Managed Identity に Key Vault の Get / List 権限を付与する Git 連携中の場合、必ず Publish してからトリガー実行をテストする埋め込みシークレットは Git に同期されないため、Live 環境側では空欄扱いとなり、結果として invalid_client_id になる。
③ テスト接続と実行で使用している Integration Runtime が異なるLinked Service の IR 設定と、パイプラインの IR 設定が一致しているか確認 自己ホスト IR を使う場合、トリガー実行でも同じ IR を参照しているか確認IR が変わると利用される Managed Identity やネットワーク経路が変わり、Key Vault から Secret が取得できずに認証失敗することがある。
④ Salesforce Connected App の設定不整合OAuth スコープに api、必要に応じて refresh_token / offline_access / full などが含まれているか確認 IP Relaxation を「Relax IP Restrictions」に設定してテストする Client Credentials フローであれば「Run As User」に Salesforce 上で API Enabled 権限を持つユーザーが割り当てられているか確認スコープ不足や IP 制限により、トークン要求が拒否され、結果として invalid_client_id 相当のエラーにマッピングされるケースがある。
⑤ Salesforce コネクタのバージョンやサービス仕様の差異Synapse の Salesforce コネクタを最新の安定版に更新し、Linked Service を作り直してみる 同じ設定で Azure Data Factory 側のコネクタでも再現するか確認する特定バージョンでのみ発生する不具合や、仕様変更の影響でエラー内容が invalid_client_id として返ってくることもある。

以下では、それぞれの原因についてもう少し踏み込んで説明し、実際の画面操作レベルでどこを確認すればよいかを解説します。

原因1: URL や Client ID / Secret に混入した空白・タイプミス

最もシンプルで、しかし意外と見落とされがちなのが文字列の空白やタイプミスです。

よくあるパターン

  • Salesforce のログイン URL をコピーした際、末尾にスペースや改行が入っていた
  • Client ID の前後に全角スペースが入っている(特に日本語 IME でコピー操作をした場合)
  • Client Secret を一度メモ帳などに貼り付けてから再コピーする過程で改行が紛れ込んだ
  • Sandbox 環境なのに https://login.salesforce.com を指定している(本来は https://test.salesforce.com)

テスト接続で成功しているのにトリガー実行で失敗する原因としては、次のような微妙な差が影響することがあります。

  • テスト接続時: UI が値をうまくトリム(前後の空白削除)してくれている
  • トリガー実行時: JSON 定義に保存された値をそのまま HTTP リクエストに使用しており、空白も含めて送信されている

これにより、Salesforce 側から見ると

  • 存在しない Client ID(実際の ID の末尾にスペース付きの ID)
  • 別環境の URL からのリクエスト

として扱われ、結果的に invalid_client_id が返されることがあります。

チェック手順

  1. Synapse の 管理 > Linked services から Salesforce の Linked Service を開く
  2. Salesforce の URL / Client ID / Client Secret をそれぞれ選択し、文字列の先頭と末尾にカーソルを合わせて空白がないか確認
  3. 念のため、Salesforce 管理画面から Client ID / Secret を再コピーし、上書きする
    • このとき、コピー元のテキストの前後に不要な文字が含まれていないか確認する
  4. 保存後、再度 Publish を実行し、トリガー実行で結果を確認する

原因2: Git 連携時の「Secret 未同期」問題と Key Vault への移行

Azure Synapse や Azure Data Factory で Git 連携を有効にしている場合、Linked Service の「埋め込みシークレット」が Live 環境へコピーされないという仕様があります。これが「テスト接続だけ成功/トリガー実行は失敗」の典型的な原因です。

なぜ Secret が Live 環境に存在しないのか

  • Git 連携を有効にすると、Linked Service の JSON 定義は Git リポジトリ側に保存される
  • セキュリティの観点から、Client Secret のような値は Git に保存されない(JSON 上は空欄または Key Vault 参照)
  • ユーザーはポータル上の UI で Secret を入力してテスト接続を実行するが、その値は 一時的に UI 上に保持されているだけ のケースがある
  • Publish を行っても、その Secret 値は Live 環境にコピーされず、結果として Live 側の Linked Service では Secret が空欄 の状態になる

この状態でトリガー実行を行うと、Synapse は

  • Client Secret = 空文字 または null

の状態で Salesforce にトークン要求を送るため、結果的に invalid_client_id や invalid_client といったエラーになってしまいます。

推奨される解決策: Azure Key Vault へ移行する

この問題を根本から解消するには、すべてのシークレットを Azure Key Vault に集約し、Synapse 側からは Key Vault 参照に切り替えるのがベストプラクティスです。

Key Vault への移行手順の一例

  1. Azure ポータルで Key Vault を用意する(既存のものでも可)
  2. Key Vault に SalesforceClientSecret などの名前で Secret を作成し、Salesforce Connected App の Client Secret を登録
  3. Synapse ワークスペースの Managed Identity(システム割り当て / ユーザー割り当て)に対して、Key Vault のアクセス権を付与
    • アクセス ポリシー方式の場合: Get / List の Secret 権限を付与
    • RBAC 方式の場合: Key Vault Secrets User など適切なロールを割り当て
  4. Synapse の Salesforce Linked Service 設定画面で、Client Secret を 「Azure Key Vault から取得」に変更し、上記 Secret 名を指定
  5. 保存後、必ず Publish を実行し、トリガー実行で動作確認

Key Vault 参照にしておけば、Git 連携かどうかに関係なく、テスト接続・デバッグ・トリガー実行の全てで同じ Secretが使用されるため、動作差異をなくすことができます。

原因3: テスト接続とトリガー実行で Integration Runtime が異なる

Synapse の Salesforce コネクタは、指定された Integration Runtime(IR) 上で動作します。IR が異なると、以下のような差が出ます。

  • 利用される Managed Identity の種類(自己ホスト IR ならローカル環境の認証、Azure IR なら Synapse の MI)
  • 到達できる ネットワーク範囲(企業のプロキシ経由か、インターネット直通か など)
  • 到達可能な Key Vault やプライベート エンドポイント

その結果、次のような問題が発生する場合があります。

  • テスト接続は Azure IR で実行され、Key Vault から Secret を取得できるため成功
  • トリガー実行は自己ホスト IR を参照しており、Key Vault への接続が失敗したり、別の認証コンテキストになっている
  • 結果として、Salesforce へのトークン要求に渡される Client ID / Secret が不正な値または空になり、invalid_client_id となる

IR 設定の確認ポイント

  1. Salesforce Linked Service の「接続/認証」画面で、どの IR が選択されているかを確認
  2. パイプラインの Copy アクティビティの詳細設定で、「Integration Runtime の上書き」が行われていないか確認
  3. もし上書きされている場合は、Linked Service と同じ IR を使うか、どちらかに統一する

IR を統一することで、テスト接続とトリガー実行で同じ経路・同じ認証コンテキストが使用されるようになり、原因の切り分けが容易になります。

原因4: Salesforce Connected App の設定不整合

Client ID / Secret が正しくても、Salesforce 側の Connected App 設定が不十分な場合、トークン要求が拒否されて invalid_client_id 相当のエラーが返されることがあります。

確認すべき主な項目

  • OAuth スコープ
    • 最低限、Access and manage your data (api) に相当する api スコープが必要
    • 長時間動作させる場合は refresh_token / offline_access なども検討
  • IP Relaxation(IP 制限)
    • まずは「Relax IP Restrictions」に設定して動作確認
    • 正常動作を確認した後で、必要であれば適切な IP 制限ルールを適用する
  • Client Credentials フローの有効化
    • 新しい OAuth 2.0 Client Credentials Flow を使用する設定になっているか
    • Run As User に指定しているユーザーに API Enabled 権限が付与されているか

特に Sandbox / 本番 / カスタムドメイン を跨いで環境を用意している場合、

  • Connected App 自体が別インスタンス(Sandbox vs Production)に存在している
  • Synapse からアクセスしている URL が Connected App を作成した組織と異なる

といった齟齬により、Salesforce 側で「その Client ID はこの組織のものではない」と判断され、invalid_client_id が返されるケースもあります。

外部ツールでの再現テスト

Synapse 側の問題か Salesforce 側の問題か切り分けるには、Postman など外部ツールから同じ Client ID / Secret / URL でトークン要求を投げてみるのが有効です。

例えば、次のようなリクエストを作成します(Client Credentials フローの一例)。

POST https://login.salesforce.com/services/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&
client_id=(Connected App の Consumer Key)&
client_secret=(Connected App の Consumer Secret)

ここでトークンが正常に取得できれば、Salesforce 側の Connected App 設定は概ね問題ないと判断できます。逆にここでも invalid_client_id となる場合は、Connected App の設定や環境の組み合わせを重点的に見直す必要があります。

原因5: Salesforce コネクタのバージョン差異やサービス側の仕様変更

まれに、特定バージョンの Salesforce コネクタや特定リージョンの組み合わせで、同じ設定にもかかわらずエラーの内容が変わる場合があります。

  • 以前は invalid_client と返っていたが、最近は invalid_client_id になった
  • 一部のオプションを有効にするとエラー内容が変わる

このような場合、次のような確認をしておくと安心です。

  • 別の環境(検証用 Synapse ワークスペース)で同じ設定を再現してみる
  • Azure Data Factory 側の Salesforce コネクタでも同様の挙動か確認する
  • コネクタの更新や Synapse の更新後に挙動が変わっていないか把握しておく

もし同じ設定で別ワークスペースでは成功するのに、特定のワークスペースだけ失敗する場合は、そのワークスペース固有の設定(IR、Managed Identity、Key Vault など)を重点的に疑うとよいでしょう。

切り分けのためのステップバイステップ手順

実際に「invalid_client_id」エラーが出ている環境での切り分けの流れを、ステップごとに整理します。

ステップ1: 実際に送られている Client ID / URL を確認する

Synapse パイプラインの実行結果から、Copy アクティビティの詳細ログを確認します。

  1. 失敗したパイプライン実行を開く
  2. 該当の Copy アクティビティを選択し、「入力」や「詳細」タブを確認
  3. Linked Service の設定名や、使用している IR 名、Key Vault 参照名などをメモする

必要に応じて、Synapse ワークスペースに 診断ログ(Log Analytics 等) を有効化すると、HTTP リクエストの先頭部分やエラー内容をより詳細に確認できます。

ステップ2: テスト接続とトリガー実行で同じ Linked Service / IR を使っているか照合

次の観点で「テストと本番」が同じ経路を通っているかチェックします。

  • Linked Service 名が同一か
  • Linked Service に指定されている IR と、パイプラインのアクティビティに指定されている IR が同じか
  • Git 連携中であれば、Linked Service の変更を Publish 済みか

ここで差異が見つかれば、まずはそれを揃えるだけで問題が解消することも多いです。

ステップ3: Key Vault 参照か、埋め込みシークレットかを確認

  • すでに Key Vault 参照にしている場合
    • Synapse の Managed Identity に Key Vault へのアクセス権限があるか
    • IR が変わることで別の認証コンテキストになっていないか
  • 埋め込みシークレットを使っている場合
    • Git 連携中なら、Live 環境に Secret がコピーされていない可能性が高い
    • このタイミングで Key Vault 参照への移行を検討する

ステップ4: Salesforce 側の Connected App をチェック

Synapse 側を見直しても改善しない場合は、Salesforce 側の設定を再確認します。

  • Client Credentials フローが有効化されているか
  • Run As User に指定しているユーザーに API Enabled 権限があるか
  • 想定している Salesforce 組織(本番 / Sandbox / Developer Org など)で Connected App が作成されているか
  • Synapse からアクセスしている URL が、その組織に対応したエンドポイントになっているか

この時点まで確認しても原因が見つからない場合は、Postman などでトークン取得を試して、問題の所在を Salesforce 側 / Azure 側 のどちらかに切り分けると調査が進みやすくなります。

再発防止のためのベストプラクティス

最後に、「とりあえず動いた」で終わらせないために、設計段階から意識しておきたいベストプラクティスをまとめます。

すべてのシークレットは必ず Azure Key Vault へ

  • Client Secret、パスワード、証明書などは 一切 Synapse に直接書かない
  • Git 連携の有無にかかわらず、Key Vault に集約すれば
    • ローテーション時に Synapse 側の設定変更が不要
    • テスト接続とトリガー実行の設定差異を最小化できる
    • セキュリティ チームとの役割分担もしやすい

環境ごとの URL・資格情報をパラメータ化する

本番・Sandbox・開発環境などを複数運用する場合、URL や資格情報をパイプラインのパラメータに切り出しておくと、設定ミスを防ぎやすくなります。

環境エンドポイント URLClient ID / Secret の保管場所
開発https://test.salesforce.comKey Vault(Secret 名: Dev-Salesforce-ClientSecret)
検証https://test.salesforce.comKey Vault(Secret 名: Stg-Salesforce-ClientSecret)
本番https://login.salesforce.comKey Vault(Secret 名: Prod-Salesforce-ClientSecret)

こうしておけば、デプロイ時に環境に応じて パラメータ値だけを切り替えることで、同じパイプライン定義を安全に再利用できます。

Postman 等での再現テストを標準フローに含める

新しい Salesforce 連携を組む際には、Synapse 実装前に Postman などでトークン取得~API 呼び出しまでを一度通しておくと、トラブルシューティングが格段に楽になります。

  • Synapse 実装で問題が起きたとき
    • Postman では成功する → Synapse 側の設定差異を疑う
    • Postman でも失敗する → Salesforce 側の Connected App / 権限を重点的に確認する

「Azure からのアクセス」であることにこだわり過ぎず、純粋な HTTP クライアントとしての切り分け手段を用意しておくと、調査時間を大きく削減できます。

エラーコード別にログを取り、ナレッジを蓄積する

invalid_client_id に限らず、Salesforce との連携ではさまざまな OAuth エラーが発生します。組織内で

  • invalid_client_id → Client ID / Secret / URL / Key Vault / Git 連携の差異を疑う
  • invalid_grant → 認可コードや refresh token の期限切れ、ユーザー権限を疑う
  • invalid_scope → Connected App 側の OAuth スコープ設定不足を疑う

といった形で「エラーコード別のチェックリスト」を整備しておくと、新しいメンバーでも同じ品質でトラブルシューティングができるようになります。

まとめ: 「URL の空白」と「Key Vault 移行」で多くのケースは解消

Azure Synapse から Salesforce へコピーするときに発生する invalid_client_id エラーは、メッセージだけを見ると「Client ID の値が間違っている」と思いがちですが、実際には

  • URL・Client ID / Secret に紛れ込んだ空白やタイプミス
  • Git 連携により Live 環境へ Secret が同期されていないこと
  • テスト接続とトリガー実行で異なる IR / 資格情報を使っていること
  • Salesforce Connected App のスコープや Run As User の権限不足

といった要因が複合的に絡んでいることがほとんどです。

まずは

  • URL・Client ID / Secret の前後の空白を徹底的に取り除く
  • Client Secret を Azure Key Vault に移行し、Git 連携の影響を排除する

という 2 点から着手することで、多くのケースで「テスト実行・デバッグ・スケジュール実行のすべてが成功する」状態に近づけることができます。

そのうえで、IR や Connected App の設計を見直し、環境ごとの設定をパラメータ化しておけば、将来的な拡張や運用の負荷も大きく下がります。この記事のチェックリストを、自身のプロジェクトの標準フローに組み込んでおくことをおすすめします。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次