AWS Glue Studio だけでノーコード PII マスキングをやってみる(Detect Sensitive Data)
TL;DR
- Glue Studio の Detect Sensitive Data(Detect PII) トランスフォームだけで、スクリプト無しのデータマスキングができる
- 構成は S3(入力) → Detect PII → S3(出力) の 3 ノードで OK
- 本記事のサンプル JSON を S3 に置き、チェックリスト通りに設定すればそのまま再現可能
つくるもの
- 検出: Include all available types (256)
- マスク: ****** に置き換え
- 出力: JSON
前提
- AWS アカウント(Glue/Athena/S3 の基本操作ができる権限)
- Glue Version 4.0 以上(部分マスク等の機能が安定)
1. テストデータ(コピーして pii_sample.json
として保存)
※すべてAIで生成したダミーデータ。実在の個人とは無関係です。
[
{
"customer_id": "1",
"full_name": "鈴木 太郎",
"email": "user61_92346728592a90ce@example.com",
"phone": "098-6390-6191",
"birth_date": "1987-05-04",
"address": "埼玉県川崎市中原区1-7-19",
"national_id": "674377254175"
},
{
"customer_id": "2",
"full_name": "高橋 花子",
"email": "user11_9ad86a4b82b15b50@example.com",
"phone": "097-7420-2184",
"birth_date": "1999-06-19",
"address": "神奈川県札幌市中央区4-12-17",
"national_id": "926382426486"
},
{
"customer_id": "3",
"full_name": "佐藤 太郎",
"email": "user27_4f5085e4592a90ce@example.com",
"phone": "073-9792-9433",
"birth_date": "1979-03-16",
"address": "埼玉県川崎市中原区2-9-2",
"national_id": "367203173155"
}
]
2. S3 に配置
- 入力:
s3://<bucket-name>/glue-pii-mask/input/pii_sample.json
- 出力:
s3://<bucket-name>/glue-pii-mask/output/
3. Glue Studio でジョブを作成(ノーコード)
- Glue Studio → Create job → Visual ETL
- ノード構成を S3/Data Catalog → Detect Sensitive Data → S3 にする
- 各ノードを以下の通り設定
Source(Amazon S3)
- 形式:
JSON
- スキーマ: Data Catalog テーブルを利用するか、プレビューから推論可能
Transform — Detect Sensitive Data(Detect PII)
検出モード
-
Find columns that contain sensitive data(列レベル / 高速・低コスト)
構造化カラム(email/phone/national_id)向け -
Find sensitive data in each row(セル内容スキャン / 厳密・コスト高)
自由記述(address など)向け
今回の実験では、後者を利用しています。
対象エンティティ
- まずは Include all available types (256)
→ 不足時は Select specific patterns や Custom pattern を追加
例:12 桁の独自 ID → 正規表現\b\d{12}\b
結果を少し書いてしまいますが、AWSのデフォルト設定では、「住所」や「名前」のマスキングはできませんでした。
必要に応じてカスタムパターンを追加する必要があります。
既定アクション(Global action)
-
Enrich data with detection results
→ 検出結果を含む列(例: 検出したエンティティや位置情報)を元データに追加 -
Redact detected text
→ 一括マスク -
Partially redact detected text
→ 先頭/末尾を残す -
Apply cryptographic hash
→ SHA-256
Fine-grained action overrides(列別の上書き例)
今回は指定していませんが、以下のような設定が可能です。
* `full_name`: Partially redact(先頭 1 文字のみ可視)
* `email`: Apply cryptographic hash
* `phone`: Partially redact(末尾 4 桁を残す)
* `address`: Detect PII in each cell に切り替え、Redact で数字のみマスクしたい場合は Custom pattern を併用
* `national_id`: Custom pattern(`\b\d{12}\b`)+ Apply cryptographic hash
同じ列に「マスク+ハッシュ」を同時適用することはできません。別列に出力するか、ジョブを段階分割します。
Target(Amazon S3)
- 形式: Parquet(推奨。圧縮/クエリ効率◎)または JSON
- 圧縮形式: GZIP
- 出力先:
s3://<bucket-name>/glue-pii-mask/output/
- (任意)Partition keys:
birth_year
など 非 PII 列
出力形式として CSV は推奨できません。
理由は、Detect Sensitive Data Transform の処理結果に DetectedEntities
という Map 型フィールドが含まれるためです。
CSV 形式はネストされたデータ型(Map, Array など)をサポートしていないため、出力時に以下のエラーが発生します。
Job 設定
- Glue version: 5.0
- Worker type: 小規模なら G.1X
- Job bookmark: 検証中は Disabled(毎回全件処理)
4. 結果
S3 出力(JSON)は長いので、本文では要点のみ記します(末尾に掲載)。
以下の知見が得られました。
-
デフォルトだけでは「氏名」や「住所」がマスクされないことがある
- プリセットのエンティティに含まれないケースがあり、カスタムエンティティ(正規表現+コンテキスト語)を追加して対象に含める。
- あるいは Transform 後に PySpark/ApplyMapping で対象列を強制マスク(既知列なら実務上はこれが堅い)
-
DetectedEntities
(Map 型)が巨大化しやすい- 監査目的で保持するのでなければ、最終出力前に
DropFields
等で削除する。 - CSV に落とす必要がある場合は
DetectedEntities
をto_json()
で文字列化する(Map をそのまま CSV 出力は不可)
- 監査目的で保持するのでなければ、最終出力前に
よくあるつまずき
-
列レベル検出だけで自由記述の PII を取りこぼす
→address
やcomment
は セル内検出を併用 -
誤検知/過検知
→ 感度(High/Low)と Fine-grained overrides / Custom pattern で調整 -
出力がどんどん増える
→ 出力バケットの上書き方針 or 日付パーティションを設計 -
コスト最適化
→ 列レベル検出+サンプリング率(Sample portion)で調整。自由記述だけセル内検出
参考資料(公式)
- AWS Glue Studio — Detect Sensitive Data(Detect PII)
- AWS Glue Studio(Detect and process sensitive data)
- AWS Glue Studio(Visual ETL の基本)
マスキング結果
{
"customer_id": "2",
"full_name": "高橋 花子",
"email": "******",
"phone": "******",
"birth_date": "1999-06-19",
"address": "神奈川県札幌市中央区4-12-17",
"national_id": "******",
"DetectedEntities": {
"email": [
{
"entityType": "EMAIL",
"actionUsed": "REDACT",
"start": 0,
"end": 35
},
{
"entityType": "LUXEMBOURG_PASSPORT_NUMBER",
"actionUsed": "REDACT",
"start": 24,
"end": 31
}
],
"national_id": [
{
"entityType": "SRI_LANKA_NATIONAL_IDENTIFICATION_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "FRANCE_NATIONAL_IDENTIFICATION_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "USA_DRIVING_LICENSE",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "BANK_ACCOUNT",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "CANADA_PERSONAL_HEALTH_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "LUXEMBOURG_TAX_IDENTIFICATION_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "JAPAN_DRIVING_LICENSE",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "CYPRUS_DRIVING_LICENSE",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "FRANCE_DRIVING_LICENSE",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "INDIA_AADHAAR_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "LIECHTENSTEIN_TAX_IDENTIFICATION_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "SPAIN_SSN",
"actionUsed": "REDACT",
"start": 0,
"end": 12
}
],
"phone": [
{
"entityType": "UK_PHONE_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 13
},
{
"entityType": "CHINA_PHONE_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 13
}
]
}
}
{
"customer_id": "3",
"full_name": "佐藤 太郎",
"email": "******",
"phone": "******",
"birth_date": "1979-03-16",
"address": "埼玉県川崎市中原区2-9-2",
"national_id": "******",
"DetectedEntities": {
"email": [
{
"entityType": "EMAIL",
"actionUsed": "REDACT",
"start": 0,
"end": 35
},
{
"entityType": "LUXEMBOURG_PASSPORT_NUMBER",
"actionUsed": "REDACT",
"start": 24,
"end": 31
}
],
"national_id": [
{
"entityType": "SRI_LANKA_NATIONAL_IDENTIFICATION_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "FRANCE_NATIONAL_IDENTIFICATION_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "USA_DRIVING_LICENSE",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "BANK_ACCOUNT",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "CANADA_PERSONAL_HEALTH_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "LUXEMBOURG_TAX_IDENTIFICATION_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "JAPAN_DRIVING_LICENSE",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "CYPRUS_DRIVING_LICENSE",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "FRANCE_DRIVING_LICENSE",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "INDIA_AADHAAR_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "LIECHTENSTEIN_TAX_IDENTIFICATION_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 12
},
{
"entityType": "SPAIN_SSN",
"actionUsed": "REDACT",
"start": 0,
"end": 12
}
],
"phone": [
{
"entityType": "UK_PHONE_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 13
},
{
"entityType": "CHINA_PHONE_NUMBER",
"actionUsed": "REDACT",
"start": 0,
"end": 13
}
]
}
}