先ほどこちらの記事を翻訳しました。中ではデモ用のリポジトリが紹介されていました。
こちらのデモリポジトリをウォークスルーしてみます。
00 - Setup
ワークスペースファイルからデータを読み込んでテーブルを作成するので、サーバレスコンピュートではなくクラシックコンピュートを使います。
事前準備
ABACベータ版を有効にするには、以下の手順を実施してください:
- ワークスペース管理者として、Databricksワークスペースの上部バーでユーザー名をクリックします。
- メニューから「プレビュー」を選択します。
- 「属性ベースアクセス制御(ABAC)」のトグルをオンにします。
制限事項
ABACプレビュー段階では、以下の制限があります:
- テーブルのMODIFY権限を持つユーザーが、ASSIGNタグポリシー権限を持たなくても、管理タグが付与されたカラムを削除できます。これによりテーブル構造が変更され、該当カラムに紐づくABACポリシーが無効になる可能性があります。
- ABACベータはワークスペース単位で有効化されます。ベータが有効化されていないワークスペースからカタログにアクセスした場合、ABACポリシーは強制されません。
- 必要なDelta Sharing権限を持つユーザーは、ABACポリシーで保護されたテーブルをDelta共有できます。ポリシーは受信者のアクセスを制御しません。
- ビューはサポートされていません。
- 外部カタログはサポートされていません。
- マテリアライズドビューとストリーミングテーブルはサポートされていません。
- オブジェクト階層内の特定のカラムや行に対して、複数のカラムマスクや行フィルターを同時に適用することはできません。複数適用するとテーブルにアクセスできなくなる場合があります。
- ABACはAWS GovCloudやFedRAMP moderate準拠コントロールが有効なワークスペースではサポートされていません。
dbutils.widgets.removeAll()
dbutils.widgets.text("catalog", "takaakiyayoi_catalog")
dbutils.widgets.text("database", "abac")
dbutils.widgets.text("volume", "input")
## 必要に応じてこれらの変数を変更してください
catalog = dbutils.widgets.get("catalog")
database = dbutils.widgets.get("database")
volume = dbutils.widgets.get("volume")
print(f"{catalog}.{database}")
# 現在のノートブックパスを取得
current_path = dbutils.notebook.entry_point.getDbutils().notebook().getContext().notebookPath().get()
# 現在のパスを表示
display(current_path)
'/Users/takaaki.yayoi@databricks.com/abac-demo/notebooks/00 - Setup'
PII(個人情報)を含むデータセットの読み込み
# データセットのファイルパスを作成
file_path = f"file:/Workspace/{current_path}/../../data/pii_dataset.csv"
# データを読み込み、表示
df = spark.read.csv(f"{file_path}", header=True, inferSchema=True)
df.display()
# テーブルとして保存
df.write.saveAsTable(f"{catalog}.{database}.students")
電話番号の列があること、.govで終わるアドレスがあることを覚えておいてください。
データ分類の有効化(オプション)
データ分類機能は、カタログ内のテーブルを自動的に分類・タグ付けします。分類結果は、下流のポリシーで行レベル・カラムレベルのセキュリティ(ABAC)を強制するためのタグとして利用できます。
手順:
- この機能はUIから有効化できます
- 現時点ではプログラムから有効化できません
制限事項:
- ビューはサポートされていません
- Delta Sharingカタログはサポートされていません
- サポートされる分類クラスは限定的ですが、今後拡張予定(カスタムも含む)
出典: https://learn.microsoft.com/ja-jp/azure/databricks/lakehouse-monitoring/data-classification
今回は次のノートブックで手動で付与するのでこの手順はスキップしています。
01 - Apply tags and policies
%python
dbutils.widgets.removeAll()
%python
dbutils.widgets.text("catalog", "takaakiyayoi_catalog")
dbutils.widgets.text("database", "abac")
dbutils.widgets.text("warehouse_id", "23ed98665852ebad")
dbutils.widgets.text("client_id", "1234")
%python
## 必要に応じてこれらの変数を変更してください
catalog = dbutils.widgets.get("catalog")
database = dbutils.widgets.get("database")
warehouse_id = dbutils.widgets.get("warehouse_id")
client_id = dbutils.widgets.get("client_id")
print(f"{catalog}.{database}")
タグの作成と適用(任意)
データ分類を有効にしている場合は、このステップをスキップして「ポリシー(ABAC)の作成」に進んでください。
SET TAG ON COLUMN ${catalog}.${database}.students.name `class.name`;
SET TAG ON COLUMN ${catalog}.${database}.students.username `class.name`;
SET TAG ON COLUMN ${catalog}.${database}.students.address `class.location`;
SET TAG ON COLUMN ${catalog}.${database}.students.phone `class.phone_number`;
SET TAG ON COLUMN ${catalog}.${database}.students.email `class.email_address`;
SQLクエリーには$パラメーターが含まれています。パラメーターマーカー構文に移行と言ったメッセージが出てきますが、現状は問題なく動きますのでこのままにします。:param構文への移行が推奨されていますが、この構文ではこの後の関数定義に対応できません。
カタログエクスプローラでテーブルにアクセスするとタグを確認できます。

マスキング関数の作成
-- 入力値を完全にマスクした値で返す関数
CREATE FUNCTION ${catalog}.${database}.mask_phone(value STRING)
RETURN '***-**-****' ;
-- pubsec(公共部門)職員の行を非表示にする関数
CREATE OR REPLACE FUNCTION ${catalog}.${database}.pubsec_email(email STRING)
RETURNS BOOLEAN
RETURN NOT LOWER(email) like '%.gov'
動作確認します。
SELECT ${catalog}.${database}.pubsec_email('roger@pubsec.gov')
.govで終わるメールアドレスの場合はfalseになります。
SELECT ${catalog}.${database}.mask_phone('123-45-6789')
ポリシーの作成
ABACルールのテスト用サービスプリンシパル(SP)の作成
%python
import time
from databricks.sdk import WorkspaceClient, AccountClient
from databricks.sdk.service import iam
w = WorkspaceClient()
a = AccountClient()
spn = w.service_principals.create(
display_name=f"sp-abac-demo-{catalog}",
groups=[],
)
spn
%python
spn_id = spn.application_id
spn_id
'737b8711-bd51-4bc2-8ecb-06152123efcc'
SPのIDをウィジェットにコピー&ペースト
SELECT :spn_id
これでSQLからSPを操作できるようになります。
GRANT SELECT ON SCHEMA ${catalog}.${database} TO `${spn_id}`;
GRANT USAGE ON SCHEMA ${catalog}.${database} TO `${spn_id}`;
CREATE POLICY hide_pii_rows
ON TABLE ${catalog}.${database}.students
COMMENT 'Hide rows from pubsec employees'
ROW FILTER ${catalog}.${database}.pubsec_email
TO `${spn_id}`
FOR TABLES
MATCH COLUMNS
hasTag('class.email_address') AS email_address
USING COLUMNS (email_address);
CREATE POLICY mask_information
ON TABLE ${catalog}.${database}.students
COMMENT 'Mask phone numbers'
COLUMN MASK ${catalog}.${database}.mask_phone
TO `${spn_id}`
FOR TABLES
MATCH COLUMNS
hasTag('class.phone_number') as phone_number
ON COLUMN phone_number;
hasTagでタグclass.email_addressやclass.phone_numberが付与されているカラムをターゲットにしているところが肝です。これによって、タグとユーザーの組み合わせでアクセスコントロールを適用することができます。
これでテーブルに対するタグポリシーが定義されます。
SHOW POLICIES ON TABLE ${catalog}.${database}.students;
| Policy Name | Policy Type | Catalog | Schema | Table | Comment |
|---|---|---|---|---|---|
| hide_pii_rows | ROW_FILTER | takaakiyayoi_catalog | abac | students | Hide rows from pubsec employees |
| mask_information | COLUMN_MASK | takaakiyayoi_catalog | abac | students | Mask phone numbers |
現在のユーザーでデータを選択
カラム・行フィルターは適用されません。
SELECT * FROM ${catalog}.${database}.students
テスト
サービスプリンシパルでテストします。
サービスプリンシパル用のクライアントシークレット作成
サービスプリンシパル用のクライアントシークレットを作成してください。
- 設定 → Identity and access → Service principals → Secrets
- クライアントIDをウィジェットにコピー&ペースト
- クライアントシークレットは値入力時にコピー&ペースト
%python
client_id = dbutils.widgets.get("client_id")
client_id
'737b8711-bd51-4bc2-8ecb-06152123efcc'
SPでの認証に使用するクライアントシークレットを入力します。
%python
import getpass
client_secret = getpass.getpass("Enter service principal oauth secret: ")
print("Secret received.")
元のソースですとクエリー結果を取得できるまで待っていなかったので修正しています。あと、サービスプリンシパルがSQLウェアハウスにアクセスできるように使用可能の権限を付与する必要があります。
%python
import pandas as pd
from databricks.sdk import WorkspaceClient
import time
# 実際のサービスプリンシパルの詳細に置き換えてください
workspace_url = dbutils.notebook.entry_point.getDbutils().notebook().getContext().apiUrl().get()
# サービスプリンシパル認証でWorkspaceClientを初期化
w = WorkspaceClient(
host=workspace_url,
client_id=client_id,
client_secret=client_secret
)
# 実行するクエリを定義
query = f"SELECT * FROM {catalog}.{database}.students"
# execute_statementを使用してクエリを実行
# ウェアハウスID
statement_response = w.statement_execution.execute_statement(query, warehouse_id)
#print(statement_response)
# クエリの実行が完了するまで待機
while statement_response.status.state == 'PENDING':
time.sleep(5) # 5秒待機
statement_response = w.statement_execution.get_statement(statement_response.statement_id)
if statement_response.result:
# ステートメントレスポンスからデータ配列を抽出
data_array = statement_response.result.data_array or []
if data_array:
# データ配列をpandas DataFrameに変換
df = pd.DataFrame(data_array)
# DataFrameを表示
display(df)
else:
print("クエリは空のデータセットを返しました。")
else:
print("クエリの実行に失敗しました。")
govを含むメールアドレスの行が表示されていないこと、phone_numberの列がマスキングされていることがわかります。










