O Sertão será Cloud

Conectando-se ao Azure SQL Server com Azure Functions Usando Drivers Nativos e Managed Identity

Bem-vindo ao nosso guia prático sobre como conectar Azure Functions ao Azure SQL Server 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 SQL Server. Exploraremos métodos de autenticação como identidade gerenciada e service principal. 🌐

O que é o Azure SQL Server?

O Azure SQL Server é um serviço de banco de dados relacional totalmente gerenciado, fornecido pela Microsoft. Ele oferece alto desempenho, escalabilidade e segurança, tornando-o uma solução ideal para aplicativos hospedados na nuvem que exigem bancos de dados relacionais.

Principais Recursos e Benefícios:

  • Administração Simplificada: O Azure SQL Server cuida de tarefas críticas, como backups automáticos, atualizações, monitoramento e recuperação de desastres, permitindo que os desenvolvedores foquem na construção e melhoria de aplicativos.
  • Escalabilidade Elástica: Aumente ou diminua a capacidade de computação e armazenamento conforme necessário, sem tempo de inatividade, garantindo que sua aplicação escale de forma contínua.
  • Alta Disponibilidade: Com replicação integrada, opções de failover e backups georredundantes, o Azure SQL Server garante que seus dados estejam sempre acessíveis, mesmo em casos de falhas.
  • Segurança Avançada: O Azure SQL Server oferece criptografia em repouso e em trânsito, juntamente com proteção contra ameaças avançadas e configurações de firewall, garantindo que seus dados estejam seguros. A integração com o Microsoft Entra ID (Azure Active Directory) permite controle de acesso baseado em identidade.
  • Desempenho Otimizado: O ajuste avançado de consultas, indexação e monitoramento detalhado garantem que o banco de dados atenda de forma ideal às suas necessidades de carga de trabalho.
  • Compatibilidade e Flexibilidade: Totalmente compatível com o SQL Server on-premises, facilitando a migração ou expansão de suas aplicações para a nuvem sem a necessidade de mudanças no código.

Casos de Uso Comuns:

  • Aplicações Empresariais: Ideal para hospedar aplicativos críticos que exigem alto desempenho e disponibilidade.
  • Armazenamento de Dados e Inteligência de Negócios: Excelente para rodar grandes volumes de análises e relatórios com as poderosas capacidades de consulta do SQL Server.
  • E-commerce e Finanças: Sistemas de processamento transacional seguros, rápidos e confiáveis se beneficiam enormemente do Azure SQL Server.

Passo 1: Configuração do Ambiente

Certifique-se de ter as seguintes dependências instaladas:

npm install @azure/identity mssql

Passo 2: Arquitetura com SQLClientFactory

A seguir, está o código da SQLClientFactory, que lida com os diferentes tipos de autenticação (identidade gerenciada e service principal):

import sql from 'mssql';

/**
* SQLClientFactory is responsible for creating a connection to Azure SQL Server
* using different authentication methods, such as Managed Identity or Service Principal.
*/
class SQLClientFactory {
private server: string;
private database: string;
private port: number;
private authenticationType: string;
private clientId?: string;
private clientSecret?: string;
private tenantId?: string;

/**
* Creates an instance of SQLClientFactory.
*
* @param server - The Azure SQL Server address.
* @param database - The name of the Azure SQL database.
* @param port - The port to connect to the Azure SQL database.
* @param authenticationType - The type of authentication to use (e.g., 'ManagedIdentity' or 'ServicePrincipal').
* @param clientId - (Optional) The client ID for user-assigned managed identity or service principal.
* @param clientSecret - (Optional) The client secret for service principal authentication.
* @param tenantId - (Optional) The tenant ID for service principal authentication.
*/
constructor(
server: string,
database: string,
port: number,
authenticationType: string,
clientId?: string,
clientSecret?: string,
tenantId?: string
) {
this.server = server;
this.database = database;
this.port = port;
this.authenticationType = authenticationType;
this.clientId = clientId;
this.clientSecret = clientSecret;
this.tenantId = tenantId;
}

/**
* Builds the configuration object based on the authentication type.
*
* @returns The configuration object to be used by mssql to establish a connection.
*/
private getConfig() {
const config: any = {
server: this.server,
port: this.port,
database: this.database,
authentication: {
type: this.authenticationType,
},
options: {
encrypt: true,
},
};

// Config for user-assigned managed identity
if (this.authenticationType === 'ManagedIdentity' && this.clientId) {
config.options.clientId = this.clientId;
}

// Config for service principal
if (this.authenticationType === 'ServicePrincipal') {
config.options.clientId = this.clientId;
config.options.clientSecret = this.clientSecret;
config.options.tenantId = this.tenantId;
}

return config;
}

/**
* Creates and returns an SQL connection pool using the configuration.
*
* @returns A Promise that resolves to the SQL connection pool.
*/
async createClient() {
const config = this.getConfig();
return await sql.connect(config);
}
}

Passo 4: Usando a Fábrica em uma Azure Function

Vamos integrar isso em uma Azure Function usando Managed Identity.

Exemplo 1: Azure Function com Identidade Gerenciada Atribuída ao Sistema

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

/**
* Azure Function using system-assigned managed identity to connect to Azure SQL Server.
*
* @param context - The Azure Function context.
* @param req - The incoming HTTP request.
* @returns A promise that resolves with a response containing SQL query results.
*/
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const factory = new SQLClientFactory(
process.env.AZURE_SQL_SERVER,
process.env.AZURE_SQL_DATABASE,
parseInt(process.env.AZURE_SQL_PORT),
'ManagedIdentity'
);

const pool = await factory.createClient();
const result = await pool.request().query('SELECT TOP 10 * FROM YourTable');

context.res = {
body: result.recordset,
};
};

export default httpTrigger;

Exemplo 2: Azure Function com Identidade Gerenciada Atribuída ao Usuário

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

/**
* Azure Function using user-assigned managed identity to connect to Azure SQL Server.
*
* @param context - The Azure Function context.
* @param req - The incoming HTTP request.
* @returns A promise that resolves with a response containing SQL query results.
*/
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const factory = new SQLClientFactory(
process.env.AZURE_SQL_SERVER,
process.env.AZURE_SQL_DATABASE,
parseInt(process.env.AZURE_SQL_PORT),
'ManagedIdentity',
process.env.AZURE_SQL_CLIENTID
);

const pool = await factory.createClient();
const result = await pool.request().query('SELECT TOP 10 * FROM YourTable');

context.res = {
body: result.recordset,
};
};

export default httpTrigger;

Exemplo 3: Azure Function com Service Principal

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

/**
* Azure Function using service principal to connect to Azure SQL Server.
*
* @param context - The Azure Function context.
* @param req - The incoming HTTP request.
* @returns A promise that resolves with a response containing SQL query results.
*/
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const factory = new SQLClientFactory(
process.env.AZURE_SQL_SERVER,
process.env.AZURE_SQL_DATABASE,
parseInt(process.env.AZURE_SQL_PORT),
'ServicePrincipal',
process.env.AZURE_SQL_CLIENTID,
process.env.AZURE_SQL_CLIENTSECRET,
process.env.AZURE_SQL_TENANTID
);

const pool = await factory.createClient();
const result = await pool.request().query('SELECT TOP 10 * FROM YourTable');

context.res = {
body: result.recordset,
};
};

export default httpTrigger;

Conclusão

Você configurou com sucesso o Azure SQL Server utilizando autenticação em Azure Functions, com uma arquitetura modular e flexível para suportar diferentes métodos de autenticação (Managed Identity e Service Principal). Essa abordagem facilita a escalabilidade e manutenção do sistema.