Search code examples
c#azurerestazure-storageazure-blob-storage

How to find and delete Azure Storage Container using C# REST API?


Here's my code to find Storage Containers:

var api = $"https://{storageAccountName}.blob.core.windows.net/?comp=list";
using (var client = new HttpClient())
{
    client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken); //token obtained from https://storage.azure.com/
    client.BaseAddress = new Uri($"https://{storageAccountName}.blob.core.windows.net/");
    using (var responseGet = client.GetAsync(api).Result)
    {
        if (responseGet.IsSuccessStatusCode)
        {
            var xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(responseGet.Content.ReadAsStringAsync().Result);
            foreach (XmlNode a in xmlDocument.DocumentElement.SelectNodes("Containers/Container"))
            {
                containerNameList.Add(a.SelectSingleNode("Name").FirstChild.Value);
            }
        }
    }
}

I got an error:

`StatusCode: 403, ReasonPhrase: 'Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.', Version: 1.1, Content:

System.Net.Http.HttpConnection+HttpConnectionResponseContent, Headers:
{
  Server: Windows-Azure-Blob/1.0
  Server: Microsoft-HTTPAPI/2.0
  x-ms-request-id: 9d70d7ff-901e-0096-4c5b-aec38d000000
  Date: Mon, 09 Dec 2019 06:38:16 GMT
  Content-Length: 438
  Content-Type: application/xml
}`

I obtained the access token from https://storage.azure.com/

And here's the code to remove the storage container:

var strApi = $"https://{storageAccountName}.blob.core.windows.net/{storageContainerName}?restype=container";
using (var client = new HttpClient())
{
    client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
    client.BaseAddress = new Uri(BaseManagementUri);
    using (var responseGet = client.DeleteAsync(strApi).Result)
    {
        if (responseGet.IsSuccessStatusCode)
        {
            log.LogInformation($"Deleted {storageAccountName}");
        }
        else
        {
            log.LogWarning($"Failed to deleted {storageAccountName}\n{responseGet.Content.ReadAsByteArrayAsync().Result}");
        }
    }
}

How to obtain the proper access token and what all headers required for the above operations ?


Solution

  • According to my research, we can use Azure Active Directory (AD) to authorize requests to Blob storage. For more details, please refer to the document

    The detailed steps are as below.

    1. Create a service principal and assign Storage Blob Data Contributor role to the sp. You can refer to the article to know more details about how to do it.
    az ad sp create-for-rbac --name "" --scope <"/subscriptions/<subscription>/resourceGroups/<resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account>"> --role "Storage Blob Data Contributor"
    
    1. Get Azure AD access token
    URL : https://login.microsoftonline.com/{tenant}/v2.0/token
    Method : POST
    Headers : Content-Type: application/x-www-form-urlencoded
    Body :
           "grant_type" : "client_credentials"
           "scope" : "https://storage.azure.com/.default"
           "client_id" : "<your sp app id>"
           "client_secret" : "<your sp password>"
    

    enter image description here

    1. Call Azure Blob rest api
      • list containers
    URL: https://myaccount.blob.core.windows.net/?comp=list
    Method: Get
    Headers:
             x-ms-version : 2019-02-02
             Authorization: Bearer <access token>
    

    enter image description here

    • delete container
    URL: https://myaccount.blob.core.windows.net/mycontainer?restype=container  
    Method : DELETE
    Headers:
             x-ms-version : 2019-02-02
             Authorization: Bearer <access token>
    

    enter image description here

    Besides, if you want to do that with Azure MSI, please refer to the blog


    Update

    Regarding how to call Azure storage rest api with MSI in Azure function, please refer to the following steps. 1. Adding a system-assigned identity

    1. Assign the Storage Blob Data Contributor role to MSI enter image description here

    2. Code

    using System.IO;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Azure.WebJobs;
    using Microsoft.Azure.WebJobs.Extensions.Http;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.Logging;
    using Newtonsoft.Json;
    using System.Xml.Linq;
    using Microsoft.Azure.Services.AppAuthentication;
    using RestSharp;
    using System.Text;
    
    namespace TestFunV2
    {
        public static class Function1
        {
            [FunctionName("Function1")]
            public static async Task<IActionResult> Run(
                [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
                ILogger log)
            {
                log.LogInformation("C# HTTP trigger function processed a request.");
    
                var tokenProvider = new AzureServiceTokenProvider();
                var accesstoken = await tokenProvider.GetAccessTokenAsync("https://storage.azure.com/");
    
                var client = new RestClient("https://hurystorage.blob.core.windows.net/?comp=list");
                var request = new RestRequest(Method.GET);
    
                request.AddHeader("Authorization", "Bearer " + accesstoken);
                request.AddHeader("x-ms-version", "2019-02-02");
                IRestResponse response = await client.ExecuteTaskAsync(request);
    
                if (response.IsSuccessful) {
                    var xmlstring = response.Content;
                    string _byteOrderMarkUtf8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
                    if (xmlstring.StartsWith(_byteOrderMarkUtf8))
                    {
                        xmlstring = xmlstring.Remove(0, _byteOrderMarkUtf8.Length);
                    }
                    XElement x = XElement.Parse(xmlstring);
                    foreach (XElement container in x.Element("Containers").Elements("Container"))
                    {
                        log.LogInformation("Container name = {0}", container.Element("Name").Value);
    
                    }
                    return (ActionResult)new OkObjectResult("ok");
                }
                return new BadRequestObjectResult("failure");
    
    
            }
        }
    }
    
    

    enter image description here