Creating storage accounts and blob containers using Azure fluent API-s
Using Azure fluent API-s it is easy to create storage accounts and blob containers. After experimenting with fluent API of Azure storage I found it to be good match for multitenant web applications where tenant files are held on Azure blob storage. I was surprised how clean and short code I got using fluent API. Here’s my overview of fluent API for Azure storage with some code samples I wrote.
Getting started
Before getting to code we need Azure portal account and some configuration parameters:
- TenantId – ID of Azure AD tenant. Open Azure AD settings in Azure portal to get tenant ID.
- ClientId and ClientSecret – these are for service principal (or account) we want to use for accessing Azure services. To set up service principal please jump to Azure documentation page How to: Use the portal to create an Azure AD application and service principal that can access resources.
- SubscriptionId – ID of Azure subscription where we want to create storage accounts. Open Subscriptions in Azure portal to get subscription ID.
- ResourceGroup – name of resource group under what storage accounts are created. Make sure the resource group exists already or create new one if you need.
With settings ready it’s time to get into code.
Fluent syntax for Azure blob storage
There are few important things to know before implementing class to create storage accounts. Fluent syntax for Azure services uses IAzure interface as a bridge head to row of fluent method calls. When building IAzure instance we need to provide all configuration parameters mentioned above.
Here’s the example. Azure.Configure() method starts the row of fluent configuring methods to build up client class we will use later.
var principalLogIn = new ServicePrincipalLoginInformation();
principalLogIn.ClientId = ClientId;
principalLogIn.ClientSecret = ClientSecret;
var environment = AzureEnvironment.AzureGlobalCloud;
var credentials = new AzureCredentials(principalLogIn, TenantId, environment);
_azure = Azure.Configure()
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.Authenticate(credentials)
.WithSubscription(SubscriptionId);
To create blob storage account we will use initialized Azure client like shown in the following example.
await _azure.StorageAccounts.Define(accountName)
.WithRegion(Region.EuropeWest)
.WithExistingResourceGroup(ResourceGroup)
.WithAccessFromAllNetworks()
.WithGeneralPurposeAccountKindV2()
.WithOnlyHttpsTraffic()
.WithSku(StorageAccountSkuType.Standard_LRS)
.CreateAsync();
Although fluent methods shown above are just few of those provided, notice how new account is defined using clear and readable fluent methods that specify important aspects of account. Finally CreateAsync() method is called and then new storage account is created.
Similar fluent API-s are available also for some other Azure services and in big part they follow the same idea as shown here with Azure storage account.
Creating storage account and blob containers
It’s time to get to code and build something real. Let’s start with NuGet packages we need in our project:
- Microsoft.Azure.Management.Fluent
- Microsoft.Azure.Management.Storage.Fluent
- WindowsAzure.Storage (if you want to manage blobs in containers)
To try fluent storage API-s out, it doesn’t matter much what kind of application to create – be it console or web application or maybe Azure Function. I decided to go with web application as I’m building demo tenants manager application for some of my blog posts.
When new tenant is created we need to create also storage account for it and add blob containers that multitenant application expects to exist. Tenant can be created using long spaghetti code but I don’t like chaos and therefore I want more intelligent approach. Let’s try to write simple client class for creating storage accounts for tenants.
I start with simple interface.
public interface IStorageManager
{
Task CreateAccountWithFolders(string name, params string[] container);
}
CreateAccountWithFolders() method takes new storage account name and array of container names that must be created with account. For this blog post this minimalistic interface is enough.
Let’s add a class called AzureBlobStorageManager to our solution. Here are namespaces used in this class.
using System;
using System.Threading.Tasks;
using Microsoft.Azure.Management.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Authentication;
using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
using Microsoft.Azure.Management.Storage.Fluent;
using Microsoft.Azure.Management.Storage.Fluent.Models;
Class to create Azure storage accounts is here. Notice you need all configuration parameter values now to make the class actually work.
public class AzureBlobStorageManager : IStorageManager
{
private const string TenantId = "4dcea3b0-b401-4b25-af70-971068b99d1c";
private const string ClientId = "9cce9aed-69eb-45af-9bed-a5d7e403019f";
private const string ClientSecret = "74d99bc5-e239-4308-ba15-e3e2acdb7fcf";
private const string SubscriptionId = "6ca601d2-417f-4fa4-ae19-cd465102f8de";
private const string ResourceGroup = "TestResourceGroup";
private readonly IAzure _azure;
public AzureBlobStorageManager()
{
var principalLogIn = new ServicePrincipalLoginInformation();
principalLogIn.ClientId = ClientId;
principalLogIn.ClientSecret = ClientSecret;
var environment = AzureEnvironment.AzureGlobalCloud;
var credentials = new AzureCredentials(principalLogIn, TenantId, environment);
_azure = Azure.Configure()
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.Authenticate(credentials)
.WithSubscription(SubscriptionId);
}
public async Task CreateAccountWithFolders(string accountName, params string[] containerNames)
{
var availabilityResult = await _azure.StorageAccounts.CheckNameAvailabilityAsync(accountName);
if(!availabilityResult.IsAvailable.Value)
{
throw new Exception("Account name is not available");
}
var account = await _azure.StorageAccounts.Define(accountName)
.WithRegion(Region.EuropeWest)
.WithExistingResourceGroup(ResourceGroup)
.WithAccessFromAllNetworks()
.WithGeneralPurposeAccountKindV2()
.WithOnlyHttpsTraffic()
.WithSku(StorageAccountSkuType.Standard_LRS)
.CreateAsync();
var containers = account.Manager.BlobContainers;
foreach (var containerName in containerNames)
{
await containers.DefineContainer(containerName)
.WithExistingBlobService(ResourceGroup, accountName)
.WithPublicAccess(PublicAccess.None)
.CreateAsync();
}
}
}
For web applications we can register this class in framework-level dependency injection to inject it to controllers. This is how ConfigureServices() method of my Starpup class looks like.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddScoped<IStorageManager, AzureBlobStorageManager>();
}
And here is the minimalistic example of controller that makes use of IStorageManager.
public class HomeController : Controller
{
private readonly IStorageManager _storageManager;
public HomeController(IStorageManager storageManager)
{
_storageManager = storageManager;
}
// ...
[HttpPost]
public async Task<IActionResult> Edit(string account)
{
await _storageManager.CreateAccountWithFolders(account, "images", "documents");
return View();
}
// ...
}
Using the code above I created new storage account with two blob containers – documents and images.
In practice we don’t get away so easily as we need to read out storage connection string after account is created and probably we also need SAS key for another services that access files on Azure storage. But with simple client class shown here you have good starting point for your own Azure storage management client.
Wrapping up
Although it was just an experiment to see how fluent API for Azure storage works, it was good find. When creating Azure storage accounts in code it’s important to have a good overview of features that new account will have. With fluent API-s I was able to write the code that looks like specification defining new account to be created. Good news is that there are similar fluent API-s available for other Azure services too.
Why do you show a very bad example of putting secretid in the code.
There is Managed Identity….
This post is about creating storage account and blob containers using fluent API. Every developer with at least minimal common sense wouldn’t keep sensitive data in code files when writing real solutions.
Pingback:Dew Drop – March 4, 2020 (#3146) | Morning Dew
Pingback:The Morning Brew - Chris Alcock » The Morning Brew #2946