Azure Data Lake Storage で .NET を使用して ACL を管理する

この記事では、.NET を使用して、ディレクトリとファイルのアクセス制御リストを取得、設定、更新する方法について説明します。

ACL の継承は、親ディレクトリの下に作成された新しい子項目に対して既に利用可能です。 しかし、親ディレクトリの既存の子項目に対して ACL を再帰的に追加、更新、および削除することもできます。それぞれの子項目に対してこれらの変更を個別に行う必要はありません。

パッケージ (NuGet) | サンプル | API リファレンス | Gen1 から Gen2 へのマッピング | フィードバックを送る

前提条件

  • Azure サブスクリプション - 無料アカウントを作成します
  • 階層型名前空間 (HNS) が有効になっている Azure ストレージ アカウント。 作成するには、こちらの手順に従います。
  • Azure CLI バージョン 2.6.0 以上。
  • 次のセキュリティのアクセス許可のいずれか。
    • ターゲット コンテナー、ストレージ アカウント、親リソース グループ、またはサブスクリプションのスコープでストレージ BLOB データ所有者ロールが割り当てられた、プロビジョニングされた Microsoft Entra ID セキュリティ プリンシパル
    • ACL 設定を適用する予定のターゲット コンテナーまたはディレクトリの所有ユーザー。 ACL を再帰的に設定する場合、これには、ターゲット コンテナーまたはディレクトリ内のすべての子項目が含まれます。
    • ストレージ アカウント キー。

プロジェクトの設定

このセクションでは、Azure Storage Data Lake クライアント ライブラリを使用するようにプロジェクトを設定する方法について説明します。

パッケージをインストールする

プロジェクト ディレクトリから、dotnet add package コマンドを使用して、Azure Storage Data Lake と Azure ID クライアント ライブラリのパッケージをインストールします。 Azure サービスへのパスワードレス接続には、Azure.Identity パッケージが必要です。

dotnet add package Azure.Storage.Files.DataLake
dotnet add package Azure.Identity

using ディレクティブを追加します

次の using ディレクティブをコード ファイルの先頭に追加します。

using Azure;
using Azure.Core;
using Azure.Storage;
using Azure.Storage.Files.DataLake;
using Azure.Storage.Files.DataLake.Models;
using System.Collections.Generic;
using System.Threading.Tasks;

アカウントに接続する

この記事のコード例を実行するには、ストレージ アカウントを表す DataLakeServiceClient インスタンスを作成する必要があります。 Microsoft Entra ID の資格情報またはアカウント キーを使用してクライアント オブジェクトを認可できます。

.NET 用 Azure ID クライアント ライブラリを使用して、Microsoft Entra ID でアプリケーションを認証できます。

Note

Microsoft Entra ID を使用してアクセスを認可している場合は、セキュリティ プリンシパルにストレージ BLOB データ所有者ロールが割り当てられていることを確認します。 ACL アクセス許可の適用方法とその変更による影響の詳細については、「Azure Data Lake Storage のアクセス制御モデル」を参照してください。

まず、次のいずれかの Azure ロールベースのアクセス制御 (Azure RBAC) ロールをセキュリティ プリンシパルに割り当てます。

ロール ACL 設定機能
ストレージ BLOB データ所有者 アカウント内のすべてのディレクトリとファイル。
ストレージ BLOB データ共同作成者 セキュリティ プリンシパルによって所有されているディレクトリとファイルのみ。

次に、DataLakeServiceClient インスタンスを作成し、DefaultAzureCredential クラスの新しいインスタンスを渡します。

public static DataLakeServiceClient GetDataLakeServiceClient(string accountName)
{
    string dfsUri = $"https://{accountName}.dfs.core.windows.net";

    DataLakeServiceClient dataLakeServiceClient = new DataLakeServiceClient(
        new Uri(dfsUri),
        new DefaultAzureCredential());

    return dataLakeServiceClient;
}

DefaultAzureCredential を使用してデータへのアクセスを承認する方法の詳細については、Azure サービスで .NET アプリケーションを認証する方法に関するページを参照してください。

ACL を設定する

ACL を設定する場合は、ACL 全体 (そのすべてのエントリを含む) を置換します。 セキュリティ プリンシパルのアクセス許可レベルの変更または ACL への新しいセキュリティ プリンシパルの追加を、他の既存のエントリに影響を与えることなく行いたい場合は、代わりに ACL を "更新" する必要があります。 ACL を置換するのでなく更新するには、この記事の「ACL を更新する」セクションを参照してください。

ACL を "設定" する場合は、所有ユーザーのエントリ、所有グループのエントリ、および他のすべてのユーザーのエントリを追加する必要があります。 所有ユーザー、所有グループ、およびその他のすべてのユーザーの詳細については、「ユーザーと ID」を参照してください。

ここでは、次の操作方法について説明します。

  • ディレクトリの ACL を設定する
  • ファイルの ACL を設定する
  • ACL を再帰的に設定する

ディレクトリの ACL を設定する

DataLakeDirectoryClient.GetAccessControlAsync メソッドを呼び出してディレクトリのアクセス制御リスト (ACL) を取得し、DataLakeDirectoryClient.SetAccessControlList メソッドを呼び出して ACL を設定します。

この例では、my-directory という名前のディレクトリの ACL を取得して設定します。 user::rwx,group::r-x,other::rw- 文字列により、所有ユーザーには読み取り、書き込み、および実行のアクセス許可が、所有グループには読み取りと実行のアクセス許可のみが、他のすべてのユーザーに読み取りと書き込みのアクセス許可が付与されます。

public async Task ManageDirectoryACLs(DataLakeFileSystemClient fileSystemClient)
{
    DataLakeDirectoryClient directoryClient =
      fileSystemClient.GetDirectoryClient("");

    PathAccessControl directoryAccessControl =
        await directoryClient.GetAccessControlAsync();

    foreach (var item in directoryAccessControl.AccessControlList)
    {
        Console.WriteLine(item.ToString());
    }

    IList<PathAccessControlItem> accessControlList
        = PathAccessControlExtensions.ParseAccessControlList
        ("user::rwx,group::r-x,other::rw-");

    directoryClient.SetAccessControlList(accessControlList);

}

また、コンテナーのルート ディレクトリの ACL を取得して設定することもできます。 ルート ディレクトリを取得するには、DataLakeFileSystemClient.GetDirectoryClient メソッドに空の文字列 ("") を渡します。

ファイルの ACL を設定する

DataLakeFileClient.GetAccessControlAsync メソッドを呼び出してファイルのアクセス制御リスト (ACL) を取得し、DataLakeFileClient.SetAccessControlList メソッドを呼び出して ACL を設定します。

この例では、my-file.txt という名前のファイルの ACL を取得して設定します。 user::rwx,group::r-x,other::rw- 文字列により、所有ユーザーには読み取り、書き込み、および実行のアクセス許可が、所有グループには読み取りと実行のアクセス許可のみが、他のすべてのユーザーに読み取りと書き込みのアクセス許可が付与されます。

public async Task ManageFileACLs(DataLakeFileSystemClient fileSystemClient)
{
    DataLakeDirectoryClient directoryClient =
        fileSystemClient.GetDirectoryClient("my-directory");

    DataLakeFileClient fileClient =
        directoryClient.GetFileClient("hello.txt");

    PathAccessControl FileAccessControl =
        await fileClient.GetAccessControlAsync();

    foreach (var item in FileAccessControl.AccessControlList)
    {
        Console.WriteLine(item.ToString());
    }

    IList<PathAccessControlItem> accessControlList
        = PathAccessControlExtensions.ParseAccessControlList
        ("user::rwx,group::r-x,other::rw-");

    fileClient.SetAccessControlList(accessControlList);
}

ACL を再帰的に設定する

DataLakeDirectoryClient.SetAccessControlRecursiveAsync メソッドを呼び出すことによって、ACL を再帰的に設定します。 このメソッドに PathAccessControlItemList を渡します。 PathAccessControlItem はそれぞれ ACL エントリを定義します。

既定の ACL エントリを設定する場合は、PathAccessControlItemPathAccessControlItem.DefaultScope プロパティを true に設定できます。

この例では、my-parent-directory という名前のディレクトリの ACL を設定します。 このメソッドは、既定の ACL を設定するかどうかを指定する isDefaultScope という名前のブール型パラメーターを受け取ります。 そのパラメーターは PathAccessControlItem のコンストラクターで使用されます。 これらの ACL エントリでは、所有ユーザーには読み取り、書き込み、実行のアクセス許可を付与し、所有グループには読み取りと実行のアクセス許可のみを付与し、他のすべてのユーザーにはアクセスを許可しません。 この例の最後の ACL エントリでは、xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx というオブジェクト ID を持つ特定のユーザーに、読み取りと実行のアクセス許可を付与しています。

    public async Task SetACLRecursively(DataLakeServiceClient serviceClient, bool isDefaultScope)
{
    DataLakeDirectoryClient directoryClient =
        serviceClient.GetFileSystemClient("my-container").
            GetDirectoryClient("my-parent-directory");

    List<PathAccessControlItem> accessControlList =
        new List<PathAccessControlItem>()
    {
new PathAccessControlItem(AccessControlType.User,
    RolePermissions.Read |
    RolePermissions.Write |
    RolePermissions.Execute, isDefaultScope),

new PathAccessControlItem(AccessControlType.Group,
    RolePermissions.Read |
    RolePermissions.Execute, isDefaultScope),

new PathAccessControlItem(AccessControlType.Other,
    RolePermissions.None, isDefaultScope),

new PathAccessControlItem(AccessControlType.User,
    RolePermissions.Read |
    RolePermissions.Execute, isDefaultScope,
    entityId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"),
    };

    await directoryClient.SetAccessControlRecursiveAsync
        (accessControlList, null);
}

ACL を更新する

ACL を "更新する" 場合は、ACL を置換するのでなく ACL を変更します。 たとえば、ACL にリストされている他のセキュリティ プリンシパルに影響を与えることなく、新しいセキュリティ プリンシパルを ACL に追加することができます。 ACL を更新するのでなく、置換する場合は、この記事の「ACL を設定する」セクションを参照してください。

ここでは、次の操作方法について説明します。

  • ACL を更新する
  • ACL を再帰的に更新する

ACL を更新する

まず、DataLakeDirectoryClient.GetAccessControlAsync メソッドを呼び出して、ディレクトリの ACL を取得します。 ACL エントリの一覧を PathAccessControl オブジェクトの新しい List にコピーします。 次に、一覧内で更新するエントリを見つけて、置き換えます。 DataLakeDirectoryClient.SetAccessControlList メソッドを呼び出して ACL を設定します。

この例では、他のすべてのユーザーの ACL エントリを置き換えることで、コンテナーのルート ACL を更新します。

public async Task UpdateDirectoryACLs(DataLakeFileSystemClient fileSystemClient)
{
    DataLakeDirectoryClient directoryClient =
      fileSystemClient.GetDirectoryClient("");

    PathAccessControl directoryAccessControl =
        await directoryClient.GetAccessControlAsync();

    List<PathAccessControlItem> accessControlListUpdate 
        = (List<PathAccessControlItem>)directoryAccessControl.AccessControlList;

    int index = -1;

    foreach (var item in accessControlListUpdate)
    {
        if (item.AccessControlType == AccessControlType.Other)
        {
            index = accessControlListUpdate.IndexOf(item);
            break;
        }
    }

    if (index > -1)
    {
        accessControlListUpdate[index] = new PathAccessControlItem(AccessControlType.Other,
        RolePermissions.Read |
        RolePermissions.Execute);

        directoryClient.SetAccessControlList(accessControlListUpdate);
    }

   }

ACL を再帰的に更新する

ACL を再帰的に更新するには、更新したい ACL エントリを含む新しい ACL オブジェクトを作成してから、そのオブジェクトを ACL の更新操作で使用します。 既存の ACL は取得せず、更新する ACL エントリを指定するだけです。

DataLakeDirectoryClient.UpdateAccessControlRecursiveAsync メソッドを呼び出すことによって、ACL を再帰的に更新します。 このメソッドに PathAccessControlItemList を渡します。 PathAccessControlItem はそれぞれ ACL エントリを定義します。

既定の ACL エントリを更新する場合は、PathAccessControlItemPathAccessControlItem.DefaultScope プロパティを true に設定できます。

この例では、書き込みアクセス許可を持つ ACL エントリを更新します。 このメソッドは、既定の ACL を更新するかどうかを指定する isDefaultScope という名前のブール型パラメーターを受け取ります。 そのパラメーターは PathAccessControlItem のコンストラクターで使用されます。

public async Task UpdateACLsRecursively(DataLakeServiceClient serviceClient, bool isDefaultScope)
{
    DataLakeDirectoryClient directoryClient =
        serviceClient.GetFileSystemClient("my-container").
        GetDirectoryClient("my-parent-directory");

    List<PathAccessControlItem> accessControlListUpdate =
        new List<PathAccessControlItem>()
    {
new PathAccessControlItem(AccessControlType.User,
    RolePermissions.Read |
    RolePermissions.Write |
    RolePermissions.Execute, isDefaultScope,
    entityId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"),
    };

    await directoryClient.UpdateAccessControlRecursiveAsync
        (accessControlListUpdate, null);

}

ACL エントリを削除する

1 つ以上の ACL エントリを削除できます。 ここでは、次の操作方法について説明します。

  • ACL エントリを削除する
  • ACL エントリを再帰的に削除する

ACL エントリを削除する

まず、DataLakeDirectoryClient.GetAccessControlAsync メソッドを呼び出して、ディレクトリの ACL を取得します。 ACL エントリの一覧を PathAccessControl オブジェクトの新しい List にコピーします。 次に、削除するエントリを見つけて、コレクションの Remove メソッドを呼び出します。 DataLakeDirectoryClient.SetAccessControlList メソッドを呼び出して、更新された ACL を設定します。

この例では、他のすべてのユーザーの ACL エントリを置き換えることで、コンテナーのルート ACL を更新します。

public async Task RemoveDirectoryACLEntry
    (DataLakeFileSystemClient fileSystemClient)
{
    DataLakeDirectoryClient directoryClient =
      fileSystemClient.GetDirectoryClient("");

    PathAccessControl directoryAccessControl =
        await directoryClient.GetAccessControlAsync();

    List<PathAccessControlItem> accessControlListUpdate
        = (List<PathAccessControlItem>)directoryAccessControl.AccessControlList;

    PathAccessControlItem entryToRemove = null;

    foreach (var item in accessControlListUpdate)
    {
        if (item.EntityId == "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
        {
            entryToRemove = item;
            break;
        }
    }

    if (entryToRemove != null)
    {
        accessControlListUpdate.Remove(entryToRemove);
        directoryClient.SetAccessControlList(accessControlListUpdate);
    }

}

ACL エントリを再帰的に削除する

ACL エントリを再帰的に削除するには、削除する ACL エントリ用に新しい ACL オブジェクトを作成してから、そのオブジェクトを ACL の削除操作で使用します。 既存の ACL は取得せず、削除する ACL エントリを指定するだけです。

DataLakeDirectoryClient.RemoveAccessControlRecursiveAsync メソッドを呼び出すことによって、ACL を再帰的に削除します。 このメソッドに PathAccessControlItemList を渡します。 PathAccessControlItem はそれぞれ ACL エントリを定義します。

既定の ACL エントリを削除する場合は、PathAccessControlItemPathAccessControlItem.DefaultScope プロパティを true に設定できます。

この例では、my-parent-directory という名前のディレクトリの ACL から ACL エントリを削除します。 このメソッドは、既定の ACL からエントリを削除するかどうかを指定する isDefaultScope という名前のブール型パラメーターを受け取ります。 そのパラメーターは PathAccessControlItem のコンストラクターで使用されます。

public async Task RemoveACLsRecursively(DataLakeServiceClient serviceClient, bool isDefaultScope)
{
    DataLakeDirectoryClient directoryClient =
        serviceClient.GetFileSystemClient("my-container").
            GetDirectoryClient("my-parent-directory");

    List<RemovePathAccessControlItem> accessControlListForRemoval =
        new List<RemovePathAccessControlItem>()
        {
    new RemovePathAccessControlItem(AccessControlType.User, isDefaultScope,
    entityId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"),
        };

    await directoryClient.RemoveAccessControlRecursiveAsync
        (accessControlListForRemoval, null);

}

エラーからの復旧

ACL を再帰的に変更するときに、実行時またはアクセス許可のエラーが発生する場合があります。 実行時エラーが発生した場合は、プロセスを最初から再開します。 アクセス許可エラーは、セキュリティ プリンシパルに、変更対象のディレクトリ階層内にあるディレクトリまたはファイルの ACL を変更するための十分なアクセス許可がない場合に発生する可能性があります。 アクセス許可の問題を解決してから、継続トークンを使用してエラーの発生時点からプロセスを再開するか、最初からプロセスを再開するかを選択します。 最初から再開する場合は、継続トークンを使用する必要はありません。 ACL エントリは、悪影響を及ぼすことなく再適用できます。

この例では、エラーが発生した場合に継続トークンを返します。 アプリケーションでは、エラーが解決された後にこの例のメソッドを再び呼び出し、継続トークンを渡すことができます。 この例のメソッドが初めて呼び出される場合は、アプリケーションで継続トークン パラメーターに対して null の値を渡すことができます。

public async Task<string> ResumeAsync(DataLakeServiceClient serviceClient,
    DataLakeDirectoryClient directoryClient,
    List<PathAccessControlItem> accessControlList,
    string continuationToken)
{
    try
    {
        var accessControlChangeResult =
            await directoryClient.SetAccessControlRecursiveAsync(
                accessControlList, continuationToken: continuationToken, null);

        if (accessControlChangeResult.Value.Counters.FailedChangesCount > 0)
        {
            continuationToken =
                accessControlChangeResult.Value.ContinuationToken;
        }

        return continuationToken;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
        return continuationToken;
    }

}

アクセス許可エラーによって中断されずにプロセスを完了したい場合は、それを指定できます。

プロセスが中断されずに確実に完了されるようにするには、AccessControlChangedOptions オブジェクトを渡し、そのオブジェクトの ContinueOnFailure プロパティを true に設定します。

この例では、ACL エントリを再帰的に設定します。 このコードでアクセス許可エラーが発生した場合は、そのエラーが記録されて、実行が継続されます。 この例では、エラーの数をコンソールに出力します。

public async Task ContinueOnFailureAsync(DataLakeServiceClient serviceClient,
    DataLakeDirectoryClient directoryClient,
    List<PathAccessControlItem> accessControlList)
{
    var accessControlChangeResult =
        await directoryClient.SetAccessControlRecursiveAsync(
            accessControlList, null, new AccessControlChangeOptions()
            { ContinueOnFailure = true });

    var counters = accessControlChangeResult.Value.Counters;

    Console.WriteLine("Number of directories changed: " +
        counters.ChangedDirectoriesCount.ToString());

    Console.WriteLine("Number of files changed: " +
        counters.ChangedFilesCount.ToString());

    Console.WriteLine("Number of failures: " +
        counters.FailedChangesCount.ToString());
}

ベスト プラクティス

このセクションでは、ACL を再帰的に設定するためのベスト プラクティス ガイドラインについて説明します。

実行時エラーの処理

実行時エラーは、さまざまな理由で発生する可能性があります (たとえば、停電やクライアント接続の問題など)。 実行時エラーが発生した場合は、再帰的な ACL プロセスを再開します。 ACL は、悪影響を及ぼすことなく項目に再適用できます。

アクセス許可エラー (403) の処理

再帰的な ACL プロセスの実行中にアクセス制御の例外が発生した場合、AD のセキュリティ プリンシパルに、ディレクトリ階層内にある 1 つ以上の子項目に ACL を適用するための十分なアクセス許可がない可能性があります。 アクセス許可エラーが発生すると、プロセスが停止し、継続トークンが提供されます。 アクセス許可の問題を修正してから、継続トークンを使用して残りのデータセットを処理します。 既に正常に処理済みのディレクトリとファイルをもう一度処理する必要はありません。 再帰的な ACL プロセスを再開することも選択できます。 ACL は、悪影響を及ぼすことなく項目に再適用できます。

資格情報

ターゲット ストレージ アカウントまたはコンテナーのスコープでストレージ BLOB データ所有者ロールが割り当てられている、Microsoft Entra セキュリティ プリンシパルをプロビジョニングすることをお勧めします。

パフォーマンス

待機時間を短縮するには、ストレージ アカウントと同じリージョンにある Azure 仮想マシン (VM) で再帰的な ACL プロセスを実行することをお勧めします。

ACL の制限

ディレクトリまたはファイルに適用できる ACL の最大数は、アクセス ACL が 32 個と既定 ACL が 32 個です。 詳細については、Azure Data Lake Storage Gen2 でのアクセス制御に関するページを参照してください。

関連項目