Conectando-se ao Azure Database for MySQL com Azure Functions através de drivers nativos e Managed Identity
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.