Azure / azure-sdk-for-java

This repository is for active development of the Azure SDK for Java. For consumers of the SDK we recommend visiting our public developer docs at https://docs.microsoft.com/java/azure/ or our versioned developer docs at https://azure.github.io/azure-sdk-for-java.
MIT License
2.33k stars 1.97k forks source link

[FEATURE REQ]Provide a way to know whether a container has hierarchical namespace enabled when using token credentials #38912

Open findinpath opened 7 months ago

findinpath commented 7 months ago

Is your feature request related to a problem? Please describe. This is a revival attempt of the reported issue https://github.com/Azure/azure-sdk-for-java/issues/26733

Describe the solution you'd like I'm looking for a way to identify whether the storage account to which the container belongs has hierarchical namespace enabled.

I know about the limitations exposed by @rickle-msft here https://github.com/Azure/azure-sdk-for-java/issues/26733#issuecomment-1026046740

It would be extremely helpful to know whether we can use on a hierarchical container DataLakeFileSystemClient / Data Lake Storage Gen2 techniques or rather or BlobContainerClient/ ABFS on a flat container for listing / deleting its directories and files.

Information Checklist Kindly make sure that you have added all the following information above and checkoff the required fields otherwise we will treat the issuer as an incomplete report

alzimmermsft commented 7 months ago

Thanks for filing this issue @findinpath.

I'll start by assuming when you want to make this check you already have a BlobContainerClient, or BlobContainerAsyncClient (and I'm going to write the following answer using the synchronous client but the same / equivalent APIs should exist in the async client).

You can get the BlobServiceClient using BlobContainerClient.getServiceClient(). The BlobServiceClient should then have a method getAccountInfo returning a StorageAccountInfo object. This has an is getter isHierarchicalNamespaceEnabled which will indicate if the Storage account being used has hierarchical namespace enabled.

boolean isHierarchicalNamespaceAccount = blobContainerClient.getServiceClient()
    .getAccountInfo()
    ,isHierarchicalNamespaceEnabled();
findinpath commented 7 months ago

@alzimmermsft thank you for your reply.

Do note that the above mentioned method doesn't work when using OAuth2 authentication.

As mentioned here

This operation doesn't support OAuth-based authorization via an access token from Azure Active Directory/MSI or a user delegation SAS.

I'm not sure why this information is not being provided when using token credentials on the service level. I'd like though to know whether there is a workaround to the current situation.

alzimmermsft commented 7 months ago

Hmm, this one I don't have a good answer for, @seanmcc-msft do you have any additional information on how to use this without a connection string?

findinpath commented 7 months ago

Workaround

az storage account blob-service-properties show --account-name my-storage-account
{
  "id": "/subscriptions/xxxxxxx-xxxxx-xxxx-xxxxx-xxxxxxxxxx/resourceGroups/myresourcegroup/providers/Microsoft.Storage/storageAccounts/mystorageaccount/blobServices/default",
.....
  "resourceGroup": "myresourcegroup",
....
  "type": "Microsoft.Storage/storageAccounts/blobServices"
}

https://stackoverflow.com/a/75711750

az resource show --name mystorageaccount --resource-group myresourcegroup  --resource-type "Microsoft.Storage/storageAccounts"  --query properties.isHnsEnabled

This will show whether HNS is enabled or not for the storage account while using az tool.

Could you please add to the class https://github.com/Azure/azure-sdk-for-java/blob/eee5dc85bf53571289ee1cb5e3a0d0e6bf06df9c/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobServiceProperties.java also the following information:

I need them to extract via the resourceManager the HNS related information

        AzureProfile profile = new AzureProfile(tenantId, "xxxxxxx-xxxxx-xxxx-xxxxx-xxxxxxxxxx", AzureEnvironment.AZURE);
        ResourceManager resourceManager  = ResourceManager
            .authenticate(credential, profile)
            .withDefaultSubscription();
        ResourcesClient resourceClient = resourceManager.serviceClient().getResources();
        GenericResourceInner resource = resourceClient.get("myresourcegroup", "Microsoft.Storage", "", "storageAccounts", "mystorageaccount", "2021-04-01");
        Map<String, Object> resourceProperties = (Map<String, Object>)resource.properties();
        boolean isHnsEnabled = Optional.ofNullable(resourceProperties.get("isHnsEnabled")).map(Boolean.class::cast).orElse(false);

Is there maybe a more elegant way to extract the subscription id from the BlobServiceProperties output?

findinpath commented 7 months ago

With the above workaround aside, it feels though unnatural to go the above mentioned route just to know whether the container i'm dealing with is hierarchical or not and this only when using service tokens. :(

charlesjmorgan commented 7 months ago

We found an alternate method that seems to work. It calls this API endpoint directly using the com.azure.core.http.HttpClient. If HNS is enabled for the account then it will return a 401 response status since we aren't authenticated, if HNS is not enabled then it will return a 400 response status that says it is an invalid URI. The upn query parameter is only valid for HNS account types.

private boolean isHierarchicalNamespaceEnabled(String accountName)
        throws IOException
{
    URL url = UrlBuilder.parse("https://%s.dfs.core.windows.net/".formatted(accountName))
            .addQueryParameter("action", "getStatus")
            .addQueryParameter("upn", "true")
            .toUrl();
    HttpRequest request = new HttpRequest(HttpMethod.HEAD, url);
    try (HttpResponse response = httpClient.send(request).block()) {
        return Optional.ofNullable(response)
                .map(HttpResponse::getStatusCode)
                .map(statusCode -> !Objects.equals(statusCode, 400))
                .orElseThrow();
    }
}
findinpath commented 7 months ago

Yet, another (maybe more elegant way)

        ClientSecretCredential credential = new ClientSecretCredentialBuilder()
                .authorityHost(authorityHost)
                .tenantId(tenantId)
                .clientId(clientId)
                .clientSecret(secret)
                .build();

        DataLakeDirectoryClient dataLakeDirectoryClient = client.getFileSystemClient(azureLocation.container().orElseThrow()).getDirectoryClient("/");
        boolean isHnsEnabled = dataLakeDirectoryClient.exists();

I'd like somebody from MSFT to confirm whether this solution should be correct in the long run.

charlesjmorgan commented 7 months ago

@alzimmermsft @seanmcc-msft Just want to check in here, can either of you confirm if any of the suggested solutions above will be correct in the long run?

I would also like to know if you plan to add support for OAuth based access token authentication to the "Get Account Information" endpoint?

github-actions[bot] commented 6 months ago

Hi @findinpath, since you haven’t asked that we /unresolve the issue, we’ll close this out. If you believe further discussion is needed, please add a comment /unresolve to reopen the issue.

hashhar commented 6 months ago

/unresolve

It'd still be helpful to answer https://github.com/Azure/azure-sdk-for-java/issues/38912#issuecomment-1972481900

github-actions[bot] commented 6 months ago

Hi $hashhar, only the original author of the issue can ask that it be unresolved. Please open a new issue with your scenario and details if you would like to discuss this topic with the team.

findinpath commented 6 months ago

/unresolve