Conectando-se ao Azure Database for MySQL com Azure Functions através de drivers nativos e Managed Identity

Cláudio Rapôso
7 min readSep 16, 2024

Bem-vindo ao nosso guia prático sobre como conectar Azure Functions ao Azure MySQL, usando técnicas de autenticação seguras e escaláveis. Neste artigo, você aprenderá a configurar seu ambiente de desenvolvimento e implementar uma Azure Function robusta com MySQL. Vamos explorar métodos de autenticação como identidade gerenciada e service principal. 🌐

O que é o Azure Database for MySQL?

O Azure Database for MySQL é um serviço gerenciado de banco de dados relacional, baseado no popular sistema de gerenciamento de banco de dados MySQL. Ele oferece uma infraestrutura segura e confiável para hospedar suas aplicações na nuvem, com foco em simplicidade e desempenho.

Principais Recursos e Benefícios:

  • Administração Simplificada: O Azure for MySQL cuida de tarefas críticas como backups automáticos, atualizações de software e recuperação de desastres, permitindo que os desenvolvedores foquem no crescimento da aplicação.
  • Escalabilidade Elástica: Aumente ou reduza a capacidade de computação e armazenamento conforme necessário, sem interrupções significativas, o que é ideal para aplicativos em crescimento.
  • Alta Disponibilidade Integrada: Com suporte a replicação geográfica e failover automático, o banco de dados garante que sua aplicação esteja sempre disponível, mesmo em cenários de falhas regionais.
  • Segurança de Classe Mundial: Seus dados estão protegidos com criptografia SSL/TLS em trânsito e em repouso, além de controle de acesso por meio de integração com o Microsoft Entra ID (anteriormente conhecido como Azure Active Directory), oferecendo autenticação e controle de acesso baseados em identidades.
  • Desempenho Otimizado: Com monitoramento e tuning integrados, o Azure MySQL permite ajustes finos no desempenho do banco de dados, garantindo que ele atenda às suas necessidades de resposta e carga de trabalho.
  • Compatibilidade e Flexibilidade: Totalmente compatível com a versão padrão do MySQL, permitindo que você traga facilmente seus aplicativos MySQL existentes para a nuvem sem grandes ajustes. Além disso, ele suporta extensões populares e recursos como tabelas particionadas, replicação assíncrona e consultas complexas.

Casos de Uso Comuns:

  • Aplicações Web e Móveis: Ideal para aplicativos que precisam de um banco de dados relacional escalável e de alto desempenho.
  • Análise de Dados em Tempo Real: Para empresas que precisam realizar consultas SQL complexas e relatórios dinâmicos em grandes volumes de dados.
  • Sistemas de E-commerce e ERP: Aplicações empresariais que necessitam de transações rápidas e consistentes, garantindo alta disponibilidade e integridade dos dados.

Passo 1: Configuração do Ambiente

Certifique-se de ter as seguintes dependências instaladas: OBS: Caso precise ver os Passos 1 ao 3 veja o artigo anterior sobre o tema

npm install @azure/identity mysql2

Passo 2: Criando a Abstração CredentialProvider

Vamos criar uma interface CredentialProvider que define um método getCredential.

import { TokenCredential } from "@azure/identity";
/**
* Interface for providing Azure credentials.
*/
interface CredentialProvider {
/**
* Retrieves the Azure credential.
* @returns {TokenCredential} The Azure credential.
*/
getCredential(): TokenCredential;
}

Passo 3: Implementando Provedores de Credenciais

Criaremos diferentes implementações de CredentialProvider para vários métodos de autenticação.

Método 1: SystemAssignedManagedIdentityCredentialProvider

import { DefaultAzureCredential } from "@azure/identity";

/**
* Provides credentials for system-assigned managed identity.
*/
class SystemAssignedManagedIdentityCredentialProvider implements CredentialProvider {
/**
* Retrieves the Azure credential for system-assigned managed identity.
* @returns {TokenCredential} The Azure credential.
*/
getCredential(): TokenCredential {
return new DefaultAzureCredential();
}
}

Método 2: UserAssignedManagedIdentityCredentialProvider

import { DefaultAzureCredential } from "@azure/identity";

/**
* Provides credentials for user-assigned managed identity.
*/
class UserAssignedManagedIdentityCredentialProvider implements CredentialProvider {
private clientId: string;
/**
* Creates an instance of UserAssignedManagedIdentityCredentialProvider.
* @param {string} clientId - The client ID of the user-assigned managed identity.
*/
constructor(clientId: string) {
this.clientId = clientId;
}
/**
* Retrieves the Azure credential for user-assigned managed identity.
* @returns {TokenCredential} The Azure credential.
*/
getCredential(): TokenCredential {
return new DefaultAzureCredential({
managedIdentityClientId: this.clientId
});
}
}

Método 3: ServicePrincipalCredentialProvider

import { ClientSecretCredential } from "@azure/identity";
/**
* Provides credentials for service principal authentication.
*/
class ServicePrincipalCredentialProvider implements CredentialProvider {
private tenantId: string;
private clientId: string;
private clientSecret: string;
/**
* Creates an instance of ServicePrincipalCredentialProvider.
* @param {string} tenantId - The tenant ID.
* @param {string} clientId - The client ID.
* @param {string} clientSecret - The client secret.
*/
constructor(tenantId: string, clientId: string, clientSecret: string) {
this.tenantId = tenantId;
this.clientId = clientId;
this.clientSecret = clientSecret;
}
/**
* Retrieves the Azure credential for service principal authentication.
* @returns {TokenCredential} The Azure credential.
*/
getCredential(): TokenCredential {
return new ClientSecretCredential(this.tenantId, this.clientId, this.clientSecret);
}
}

Passo 4: Arquitetura com Provedores de Credenciais

Aqui está uma fábrica para instanciar o cliente MySQL com o CredentialProvider.

import mysql from 'mysql2/promise';
import { TokenCredential } from '@azure/identity';

/**
* Factory for creating MySQL client instances.
*
* @remarks
* This class uses the Factory pattern to instantiate MySQL clients with
* credentials provided by Azure Identity. The goal is to centralize the
* connection creation logic, making the code more modular and reusable.
*/
class MySQLClientFactory {
private host: string;
private user: string;
private database: string;
private port: number;
private ssl: boolean;
private credentialProvider: any;

/**
* Creates a new instance of MySQLClientFactory.
*
* @param host - The MySQL database host.
* @param user - The database user name.
* @param database - The database name.
* @param port - The port used for MySQL connection.
* @param ssl - Indicates if SSL should be used.
* @param credentialProvider - The Azure credential provider.
*/
constructor(host: string, user: string, database: string, port: number, ssl: boolean, credentialProvider: any) {
this.host = host;
this.user = user;
this.database = database;
this.port = port;
this.ssl = ssl;
this.credentialProvider = credentialProvider;
}

/**
* Creates and returns an instance of the MySQL client.
*
* @returns A promise that resolves with the configured MySQL client instance.
*/
async createClient() {
const credential = this.credentialProvider.getCredential();
const accessToken = await credential.getToken('https://ossrdbms-aad.database.windows.net/.default');

const client = await mysql.createConnection({
host: this.host,
user: this.user,
password: accessToken.token,
database: this.database,
port: this.port,
ssl: this.ssl
});

return client;
}
}

Passo 5: Utilizando a Fábrica em uma Azure Function

Vamos integrar isso em uma Azure Function, usando Managed Identity.

Exemplo 1: Azure Function com Managed Identity Atribuída ao Sistema

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

const host = process.env.AZURE_MYSQL_HOST;
const user = process.env.AZURE_MYSQL_USER;
const database = process.env.AZURE_MYSQL_DATABASE;
const port = Number(process.env.AZURE_MYSQL_PORT);
const ssl = Boolean(process.env.AZURE_MYSQL_SSL);


/**
* Azure HTTP Function using managed identity to access Azure MySQL.
*
* @param context - The Azure Function context.
* @param req - The HTTP request.
*
* @remarks
* This Azure Function demonstrates how to use a managed identity to authenticate
* with Azure MySQL without the need for passwords or secret keys.
*
* @returns A promise that resolves when the function is complete.
*/
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const credentialProvider = new SystemAssignedManagedIdentityCredentialProvider();

const clientFactory = new MySQLClientFactory(
host,
user,
database,
port,
ssl,
credentialProvider
);

const client = await clientFactory.createClient();
await client.connect();

// Execute database operations
await client.end();

context.res = {
body: "Azure Function using system-assigned managed identity to access Azure MySQL"
};
};

export default httpTrigger;

Exemplo 2: Azure Function com Managed Identity Atribuída ao Usuário

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

const host = process.env.AZURE_MYSQL_HOST;
const user = process.env.AZURE_MYSQL_USER;
const database = process.env.AZURE_MYSQL_DATABASE;
const port = Number(process.env.AZURE_MYSQL_PORT);
const ssl = Boolean(process.env.AZURE_MYSQL_SSL);
const clientId = process.env.AZURE_MYSQL_CLIENTID;

/**
* Azure Function using user-assigned managed identity to access Azure MySQL.
*
* @param context - The Azure Function context.
* @param req - The HTTP request.
*
* @returns A promise that resolves when the function is complete.
*/
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const credentialProvider = new UserAssignedManagedIdentityCredentialProvider(
clientId,
);

const clientFactory = new MySQLClientFactory(
host,
user,
database,
port,
ssl,
credentialProvider
);

const client = await clientFactory.createClient();
await client.connect();

// Execute database operations
await client.end();

context.res = {
body: "Azure Function using user-assigned managed identity to access Azure MySQL"
};
};

export default httpTrigger;

Exemplo 3: Azure Function com Service Principal

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

const host = process.env.AZURE_MYSQL_HOST;
const user = process.env.AZURE_MYSQL_USER;
const database = process.env.AZURE_MYSQL_DATABASE;
const port = Number(process.env.AZURE_MYSQL_PORT);
const ssl = Boolean(process.env.AZURE_MYSQL_SSL);
const tenantId = process.env.AZURE_MYSQL_TENANTID;
const clientId = process.env.AZURE_MYSQL_CLIENTID;
const clientSecret = process.env.AZURE_MYSQL_CLIENTSECRET;

/**
* Azure Function using Service Principal to access Azure MySQL.
*
* @param context - The Azure Function context.
* @param req - The HTTP request.
*
* @remarks
* This Azure Function demonstrates how to use a Service Principal for
* authentication with Azure MySQL. The Service Principal credentials (tenant ID, client ID, and client secret)
* are securely stored in environment variables and accessed through the credential provider.
*
* @returns A promise that resolves when the function is complete.
*/
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const credentialProvider = new ServicePrincipalCredentialProvider(tenantId, clientId, clientSecret);

const clientFactory = new MySQLClientFactory(
host,
user,
database,
port,
ssl,
credentialProvider
);

const client = await clientFactory.createClient();
await client.connect();
// Execute database operations
await client.end();
context.res = {
body: "Azure Function using Service Principal to access Azure MySQL"
};
};

export default httpTrigger;

Conclusão

Você configurou com sucesso o Azure MySQL utilizando autenticação em Azure Functions, com uma arquitetura modular para facilitar a manutenção.

--

--

Cláudio Rapôso
Cláudio Rapôso

Written by Cláudio Rapôso

Microsoft MVP | Software Architect | Teacher | Book Author | MCT | 12x Microsoft Certified Connect with me in https://www.linkedin.com/in/cfraposo