O Sertão será Cloud

Configuring Azure Functions for Azure Table Access with SOLID and Factory Pattern in TypeScript

This is the second article in a series on configuring Azure Functions for accessing Azure services using SOLID principles (Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion). In the previous article, we covered Azure Blob Storage access. In this article, we will focus on accessing Azure tables using Azure Data Tables.

How to Configure Azure Blob Service Client Applying SOLID Principles in an Azure Function with…

Step 1: Setting Up the Environment

Before starting, make sure you have the following dependencies installed:

npm install @azure/identity @azure/data-tables

NOTE: If you need to see Steps 1 to 3, please refer to the previous article on the subject.

Step 4: Creating the TableServiceClientFactory

Now let’s create a factory that uses the CredentialProvider to create instances of TableServiceClient.

/**
* Factory for creating instances of TableServiceClient.
*/
class TableServiceClientFactory {
private accountUrl: string;
private credentialProvider: CredentialProvider;

/**
* Creates an instance of TableServiceClientFactory.
* @param {string} accountUrl - The account URL for the TableServiceClient.
* @param {CredentialProvider} credentialProvider - The provider for Azure credentials.
*/
constructor(accountUrl: string, credentialProvider: CredentialProvider) {
this.accountUrl = accountUrl;
this.credentialProvider = credentialProvider;
}

/**
* Creates a TableServiceClient instance.
* @returns {TableServiceClient} The TableServiceClient instance.
*/
createTableServiceClient(): TableServiceClient {
const credential = this.credentialProvider.getCredential();
return new TableServiceClient(this.accountUrl, credential);
}
}

Step 5: Using the Factory in an Azure Function

Let’s see how we can use the factory to create an instance of TableServiceClient within an Azure Function. We’ll demonstrate three examples: System-assigned managed identity, User-assigned managed identity, and Service Principal.

Example 1: Azure Function with System-assigned Managed Identity

import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import { TableServiceClientFactory } from "./TableServiceClientFactory";
import { SystemAssignedManagedIdentityCredentialProvider } from "./SystemAssignedManagedIdentityCredentialProvider";

/**
* Azure Function triggered by an HTTP request.
* @param {Context} context - The Azure Function context object.
* @param {HttpRequest} req - The HTTP request object.
*/
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const accountUrl = process.env.AZURE_STORAGETABLE_RESOURCEENDPOINT;
const credentialProvider = new SystemAssignedManagedIdentityCredentialProvider();
const tableServiceClientFactory = new TableServiceClientFactory(accountUrl, credentialProvider);

const tableServiceClient = tableServiceClientFactory.createTableServiceClient();
const list = await tableServiceClient.listTables();

context.res = {
status: 200,
body: list
};
};

export default httpTrigger;

Example 2: Azure Function with User-assigned Managed Identity

import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import { TableServiceClientFactory } from "./TableServiceClientFactory";
import { UserAssignedManagedIdentityCredentialProvider } from "./UserAssignedManagedIdentityCredentialProvider";

/**
* Azure Function triggered by an HTTP request.
* @param {Context} context - The Azure Function context object.
* @param {HttpRequest} req - The HTTP request object.
*/
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const accountUrl = process.env.AZURE_STORAGETABLE_RESOURCEENDPOINT;
const clientId = process.env.AZURE_STORAGETABLE_CLIENTID;
const credentialProvider = new UserAssignedManagedIdentityCredentialProvider(clientId);
const tableServiceClientFactory = new TableServiceClientFactory(accountUrl, credentialProvider);

const tableServiceClient = tableServiceClientFactory.createTableServiceClient();
const list = await tableServiceClient.listTables();

context.res = {
status: 200,
body: list
};
};

export default httpTrigger;

Example 3: Azure Function with Service Principal

import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import { TableServiceClientFactory } from "./TableServiceClientFactory";
import { ServicePrincipalCredentialProvider } from "./ServicePrincipalCredentialProvider";

/**
* Azure Function triggered by an HTTP request.
* @param {Context} context - The Azure Function context object.
* @param {HttpRequest} req - The HTTP request object.
*/
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const accountUrl = process.env.AZURE_STORAGETABLE_RESOURCEENDPOINT;
const tenantId = process.env.AZURE_STORAGETABLE_TENANTID;
const clientId = process.env.AZURE_STORAGETABLE_CLIENTID;
const clientSecret = process.env.AZURE_STORAGETABLE_CLIENTSECRET;
const credentialProvider = new ServicePrincipalCredentialProvider(tenantId, clientId, clientSecret);
const tableServiceClientFactory = new TableServiceClientFactory(accountUrl, credentialProvider);

const tableServiceClient = tableServiceClientFactory.createTableServiceClient();
const list = await tableServiceClient.listTables();

context.res = {
status: 200,
body: list
};
};

export default httpTrigger;

Conclusion

In this article, we demonstrated how to configure an Azure Function for accessing Azure tables using different authentication methods applying SOLID principles. This modular design facilitates adding new authentication methods and maintaining code.

References

Início Rápido – Criar uma conexão de serviço no aplicativo de funções por meio do portal do Azure