1
votes

I have an Azure Function that has been assigned a System Identity:

System Assigned Identity

I would like the Azure Function to access a Storage account. The Function has the Reader & Data Access role on that storage account:

RBAC permissions

The Function has been configured with the name of the Storage account to use. The Function then tries to get an instance of CloudTableClient:

public async Task InitAsync(string accountsStorageName)
{
    var azureServiceTokenProvider = new AzureServiceTokenProvider();
    string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync(accountsStorageName);
    
    var storageCredential = new StorageCredentials(accessToken);
    var storageAccount = new CloudStorageAccount(storageCredential, accountsStorageName, "core.windows.net", true);
                
     //  Gets the client to the account's Table storage.
    m_tableClient = storageAccount.CreateCloudTableClient();
}

Question

The above code fails because it is unable to get an access token:

Error

How can an Azure Function get a reference to Azure Table storage using Managed Identity?

1
Table Storage is not supported, see also stackoverflow.com/questions/53564739/…. As you can see in the roles there are only rbac roles for blob, queues and files.Peter Bons
@PeterBons In my case, the storage account is a key vault managed storage account. Is there a way to get access to the storage account via the key vault then?Kzrystof
I've no experience with kv managed storage account but it seems to me you should be able to use a managed identity to access the kv and create a SAS token: docs.microsoft.com/en-us/azure/key-vault/secrets/…Peter Bons
@Kzrystof Do you have any other concerns? If you have no other concerns, could you please accept it as an answer?Jim Xu

1 Answers

1
votes

Azure key vault can generate shared access signature tokens. A shared access signature provides delegated access to resources in your storage account. You can grant clients access to resources in your storage account without sharing your account keys. For more details, please refer to here

  1. Set an account shared access signature definition
$storageAccountName = ""
$keyVaultName = ""
$storageContext = New-AzStorageContext -StorageAccountName $storageAccountName -Protocol Https -StorageAccountKey Key1
$start = [System.DateTime]::Now.AddDays(-1)
$end = [System.DateTime]::Now.AddMonths(1)

$sasToken = New-AzStorageAccountSasToken -Service blob,file,Table,Queue -ResourceType Service,Container,Object -Permission "racwdlup" -Protocol HttpsOnly -StartTime $start -ExpiryTime $end -Context $storageContext


Set-AzKeyVaultManagedStorageSasDefinition -AccountName $storageAccountName -VaultName $keyVaultName -Name "<YourSASDefinitionName>" -TemplateUri $sasToken -SasType 'account' -ValidityPeriod ([System.Timespan]::FromDays(30))

enter image description here

  1. Configure access policy for Azure Function
Set-AzKeyVaultAccessPolicy -VaultName $keyVaultName -ObjectId "Azure Function MSI object id" -PermissionsToSecrets get,list
  1. code
  public static class Http
    {
        [FunctionName("Http")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
            ILogger log, ExecutionContext context)
        {
            var azureServiceTokenProvider = new AzureServiceTokenProvider();
            var kv = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));

            SecretBundle sasToken =await kv.GetSecretAsync(secretIdentifier: "https://testkey02.vault.azure.net:443/secrets/teststorage08-sasToken");
            var storageCredential = new StorageCredentials(sasToken.Value);
            var accountsStorageName = "teststorage08";
            var storageAccount = new CloudStorageAccount(storageCredential, accountsStorageName, "core.windows.net", true);

            var tableClient = storageAccount.CreateCloudTableClient();
            var table =tableClient.GetTableReference("Customer");
            await table.CreateIfNotExistsAsync();
            CustomerEntity customer = new CustomerEntity("Harp", "Walter")
            {
                Email = "[email protected]",
                PhoneNumber = "425-555-0101"
            };

            TableOperation insertOrMergeOperation = TableOperation.InsertOrMerge(customer);
            TableResult result = await table.ExecuteAsync(insertOrMergeOperation);
            CustomerEntity insertedCustomer = result.Result as CustomerEntity;

            return new OkObjectResult(insertedCustomer);

        }       
    }

    public class CustomerEntity : TableEntity
    {
        public CustomerEntity()
        {
        }

        public CustomerEntity(string lastName, string firstName)
        {
            PartitionKey = lastName;
            RowKey = firstName;
        }

        public string Email { get; set; }

        public string PhoneNumber { get; set; }
    }

enter image description here