.NET을 사용하여 Azure Data Lake Storage Gen2에서 ACL 관리

이 문서에서는 .NET을 사용하여 디렉터리와 파일의 액세스 제어 목록을 가져오고, 설정하고, 업데이트하는 방법을 보여 줍니다.

부모 디렉터리 아래에 만들어진 새 자식 항목에서는 이미 ACL 상속을 사용할 수 있는 상태입니다. 그러나 각 자식 항목을 개별적으로 변경할 필요 없이 부모 디렉터리의 기존 자식 항목에서 ACL을 재귀적으로 추가, 업데이트, 제거할 수도 있습니다.

패키지(NuGet) | 샘플 | API 참조 | Gen1에서 Gen2로 매핑 | 피드백 제공

필수 구성 요소

  • Azure 구독 Azure 평가판을 참조하세요.

  • HNS(계층 구조 네임스페이스)를 사용하도록 설정된 스토리지 계정입니다. 이러한 지침에 따라 라이브러리를 만듭니다.

  • Azure CLI 버전 2.6.0 이상.

  • 다음 보안 권한 중 하나입니다.

    • 대상 컨테이너, 스토리지 계정, 부모 리소스 그룹 또는 구독으로 범위가 할당된 Storage Blob 데이터 소유자 역할이 할당된 프로비전된 Microsoft Entra ID 보안 주체입니다.

    • ACL 설정을 적용하려는 대상 컨테이너 또는 디렉터리를 소유하는 담당 사용자. ACL을 재귀적으로 설정하기 위해 대상 컨테이너 또는 디렉터리의 모든 자식 항목을 포함합니다.

    • 스토리지 계정 키.

프로젝트 설정

시작하려면 Azure.Storage.Files.DataLake NuGet 패키지를 설치합니다.

  1. 명령 창을 엽니다(예: Windows PowerShell).

  2. 프로젝트 디렉터리에서 dotnet add package 명령을 사용하여 Azure.Storage.Files.DataLake 미리 보기 패키지를 설치합니다.

    dotnet add package Azure.Storage.Files.DataLake -v 12.6.0 -s https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-net/nuget/v3/index.json
    

    코드 파일 맨 위에 다음 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를 사용하여 연결

참고 항목

Microsoft Entra ID를 사용하여 액세스를 권한 부여하는 경우 보안 주체에 Storage Blob 데이터 소유자 역할이 할당되었는지 확인합니다. ACL 권한이 적용되는 방식과 권한 변경의 영향에 대한 자세한 내용은 Azure Data Lake Storage Gen2의 액세스 제어 모델을 참조하세요.

.NET용 Azure ID 클라이언트 라이브러리를 사용하여 Microsoft Entra ID로 애플리케이션을 인증할 수 있습니다.

패키지를 설치한 후에는 이 using 문을 코드 파일의 맨 위에 추가합니다.

using Azure.Identity;

먼저 다음 Azure RBAC(Azure 역할 기반 액세스 제어) 역할 중 하나를 보안 주체에 할당해야 합니다.

역할 ACL 설정 기능
Storage Blob 데이터 소유자 계정에 있는 모든 디렉터리 및 파일입니다.
Storage Blob 데이터 Contributor 보안 주체가 소유하는 디렉터리와 파일만 해당.

다음으로, 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 애플리케이션을 인증하는 방법을 참조하세요.

계정 키를 사용하여 연결

계정 액세스 키(공유 키)를 사용하여 데이터에 대한 액세스 권한을 부여할 수 있습니다. 이 예제에서는 계정 키로 권한이 부여된 DataLakeServiceClient 인스턴스를 만듭니다.

public static DataLakeServiceClient GetDataLakeServiceClient(string accountName, string accountKey)
{
    StorageSharedKeyCredential sharedKeyCredential =
        new StorageSharedKeyCredential(accountName, accountKey);

    string dfsUri = $"https://{accountName}.dfs.core.windows.net";

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

    return dataLakeServiceClient;
}

주의

공유 키를 사용한 권한 부여는 안전하지 않을 수 있어 권장하지 않습니다. 최적의 보안을 위해 Azure Storage 계정에 대한 공유 키 권한 부여 방지에 설명된 대로 스토리지 계정에 대해 공유 키를 통한 권한 부여를 비활성화합니다.

액세스 키 및 연결 문자열 사용은 프로덕션 또는 중요한 데이터에 액세스하지 않는 초기 개념 증명 앱 또는 개발 프로토타입으로 제한되어야 합니다. 그렇지 않으면 Azure 리소스에 인증할 때 Azure SDK에서 사용할 수 있는 토큰 기반 인증 클래스를 항상 기본으로 설정해야 합니다.

Microsoft에서는 클라이언트가 Microsoft Entra ID 또는 SAS(공유 액세스 서명)를 사용하여 Azure Storage의 데이터에 대한 액세스 권한을 부여하는 것이 좋습니다. 자세한 내용은 데이터 액세스에 대한 작업 권한 부여를 참조하세요.

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을 재귀적으로 설정합니다. 이 메서드를 PathAccessControlItem목록으로 전달합니다. 각 PathAccessControlItem은 ACL 항목을 정의합니다.

기본 ACL 항목을 설정하려는 경우 PathAccessControlItemPathAccessControlItem.DefaultScope 속성을 true로 설정할 수 있습니다.

다음 예시에서는 my-parent-directory라는 디렉터리의 ACL을 설정합니다. 이 메서드는 기본 ACL을 설정할지 여부를 지정하는 isDefaultScope라는 부울 매개 변수를 허용합니다. 이 매개 변수는 PathAccessControlItem의 생성자에서 사용됩니다. ACL의 항목은 소유 사용자에 읽기, 쓰기, 실행 권한을 부여하고, 소유 그룹에는 읽기 및 실행 권한만 제공하며, 다른 모든 사용자에게는 액세스 권한을 부여하지 않습니다. 이 예시의 마지막 ACL 항목은 개체 ID가 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx인 특정 사용자에게 읽기 및 실행 권한을 제공합니다.

    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 개체의 새 목록에 복사합니다. 그런 다음 업데이트하려는 항목을 찾아 목록에서 바꿉니다. 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을 재귀적으로 업데이트합니다. 이 메서드를 PathAccessControlItem목록으로 전달합니다. 각 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 항목 제거

하나 이상의 ACL 항목을 제거할 수 있습니다. 이 섹션에서는 다음 방법을 보여줍니다.

  • ACL 항목 제거
  • ACL 항목의 재귀적 제거

ACL 항목 제거

먼저 DataLakeDirectoryClient.GetAccessControlAsync 메서드를 호출하여 디렉터리의 ACL을 가져옵니다. ACL 항목 목록을 PathAccessControl 개체의 새 목록에 복사합니다. 그런 다음 제거하려는 항목을 찾아 컬렉션의 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 항목을 제거합니다. 이 메서드를 PathAccessControlItem목록으로 전달합니다. 각 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 프로세스를 실행하는 동안 액세스 제어 예외가 발생하는 경우 디렉터리 계층 구조에 있는 하나 이상의 자식 항목에 ACL을 적용하는 데 충분한 권한이 AD 보안 주체에게 없을 수 있습니다. 권한 오류가 발생하면 프로세스가 중지되고 연속 토큰이 제공됩니다. 권한 문제를 해결한 다음 연속 토큰을 사용하여 나머지 데이터 세트를 처리하세요. 이미 성공적으로 처리된 디렉터리와 파일은 다시 처리하지 않아도 됩니다. 재귀 ACL 프로세스를 다시 시작하도록 선택할 수도 있습니다. 부정적인 영향 없이 항목에 ACL을 다시 적용할 수 있습니다.

자격 증명

대상 스토리지 계정 또는 컨테이너 범위에서 Storage Blob 데이터 소유자 역할이 할당된 Microsoft Entra 보안 주체를 프로비전하는 것이 좋습니다.

성능

대기 시간을 줄이려면 스토리지 계정과 동일한 지역에 있는 Azure VM(가상 머신)에서 재귀 ACL 프로세스를 실행하는 것이 좋습니다.

ACL 한도

디렉터리 또는 파일에 적용할 수 있는 최대 ACL 수는 액세스 ACL 32개 및 기본 ACL 32개입니다. 자세한 내용은 Azure Data Lake Storage Gen2의 액세스 제어를 참조하세요.

참고 항목