Serverless e Azure Functions

Azure Functions é o serviço serverless da Microsoft que executa trechos de código em resposta a eventos, sem que você precise criar, configurar ou manter servidores. Neste guia, você entende o conceito de serverless, conhece os principais gatilhos e acompanha o caminho completo, da primeira função local até a publicação no Azure com .NET 8.

O que é serverless

Serverless é uma arquitetura de computação na nuvem em que o provedor gerencia a execução do código, eliminando a necessidade de gerir servidores. Ou seja, você entrega a lógica da aplicação e a plataforma cuida do resto.

O nome engana um pouco, porque ainda existem servidores por trás de tudo. A diferença é que eles ficam invisíveis para você, já que provisionamento, atualização do sistema operacional e escala ficam a cargo do provedor.

Na prática, o modelo serverless se apoia em três ideias principais. Primeiro, o código roda sob demanda, disparado por um evento.

Em seguida, a plataforma escala automaticamente, adicionando instâncias quando a carga cresce e removendo quando ela cai. Por fim, a cobrança acompanha o uso real, e não o tempo em que uma máquina fica ligada.

Por isso, serverless combina muito bem com cargas variáveis, tarefas agendadas e integrações entre sistemas. Em vez de pagar por uma máquina ociosa, você paga pelas execuções.

O que é Azure Functions

O Azure Functions é um serviço do Microsoft Azure, a plataforma de computação na nuvem da Microsoft, que implementa a arquitetura serverless. Assim, cada função é um pedaço de código pequeno e focado, executado quando um evento acontece.

Segundo a documentação oficial do Azure Functions, o serviço acompanha todo o ciclo de vida da aplicação. Você escreve o código, depura localmente, publica no Azure e monitora com o Azure Monitor e o Application Insights.

Além disso, as funções ficam agrupadas em uma function app, que é a unidade de publicação e de escala. Uma mesma function app pode conter várias funções, cada uma com seu próprio gatilho.

Linguagens suportadas no Azure Functions

O Azure Functions suporta diferentes linguagens, como Python, Java, JavaScript, TypeScript, PowerShell e, claro, C# com .NET. A documentação também lista o Go em preview.

Para linguagens sem suporte oficial, como Rust, existem os custom handlers. Nesse caso, um servidor web leve recebe os eventos do host do Functions por HTTP.

Contudo, existe uma regra importante: desde a versão 2.x do runtime, uma function app suporta uma única linguagem. A configuração FUNCTIONS_WORKER_RUNTIME define qual delas o host carrega.

Modelo isolado e modelo in-process

No .NET, existem dois modelos de execução. No modelo in-process, o código da função roda dentro do mesmo processo do host do Functions.

Já no modelo isolado (isolated worker), o código roda em um processo .NET separado. Esse modo surgiu com o .NET 5 e a versão 3 do runtime do Functions, e permite que seus processos rodem de forma independente do host.

Com isso, você ganha controle total sobre injeção de dependência, middleware e versão do .NET. Além disso, a Microsoft anunciou o fim do suporte ao modelo in-process para 10 de novembro de 2026, então o modelo isolado é o caminho recomendado para projetos novos.

Gatilhos e bindings do Azure Functions

As funções do Azure Functions são disparadas por gatilhos (triggers), que podem ser mecanismos ativos ou passivos. Cada função tem exatamente um gatilho, que define quando ela executa.

Além dos gatilhos, existem os bindings de entrada e de saída. Eles conectam a função a outros serviços de forma declarativa, por exemplo lendo uma fila e gravando em um banco ao mesmo tempo, sem que você escreva o código de conexão.

HTTP trigger

O HTTP trigger ativa funções em resposta a requisições HTTP, com métodos como GET, POST, PUT e DELETE. Por isso, ele aparece com frequência na criação de APIs e serviços web.

Timer trigger

O Timer trigger executa funções em um horário específico ou em intervalos regulares, usando uma expressão CRON. Ele é útil para tarefas agendadas, como limpeza de banco de dados ou atualizações periódicas.

O Azure Functions interpreta essas expressões com a biblioteca NCronTab, que aceita um sexto campo opcional para os segundos, no início da expressão. Por exemplo, 0 */5 * * * * dispara a cada cinco minutos, e o fuso padrão é o UTC.

Blob trigger

O Blob trigger dispara quando arquivos chegam ou mudam em um contêiner do Azure Blob Storage. Assim, ele ajuda a processar dados de entrada, como imagens ou arquivos de log.

Queue trigger

O Queue trigger dispara funções quando uma mensagem chega a uma fila do Azure Storage. Portanto, ele é ideal para processamento assíncrono e arquiteturas orientadas a eventos.

Event Hub trigger

O Event Hub trigger ativa a função a partir de eventos enviados ao Azure Event Hubs, um serviço de streaming de eventos em larga escala. Esse gatilho atende bem cenários de processamento em tempo real, como telemetria.

Service Bus trigger

O Service Bus trigger dispara funções em resposta a mensagens em filas ou tópicos do Azure Service Bus. Ele se encaixa em cenários de comunicação mais complexa entre aplicações e serviços.

Cosmos DB trigger

O Cosmos DB trigger executa funções quando documentos do Azure Cosmos DB mudam. Dessa forma, você reage a alterações de dados em tempo real e aciona processos posteriores.

Event Grid trigger

O Event Grid trigger ativa funções a partir de eventos de várias fontes do Azure, roteados pelo Azure Event Grid. Esse gatilho oferece um alto nível de personalização para reagir a eventos específicos.

Planos de hospedagem do Azure Functions

Ao criar uma function app, você escolhe um plano de hospedagem. Essa escolha define como a aplicação escala, quais recursos cada instância recebe e quanto você paga.

O plano Consumption foi, durante anos, a opção serverless clássica do Azure Functions. Nele, a cobrança considera o número de execuções, o tempo de execução e a memória usada.

No entanto, a documentação atual trata o Consumption como legado. Para novas aplicações serverless, a Microsoft recomenda o plano Flex Consumption, que roda em Linux e escala até mil instâncias.

Além disso, o Flex Consumption permite instâncias sempre prontas para reduzir o cold start, integração com rede virtual e tamanhos de memória configuráveis. Vale lembrar que ele suporta apenas o modelo isolado do .NET.

Por fim, existem os planos Premium e Dedicated, indicados para cargas contínuas ou execuções longas. Também é possível hospedar funções em contêineres no Azure Container Apps.

Como criar Azure Functions locais

É possível rodar o Azure Functions localmente, e isso é uma excelente ideia para testes. Dessa forma, você valida o comportamento antes de gastar qualquer recurso na nuvem.

Para executar uma função local, primeiro você configura o ambiente de desenvolvimento. Por exemplo, se vamos criar as funções com .NET 8, precisamos do SDK devidamente instalado e configurado.

Caso nunca tenha instalado o .NET, este tutorial sobre instalação e primeiros passos com .NET ajuda você a preparar a máquina.

Instalando o Azure Functions Core Tools

O primeiro passo é instalar a ferramenta de linha de comando do Azure Functions, chamada oficialmente de Azure Functions Core Tools. Ela fornece o runtime local e suporta Windows, Mac e Linux.

No Mac, você faz a instalação com o Homebrew:

brew tap azure/functions
brew install azure-functions-core-tools@4

# confere se o comando func está disponível
func -h

Para Windows e Linux, consulte o guia de desenvolvimento local com o Core Tools.

Criando o projeto de Azure Functions

Para criar um projeto com o CLI, basta executar o comando func init, seguido dos parâmetros desejados.

func init MyLocalFunction \
  --worker-runtime dotnet-isolated \
  --target-framework net8.0

Neste caso, o --worker-runtime aponta para dotnet-isolated e o --target-framework aponta para net8.0. Isso significa que as funções deste projeto usam .NET 8 no modelo isolado.

Entendendo o projeto

O comando anterior criou um projeto de funções, ou seja, um contêiner que pode reunir diversas funções. Elas podem ser independentes e usar gatilhos diferentes.

Porém, não é possível misturar linguagens no mesmo projeto. Assim, este projeto só aceita funções em .NET, já que os projetos de funções se agrupam por tecnologia.

Além disso, o projeto traz o arquivo local.settings.json, que guarda as configurações usadas apenas na execução local. Nele ficam valores como FUNCTIONS_WORKER_RUNTIME e AzureWebJobsStorage.

Adicionando funções

Para adicionar funções ao projeto, use o comando func new, informando nome, template e nível de autenticação.

Importante: a função de Blob não roda localmente sem um emulador de armazenamento. O antigo Azure Storage Emulator deu lugar ao Azurite, então, caso queira rodar as demos sem ele, pule o segundo comando.

cd MyLocalFunction

func new --name HttpExample \
  --template "HTTP trigger" \
  --authlevel "anonymous"

func new --name BlobExample \
  --template "Blob trigger"

func new --name TimerExample \
  --template "Timer trigger"

Quando o Azurite está rodando, a chave AzureWebJobsStorage com o valor UseDevelopmentStorage=true no local.settings.json faz o host local usar o emulador. Contudo, esse valor nunca deve ir para o Azure, onde a configuração precisa apontar para uma storage account real.

O código de uma função HTTP no Azure Functions

O template de HTTP trigger gera uma classe semelhante à do exemplo abaixo, que segue o padrão da documentação para o modelo isolado com integração ASP.NET Core.

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace MyLocalFunction;

public class HttpExample(ILogger<HttpExample> logger)
{
    // O atributo Function define o nome exposto pelo host
    [Function("HttpExample")]
    public IActionResult Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequest req)
    {
        logger.LogInformation("C# HTTP trigger function processed a request.");

        // Lê o parâmetro name da query string
        string? name = req.Query["name"];

        return new OkObjectResult($"Welcome to Azure Functions, {name}!");
    }
}

Repare que o gatilho aparece como atributo no parâmetro do método. Já o ILogger chega pela injeção de dependência, como em qualquer aplicação .NET moderna.

Executando a função local

Para executar as funções localmente, use o comando func start, que inicia todas as funções do projeto. Em seguida, o terminal exibe as URLs dos endpoints HTTP.

func start

# em outro terminal, chame a função (a porta padrão é 7071)
curl "http://localhost:7071/api/HttpExample?name=balta"

Pronto, você tem sua primeira função do Azure Functions rodando localmente. Vale notar que a execução local não exige chave de acesso, mesmo em funções protegidas.

Publicando Azure Functions no Azure

Depois de testar localmente, chega a hora de levar o código para a nuvem. Para isso, você precisa do Azure CLI, que roda no Windows, no Mac e no Linux.

Instalando o Azure CLI

Abaixo estão os comandos para instalar o Azure CLI no Mac.

brew update
brew install azure-cli

# confere a instalação
az -h

Autenticando no Azure

O próximo passo é se autenticar no Azure com o comando az login, que abre o navegador para concluir o login.

az login

Além disso, os comandos az account list, az account set e az account show listam as contas autenticadas, escolhem uma assinatura para a sessão atual e exibem os dados da conta ativa, respectivamente.

az account list
az account set --subscription "SUBSCRIPTION_ID"
az account show

Criando um resource group

Para criar uma function app, você precisa primeiro de um grupo de recursos e de uma conta de armazenamento.

O grupo de recursos mantém os recursos relacionados organizados no mesmo lugar. Assim, você gerencia, monitora e remove tudo de uma vez.

az group create \
  --name baltafunc-rg \
  --location brazilsouth

Para listar todas as localizações disponíveis na sua conta, use o comando az account list-locations.

Criando uma storage account

Em seguida, crie uma storage account, ou seja, uma conta de armazenamento. O Azure Functions usa essa conta para guardar dados importantes da aplicação, incluindo, em alguns planos, os arquivos do próprio código.

az storage account create \
  --name baltafuncstorage \
  --location brazilsouth \
  --resource-group baltafunc-rg \
  --sku Standard_LRS \
  --allow-blob-public-access false

O nome da conta precisa ter de 3 a 24 caracteres, apenas com números e letras minúsculas, e deve ser único em todo o Azure. Por isso, ajuste o nome se o comando reclamar.

Criando a function app no Azure Functions

Por fim, crie a function app com o comando az functionapp create. O exemplo a seguir usa o plano Consumption, como na demonstração original.

az functionapp create \
  --resource-group baltafunc-rg \
  --consumption-plan-location brazilsouth \
  --runtime dotnet-isolated \
  --functions-version 4 \
  --name baltafunc \
  --storage-account baltafuncstorage

Como o Consumption virou um plano legado, vale conhecer também o equivalente no Flex Consumption. Nesse caso, confira antes as regiões disponíveis com az functionapp list-flexconsumption-locations.

az functionapp create \
  --resource-group baltafunc-rg \
  --name baltafunc \
  --storage-account baltafuncstorage \
  --flexconsumption-location <REGION> \
  --runtime dotnet-isolated \
  --runtime-version 8.0

Publicação

Com tudo pronto, publique a função com o comando func azure functionapp publish, informando o nome da function app criada anteriormente.

func azure functionapp publish baltafunc

Esse processo pode levar alguns minutos. Depois disso, você já acessa a função pela URL da aplicação.

curl https://baltafunc.azurewebsites.net/api/httpexample

Para automatizar esse fluxo a cada commit, o artigo sobre deploy no Azure com GitHub Actions mostra como montar um pipeline de publicação contínua.

Quando usar Azure Functions

O Azure Functions brilha em cargas orientadas a eventos e de volume variável. Por exemplo, processar imagens enviadas a um storage, reagir a mensagens de fila ou expor pequenas APIs são casos clássicos.

Também faz sentido para tarefas agendadas, que antes exigiam uma máquina ligada só para rodar um job. Nesse caso, um Timer trigger resolve o problema com custo proporcional ao uso.

Por outro lado, cargas contínuas e pesadas podem custar menos em um plano dedicado ou em contêineres. Além disso, execuções muito longas esbarram no tempo limite dos planos serverless.

Outro ponto de atenção é o cold start, o atraso da primeira execução depois de um período ocioso. Nesse sentido, o Flex Consumption oferece instâncias sempre prontas para reduzir esse efeito.

Se o seu cenário envolve arquivos, o artigo sobre upload de imagens para o Azure Storage com C# complementa bem o uso do Blob trigger.

Palestra na íntegra

No dia 16 de abril de 2024, realizei uma palestra com todas estas informações e muito mais detalhes no canal do Fabrício Veronez no YouTube.

Você pode conferir a gravação abaixo:

Apresentação (PowerPoint)

Faça o download da apresentação completa que utilizei durante a live!

FAZER O DOWNLOAD DA APRESENTAÇÃO

Conclusão

O Azure Functions leva o modelo serverless para o ecossistema Microsoft, permitindo que você foque no código enquanto o Azure cuida da infraestrutura. Com gatilhos para HTTP, agendamentos, filas, blobs e eventos, ele cobre boa parte das integrações do dia a dia.

Neste guia, você criou um projeto com .NET 8 no modelo isolado, adicionou funções, executou tudo localmente e publicou no Azure com poucos comandos. Além disso, viu por que o modelo isolado e o plano Flex Consumption são os caminhos recomendados hoje.

Portanto, o próximo passo é experimentar outros gatilhos e bindings no seu próprio projeto. Comece pequeno, meça o custo e evolua conforme a carga real da aplicação.

FAQ

Serverless significa que não existem servidores?

Não. Os servidores existem, mas o provedor gerencia provisionamento, atualização e escala, então você não precisa administrá-los.

Quais linguagens posso usar no Azure Functions?

C#, Java, JavaScript, TypeScript, PowerShell e Python têm suporte oficial, e o Go aparece em preview. Outras linguagens podem rodar por meio de custom handlers.

Qual a diferença entre o modelo isolado e o in-process?

No modelo isolado, o código roda em um processo .NET separado do host. O modelo in-process roda dentro do host e perde o suporte em 10 de novembro de 2026.

Preciso de uma conta no Azure para testar funções?

Não. Com o Core Tools e o Azurite, você executa e testa as funções localmente antes de publicar.

Qual plano de hospedagem escolher para uma aplicação nova?

Para cargas serverless, a Microsoft recomenda o Flex Consumption. Cargas contínuas ou execuções longas tendem a se encaixar melhor nos planos Premium ou Dedicated.