Azure Storage Connection - arp6333/xplusplus GitHub Wiki

Connecting to Azure storage without using WindowsAzure.Storage / Connection String (deprecated in 10.0.44)

Follow the initial steps by Microsoft to register an Azure app and assign it the correct roles for the Azure storage location. The only steps needed are "Register the app in Azure" and "Assign roles to the application service principal". Be sure to keep note of the Application (Client) Id, Directory (Tenant) Id, and Client Secret from the "Register the app in Azure" step (the client secret especially as it will be censored after generation).

Link here


The following code shows how to connect to the storage location, along with pulling files from storage and uploading a file to storage.

We use a custom table called ConnectionParameters as the method parameter for these functions, which is a parameter table containing the fields necessary to connect. The application id, directory id, and client secret fields retrieved in the setup steps above are stored in the database as an EncryptedField EDT. This makes the data obfuscated, including unable to be parsed when a data refresh is completed (ex. Production data refreshed to Test / Dev will NOT allow any EncryptedField EDT values to be viewed or used and they will need to be reinputted). You will see in the code below that retrieving these fields is via a special method which decrypts the field (ex. 'azureAppIdEdit()'). The app id and tenant id don't necessarily need to be encrypted, but the secret definitely should be. See here for more info on this process of creating / using an EncryptedField.

The relevant list of fields from this table include:

  • Application (client) id from Azure app created above, encrypted [AzureAppId]
  • Directory (tenant) id from Azure app created above, encrypted [AzureTenantId]
  • Client secret from Azure app created above, encrypted [AzureSecretValue]
  • Name of the Azure storage account [AzureStorageAccount] image
  • Name of the Azure container within the Azure storage account [AzureContainer] image
  • Name of the Azure folder within the Azure container; optional if there are no inner folders [AzureFolder] image
using System.Net;
using System.IO;
using System.Collections;
using System.Uri;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.BlobClient;
using Azure.Storage.Blobs.BlobServiceClient;
using Azure.Storage.Blobs.Models;
using Azure.Identity;
using System.Threading;

/// <summary>
/// Helper class for Azure blob storage functionality.
/// </summary>
public static class AzureStorageHelper
{
    /// <summary>
    /// Connect to an Azure blob storage location.
    /// </summary>
    /// <param name = "connParam">Connection parameters.</param>
    /// <returns>Blob container client.</returns>
    public static BlobContainerClient connectToStorage(ConnectionParameters connParam)
    {
        str                     blobUri             = strFmt("https://%1.blob.core.windows.net", connParam.AzureStorageAccount); // This can be a label
        str                     appId               = connParam.azureAppIdEdit(false, ''); // Decrypt the field
        str                     tenantId            = connParam.azureTenantIdEdit(false, ''); // Decrypt the field
        str                     clientSecret        = connParam.azureSecretValueEdit(false, ''); // Decrypt the field
        ClientSecretCredential  credential          = new ClientSecretCredential(tenantId, appId, clientSecret);

        BlobServiceClient       blobServiceClient   = new BlobServiceClient(new System.Uri(blobUri), credential);
        BlobContainerClient     blobContainerClient = blobServiceClient.GetBlobContainerClient(connParam.AzureContainer);

        return blobContainerClient;
    }

    /// <summary>
    /// Retrieve files from an Azure blob storage location.
    /// </summary>
    /// <param name = "connParam">Connection parameters.</param>
    /// <returns>List of files retrieved.</returns>
    public static List retrieveFilesFromBlob(ConnectionParameters connParam)
    {
        IEnumerable         blobEnumerable;
        IEnumerator         blobEnumerator;
        BlobContainerClient blobContainerClient;
        BlobClient          blobClient;
        BlobItem            blobFile;
        List                fileList = new List(Types::AnyType);

        // Connect to Azure storage
        blobContainerClient = AzureStorageHelper::connectToStorage(connParam);

        // Add each blob file to the list of files to return
        blobEnumerable      = blobContainerClient.GetBlobs(
            BlobTraits::None, BlobStates::None, connParam.AzureFolder + @'/', CancellationToken::None);
        blobEnumerator      = blobEnumerable.GetEnumerator();
        while (blobEnumerator.MoveNext())
        {
            var blobItem = blobEnumerator.Current;
            if (blobItem is BlobItem)
            {
                blobFile    = blobItem;
                blobClient  = blobContainerClient.GetBlobClient(blobFile.Name);
                fileList.addEnd(blobClient);
            }
        }

        return fileList;
    }

    /// <summary>
    /// Upload a file to Azure blob storage.
    /// </summary>
    /// <param name = "connParam">Connection parameters.</param>
    /// <param name = "fileName">File name to upload.</param>
    /// <param name = "fileStream">File stream to upload.</param>
    public static void sendFileToBlob(ConnectionParameters connParam, str fileName, System.IO.MemoryStream fileStream)
    {
        BlobContainerClient blobContainerClient;
        BlobClient          blobClient;

        // Connect to Azure storage
        blobContainerClient = AzureStorageHelper::connectToStorage(connParam);

        // Upload the file
        blobClient          = blobContainerClient.GetBlobClient(fileName);
        blobClient.Upload(fileStream, true, CancellationToken::None);
        fileStream.Close();
    }

}

To read in the actual file from the list of files given in the retrieveFilesFromBlob() method, you can use this code (or you can put this code directly in the method to read the files right away if that works better for your needs):

str                 fileName, blobContents;
BlobClient          cloudBlockBlob;
BlobDownloadResult  downloadResult;
List                fileList        = AzureStorageHelper::retrieveFilesFromBlob(connParam);
ListIterator        fileIterator    = new ListIterator(fileList);
while (fileIterator.more())
{
    try
    {
        blobClient      = fileIterator.value();
        fileName        = blobClient.Name;
    
        // Read in the file
        downloadResult  = blobClient.DownloadContent();
        blobContents    = downloadResult.Content.ToString();
    }
}
⚠️ **GitHub.com Fallback** ⚠️