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

Cláudio Rapôso
6 min readSep 15, 2024

Bem-vindo ao nosso guia prático sobre como conectar Azure Functions ao Azure PostgreSQL com várias técnicas de autenticação e aplicando os conceitos de fabrica! 🎉 Neste artigo, você aprenderá a configurar seu ambiente de desenvolvimento e implementar uma Azure Function robusta e escalável. Vamos explorar autenticação usando identidade gerenciada e service principal. 🌐

O que é o Azure Database for PostgreSQL?

Azure Database for PostgreSQL é um serviço gerenciado de banco de dados relacional na nuvem oferecido pela Microsoft Azure. Ele é baseado no popular sistema de gerenciamento de banco de dados de código aberto PostgreSQL, conhecido por sua robustez, conformidade com os padrões SQL, e extensibilidade. Aqui estão alguns dos principais recursos e benefícios do Azure PostgreSQL:

Principais Recursos e Benefícios do Azure PostgreSQL

Serviço Gerenciado:

  • Administração Simplificada: Azure PostgreSQL cuida da manutenção do servidor, atualizações de software, backups automáticos, e recuperação de desastres.
  • Escalabilidade: Aumente ou diminua a capacidade de computação e armazenamento de acordo com a necessidade, sem tempo de inatividade significativo.

Alta Disponibilidade:

  • Backup e Recuperação: Backups automáticos com retenção configurável e recuperação pontual.
  • Replicação e Failover: Configuração de alta disponibilidade com replicação geográfica e failover automático.

Segurança:

  • Criptografia: Dados em trânsito e em repouso são criptografados usando SSL/TLS e criptografia transparente de dados (TDE).
  • Controle de Acesso: Integração com Azure Active Directory para autenticação e controle de acesso baseado em funções (RBAC).

Desempenho:

  • Monitoramento e Tuning: Ferramentas integradas de monitoramento e tuning para identificar gargalos de desempenho e ajustar a configuração.
  • Opções de Preço e Desempenho: Diferentes camadas de serviço (Basic, General Purpose, e Memory Optimized) para atender a diversas necessidades de desempenho e orçamento.

Compatibilidade:

  • Extensões: Suporte a várias extensões populares do PostgreSQL, como PostGIS para geoespacial, PLV8 para programação em JavaScript, e muitas outras.
  • Conformidade com Padrões: Total conformidade com os padrões SQL e suporte a funcionalidades avançadas do PostgreSQL, como índices GIN e GIST, tipos de dados JSON e XML, e mais.

Desenvolvimento e Integração:

  • Ferramentas de Desenvolvimento: Suporte a diversas ferramentas de desenvolvimento, como pgAdmin, DBeaver, e APIs RESTful.
  • Integração com Serviços Azure: Fácil integração com outros serviços do Azure, como Azure Functions, Azure Kubernetes Service (AKS), Azure Logic Apps, e mais.

Casos de Uso Comuns

  • Aplicações Web e Móveis: Bancos de dados para backend de aplicações web e móveis escaláveis.
  • Análise de Dados: Soluções de análise e relatórios utilizando capacidades avançadas de SQL e extensões como PostGIS.
  • Sistemas Geoespaciais: Aplicações que requerem armazenamento e consulta de dados geoespaciais.
  • Aplicações Empresariais: Sistemas ERP, CRM, e outras aplicações empresariais que necessitam de um banco de dados relacional robusto e confiável.

Azure PostgreSQL oferece uma solução robusta, segura e escalável para suas necessidades de banco de dados relacional, com todas as vantagens de um serviço gerenciado na nuvem. Seja para aplicações web, análise de dados ou sistemas empresariais, ele fornece as ferramentas e recursos necessários para desenvolver, implantar e gerenciar seus bancos de dados de maneira eficiente e eficaz.

Passo 1: Configuração do Ambiente

Antes de começar, certifique-se de ter as seguintes dependências instaladas:

npm install @azure/identity pg

OBS: Caso precise ver os Passos 1 ao 3 veja o artigo anterior sobre o tema:

Passo 4: Arquitetura com Provedores de Credenciais

Vamos criar uma fábrica para instanciar o Client do PostgreSQL usando nosso CredentialProvider.

import { Client } from 'pg';
import { TokenCredential } from '@azure/identity';

/**
* Factory for creating PostgreSQL Client instances.
*/
class PostgresClientFactory {
private host: string;
private user: string;
private database: string;
private port: number;
private ssl: boolean;
private credentialProvider: CredentialProvider;

/**
* Creates a new instance of PostgresClientFactory.
* @param {string} host - The PostgreSQL host.
* @param {string} user - The PostgreSQL user.
* @param {string} database - The database name.
* @param {number} port - The PostgreSQL port.
* @param {boolean} ssl - Indicator for using SSL.
* @param {CredentialProvider} credentialProvider - The credential provider to use.
*/
constructor(host: string, user: string, database: string, port: number, ssl: boolean, credentialProvider: CredentialProvider) {
this.host = host;
this.user = user;
this.database = database;
this.port = port;
this.ssl = ssl;
this.credentialProvider = credentialProvider;
}

/**
* Creates a PostgreSQL Client instance.
* @returns {Promise<Client>} A promise that resolves with the configured Client instance.
*/
async createClient(): Promise<Client> {
const credential = this.credentialProvider.getCredential();
const accessToken = await credential.getToken('https://ossrdbms-aad.database.windows.net/.default');
const client = new Client({
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 tudo isso em uma Azure Function, mostrando cada método de autenticação.

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

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

const host = process.env.AZURE_POSTGRESQL_HOST;
const user = process.env.AZURE_POSTGRESQL_USER;
const database = process.env.AZURE_POSTGRESQL_DATABASE;
const port = Number(process.env.AZURE_POSTGRESQL_PORT);
const ssl = Boolean(process.env.AZURE_POSTGRESQL_SSL);

/**
* Azure Function using system-assigned managed identity.
* @param {Context} context - The function context.
* @param {HttpRequest} req - The HTTP request.
* @returns {Promise<void>} 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 PostgresClientFactory(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 PostgreSQL"
};
};

export default httpTrigger;

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

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

const host = process.env.AZURE_POSTGRESQL_HOST;
const user = process.env.AZURE_POSTGRESQL_USER;
const database = process.env.AZURE_POSTGRESQL_DATABASE;
const port = Number(process.env.AZURE_POSTGRESQL_PORT);
const ssl = Boolean(process.env.AZURE_POSTGRESQL_SSL);
const clientId = process.env.AZURE_POSTGRESQL_CLIENTID;

/**
* Azure Function using user-assigned managed identity.
* @param {Context} context - The function context.
* @param {HttpRequest} req - The HTTP request.
* @returns {Promise<void>} 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 PostgresClientFactory(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 PostgreSQL"
};
};

export default httpTrigger;

Exemplo 3: Azure Function com Service Principal

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

const host = process.env.AZURE_POSTGRESQL_HOST;
const user = process.env.AZURE_POSTGRESQL_USER;
const database = process.env.AZURE_POSTGRESQL_DATABASE;
const port = Number(process.env.AZURE_POSTGRESQL_PORT);
const ssl = Boolean(process.env.AZURE_POSTGRESQL_SSL);
const tenantId = process.env.AZURE_POSTGRESQL_TENANTID;
const clientId = process.env.AZURE_POSTGRESQL_CLIENTID;
const clientSecret = process.env.AZURE_POSTGRESQL_CLIENTSECRET;

/**
* Azure Function using Service Principal.
* @param {Context} context - The function context.
* @param {HttpRequest} req - The HTTP request.
* @returns {Promise<void>} 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 PostgresClientFactory(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 PostgreSQL"
};
};

export default httpTrigger;

Conclusão

Parabéns! Você configurou com sucesso o Azure PostgreSQL utilizando várias técnicas de autenticação em Azure Functions, aplicando o Factory Pattern para orquestrar a criação de conexões. Nossa abordagem modular facilita a manutenção e a extensão do código. 🎉

--

--

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