2
votes

My Application is in a Kubernetes cluster and I'm using Java v12 SDK to interact with the Blob Storage. To authorize against Blob Storage I'm using Managed Identities.

My application needs to copy blobs within one container. I haven't found any particular recommendations or examples of how SDK should be used to do the copy.

I figured that the following approach works when I'm working with the emulator

copyBlobClient.copyFromUrl(sourceBlobClient.getBlobUrl());

However, when this gets executed in the cluster I get the following error

<Error>
   <Code>CannotVerifyCopySource</Code>
   <Message>The specified resource does not exist. RequestId: __ Time: __ </Message>
</Error> 

Message says "resource does not exist" but the blob is clearly there. My container has private access, though.

Now when I change the public access level to "Blob(anonymous read access for blobs only)" everything works as excepted. However, public access not acceptable to me.

Main question - what are the right ways to implement copy blob using Java v12 SDK.

What I could miss or misconfigured in my situation?

And the last is the error message itself. There is a part which says "CannotVerifyCopySource" which kind of helps you understand that there is something with access, but the message part is clearly misleading. Shouldn't it be more explicit about the error?

2
Please create a sas token for the source blob :github.com/Azure/azure-sdk-for-java/blob/….Jim Xu
Do you have any other concerns?Jim Xu
@JimXu Thank you for your comments and answer. I'm going to try it as soon as I have the possibility, will provide feedback.vzhemevko

2 Answers

4
votes

If you want to use Azure JAVA SDK to copy blob with Azure MSI, please refer to the following details

  • Copy blobs between storage accounts

If you copy blobs between storage accounts with Azure MSI. We should do the following actions

  1. Assign Azure Storage Blob Data Reader to the MSI in the source container

  2. Assign Azure Storage Blob Data Contributor to the MSI in the dest container. Besides when we copy blob, we need write permissions to write content to blob

  3. Generate SAS token for the blob. If the souce blob is public, we can directly use source blob URL without sas token.

For example

 try {
            BlobServiceClient blobServiceClient = new BlobServiceClientBuilder()
                    .endpoint("https://<>.blob.core.windows.net/" )
                    .credential(new DefaultAzureCredentialBuilder().build())
                    .buildClient();
            // get User Delegation Key
            OffsetDateTime delegationKeyStartTime = OffsetDateTime.now();
            OffsetDateTime delegationKeyExpiryTime = OffsetDateTime.now().plusDays(7);
            UserDelegationKey key =blobServiceClient.getUserDelegationKey(delegationKeyStartTime,delegationKeyExpiryTime);

            BlobContainerClient sourceContainerClient = blobServiceClient.getBlobContainerClient("test");
            BlobClient sourceBlob = sourceContainerClient.getBlobClient("test.mp3");
            // generate sas token
            OffsetDateTime expiryTime = OffsetDateTime.now().plusDays(1);
            BlobSasPermission permission = new BlobSasPermission().setReadPermission(true);

            BlobServiceSasSignatureValues myValues = new BlobServiceSasSignatureValues(expiryTime, permission)
                    .setStartTime(OffsetDateTime.now());
            String sas =sourceBlob.generateUserDelegationSas(myValues,key);

            // copy
            BlobServiceClient desServiceClient = new BlobServiceClientBuilder()
                    .endpoint("https://<>.blob.core.windows.net/" )
                    .credential(new DefaultAzureCredentialBuilder().build())
                    .buildClient();
            BlobContainerClient desContainerClient = blobServiceClient.getBlobContainerClient("test");
            String res =desContainerClient.getBlobClient("test.mp3")
                    .copyFromUrl(sourceBlob.getBlobUrl()+"?"+sas);
            System.out.println(res);
        } catch (Exception e) {
            e.printStackTrace();
        }

enter image description here

  • Copy in the same account

If you copy blobs in the same storage account with Azure MSI, I suggest you assign Storage Blob Data Contributor to the MSI in the storage account. Then we can do copy action with the method copyFromUrl.

For example

a. Assign Storage Blob Data Contributor to the MSI at the account level

b. code

  try {
            BlobServiceClient blobServiceClient = new BlobServiceClientBuilder()
                    .endpoint("https://<>.blob.core.windows.net/" )
                    .credential(new DefaultAzureCredentialBuilder().build())
                    .buildClient();

            BlobContainerClient sourceContainerClient = blobServiceClient.getBlobContainerClient("test");
            BlobClient sourceBlob = sourceContainerClient.getBlobClient("test.mp3");

            BlobContainerClient desContainerClient = blobServiceClient.getBlobContainerClient("output");
            String res =desContainerClient.getBlobClient("test.mp3")
                    .copyFromUrl(sourceBlob.getBlobUrl());
            System.out.println(res);
        } catch (Exception e) {
            e.printStackTrace();
        }

enter image description here

For more details, please refer to here and here

1
votes

I had the same issue using the Java SDK for Azure I solved it by copying the blob using the URL + the SAS token. Actually the resource you're getting through the URL won't appear as available if you don't have the right access to it. Here is the code I used to solve the problem:

BlobClient sourceBlobClient = blobServiceClient
                        .getBlobContainerClient(currentBucketName)
                        .getBlobClient(sourceKey);
// initializing the copy blob client
BlobClient copyBlobClient = blobServiceClient
        .getBlobContainerClient(newBucketName)
        .getBlobClient(newKey);
// Creating the SAS Token to get the permission to copy the source blob 
OffsetDateTime expiryTime = OffsetDateTime.now().plusDays(1);
BlobSasPermission permission = new BlobSasPermission().setReadPermission(true);
BlobServiceSasSignatureValues values = new BlobServiceSasSignatureValues(expiryTime, permission)
.setStartTime(OffsetDateTime.now());
String sasToken = sourceBlobClient.generateSas(values);

//Making the copy using the source blob URL + generating the copy 
var res = copyBlobClient.copyFromUrl(sourceBlobClient.getBlobUrl() +"?"+ sasToken);