Microsoft Entra ID(旧Azure AD)でSSOやSCIMプロビジョニングを使っていると、「どうせならプロフィール写真もEntra IDを起点に全部のSaaSへ同期したい」と考える方は多いと思います。しかし現時点の標準機能だけでは、思ったほどシンプルには実現できません。本記事では、制約の正体とSlack/Zoom/Google Workspaceなどへの現実的な同期パターンを詳しく解説します。
Entra IDのプロフィール写真を外部アプリに揃えたい理由
クラウドサービスが増えるほど、ユーザーは次のような不満を抱えがちです。
- 入社・異動のたびに、Slack・Zoom・Google Workspace・各種SaaSごとに写真を設定し直すのが面倒
- サービスによって古い写真が残っており、誰が最新かわかりにくい
- ブランドガイドラインに沿った写真(背景色・フレームなど)を全社で統一したい
一方、管理者から見ると、次のようなニーズがあります。
- ユーザー情報と同じように、「プロフィール写真もEntra IDを真実のソース(SoT)」にしたい
- 退職・休職者の写真を素早く更新/削除して、誤解を避けたい
- 監査やコンプライアンス観点で、顔写真をどのシステムにどのように保存しているか把握したい
理想は「Entra IDに写真を1回登録すれば、あとは自動で各SaaSへ同期される」状態です。ところが、ここで最初にぶつかる壁が、Entra ID標準のSCIMプロビジョニング機能の仕様です。
結論:Entra ID標準のSCIMプロビジョニングでは写真同期はできない
先に結論から書くと、2025年時点でもMicrosoft Entra IDの標準SCIMプロビジョニングだけでプロフィール写真を外部アプリへ同期することはできません。
理由はシンプルで、Microsoft公式ドキュメントに次のように明記されています。
- 属性マッピングをカスタマイズするドキュメントの「注意事項」に、
「アプリにプロビジョニングする写真属性を追加することは現在サポートされていません。写真の同期形式を指定できないためです。」と記載されている。 - 2025年9月のMicrosoft Q&Aでも、「Entra IDからアプリケーションへの写真の同期は現時点ではサポートされていない」と公式に回答されています。
- WorkOSのEntra ID SCIM統合ドキュメントでも、「Entra IDのSCIMプロビジョニングは画像の送信をサポートしない」と明記されています。
つまり、Entra IDの「プロビジョニング > 属性マッピング」画面から、ユーザーオブジェクトの写真をSCIM属性として追加で送ることはできません。
なぜ写真だけSCIMで送れないのか
SCIM仕様自体は、ユーザーの写真を表す photos 属性を持っていますし、多くのアプリはこれを受け取れるように実装しています。SlackのSCIM APIドキュメントでは、photos の value に公開URLまたはデータURL(Base64)を設定できると解説されています。
しかし、Entra IDのSCIMクライアント(プロビジョニングサービス)は次の点で制約があります。
- Entra ID内部のプロフィール写真は、ユーザーの通常属性(displayName, mail など)とは別扱いで、Graphの
/users/{id}/photoエンドポイントから取得する特殊なリソースになっている。 - 属性マッピングで扱えるのは、基本的に「文字列・数値・ブール値・DateTime」などで、Graphのバイナリ写真をそのまま引き当てる仕組みがない
- アプリごとに許容する写真サイズやフォーマット(JPEG, PNGなど)が異なるため、「どの形式で送るか」をSCIMの一律なスキーマでは指定しづらい
このため、Microsoftとしては「写真はSCIMで送らない」という方針になっており、現状はカスタム属性の追加などで回避するしかありません。
Entra ID × プロビジョニングでできること・できないこと
| 項目 | 可否 | 補足 |
|---|---|---|
| 氏名・メール・部署などのテキスト属性をSCIMで同期 | ◯ | 標準機能。属性マッピングのUIから自由に制御可能。 |
| Entra IDユーザーのプロフィール写真を、そのままSCIMで同期 | ✕ | 写真属性はサポート外と明記されている。 |
| 任意の文字列属性(例:写真URL)をSCIMで同期 | ◯ | カスタム属性やディレクトリ拡張属性として追加可能。 |
SCIMアプリ側で photos[*].value に文字列を受け取る | アプリ次第 | Atlassianなど対応アプリもあるが、全てのSaaSが対応しているわけではない。 |
3つの現実的な回避策の全体像
標準SCIMだけで完結しない以上、次のいずれか(または組み合わせ)で設計することになります。
| 方式 | 概要 | 向いているケース | 難易度 |
|---|---|---|---|
| ① API連携での自動更新 | Graph APIでEntra IDの写真を取得し、各アプリの管理APIで写真を更新する。 | Slack / Zoom / Google Workspace など、多数のSaaSを横断的に同期したい。 | 中〜高(スクリプト・フローの実装が必要) |
| ② 写真URL配布方式 | 写真を別サーバー等に保存し、そのURLをEntra IDの属性としてSAML/OIDC/SCIMで渡す。 | アプリ側が「写真URLを受け取って表示」できる機能を持つ場合(Atlassianなど)。 | 中(ストレージ・URL設計+属性マッピング) |
| ③ アプリ独自の写真同期機能を利用 | 各アプリが持つHR連携やストレージ連携などの「写真取り込み機能」を使用する。 | 特定アプリに強い依存があって、Entra ID経由にこだわらない場合。 | 低〜中(アプリごとの設定に依存) |
以下では、それぞれをもう少し具体的に掘り下げていきます。
① Graph API+各アプリ管理APIでの自動更新(おすすめ)
全体フローのイメージ
最も汎用性が高く、Entra IDを写真の真実のソースとして扱えるパターンです。フローはおおむね次のようになります。
- (日次・週次など)対象ユーザーを抽出
- Entra IDに写真が設定されているユーザーのみ
- 退職済み・無効ユーザーは除外
- Microsoft Graph APIの
/users/{id}/photo/$valueで写真バイナリを取得 - 各アプリの制約に合わせて、正方形トリミング・リサイズ・JPEG/PNG変換を実施
- メールアドレス(UPN)などをキーにして、各アプリ側ユーザーを検索
- Slack / Zoom / Google Workspace などの管理APIで、プロフィール写真を更新
- 実行結果(成功・失敗・スキップ理由)をログ&監査用ストレージに記録し、失敗分はキュー化・リトライ
これを実装する手段としては、次のような選択肢があります。
- Power Automate(クラウドフロー)
- Azure Functions(C# / Node.js / PowerShell など)
- Azure Automation Runbook やオンプレ/クラウドのスケジュールPowerShell
Graph APIでEntra IDユーザー写真を取得する
Entra IDのユーザー写真は、Microsoft Graphで次のように取得できます。
GET https://graph.microsoft.com/v1.0/users/{userPrincipalName}/photo/$value
- 戻り値:
image/jpeg等のバイナリ(ストリーム) - 必要な権限の例:
- アプリケーション権限:
User.Read.AllまたはUser.ReadWrite.All - 管理者同意が必要
- アプリケーション権限:
注意点として、GraphのQ&Aでも指摘されている通り、photo はユーザーリソースの通常属性ではなく、別APIで取得する必要がある点があります。
また、組織によっては「誰が写真を変更できるか」をGraph APIで制御しているケースもあるため、同期バッチが書き換えてよい運用かどうかを事前に確認しておくのが安全です。
各アプリへの反映方法(Slack / Zoom / Google Workspace)
Slackに写真を反映する
Slackでは、次の2ルートがあります。
- Slack Web API
users.setPhotoを使う方法 - (アプリによっては)SCIMの
photos属性経由でURLやデータURLを渡す方法
現実的には、Entra IDからのSCIM連携とは別に、SlackのWeb APIを直接呼ぶ方が実装しやすいケースが多いです。
- エンドポイント:
POST https://slack.com/api/users.setPhoto - トークン:SlackのBot TokenまたはUser Token(
users.profile:writeなどのスコープ) - 引数:
imageパラメーターに画像バイナリを multipart/form-data で送信 - 推奨サイズ:最低512×512ピクセル〜最大1024×1024ピクセル程度
Graphから取得した写真を、中継スクリプト側で512〜1024pxの正方形JPEGに整形してからSlackに渡す、というパターンがよく使われます。
Zoomに写真を反映する
Zoomはユーザーのプロフィール写真をアップロードする専用APIを提供しています。
- エンドポイント:
POST https://api.zoom.us/v2/users/{userId}/picture - 認証:OAuth(サーバー間アプリ)などで取得したアクセストークン
- Content-Type:
multipart/form-data - フォームフィールド:
pic_fileに画像ファイルを添付 - 対応フォーマット:JPEG / PNG
PowerShellやPythonからのアップロード例もコミュニティに多く公開されており、既存のテンプレートを流用しやすいのもメリットです。
Google Workspace(Directory API)に写真を反映する
Google Workspaceでは、管理者向けのDirectory APIからユーザー写真を更新できます。
- エンドポイント:
PUT https://admin.googleapis.com/admin/directory/v1/users/{userKey}/photos/thumbnail userKey:ユーザーのメールアドレスまたはID- スコープ:
https://www.googleapis.com/auth/admin.directory.userなど - リクエストボディ:
UserPhotoオブジェクト(Base64エンコードされた画像など)
これにより、「Entra ID(Graph)→ 中継スクリプト → Google Directory API」という経路で、Gmail・カレンダー・ドライブなどに表示される写真を統一できます。
簡易な疑似コード例(PowerShell風)
実装イメージを掴むための、かなり簡略化した疑似コードです。
# 1. Entra ID から対象ユーザー一覧を取得(Graph /users)
$users = Get-EntraUsersWithPhoto
foreach ($u in $users) {
# 2. Graph から写真を取得
$photoBytes = Invoke-RestMethod `
-Method GET `
-Uri "https://graph.microsoft.com/v1.0/users/$($u.userPrincipalName)/photo/`$value" `
-Headers $graphHeaders
# 3. 画像を正方形 & リサイズ(ロジックは省略)
$normalizedJpeg = Convert-ToSquareJpeg -Bytes $photoBytes -Size 512
# 4. Slack ユーザー検索(メールアドレス)
$slackUserId = Get-SlackUserIdByEmail -Email $u.mail
if ($slackUserId) {
# 5. Slack の users.setPhoto でアップロード
Set-SlackUserPhoto -UserId $slackUserId -ImageBytes $normalizedJpeg
}
# Zoom / Google Workspace も同様に呼び出し
}
実際には例外処理・レート制限・差分判定などを加えていきますが、基本の考え方はこのような「Graphで取得 → 必要な分だけ各SaaS APIを呼ぶ」流れになります。
② 写真URL配布方式:Entra IDのカスタム属性+SCIM / SAML / OIDC
二つ目のパターンは、写真そのものではなく「写真のURL」を各アプリに渡す方式です。アプリ側に「URLで指定された画像をプロフィール写真として表示する」機能がある場合に有効です。
基本アイデア
- ユーザー写真ファイルを、何らかのストレージ(Azure Blob Storage, CDN, 社内Webサーバーなど)に配置し、ユーザーごとの一意なURLを決める。
- そのURLを、Entra IDのカスタム属性(例:
extension_photoUrl)や外部システム側のプロフィール属性に保存する。 - SAMLのクレーム、OIDCのIDトークン、またはSCIMの文字列属性として、各アプリにURLを渡す。
- アプリ側がそのURLから画像を取得し、プロフィール写真として表示する。
この方式のポイントは、Entra IDはあくまで「URL文字列」を配布するだけであり、実際の画像バイナリ転送は行わない点です。
Atlassian Cloudの例:Entra IDカスタム属性+SCIM
Atlassianの公式ドキュメントでは、「IdPにはプロフィール写真を同期する組み込み機能がないため、写真URLをSCIM属性として渡す」手順が紹介されています。
ざっくりとした流れは次の通りです。
- IdP(Microsoft Entraなど)側に、写真URL用の属性(例:
photoValue)と「primaryフラグ」用属性(例:photoPrimary)を作成。 - 写真ファイルをインターネットから到達可能なサーバーに配置し、そのURLを
photoValueに格納。 - AtlassianのSCIMアプリ側で属性リストを編集し、ターゲット属性として
photos[type eq "photo"].value(文字列)photos[type eq "photo"].primary(Boolean)
- Entra IDの属性マッピングで、
photoValue → photos[type eq "photo"].value、photoPrimary → photos[type eq "photo"].primaryをマッピング。
このように、「Entra IDのカスタム属性に写真URLを入れる → SCIMでURLを流す → アプリ側がURLから画像を取得して表示」という構成がとれます。
Slackやその他SCIMアプリでも応用可能か?
SlackのSCIM APIは photos 属性をサポートしており、その value に公開URLまたはデータURLを指定できる仕様です。
ただし実際に「Entra IDギャラリーアプリの属性マッピングから photos[...].value に値を流せるか」はアプリごと/テナントごとに挙動が異なることがあります。Atlassianのように公式に手順が整備されているケース以外では、次のように検証が必要です。
- Entra IDのプロビジョニング画面で、ターゲット属性に
photos[type eq "photo"].valueを追加できるか - その値をSCIM経由で送ったとき、アプリ側がエラーを出さずに受け取り・反映してくれるか
うまくいかない場合は、この方式ではなく「① API連携による直接更新」に切り替えるのが無難です。
③ アプリ独自の写真同期機能を利用する
三つ目のパターンは、Entra IDを経由せず、各アプリが持つ独自の写真管理機能を活用する方法です。
- Google Workspace:Directoryに直接アップロード、またはGCDSやサードパーティツールで一括設定
- Zoom:APIでの一括アップロードスクリプトやサードパーティモジュールで更新
- その他SaaS:HRシステム連携、CSV一括アップロードなど独自機能を持つものもある
この場合、Entra IDは「認証・属性連携のハブ」と割り切り、写真の真実のソースを別システム(例:人事システムやGoogle Workspace)に置く設計になります。
複数システムに分散するデメリットはありますが、「既にGoogle Workspace側に全社員の写真があり、それをSlackやZoomに同期したい」といったケースでは、Entra IDを経由しない方がシンプルに済むこともあります。
設計・運用時のチェックポイント
どの方式を採用するにせよ、プロフィール写真は通常の属性よりもセンシティブで、運用設計が重要になります。
サイズ・形式・縦横比の統一
- Slack:最小512×512〜最大1024×1024px程度、正方形推奨。
- Zoom:JPEG/PNGのみ、サイズ上限あり。
- Google Workspace:Directory APIの仕様上、推奨サイズやフォーマットがある。
中継スクリプト側で共通フォーマット(例:512×512 JPEG)に正規化しておくと、アプリごとに処理を分ける必要が減ります。
| 項目 | 例 | 決めておきたいポリシー |
|---|---|---|
| 解像度 | 512×512 / 1024×1024 など | 最低・推奨・上限サイズを決め、超過時は自動リサイズ。 |
| 画像形式 | JPEG固定 | 透過PNGをどう扱うか(背景色を付けるか)を決める。 |
| 縦横比 | 1:1(正方形) | 縦長写真はセンタークロップか、上下余白を入れるか。 |
権限・同意・プライバシー
- Graphの写真取得には、組織全体のユーザー写真にアクセスできる強力な権限が必要です。
- ZoomやGoogle Admin SDKも「ユーザー管理」レベルの権限を要求するため、クライアントID・シークレットの保護が非常に重要です。
- 顔写真は個人情報に該当することが多く、利用目的の明示と社内ポリシーへの明文化、データ保持期間などを整理しておく必要があります。
また、従業員の中には「顔写真を使われたくない」「社外システムには表示されたくない」と考える方もいます。可能であれば、次のようなオプションを用意しておくとよいでしょう。
- 同意したユーザーのみ自動同期対象に含める(属性フラグで制御)
- 写真を「社内向けのみ」「社外向けもOK」などレベル分けして扱う
差分同期・エラー処理の設計
毎回全ユーザーの写真を流すと、APIレート制限や処理時間の問題が出てきます。次のような工夫がおすすめです。
- Entra ID側で、「写真が更新されたユーザー」だけを抽出するロジック(更新日時やハッシュ値)を導入
- 各アプリ側でも写真のハッシュやETagを保存し、「本当に変わった場合だけ更新」する
- エラー種別
- ユーザー未存在(既に退職など)
- レート制限(429, 503など)
- 画像サイズ・形式エラー
監査ログには、少なくとも次の情報を残しておくとトラブルシュートがしやすくなります。
- 対象ユーザー(UPN / メールアドレス / 各サービスのユーザーID)
- 処理結果(成功/失敗/スキップ)
- 失敗理由(HTTPステータスとメッセージ)
- 実行日時・バッチID
キャッシュと反映タイムラグ
多くのSaaSはプロフィール写真をCDNにキャッシュしており、「APIで更新してもUIに反映されるまで数分〜数時間かかる」ことがあります。
- ユーザーからの問い合わせに備え、「最大◯時間かかることがあります」とヘルプページに記載しておく
- 検証時は別ブラウザ・別デバイスで表示を確認する(クライアントキャッシュの影響を避ける)
実践シナリオ別:どの方式を選ぶか
シナリオA:Slack・Zoom・Google Workspaceをすべて利用している中規模組織
- 要件:全てのサービスで写真を統一し、社員はEntra IDポータルでのみ写真を管理したい。
- おすすめ:
- ① Graph+各アプリAPIの自動更新をメイン方式とする
- 夜間バッチ(例:毎日2時)で前日分の変更をまとめて反映
この場合、Entra IDの写真が唯一の真実のソースになり、異動や改姓のタイミングで写真差し替えを行えば、翌日には全てのSaaSが追随する運用を構築できます。
シナリオB:Atlassian Cloudを中心に使っており、他SaaSは少数
- 要件:Atlassianのプロフィール写真をEntra IDのユーザーと合わせたい。
- おすすめ:
- ② 写真URL配布方式(Atlassian公式手順)を採用
- 写真ファイルを社内のHTTPSサーバーまたはCDNに配置し、URLをEntra IDの
photoValue属性に格納
このアプローチでは、Entra IDから見て「写真データの実体」は外部ストレージ側にあり、Entra IDはURL文字列だけを管理するイメージになります。
シナリオC:Google Workspaceがアイデンティティの中核で、Entra IDは一部SaaS用
- 要件:Google側で既に全社員の写真管理ができており、Entra IDはSaaS用のブリッジに近い。
- おすすめ:
- ③ アプリ独自の写真同期機能を優先し、Entra ID起点の写真同期は行わない
- 必要なら「Google Workspace → Slack/Zoom」へのAPI連携を別途実装
このように、「どのシステムを写真の真実のソースとするか」によって、最適な設計が変わります。
「すぐ使える」実装フロー例(Entra ID → Slack・Zoom・Google)
最後に、①のAPI連携方式でよく使われるフローを、もう少し具体的に整理しておきます。
- 対象ユーザーの抽出
- Graphの /users クエリで
accountEnabled eq trueかつ必要なライセンスを持つユーザーだけを取得 - 「写真が設定されているユーザー」だけを対象にしたい場合は、一度 /photo に対してHEADやGETを試し、404を除外するなどの工夫をする
- Graphの /users クエリで
- 写真の取得と正規化
GET /users/{id}/photo/$valueでバイナリを取得- ImageMagickや.NETのSystem.Drawing、PythonのPillowなどで正方形トリミング+リサイズ+JPEG変換
- ユーザーIDの突合せ
- 原則としてメールアドレス(UPN)でSlack・Zoom・Googleユーザーを検索
- 別ドメインやエイリアスが混在する場合は、マッピングテーブルを用意
- 各アプリのAPI呼び出し
- Slack:
users.setPhotoでアップロード - Zoom:
POST /users/{userId}/pictureにmultipart/form-dataでアップロード - Google Workspace:Directory APIの
users.photos.updateを呼び出し
- Slack:
- ログ・監査・アラート
- 成功件数・失敗件数をメトリクスとして記録し、閾値を超えたらアラートを飛ばす
- 失敗ユーザーの一覧をCSVやストレージに保存し、別バッチでリトライ
将来の公式対応に期待しつつ、いま取れる現実解
まとめると:
- Entra ID標準のSCIMプロビジョニングでは、プロフィール写真の同期はサポートされていない(属性マッピングに写真を追加することは不可)。
- 写真同期を実現したい場合は、
- ① Graph APIで写真を取得し、各アプリの管理APIで直接更新する
- ② 写真URLをEntra IDのカスタム属性に格納し、SCIM / SAML / OIDCで文字列として渡す
- ③ アプリ独自の写真同期機能に任せる
- どの方式でも、画像フォーマット/権限/エラー処理/プライバシー保護を含めた運用設計が成功の鍵になる
Microsoftのドキュメントにもあるように、「写真属性をアプリにプロビジョニングできるようにしてほしい」という要望はフィードバックとして出すことが推奨されています。 将来的にEntra IDのSCIMクライアントが写真属性をサポートすれば、ここで紹介した多くのワークアラウンドは不要になるかもしれません。
それまでは、本記事で紹介したパターンを参考に、自組織の要件・スキルセット・セキュリティポリシーに合った「現実解」を設計してみてください。

コメント