Introduction
I wanted to perform Azure table and blob crud transactions in my code behind for an ASP.NET website, but I wanted an added layer of security for my Azure credentials. Then, as my Azure storage accounts grew to dozens, each with dozens of tables & containers, the need for an easy and efficient way of keeping track of Stored Access Policies became a priority. I wanted a dashboard – I wanted to make it in WPF and wanted to do it entirely in C# - no xml, no http. Thought I would share some of what I learned along the way and put everything together in one place.
Requirements
You’re going to need to add a reference to Azure Storage to your project. In Visual Studio, right click on your project and go to Manage NuGet Packages… find and install WindowsAzure.Storage
You will need to add some or all of the following, depending on what you’re doing.
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.Table;
using Microsoft.WindowsAzure.Storage.Auth;
Getting the Storage Account
string storageAccountName = "yourStorageAccountName";
string key = "eitherYourPrimaryOrSecondaryKey";
string connection =
String.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}",
storageAccountName, key);
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connection);
Make a table policy
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
IEnumerable<CloudTable> tables = tableClient.ListTables();
CloudTable table = tableClient.GetTableReference("yourTableName");
SharedAccessTablePolicies permissions = table.GetPermissions().SharedAccessPolicies;
There is a limit of 5 policies that can be set on any one table or container. If you already have 5, your attempt to set another will fail. Clear them like this:
permissions.SharedAccessPolicies.Clear();
SharedAccessTablePolicies
are actually a collection of type KeyValuePair<string, SharedAccessTablePolicy>
, the key being the name of the policy, the value the actual policy - you make one like this:
SharedAccessTablePolicy newPolicy = new SharedAccessTablePolicy();
You must set an expiration date/time for the policy – it’s recommended that you don’t set anything less than 15 minutes to allow for potential differences in server clocks.
newPolicy.SharedAccessExpiryTime = DateTime.UtcNow.AddYears(1);
You can optionally set a start time - if you don’t specify a policy.SharedAccessStartTime
the policy will be in effect immediately. You can either add permissions all in one shot like so:
newPolicy.Permissions = SharedAccessTablePermissions.Query | SharedAccessTablePermissions.Add |
SharedAccessTablePermissions.Update | SharedAccessTablePermissions.Delete;
or add them one at a time like this
newPolicy.Permissions |= SharedAccessTablePermissions.Query;
newPolicy.Permissions |= SharedAccessTablePermissions.Add;
newPolicy.Permissions |= SharedAccessTablePermissions.Update;
newPolicy.Permissions |= SharedAccessTablePermissions.Delete;
When you’re finished you’ll return a new
KeyValuePair<string, SharedAccessTablePolicy>( "yourNewPolicyName", newPolicy);
Add your new policy into the existing set of permissions (assuming there are less than 5 existing policies)
permissions.SharedAccessPolicies.Add(newPolicy.Key, newPolicy.Value);
When you’re finished making your policies, set them like so:
table.SetPermissions(permissions);
Your new policy generates a SharedAccessSignature - looks something like this:
"?sv=2015-04-05&tn= yourTableName &sig=bb%2SjxB0Q1eFnBAJ71KhU0IjzqW2YeeqvSG8cx%2BlHkpo%3D&se=2017-11-19T20%3A03%3A03Z&sp=raud";
The sv=2015-04-05
is the server version used to create the policy.
The table name this policy is written against
tn= yourTableName
The signature itself
sig=bb%2SjxB0Q1eFnBAJ71KhU0IjzqW2YeeqvSG8cx%2BlHkpo%3D
Next is the expiration date
se=2017-11-19T20%3A03%3A03Z
Followed by what permission are allowed
sp=raud
This one allows for read, add(write), update and delete
So, to put it all together, let’s say we’re going to overwrite all existing policies and make three separate policies, one read, one write and one update, all with an expiration date 2 years in the future. I’m going to name each policy with the table name appended with the permission it grants.
void SetReadWriteUpdate(string tableName)
{
string storageAccountName = "yourStorageAccountName";
string key = "eitherYourPrimaryOrSecondaryKey";
string connection =
String.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}",
storageAccountName, key);
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connection);
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference(tableName);
SharedAccessTablePolicies permissions = table.GetPermissions().SharedAccessPolicies;
permissions.SharedAccessPolicies.Clear();
SharedAccessTablePolicy policyRead = new SharedAccessTablePolicy();
policyRead.SharedAccessExpiryTime = DateTime.UtcNow.AddYears(2);
policyRead.Permissions = SharedAccessTablePermissions.Query;
permissions.SharedAccessPolicies.Add(new KeyValuePair<string,
SharedAccessTablePolicy>(tableName + "Read", policyRead));
SharedAccessTablePolicy policyWrite = new SharedAccessTablePolicy();
policyWrite.SharedAccessExpiryTime = DateTime.UtcNow.AddYears(2);
policyWrite.Permissions = SharedAccessTablePermissions.Add;
permissions.SharedAccessPolicies.Add(new KeyValuePair<string,
SharedAccessTablePolicy>(tableName + "Write", policyWrite));
SharedAccessTablePolicy policyUpdate = new SharedAccessTablePolicy();
policyUpdate.SharedAccessExpiryTime = DateTime.UtcNow.AddYears(2);
policyUpdate.Permissions = SharedAccessTablePermissions.Update;
permissions.SharedAccessPolicies.Add(new KeyValuePair<string,
SharedAccessTablePolicy>(tableName + "Update", policyUpdate));
table.SetPermissions(permissions);
}
Edit a Single Table Policy
If you’re just looking to update a single policy, you need to first remove it from the existing set of policies, make a new one and add it back into the collection of policies and set them back to the table.
SharedAccessTablePolicies permissions = table.GetPermissions().SharedAccessPolicies;
permissions.SharedAccessPolicies.Remove("theNameOfThePolicyToRemove");
SharedAccessTablePolicy newPolicy = new SharedAccessTablePolicy();
permissions.SharedAccessPolicies.Add(new KeyValuePair<string,
SharedAccessTablePolicy>("newPolicyName", newPolicy));
table.SetPermissions(permissions);
Make container policy
Setting policies on containers is essentially the same but substitute the term Blob wherever you find Table. It’s a bit confusing, however because you’re setting policies on containers and not blobs as the name would imply. Another difference is that container permissions allow for listing but not updating - it’s a container, you can’t update the container itself. Listing allows for listing of the blobs held in the container – you do it the same way you listed tables above.
void SetPermissionsContainer(string yourContainerName)
{
string storageAccountName = "yourStorageAccountName";
string key = "eitherYourPrimaryOrSecondaryKey";
string connection =
String.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}",
storageAccountName, key);
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connection);
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference(yourContainerName);
BlobContainerPermissions permissions = container.GetPermissions().SharedAccessPolicies;
SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy();
policy.SharedAccessExpiryTime = DateTime.UtcNow.AddYears(2);
policy.Permissions |= SharedAccessBlobPermissions.Read;
policy.Permissions |= SharedAccessBlobPermissions.Write;
policy.Permissions |= SharedAccessBlobPermissions.Delete;
policy.Permissions |= SharedAccessBlobPermissions.List;
permissions.SharedAccessPolicies.Add(policy.Key, policy.Value);
table.SetPermissions(permissions);
}
Getting the signature
Now that you have a stored access policy, what do you do with it? You will need to know what table (or container) it’s written for and the policy name.
string GetSignature(CloudTable table, string policyName)
{
SharedAccessTablePolicies policies = table.GetPermissions().SharedAccessPolicies;
SharedAccessTablePolicy policy = policies[policyName];
return table.GetSharedAccessSignature(policy);
}
Now you can use that signature in your applications or give it to another person to use, and your Azure credentials are kept confidential. To use it, you’ll need the signature itself, the storage account name and the table name.
Using the Signature
void UseMyNewPolicy(string signature, string accountName, string tableName)
{
StorageCredentials creds = new StorageCredentials(signature);
string endpoint = string.Format("https://{0}.table.core.windows.net", accountName);
CloudTableClient client = new CloudTableClient(new Uri(endpoint), creds);
CloudTable table = client.GetTableReference(tableName);
}
And now the user can perform whatever operations are allowed against the table.
Here’s a shot of the finished dashboard:
Additional resources
Designing a Scalable Partitioning Strategy for Azure Table Storage
- http://justazure.com/azure-blob-storage-part-9-shared-access-signatures/
- https://azure.microsoft.com/en-us/documentation/articles/storage-manage-access-to-resources/
- https://azure.microsoft.com/en-us/documentation/articles/storage-dotnet-shared-access-signature-part-1/
- Understanding the Table Service Data Model
- Using Azure Table Storage