Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ManagedIdentityCredential failed when used to list blob containers #5539

Closed
leonbrag opened this issue Oct 14, 2019 · 9 comments
Closed

ManagedIdentityCredential failed when used to list blob containers #5539

leonbrag opened this issue Oct 14, 2019 · 9 comments
Assignees
Labels
Azure.Identity Client This issue points to a problem in the data-plane of the library. customer-reported Issues that are reported by GitHub users external to the Azure organization. Storage Storage Service (Queues, Blobs, Files)

Comments

@leonbrag
Copy link

Versions:

{ "name": "func", "version": "1.0.0", "description": "", "scripts": { "test": "echo \"No tests yet...\"" }, "dependencies": { "@azure/identity": "^1.0.0-preview.5", "@azure/storage-blob": "^12.0.0-preview.4", "azure-function-express": "^2.0.0", "express": "^4.17.1" }, "devDependencies": {} }

I have following Azure function that lists containers, and blobs and print content of the text blobs.
When run locally, function users SharedKeyCredential and works perfectly

When deployed to Azure function, it fails to list blobs, but it can list containers.

MSI of the function is given Owner permission to Storage account using IAM:

-  | leonbragfunc/subscriptions/780fb010-REMOVED_/resourcegroups/leonbragfunc2/providers/Microsoft.Web/sites/leonbragfunc | App Service or Function App | Owner | This resource

Error only happens when blobs are listed, but only when MSI authentication used.

Error:
2019-10-14T03:00:39.448 [Information] JavaScript HTTP trigger function processed a request!!!!
2019-10-14T03:00:39.449 [Information] Runing in the cloud
2019-10-14T03:00:39.656 [Information] Container 1: leontest
2019-10-14T03:00:39.656 [Information] Listing all blobs using iter
2019-10-14T03:00:39.778 [Information] { Error: AuthorizationPermissionMismatchThis request is not authorized to perform this operation using this permission.
RequestId:58a303cd-301e-007f-653b-823796000000
Time:2019-10-14T03:00:39.7590873Z
at new RestError (D:\home\site\wwwroot\node_modules@azure\core-http\dist\coreHttp.node.js:1715:28)
at D:\home\site\wwwroot\node_modules@azure\core-http\dist\coreHttp.node.js:2948:37
at
at process._tickCallback (internal/process/next_tick.js:188:7)
code: undefined,
statusCode: 403,
request:
WebResource {
streamResponseBody: false,
url: 'https://leonbragtest.blob.core.windows.net/leontest?restype=container&comp=list',
method: 'GET',
headers: HttpHeaders { _headersMap: [Object] },
body: undefined,
query: undefined,
formData: undefined,
withCredentials: false,
abortSignal: undefined,
timeout: 0,
onUploadProgress: undefined,
onDownloadProgress: undefined,
proxySettings: undefined,
keepAlive: undefined,
operationSpec:
{ httpMethod: 'GET',
path: '{containerName}',
urlParameters: [Array],
queryParameters: [Array],
headerParameters: [Array],
responses: [Object],
isXML: true,
serializer: [Object] } },
response:
{ body: 'AuthorizationPermissionMismatchThis request is not authorized to perform this operation using this permission.\nRequestId:58a303cd-301e-007f-653b-823796000000\nTime:2019-10-14T03:00:39.7590873Z',
headers: HttpHeaders { _headersMap: [Object] },
status: 403 },
body:
{ message: 'This request is not authorized to perform this operation using this permission.\nRequestId:58a303cd-301e-007f-653b-823796000000\nTime:2019-10-14T03:00:39.7590873Z',
Code: 'AuthorizationPermissionMismatch' } }
2019-10-14T03:00:39.785 [Information] Executed 'Functions.HttpTrigger' (Succeeded, Id=d7254521-46f7-4941-8b4e-651fede3c449)

Samples code:

`const express = require("express");

const createHandler = require("azure-function-express").createHandler;

const app = express();

//https://github.com/Azure/azure-sdk-for-js/blob/feature/storage/sdk/storage/storage-blob/samples/javascript/basic.js
// A helper method used to read a Node.js readable stream into string
async function streamToString(readableStream) {
return new Promise((resolve, reject) => {
const chunks = [];
readableStream.on("data", (data) => {
chunks.push(data.toString());
});
readableStream.on("end", () => {
resolve(chunks.join(""));
});
readableStream.on("error", reject);
});
}

app.get ("/api/authorize",
//module.exports = async function (context, req) {
async (req, res) => {
context = req.context
context.log('JavaScript HTTP trigger function processed a request!!!!');
let cred = null

  const {ManagedIdentityCredential } = require ("@azure/identity")
  const { BlobServiceClient, SharedKeyCredential } = require("@azure/storage-blob"); // Change to "@azure/storage-blob" in your package
  
  let idInstance = process.env ["WEBSITE_INSTANCE_ID"]
  if (typeof (idInstance) === "undefined") {
      context.log ("running locally")
      // when deployed to prod, account key must be regenerated. This will cause next line to fail
      cred = new SharedKeyCredential ("leonbragtest", "REMOVED_FOR_PRIVACY")
  }
  else {
      context.log ("Runing in the cloud")
      cred = new ManagedIdentityCredential()
  }   
  let myStr = ""    



  const blobServiceClient = new BlobServiceClient(

      `https://leonbragtest.blob.core.windows.net`,
      cred
    );

  let i = 1;

console.log("Listing all containers using iter.next()");
i = 1;
iter = blobServiceClient.listContainers();
let containerItem = await iter.next();
while (!containerItem.done) {
  context.log(`Container ${i}: ${containerItem.value.name}`);
  myStr += `Container ${i++}: ${containerItem.value.name} `

  try {
    containerClient = blobServiceClient.getContainerClient (containerItem.value.name)
    context.log("Listing all blobs using iter");
    let j = 1;
    let iter2 = await containerClient.listBlobsFlat();
    
    let blob = await iter2.next()
    while (!blob.done)  {
      context.log(`Blob ${j++}: ${blob.value.name}`);
      myStr += `\r\n   blob: ${blob.value.name}`

      const blobClient = containerClient.getBlobClient(blob.value.name);
      const downloadBlockBlobResponse = await blobClient.download(0);
      const tmpStr = await streamToString(downloadBlockBlobResponse.readableStreamBody)
      context.log("Downloaded blob content:", tmpStr)

      myStr += `\r\n   content: ${tmpStr}`
      blob = await iter2.next()
    }
  }
  catch (e) {
      context.log (e)
  }

  containerItem = await iter.next();
}



  res.status (200).json (     {"body": "Time is:" + new Date() + "\r\n" + myStr } );

});
module.exports = createHandler(app);`

here is the output of the code running locally:

// http://localhost:7071/api/authorize

{
  "body": "Time is:Sun Oct 13 2019 20:06:01 GMT-0700 (Pacific Daylight Time)\r\nContainer 1: leontest \r\n   blob: test.txt\r\n   content: test\r\n   blob: test2.txt\r\n   content: Hello 2\r\n"
}


Here is the output running in Azure Function.

// 20191013201951
// https://leonbragfunc.azurewebsites.net/api/authorize

{
"body": "Time is:Mon Oct 14 2019 03:19:50 GMT+0000 (Coordinated Universal Time)\r\nContainer 1: leontest "
}


**The only difference it credential used**
@leonbrag
Copy link
Author

leonbrag commented Oct 14, 2019

running exactly the same code in the Azure function using SharedKeyCredential works perfectly.

So this is a bug with Managed Identity.
However using DefaultAzureCredential() for app with MSI generates the same error.

The only working credentials are shared key credentials.

@jeremymeng jeremymeng added Client This issue points to a problem in the data-plane of the library. customer-reported Issues that are reported by GitHub users external to the Azure organization. Storage Storage Service (Queues, Blobs, Files) labels Oct 14, 2019
@jeremymeng
Copy link
Contributor

@daviwil Any ideas on what's going wrong? It is strange that Owner can list containers but not blobs.

@leonbrag
Copy link
Author

leonbrag commented Oct 15, 2019 via email

@daviwil
Copy link
Contributor

daviwil commented Oct 15, 2019

Hey all, I'll look into this tomorrow and will report back with my findings.

@leonbrag
Copy link
Author

@daviwil any update on this?
thanks

@daviwil
Copy link
Contributor

daviwil commented Nov 9, 2019

Hey @leonbrag, sorry for the delay. It appears that the "Owner" role is not sufficient for listing blobs inside of your blob storage account. You'll have to use one of the "Storage Blob Data *" roles (like "Storage Blob Data Owner" before you can list blobs or containers. This seems to be the intended behavior of the blob storage service.

@daviwil
Copy link
Contributor

daviwil commented Nov 9, 2019

Also, one thing that might help you in the future is to set the environment variable AZURE_LOG_LEVEL to info in your App Service configuration. We now write out logs for our authentication flow and service calls, so it gives a better picture of what might be going wrong.

@daviwil
Copy link
Contributor

daviwil commented Nov 13, 2019

Hi @leonbrag, I believe this issue will be resolved by using the appropriate role in the access policy for your storage account so I'm going to close this issue for now. If you try with one of the roles I recommended and you still experience this issue, please let me know and I'll reopen it so that we can investigate further. Thanks a lot!

@daviwil daviwil closed this as completed Nov 13, 2019
@ghost
Copy link

ghost commented Nov 13, 2019

Thanks for working with Microsoft on GitHub! Tell us how you feel about your experience using the reactions on this comment.

@github-actions github-actions bot locked and limited conversation to collaborators Apr 12, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Azure.Identity Client This issue points to a problem in the data-plane of the library. customer-reported Issues that are reported by GitHub users external to the Azure organization. Storage Storage Service (Queues, Blobs, Files)
Projects
None yet
Development

No branches or pull requests

4 participants