platform-datamask-run について
Salesforce のサンドボックスで Data Mask を運用するスキルです。ポリシー設定からジョブ実行、マスク件数の報告まで扱い、ポリシー用 API とジョブ用 API の違いも明確にします。
主な機能
- 01マスキングポリシーの作成または特定
- 02オブジェクトとフィールドの処理設定
- 03サンドボックスのマスキングジョブ開始
- 04完了までのジョブ状態確認
- 05オブジェクト別のマスク件数報告
- 06実行中ジョブの中止
活用シーン
- 01Salesforce サンドボックスでの PII マスキング
- 02既存ポリシーを使ったジョブ実行
- 03オブジェクト別結果の確認
- 04実行中のマスキングジョブのキャンセル
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill platform-datamask-runインストール後、Claude Code で/platform-datamask-runを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/api-surface.mdpolicy-authoring.mdrun-and-abort.mdscripts/poll-job.sh詳細
platform-datamask-run:Salesforce Data Mask のエンドツーエンド運用
このスキルは、サンドボックスで Salesforce Data Mask 機能を運用するために使用します。PII フィールドに対するマスキングポリシーを設定し、マスキングジョブを開始して、完了状態になるまでポーリングし、マスキングされたレコードを報告し、実行中のジョブを中止します。
Data Mask はサンドボックス専用です。本番環境では、実行/中止用の REST エンドポイントは 403 を返します(実行時のサンドボックスガード)。開始前に、対象の組織がサンドボックスであることを確認してください。
このスキルが担当するタスク
- 設定済みのポリシーに対する Data Mask ジョブの実行
- マスキングジョブのステータスを完了までポーリング
- マスキングされたレコード数/オブジェクトごとの結果の報告
- 進行中のマスキング実行の中止(キャンセル)
- ジョブの実行対象となるポリシーの作成または特定
次の場合は、ほかのスキルに委任してください。
- 匿名化用の Apex を手作業で作成する →
platform-apex-generate - テストデータのシードまたは生成を行う →
platform-data-manage - 無関係なメタデータをデプロイする →
platform-metadata-deploy
最初に必ず押さえること:API サーフェスの対応表
最大の失敗要因は、Data Mask のエンティティが通常のデータ API オブジェクトだと思い込むことです。**そうではなく、エンティティごとに利用できるサーフェスが異なります。**何かを実行する前に、この表を覚えてください。ここで推測すると、3 秒で終わるはずのジョブが、30 分間の行き詰まりにつながります。
| エンティティ | 内容 | アクセス方法 |
|---|---|---|
DataMaskPolicy | マスキングポリシーの枠組み(設定) | Tooling API または Metadata API(薄い枠組みで、設定できるのは <label>/<description>/<runOnRefresh> のみ)— 標準 SOQL/sobject describe は不可 |
DataMaskPolicyObject | ポリシーの対象となるオブジェクト(任意の行フィルターを保持) | Tooling API のみ — クエリと挿入が可能。行のサブセットを対象にした「サンプル」実行では、ポリシーの sampleSize ではなく、ここで FilterEnabled と WhereCriteria を設定します |
DataMaskPolicyField | フィールドと、そのマスキング処理 | Tooling API のみ — クエリと挿入が可能。処理の列は MaskingCategory と MaskValue です |
DataMaskPolicyJobRun | ジョブ(1 回のマスキング実行) | 標準 SOQL — sf data query が使用できます |
DataMaskPolicyJobRunDtl | オブジェクトごとのジョブ詳細(子、FK は DataMaskPolicyJobRunId) | 標準 SOQL |
| 実行の開始 | — | REST 実行 API POST /services/data/v67.0/platform/data-resilience/data-mask/policies/{policyId}/run |
| 実行の中止 | — | REST 実行 API POST /services/data/v67.0/platform/data-resilience/data-mask/jobs/{jobRunId}/abort |
具体例:
sf sobject describe --sobject DataMaskPolicy→NOT_FOUND(標準 API に対して再試行しないでください)sf data query経由のSELECT ... FROM DataMaskPolicy→INVALID_TYPE- ポリシーのクエリには Tooling を使用します:
sf data query --use-tooling-api --query "SELECT Id, MasterLabel FROM DataMaskPolicy" - ジョブ/ジョブ詳細のクエリには標準 API を使用します:
sf data query --query "SELECT Id, Status FROM DataMaskPolicyJobRun"
コマンドの完全なリファレンス:references/api-surface.md。
リクエストに合ったワークフローを選択する
このスキルには2つの異なるワークフローがあります。ユーザーの依頼内容から最初にどちらか1つを選び、そのワークフローのすべての手順を実行してください。どちらにも省略可能な手順はありません。
| ユーザーの依頼内容 | 実行するワークフロー | 完了条件 |
|---|---|---|
| ポリシーを設定/編集してレコードをマスクし、マスクした件数を報告する | ワークフロー A — マスクして報告(以下) | 詳細行からマスク件数を報告した時点 |
| マスキング実行をキャンセル/中止する | ワークフロー B — 実行をキャンセル(この下) | ジョブのステータスが canceled と確認された時点 |
依頼の動詞を基準に選択してください。「ポリシーを作成/編集して実行する」「PII をマスクする」「マスクされたレコード数は?」なら、ワークフロー A のみです。「中止」「キャンセル」「実行を停止」なら、ワークフロー Bです。マスクして報告する依頼に中止は含まれません。中止を「実演」するために2つ目のジョブを開始しないでください。依頼されていない実行は、ジョブの所要時間が約5~10分になるため(A4 のプール下限を参照)、タスクが依頼されたマスク件数を報告し終える前にターン切れになる最大の原因です。
ワークフロー A — マスクして報告
A1. サンドボックスを確認し、組織のコンテキストを取得する
組織がサンドボックスであることを確認し、実行 API 呼び出し用のインスタンス URL とセッショントークンを取得します。
sf org display --target-org <alias> --json
A2. ポリシーを特定または作成する
既存のポリシーを再利用することを優先してください(最速で、デプロイも不要です)。
sf data query --use-tooling-api --target-org <alias> \
--query "SELECT Id, DeveloperName, MasterLabel FROM DataMaskPolicy"
必要な Contact PII を対象とするものがなければ、2段階の手順で作成します(DataMaskPolicy のメタデータ構造は薄いシェルであり、メンバーシップは Tooling で挿入します)。
- 薄いシェルを mdapi 形式でメタデータデプロイします(
--metadata-dirとpackage.xmlを使用します。ソース形式の--source-dirデプロイは「Could not infer a metadata type」エラーで失敗します)。シェルに含めるのは<label>、<description>、<runOnRefresh>のみです。これにより、A2 で必要となる有効なリビジョンを持つポリシーが作成されます。 DataMaskPolicyObject(オブジェクトごとに1つ)を Tooling で挿入し、その後DataMaskPolicyFieldの行を挿入します。各フィールド行の処理方法は、MaskingCategory(library)とMaskValue(first_name、email、phoneのような snake_case トークン)で指定します。MaskingRuleType列はありません。
挿入順序が重要です。有効なリビジョンを持たない Tooling 作成の親レコードがあると、子レコードの挿入は
INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITYエラーで失敗します。まずシェルをメタデータデプロイしてください。
詳しい手順と MaskValue トークンの一覧については、references/policy-authoring.md を参照してください。各フィールドに適切な MaskValue を選択し、一律に置き換えないでください。
A3. マスキング実行を開始する(REST 実行 API)
printf '{}' > ./empty-body.json
sf api request rest \
"/services/data/v67.0/platform/data-resilience/data-mask/policies/{policyId}/run" \
--method POST --body @./empty-body.json --target-org <alias>
エンドポイントには空の JSON ボディ({})が必要です。API がペイロードを受け取らない場合でも、sf api request rest で POST を実行するには --body が必要です。ファイルは @ 接頭辞を付けて渡してください(--body @./empty-body.json)。接頭辞を付けないと、リテラルのパスがボディとして送信され、API は JSON_PARSER_ERROR を返します。200 は jobRunId、policyId、status(実行 API のステータスは RUNNING などの大文字)と message: "Job started successfully" を返します。409/CONFLICT は、そのポリシーですでに実行中のジョブがあることを意味します。
ポーリングを始める前に、今すぐ
report.mdを作成してください。最後まで待たないでください。 マスキングジョブには数分かかります(下記参照)。このタスクが0点になる最も一般的な原因は、ポーリング中にターンが終了し、出力ファイルがまったく作成されていないことです。jobRunIdを取得したらすぐに、判明している内容(ポリシーの ID/ラベル、実行コマンド、jobRunId、RUNNINGステータス、マスク件数は「完了をポーリング中…」というプレースホルダー)をすべて記載してreport.mdを作成してください。その後、ジョブの完了時に同じファイルを更新します。「実行中」と記載されたレポートが存在するほうが、ファイルがないよりもよいです。件数を捏造するのはどちらよりも悪いことです。件数を入力するのは、詳細行から取得した後だけにしてください(A5)。
A4. 終端状態までポーリングする(標準 SOQL)
DataMaskPolicyJobRun.Status が終端値に達するまでポーリングします。実行途中の状態を最終状態として報告してはいけません。
- 実行途中(処理前):
pending、scheduled— ジョブはキューに入っていますが、まだ中止できません - 実行途中(処理中):
running— 中止が成功するのはこの状態だけです - 終端:
completed、completed_with_errors、failed - 中止後の状態:
canceled(「l」は1つ)
pending は running ではありません。 pending/scheduled のジョブを中止すると、409 CONFLICT
(「Job is not in a running state ... status=PENDING」)が返されます。中止するには、ステータスが文字どおり
running になるまでポーリングする必要があります。Workflow B を参照してください。
ジョブの処理には時間がかかります。数分を見込み、同梱のスクリプトでポーリングしてください。 Data Mask は バックエンドのプール/スケジューラーで実行され、最低でも約5~10分かかります。小規模な(20行の)ジョブでも、実行開始から数分間は通常、終端状態に達せず、詳細行も出力されません。これは行数に比例しない固定のオーバーヘッドです。実行計画に考慮してください。最大の失敗要因は、ジョブが即時に完了すると考えて短い間隔でポーリングし、タイムアウトしたり、「まだ保留中」と報告したりすることです。
scripts/poll-job.sh を単一のコマンドとして実行してください。SOQL のポーリングループを自分で作ってはいけません。
bash scripts/poll-job.sh <alias> <jobRunId> # defaults: cap 600s (10 min), 20s interval
このスクリプトは低頻度の間隔で待機し、実際の詳細行が現れた時点で直ちに処理を終了し、終端シグナル(completed/failed/canceled)を標準出力に出力して、0(またはタイムアウト時に1)で終了します。**一度だけ呼び出して結果を確認してください。独自の再試行ループで囲んではいけません。**また、10秒未満の間隔でポーリングしないでください(それ以上早く完了しないジョブに対してツール呼び出しを消費するだけです)。
実際の状態を示すのは親のステータスではなく詳細行です。 親の
DataMaskPolicyJobRun.Status は遅延することがあり、マスキングが実際に完了した後もしばらく
pending/running と表示される場合があります。total_records_masked(または
completed)の DataMaskPolicyJobRunDtl 行が存在すれば、マスキングは完了しています。
poll-job.sh には、間隔とタイムアウトの制限、実際の状態を示す詳細行が現れた際の早期終了、終端シグナルの終了コードがすでに組み込まれています。これらをインラインで再実装してはいけません。ポーリングスクリプトを一度実行し、その終了シグナルを確認してから、ポーリング前に作成したひな形の report.md を、終端ステータスと A5 のマスキング件数で更新してください。
A5. ジョブの詳細オブジェクトから結果を報告する
親ジョブには全体のステータスが記録されますが、オブジェクトごとのマスキング件数は、
DataMaskPolicyJobRunId で関連付けられた子オブジェクトの DataMaskPolicyJobRunDtl に記録されます。作り上げた件数ではなく、具体的な件数を報告してください。
sf data query --target-org <alias> \
--query "SELECT Id, DataMaskPolicyJobRunId, Status FROM DataMaskPolicyJobRunDtl WHERE DataMaskPolicyJobRunId = '<jobRunId>'"
行に文字どおり示されている内容だけを報告し、実際以上の粒度を主張しないでください。 詳細行は
オブジェクト単位の status_update エントリであり(オブジェクトの loaded、completed、
total_records_masked。例:Contact)、フィールド単位の行ではありません。そのため、オブジェクト単位での成功は観測された事実として(「Contact:27/27件をマスキング済み、エラー行は0件」)報告します。一方、フィールド単位での成功は直接観測された事実ではなく、推論として述べてください。「フィールド単位のエラー行は返されなかったため、失敗したフィールドは報告されていません」と記載し、「5つのフィールドすべてが成功しました」とは書かないでください(その主張を裏付けるフィールド単位の成功行はデータに含まれていません)。推論を観測事実として誇張することが、ここで最もよくある事実性の誤りです。
Workflow B — 実行をキャンセルする
マスキング実行の中止/キャンセルが依頼された場合は、このワークフローを使用してください。対象は現在進行中の実行です。中止は実行中のジョブに対してオンデマンドで行う操作であり、キャンセルするためだけにジョブを開始することはありません。手順 B1~B4 はすべて必須です。
B1. Sandbox を確認し、キャンセルする実行を特定する
組織が sandbox であることを確認し(sf org display)、中止する実行の jobRunId を取得します。これは、ユーザーがキャンセルを求めている実行です。DataMaskPolicyId も記録してください。中止できる時間を逃した場合(B2 の exit 3/exit 1)に、置き換えの実行を開始するために必要です。実行を開始したばかりなら、その ID を使用します。そうでなければ、実行中の実行をクエリします。
sf data query --target-org <alias> \
--query "SELECT Id, Status, DataMaskPolicyId FROM DataMaskPolicyJobRun ORDER BY CreatedDate DESC LIMIT 5"
選択した実行の DataMaskPolicyId(8dm プレフィックス)を控えてください。これは A3 で必要な <policyId> です。
B2. ジョブが running になるまで待機する(中止可能な唯一の状態)
中止できるのは、DataMaskPolicyJobRun.Status が running の間だけです。pending/scheduled のジョブでは 409 が返され、終端状態のジョブはすでに完了しています。付属のポーラーを running モードで使い、running になるまでポーリングします。このモードはステータスが running になった時点で終了します(終端状態まで待機するデフォルトモードとは異なります)。そのため、中止可能な時間を過ぎても待ち続けることはありません。
POLL_MODE=running bash scripts/poll-job.sh <alias> <jobRunId> 900 15
上限は **900s(15 min)**です。スケジューリングにかかる最低時間の約 5~10 分を上回るため、起動に時間がかかるジョブも捕捉できます。すべての終了コードに対応してください。
- Exit
0(runningを出力)→ すぐに B3 に進みます。 - Exit
3→runningを捕捉する前にジョブが終端状態になり、中止可能な時間を逃しました。B1 で記録したポリシーを指定して新しい実行を開始し(その<policyId>を使って A3 を実行)、ここに戻って新しいjobRunIdをポーリングします。 - Exit
1(タイムアウト — 上限に達しました)→ ジョブのステータスを再クエリします。
まだ終端状態でない(sf data query --target-org <alias> \ --query "SELECT Id, Status FROM DataMaskPolicyJobRun WHERE Id = '<jobRunId>'"pending/scheduled/running)場合は、待機を続けるためにポーラーをもう一度だけ(同じコマンドで)実行します。runningなら B3 に進みます。終端状態なら exit 3 と同様に扱います。新しい実行を開始し(B1 の<policyId>を使って A3 を実行)、新しいジョブをポーリングします。
プールの最低時間は約 5~10 分あるため、通常 running の時間は数分続き、捕捉する時間はあります。遅延を設けずにポーリングしないでください。
現在実行中の実行がない場合(ジョブがすでに完了している場合、または実行からキャンセルまでの流れを最初から再現する必要がある場合)は、まず A3 で実行を開始してからここに戻り、
runningになるまでポーリングして、その実行中のジョブを中止します。すでに終端状態になった古いジョブを、キャンセルを「示す」ために使わないでください。中止の対象は、実際に実行中のジョブでなければなりません。
B3. 実行 API で中止する
ジョブレコードの DML/削除ではなく、実行 API で中止します。
sf api request rest \
"/services/data/v67.0/platform/data-resilience/data-mask/jobs/{jobRunId}/abort" \
--method POST --body @./empty-body.json --target-org <alias>
上記と同様に、@ プレフィックス付きファイルを介して空の JSON ボディ({})を指定します。200 が返ると、status: "CANCELED"(大文字。実行 API から返されます)と message: "Job abort requested" が含まれます。409 は、ジョブが running 状態ではなかったことを意味します(通常はまだ pending/scheduled です)。B2 に戻ってポーリングを続けてください。
B4. キャンセルを確認して報告する
キャンセルは非同期です。DataMaskPolicyJobRun を再クエリし、Status = canceled(小文字。SOQL から返されます)であることを確認してから、中止が成功したと報告してください。次の点を確認します。
- クエリしたステータスが
runningの間に、実行中のジョブを対象として中止したことを確認しました。 - 中止後に
DataMaskPolicyJobRunを再クエリし、Status = canceledを確認しました。
シグナルの強いルール
| ルール | 理由 |
|---|---|
各 sf コマンドは単独で実行してください。パイプやリダイレクト(|、| python3、| grep、2>&1、2>/dev/null、> file)は一切追加しないでください | sf ... --json は標準出力に整形済みの JSON を出力するため、そのまま読み取れます。リダイレクトやパイプを使うと、回避不能なシェル安全ガードが作動し、実行全体がタイムアウトまで停止します。python3/grep/jq で後処理したり、標準エラー出力を抑制したりしないでください。コマンドが警告を出力しても、標準出力の --json ペイロードは有効なので、そのまま解析してください |
DataMaskPolicy* 設定オブジェクトには、標準 SOQL や sobject describe を使用しないでください | INVALID_TYPE/NOT_FOUND が返されます。Tooling API または MDAPI を使用してください |
マスク済み件数は DataMaskPolicyJobRunDtl から読み取り、決して捏造しないでください | 子詳細がオブジェクトごとの結果の正しい情報源です |
終端状態は completed/completed_with_errors/failed のみです | running/scheduled を最終状態として報告するのは誤りです |
| 中止には run API の中止エンドポイントのみを使用してください | ジョブレコードに対する DML/削除は実際の中止ではなく、状態を破損させます |
中止後は必ずステータスを再クエリし、canceled を確認してください | 中止呼び出しが 200 を返しても、ジョブが停止した証明にはなりません |
| Data Mask はサンドボックスでのみ実行できます | run/abort エンドポイントは本番環境では 403 を返します |
API バージョン v67.0 以降を使用し、/connect/ セグメントは含めないでください | run/abort エンドポイントは /services/data/v67.0/platform/data-resilience/data-mask/... です。connect セグメントを含めたり、v67 より前のバージョンを使ったりすると NOT_FOUND が返されます |
scripts/poll-job.sh でポーリングしてください(呼び出しは 1 回)。手作業で SOQL ループを作成しないでください | スクリプトは試行回数に上限を設け、正確な詳細行に基づいて早期終了します。遅延する親ステータスを手動ループで確認することが、レポートがないまま実行がタイムアウトする最大の原因です |
注意点
| 問題 | 解決策 |
|---|---|
sf sobject describe DataMaskPolicy → NOT_FOUND | Tooling/MDAPI エンティティです。--use-tooling-api を付けてクエリし、標準 API では再試行しないでください |
SELECT ... FROM DataMaskPolicy → INVALID_TYPE | 同じ原因です。ポリシーには Tooling API を使用し、標準 API は DataMaskPolicyJobRun/Dtl にのみ使用してください |
実行開始が 409 を返す | そのポリシーではすでに実行中です。既存の実行をポーリングするか、完了するまで待ってください |
中止が 409「status=PENDING」を返す | ジョブはまだ pending/scheduled で、running にはなっていません。ポーリングを続け、running と表示されてから中止してください。中止を諦めないでください |
| 中止する前に小さなジョブが完了する | 小さなサンドボックスでは running の期間は数秒です。新たに実行を開始してこまめにポーリングしてください。フローを偽装するために、以前中止したジョブを代わりに使わないでください |
中止が 200 を返すが、SOQL のステータスはまだ running | キャンセルは非同期です。SOQL のステータスが canceled になるまでポーリングを続け、早まって成功を報告しないでください |
Run API は CANCELED と表示するが、SOQL は running と表示する | 大文字小文字と表示先が異なります。Run API は大文字、SOQL の選択リストは小文字です。終端状態には SOQL の値を信頼してください |
| ジョブが瞬時に「完了」した | 再確認してください。scheduled は終端状態ではありません。実際に終端値が表示されるまでポーリングしてください |
run/abort エンドポイントが NOT_FOUND | パスは /services/data/v67.0/platform/data-resilience/data-mask/... である必要があります。/connect/ セグメントを含めず、バージョンは v67.0 以降(Core 262)にしてください。references/api-surface.md を参照してください |
出力形式
実行したワークフローのセクションのみを報告してください。もう一方のセクションは追加しないでください。**簡潔にまとめてください。各コマンドは該当する手順で一度だけ示し、すでに示した呼び出しを繰り返す「コマンドログ全体」を後から追加しないでください。**文章よりコンパクトな表を優先してください。読者が最初の画面で重要な結果を確認できるようにします。
ワークフロー A(マスクとレポート):
- 使用したポリシー(ID とラベル、再利用か新規作成か)
- 実行 — ジョブ ID、最終的な終了ステータス、マスクされたレコード数(詳細オブジェクトから取得)。ポーリングループは1行にまとめます(例:「5回ポーリング、
running→completed」)。ポーリングごとの行は出力しないでください。 - オブジェクトごとの結果 —
DataMaskPolicyJobRunDtlから取得します。行に実際に含まれるオブジェクトレベルの件数を報告してください。フィールドレベルのエラー行がない場合は、フィールドごとの成功を主張するのではなく、推論として「フィールドレベルのエラーは報告されていません」と記載してください。正確な表現については A5 を参照してください。 - 実行したコマンド — すでに上で示したものはここでは繰り返さず、まだ示していないものだけを列挙してください。手順全体を再掲しないでください。
ワークフロー B(実行のキャンセル):
- ジョブのキャンセル — ジョブ ID、中断時に
runningだったこと、中断が run-API の abort エンドポイント経由で実行されたこと、再クエリでcanceledステータスを確認したこと。 - 実行したコマンド — 上記と同様に、再掲しないでください。
正確性を高めるための注意事項:
- 実行/中断 REST レスポンスは15文字の
jobRunId(例:1aGXK0000000uob)を返します。SOQL は同じレコードの18文字形式(例:1aGXK0000000uob2AA)を返します。同じジョブなので、両方が出てくる場合はその旨を明記し、別々の ID として示さないでください。 - 実際にクエリしていないマスク件数、フィールドごとの結果、終了ステータスを主張しないでください。レポートに記載するすべての数値は、コマンドログに示されたクエリ結果に基づく必要があります。
スキル間の連携
| 必要なこと | 委任先 | 理由 |
|---|---|---|
| マスク対象となる現実的な PII レコードのシード | platform-data-manage | テストデータの作成 |
| カスタム匿名化 Apex の作成 | platform-apex-generate | Apex の作成 |
| ポリシーのメタデータを組織にデプロイ | platform-metadata-deploy | メタデータのデプロイ |
参照ファイル一覧
| ファイル | 参照するタイミング |
|---|---|
references/api-surface.md | エンティティごとの正確な API サーフェス、すべての CLI コマンド、実行/中断 REST エンドポイント、ステータスの選択肢 |
references/policy-authoring.md | 2段階の作成手順(MDAPI の薄いシェル → Tooling オブジェクト/フィールドの挿入)と、MaskingCategory/MaskValue の処理表 |
references/run-and-abort.md | 実行 → ポーリング → レポート → 再実行 → 中断の全手順とレスポンス例 |
scripts/poll-job.sh | 終了ステータスを待機する(デフォルト)、または POLL_MODE=running を指定して中断可能な running 状態を待機する、上限付きポーラー |