Azure Databricks から ADLS Gen2 に OAuth(サービス プリンシパル+クライアントシークレット)で接続しようとしたときに、突然 [CONFIG_NOT_AVAILABLE] が出てハマるケースはとても多いです。本記事では、このエラーが意味するところから、具体的な設定例、切り分け手順、再発防止のベストプラクティスまでを、現場で使えるレベルで詳しく解説します。
Databricks と ADLS Gen2 で発生する CONFIG_NOT_AVAILABLE とは
まずは、今回の主役であるエラー内容をおさらいしておきましょう。
[CONFIG_NOT_AVAILABLE] Configuration fs.azure.account.auth.type.<storage-account>.dfs.core.windows.net is not available. (SQLSTATE: 42K0I)
典型的なシナリオは次のようなものです。
- Databricks ノートブックから ADLS Gen2 へアクセスしたい
- 認証方式は OAuth(サービス プリンシパル+クライアントシークレット)
spark.conf.set(...)で必要な設定を追加しているつもりdbutils.fs.ls("abfss://...")やspark.read...を実行したタイミングで上記エラーが発生- クラスタを再作成しても直らない、アプリ登録をやり直しても直らない
ここで重要なのは、このエラーは「認証に失敗している」のではなく、「そもそも必要な設定が見つからない」ことを示している点です。つまり、Azure 側の権限やロール設定以前の話で、Spark / Hadoop 設定のキー名そのものがズレている、または反映されていない、という状況がほとんどです。
CONFIG_NOT_AVAILABLE が示していること
Databricks から ADLS Gen2 に接続する際、内部では ABFS ドライバが Spark 設定を参照しにいきます。そのときに、たとえば次のようなキーを探します。
fs.azure.account.auth.type.<storage-account>.dfs.core.windows.netfs.azure.account.oauth2.client.id.<storage-account>.dfs.core.windows.netfs.azure.account.oauth2.client.secret.<storage-account>.dfs.core.windows.net
このうち 1 つでもキーが見つからなかったり、別名で設定されていたりすると、ABFS ドライバは「設定がない」と判断し、[CONFIG_NOT_AVAILABLE] を投げてきます。逆に言うと、キー名さえ完全に合っていれば、次は認証エラーや 403 など別のエラーに変わることが多いです。
前提環境と典型的な構成
本記事で想定している主な構成は次の通りです。
| 項目 | 例 | 補足 |
|---|---|---|
| コンピュート | Azure Databricks クラスター | ノートブックで spark.conf.set を実行 |
| ストレージ | Azure Data Lake Storage Gen2 | 階層 namespace 有効のストレージアカウント |
| 認証方式 | OAuth 2.0 クライアントクレデンシャル | サービス プリンシパル+クライアントシークレット |
| エンドポイント | https://login.microsoftonline.com/<tenant>/oauth2/token | v1 エンドポイントを前提 |
| アクセス URL | abfss://<container>@<storage-account>.dfs.core.windows.net/... | dfs.core.windows.net を使用 |
Databricks SQL Warehouse(旧 SQL Analytics)からアクセスする場合は、spark.conf ではなく Unity Catalog の「ストレージ資格情報」「外部ロケーション」を使うのが基本です。この点を混同すると切り分けがぶれてしまうので注意しましょう。
最初に押さえておきたい「結論だけチェックリスト」
細かい説明の前に、とりあえずここを見れば直る可能性が高いというポイントだけを先に整理しておきます。
| チェック項目 | 内容 |
|---|---|
| キー名の完全一致 | <storage-account>.dfs.core.windows.net を含むすべての設定キーがストレージアカウント名と完全一致しているか(余分な空白や改行なし) |
| 設定の実行タイミング | spark.read や dbutils.fs.ls を実行する前に、同一セルで spark.conf.set をすべて実行しているか |
| シークレット参照 | dbutils.secrets.get(...) の戻り値を変数に受け、その変数を client.secret に渡しているか |
| abfss パスでの疎通テスト | 実際のコンテナ・パスを指定して dbutils.fs.ls または spark.read を実行し、エラー内容が変化するか確認したか |
| サービス プリンシパル権限 | 少なくとも「Storage Blob Data Contributor」ロールが付与されているか。必要ならコンテナ/ディレクトリ ACL も確認 |
このチェックリストを一通り確認しても解決しない場合は、次の章からの詳細な切り分け手順を順に試してみてください。
CONFIG_NOT_AVAILABLE のよくある原因パターン
現場で特に多い原因を整理すると、次のようなパターンに集約されます。
| 原因パターン | 具体例 | ポイント |
|---|---|---|
| キー名のタイプミス・改行混入 | fs.azure.account.auth.type.mystorage\n .dfs.core.windows.net | コピー&ペースト時の改行やゼロ幅スペースが原因 |
| <storage-account> 置換漏れ | fs.azure.account.auth.type.<storage-account>.dfs... のまま | サンプルをそのまま使って置換し忘れ |
| ドメインの取り違え | blob.core.windows.net と書いている | ABFS は dfs.core.windows.net が正解 |
| 設定の実行順序の問題 | spark.read... を先に実行し、その後で spark.conf.set | 読み込み前に設定が完了していない |
| シークレットの渡し方の問題 | 文字列リテラルでシークレット名を書いているだけ | 値ではなく「名前」を渡してしまっている |
| コンピュートの勘違い | SQL Warehouse に対して spark.conf.set を打っている | クラスター設定と Warehouse 設定は別物 |
ここからは、それぞれのポイントをもう少し実践的に見ていきます。
キー名の完全一致を徹底する
ストレージアカウント名とホスト名は 1 文字もズレてはいけない
ABFS 用の設定キーは、ストレージアカウント名とホスト名を含んだ形になっています。
fs.azure.account.auth.type.<storage-account>.dfs.core.windows.net
fs.azure.account.oauth2.client.id.<storage-account>.dfs.core.windows.net
fs.azure.account.oauth2.client.secret.<storage-account>.dfs.core.windows.net
fs.azure.account.oauth2.client.endpoint.<storage-account>.dfs.core.windows.net
例えば、ストレージアカウント名が dlstest01 なら、ホスト名は dlstest01.dfs.core.windows.net です。ここで、
dlstest01(末尾に空白)dlstest01(全角スペース)dlstest0l(数字の「1」とアルファベットの「l」の取り違え)
といった微妙な違いが 1 文字でも入ると、ABFS ドライバから見ると「別物のキー」になってしまいます。
コピー&ペーストで紛れ込む改行・不可視文字に注意
よくあるパターンが、ドキュメントやメールからコピーした結果、ストレージアカウント名の途中で改行が入ってしまうケースです。Spark のエラーログを見ると、次のように見えることがあります。
fs.azure.account.auth.type.dlstest
01.dfs.core.windows.net is not available
この場合、エラーにそのまま改行付きのキー名が出ているので、「あれ、アカウント名が途中で折り返されてないか?」と疑うのがポイントです。Python から repr でキー名を確認すると、不可視文字の混入も確認しやすくなります。
key = f"fs.azure.account.auth.type.{storage_account}.dfs.core.windows.net"
print(repr(key))
ここで 'fs.azure.account.auth.type.dlstest01.dfs.core.windows.net' のようにきれいに表示されれば OK です。
安全な設定パターン(コピペ推奨サンプル)
タイプミスや改行混入を防ぐためには、キー名をそのままベタ書きしないのが一番です。変数を使ってホスト名を組み立てるパターンにしておくと、再利用もしやすくなります。
基本形:単一ストレージアカウント向けの設定
# 1) 変数に集約してタイプミスを防止
storage_account = "<your-storage-account>" # 例: dlstest01
tenant_id = "<your-tenant-id>"
app_id = "<your-application-id>"
client_secret = dbutils.secrets.get(scope="<your-scope>", key="<your-secret-key>")
host = f"{storage_account}.dfs.core.windows.net"
# 2) すべて同一セルで実行(読み込み前)
spark.conf.set(f"fs.azure.account.auth.type.{host}", "OAuth")
spark.conf.set(f"fs.azure.account.oauth.provider.type.{host}",
"org.apache.hadoop.fs.azurebfs.oauth2.ClientCredsTokenProvider")
spark.conf.set(f"fs.azure.account.oauth2.client.id.{host}", app_id)
spark.conf.set(f"fs.azure.account.oauth2.client.secret.{host}", client_secret)
spark.conf.set(f"fs.azure.account.oauth2.client.endpoint.{host}",
f"https://login.microsoftonline.com/{tenant_id}/oauth2/token")
# 3) 設定が入っているか簡易確認(例外が出なければOK)
_ = spark.conf.get(f"fs.azure.account.auth.type.{host}")
# 4) 最小の疎通テスト(パスは実環境に置き換え)
test_path = f"abfss://<container>@{storage_account}.dfs.core.windows.net/<path/to/file_or_folder>"
display(dbutils.fs.ls(test_path))
ポイントは次の通りです。
host変数にホスト名を一元化し、すべてのキーで使い回す- 設定と疎通確認を一つのセルにまとめて実行する(セルを分けると、どこまで実行済みか分かりにくくなる)
spark.conf.getで最低限の存在確認を行う
複数ストレージアカウントに共通のヘルパー関数を使う
プロジェクトによっては、複数のストレージアカウントを使い分けることも珍しくありません。その場合は、ヘルパー関数を用意しておくとミスを減らせます。
def configure_adls_oauth(storage_account: str,
tenant_id: str,
app_id: str,
secret_scope: str,
secret_key: str) -> None:
host = f"{storage_account}.dfs.core.windows.net"
client_secret = dbutils.secrets.get(scope=secret_scope, key=secret_key)
spark.conf.set(f"fs.azure.account.auth.type.{host}", "OAuth")
spark.conf.set(f"fs.azure.account.oauth.provider.type.{host}",
"org.apache.hadoop.fs.azurebfs.oauth2.ClientCredsTokenProvider")
spark.conf.set(f"fs.azure.account.oauth2.client.id.{host}", app_id)
spark.conf.set(f"fs.azure.account.oauth2.client.secret.{host}", client_secret)
spark.conf.set(f"fs.azure.account.oauth2.client.endpoint.{host}",
f"https://login.microsoftonline.com/{tenant_id}/oauth2/token")
# 例: 利用
configure_adls_oauth(
storage_account="dlstest01",
tenant_id="<tenant-id>",
app_id="<app-id>",
secret_scope="sp-secrets",
secret_key="dlstest01-client-secret"
)
このように関数化しておくと、環境が増えたときにも「パラメータだけ差し替える」運用が可能になります。
設定の実行タイミングとセルの構成
Databricks ノートブックでは、セルの実行順序を自由に変えられます。そのため、次のような「うっかり」がよく起こります。
- 一度ノートブックを上から順に実行した
- データ読み込みのセルだけを後から再実行した
- 別ユーザーがノートブックを開き、読み込みセルだけ実行した(設定セルは実行していない)
この状態で spark.read... を実行すると、当然ながら ABFS 設定は入っていないので [CONFIG_NOT_AVAILABLE] につながります。
| パターン | 症状 | 対策 |
|---|---|---|
| 設定セルと読み込みセルが離れている | 人によって実行順が変わる | 「初期設定」セルをノートブック先頭に置き、必ず最初に実行する運用にする |
| 複数ノートブックから同じクラスターを共有 | どのノートブックで設定されたか分からなくなる | 可能ならノートブックごとにクラスターを分けるか、初期化スクリプトで共通化 |
運用上の工夫としては、ノートブックの先頭に「環境初期化」セクションを設け、そのセルにすべての spark.conf.set を集約し、誰が見ても「最初にここを実行すればよい」と分かるようにしておくと安心です。
シークレットの参照漏れ・渡し方のミス
Azure Key Vault / Databricks シークレットスコープを使ってクライアントシークレットを管理している場合、「値」と「名前」を取り違えるミスが頻出します。
よくある NG パターン
# NG 例: シークレット「名」をそのまま書いてしまっている
spark.conf.set(
f"fs.azure.account.oauth2.client.secret.{host}",
"<your-secret-key>" # これは Key Vault の「キー名」であって、値ではない
)
この場合、ABFS はクライアントシークレットとして "<your-secret-key>" という文字列そのものを送信してしまうため、認証自体は確実に失敗します。CONFIG_NOT_AVAILABLE ではなく認証エラーになりますが、根本原因としてはよく似た「参照の仕方の問題」です。
正しいパターン:必ず dbutils.secrets.get を経由する
client_secret = dbutils.secrets.get(scope="<scope-name>", key="<secret-key-name>")
spark.conf.set(
f"fs.azure.account.oauth2.client.secret.{host}",
client_secret
)
ここで client_secret は実際のシークレット値が入った文字列になります。この変数を spark.conf.set に渡すことで、ABFS から見ても正しい値が設定されることになります。
トークンエンドポイントの指定と v1 / v2 の違い
CONFIG_NOT_AVAILABLE そのものには直接関係しませんが、認証エラーに移行したときのためにトークンエンドポイントも押さえておきましょう。
- 基本形:
https://login.microsoftonline.com/<tenant-id>/oauth2/token(v1 エンドポイント) - 場合によっては v2(
.../oauth2/v2.0/token)が必要なケースもある
Databricks の公式ドキュメントでは、多くのサンプルコードが v1 エンドポイントを前提としているため、まずは v1 で試し、必要に応じて v2 を検討するとよいでしょう。いずれにせよ、CONFIG_NOT_AVAILABLE が出ている段階では「そこまで辿り着いていない」ので、まずは設定キー名の整合性を優先的に確認するのが効率的です。
Spark / Hadoop の設定値を覗いてキーを確認する
「設定を入れたつもりなのに CONFIG_NOT_AVAILABLE が出る」という状況では、実際に Spark がどんなキーを持っているのかを見に行くのが一番確実です。
Spark 設定から該当キーを検索する
storage_account = "<your-storage-account>"
for k in spark.conf.getAll().keys():
if "fs.azure.account." in k and storage_account in k:
print(repr(k))
この出力に、意図していないキーが紛れ込んでいないか確認します。
'fs.azure.account.auth.type.dlstest01.dfs.core.windows.net '(末尾にスペース)'fs.azure.account.auth.type.dlstest01..dfs.core.windows.net'(ドットが二重)'fs.azure.account.auth.type.dlstest01.dfs.core.windows.ne'(末尾のt抜け)
このようなキーが存在している場合、ABFS ドライバは期待するキーを見つけられず、CONFIG_NOT_AVAILABLE を投げ続けることになります。
Hadoop 設定から直接値を取得してみる
より低レイヤーの確認として、Hadoop 設定を直接覗く方法もあります。
storage_account = "<your-storage-account>"
host = f"{storage_account}.dfs.core.windows.net"
jconf = sc._jsc.hadoopConfiguration()
print("auth.type =", jconf.get(f"fs.azure.account.auth.type.{host}"))
print("client.id =", jconf.get(f"fs.azure.account.oauth2.client.id.{host}"))
print("endpoint =", jconf.get(f"fs.azure.account.oauth2.client.endpoint.{host}"))
ここで None や空文字が返ってくる場合は、そもそも設定が入っていない(またはキー名が違っている)ことになります。
疎通テストとエラー変化で原因を絞り込む
CONFIG_NOT_AVAILABLE の段階を抜け出せたかどうかは、エラー内容が変わったかどうかで判断するのが分かりやすいです。
最低限の疎通テスト
設定が完了したら、まずは小さなファイル or ディレクトリで疎通テストを行います。
test_path = f"abfss://<container>@{storage_account}.dfs.core.windows.net/<path>"
# ディレクトリ一覧の確認
display(dbutils.fs.ls(test_path))
# または CSV を軽く読む
df = (spark.read
.format("csv")
.option("header", "true")
.load(test_path))
df.limit(1).display()
このときのエラー内容によって、次のように切り分けられます。
| エラー内容 | 主な原因候補 | 次に見るべきポイント |
|---|---|---|
[CONFIG_NOT_AVAILABLE] | 設定キー名の不整合/設定未適用 | Spark/Hadoop 設定のキー一覧、改行・空白混入を再確認 |
| 403 Forbidden / 認可エラー | サービス プリンシパルに十分なロールがない | Storage Blob Data Contributor 等の RBAC、ACL を確認 |
| 認証エラー(invalid_client など) | クライアント ID / シークレット / テナント ID の誤り | Azure AD アプリ登録、シークレット値の見直し |
| ファイル/パスが存在しない | パス指定ミス、コンテナ名の間違い | ポータルや Storage Explorer でパスを再確認 |
このように、エラーが CONFIG_NOT_AVAILABLE から別の種類に変わったら、「設定キーは読めるようになった」と判断し、次のレイヤー(権限やパス)に問題の焦点を移すのが効率的です。
サービス プリンシパルの RBAC と ACL の整理
CONFIG_NOT_AVAILABLE の直接原因ではありませんが、設定が正しくなった後に 403 などで止まる場合は、サービス プリンシパルの権限周りを整理する必要があります。
RBAC(ロールベースアクセス制御)
最低限必要になることが多いロールは次の通りです。
| ロール名 | 主な用途 | スコープの例 |
|---|---|---|
| Storage Blob Data Contributor | Blob / Data Lake への読み書き | ストレージアカウント単位、またはコンテナ単位 |
| Storage Blob Data Reader | 読み取り専用シナリオ | 読み取りのみでよい場合に利用 |
スコープは、セキュリティ要件と運用ポリシーに応じて、サブスクリプション/リソースグループ/ストレージアカウント/コンテナのいずれかで設定します。トラブルシュート中だけ一時的に広めのスコープを与え、問題解決後に絞り込むアプローチも現実的です。
ACL(POSIX 風アクセス制御)
ADLS Gen2 では、RBAC に加えて POSIX 風の ACL が有効になっていることがあります。この場合、RBAC だけ付けても ACL で拒否されるとアクセスできません。
- 対象ディレクトリに対してサービス プリンシパルに
r-xなど適切な権限を付与 - 途中の親ディレクトリにも「実行(x)」権限が必要
ACL でブロックされている場合は、CONFIG_NOT_AVAILABLE ではなく 403 系のエラーになるため、前述の「エラー変化による切り分け」を使うと原因に辿り着きやすくなります。
Databricks SQL Warehouse を使う場合の注意点
ここまでの説明は「Databricks クラスター+ノートブック」でのケースを想定していましたが、Databricks SQL Warehouse(旧 SQL Analytics)から ADLS にアクセスする場合は、登場人物が少し変わります。
spark.conf.setは SQL Warehouse には直接効かない- 基本的には Unity Catalog の「ストレージ資格情報」「外部ロケーション」を使って接続する
- その場合は、Databricks 側のマネージド ID や別のサービス プリンシパルを使う構成になる
もし「ノートブックではうまく行くのに、SQL Warehouse からだと CONFIG_NOT_AVAILABLE になる」という状況であれば、そもそも「どのレイヤーで設定を行うべきか」を一度整理し直すことをおすすめします。
再発防止のためのベストプラクティス
最後に、同じ CONFIG_NOT_AVAILABLE で何度もハマらないための工夫をまとめます。
- キー名を変数で組み立てる
文字列ベタ書きをやめ、host = f"{storage_account}.dfs.core.windows.net"のように一元化する。 - 設定セルをノートブック先頭に固定する
「初期設定」セルを最初に実行する運用を徹底し、コメントで明示する。 - ヘルパー関数を作って共通化する
複数プロジェクトで同じような設定を毎回書かない。1 箇所を直せば全体に反映されるようにする。 - 疎通テスト用パスを決めておく
シンプルなテスト用ディレクトリ / ファイルを決めておき、切り分け時は必ずそこを使う。 - エラー内容の変化をログに残す
CONFIG_NOT_AVAILABLE → 403 → ファイル未検出… のように、どこまで進んだかを記録しておくと、後から見直したときに原因が追いやすい。
まとめ
Databricks から ADLS Gen2 に OAuth で接続する際の [CONFIG_NOT_AVAILABLE] は、一見すると「難しい認証エラー」に見えますが、実態はほとんどが設定キー名の不整合です。
- ABFS ドライバが探しているキー名と、実際に設定されているキー名が 1 文字でも違うと CONFIG_NOT_AVAILABLE になる
- キー名は
<storage-account>.dfs.core.windows.netまで含めて完全一致させる必要がある - 設定は
spark.readなどを行う前に、同一セルで一括実行するのが安全 - シークレットは
dbutils.secrets.getの戻り値(実際の値)をclient.secretに渡す - 設定が正しくなれば、次のステップでは RBAC や ACL の問題に移行することが多い
本記事で紹介したサンプルコードと切り分け手順をテンプレートとしてストックしておけば、今後同じエラーに遭遇したときにも、短時間で原因に辿り着けるはずです。Databricks と ADLS Gen2 を組み合わせたデータ基盤を運用している方は、ぜひ自分の環境用にカスタマイズした「接続設定ノートブック」を用意しておくことをおすすめします。

コメント