Conexões Dinâmicas em Azure Logic Apps Standard: Como Roteamento em Tempo de Execução Simplifica Workflows Multi-Tenant
TL;DR: O Azure Logic Apps Standard agora permite que o nome de uma conexão (SFTP, SQL, Service Bus, etc.) seja resolvido dinamicamente com expressões no campo connectionName. Isso elimina a necessidade de workflows separados para cada time ou ambiente — um único workflow pode rotear para diferentes servidores com base no payload, header ou lógica condicional. Ainda sem suporte no designer visual (apenas Code View), mas com ganho imediato de escalabilidade.
Como o roteamento dinâmico de conexões resolve o problema de workflows multi-tenant?
Times de engenharia que gerenciam integrações sabem o desafio: um mesmo workflow de Logic Apps precisa atender múltiplos times, cada um com seu próprio servidor SFTP, banco de dados ou namespace de Service Bus. A abordagem tradicional — criar workflows separados ou hardcodar uma única conexão — não escala e aumenta a complexidade operacional.
O Azure Logic Apps Standard agora oferece uma alternativa: nomes de conexão dinâmicos para conectores built-in (service provider). Em vez de fixar um nome de conexão no JSON, você pode usar qualquer expressão de workflow — input do trigger, output de ações anteriores, parâmetros ou lógica condicional — para determinar qual conexão usar em tempo de execução. O runtime avalia a expressão e resolve a conexão a partir do arquivo connections.json na hora da execução.
Ponto de atenção para times brasileiros: Essa funcionalidade funciona exclusivamente via Code View, sem suporte no designer visual. Para equipes acostumadas com a interface drag-and-drop, isso representa uma barreira inicial. Porém, para cenários de multi-tenancy — como uma fintech que precisa conectar a diferentes bancos de dados de clientes — o ganho de escalabilidade justifica a curva de aprendizado.
O padrão-chave
"serviceProviderConfiguration": {
"connectionName": "@<expressão-que-resolve-o-nome-da-conexão>",
"operationId": "listFolder",
"serviceProviderId": "/serviceProviders/Sftp"
}
Em vez de "connectionName": "sftp", você fornece uma expressão que será avaliada para o nome da conexão definido em connections.json.
Fontes possíveis para o valor dinâmico
| Origem | Exemplo |
|---|---|
| Corpo do trigger | @triggerBody()?['connectionName'] |
| Header do trigger | @triggerOutputs()['headers']['X-Connection-Name'] |
| Expressão condicional | @if(equals(triggerBody()?['team'], 'teamA'), 'sftpTeamA', 'sftpTeamB') |
| Output de ação anterior | @outputs('Resolve_Connection') |
| Parâmetro do workflow | @parameters('defaultConnection') |
Como configurar na prática? Exemplo com SFTP dinâmico
1. Defina as conexões no connections.json
{
"serviceProviderConnections": {
"sftpTeamA": {
"parameterValues": {
"sshHostAddress": "@appsetting('SftpTeamA_HostAddress')",
"username": "@appsetting('SftpTeamA_Username')",
"password": "@appsetting('SftpTeamA_Password')",
"portNumber": "@appsetting('SftpTeamA_PortNumber')",
"rootDirectory": "@appsetting('SftpTeamA_RootDirectory')"
},
"serviceProvider": {
"id": "/serviceProviders/Sftp"
},
"displayName": "SFTP - Team A"
},
"sftpTeamB": {
"parameterValues": {
"sshHostAddress": "@appsetting('SftpTeamB_HostAddress')",
"username": "@appsetting('SftpTeamB_Username')",
"password": "@appsetting('SftpTeamB_Password')",
"portNumber": "@appsetting('SftpTeamB_PortNumber')",
"rootDirectory": "@appsetting('SftpTeamB_RootDirectory')"
},
"serviceProvider": {
"id": "/serviceProviders/Sftp"
},
"displayName": "SFTP - Team B"
}
}
}
2. Adicione as Application Settings
No menu Configuration → Application settings do Logic App, inclua os valores para cada conexão:
| Setting | Example Value |
|---|---|
| SftpTeamA_HostAddress | storageacctA.blob.core.windows.net |
| SftpTeamA_Username | storageacctA.localuserA |
| SftpTeamA_Password | (generated password) |
| SftpTeamA_PortNumber | 22 |
| SftpTeamA_RootDirectory | /uploads |
| SftpTeamB_HostAddress | storageacctB.blob.core.windows.net |
| SftpTeamB_Username | storageacctB.localuserB |
| SftpTeamB_Password | (generated password) |
| SftpTeamB_PortNumber | 22 |
| SftpTeamB_RootDirectory | /uploads |
3. Crie o workflow (Code View)
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"List_files_in_folder": {
"type": "ServiceProvider",
"inputs": {
"serviceProviderConfiguration": {
"connectionName": "@if(equals(triggerBody()?['team'], 'teamA'), 'sftpTeamA', 'sftpTeamB')",
"operationId": "listFolder",
"serviceProviderId": "/serviceProviders/Sftp"
},
"parameters": {
"path": "@triggerBody()?['folderPath']"
}
},
"runAfter": {}
},
"Response": {
"type": "Response",
"kind": "Http",
"inputs": {
"statusCode": 200,
"body": "@body('List_files_in_folder')"
},
"runAfter": {
"List_files_in_folder": ["Succeeded"]
}
}
},
"triggers": {
"When_a_HTTP_request_is_received": {
"type": "Request",
"kind": "Http",
"inputs": {
"schema": {
"type": "object",
"properties": {
"team": { "type": "string" },
"folderPath": { "type": "string" }
}
}
}
}
},
"contentVersion": "1.0.0.0"
},
"kind": "Stateful"
}
4. Teste
Envie uma requisição POST para a URL de trigger do workflow:
{
"team": "teamA",
"folderPath": "/uploads"
}
Altere "team": "teamB" para rotear para o outro servidor SFTP em tempo de execução.
Exemplo de resolução em múltiplas etapas
Para lógicas mais complexas — lookups, mapeamentos ou roteamento com múltiplas condições — resolva a conexão em uma ação anterior e referencie seu output:
{
"Resolve_Connection": {
"type": "Compose",
"inputs": "@if(contains(triggerBody()?['region'], 'eu'), 'sftpEurope', if(contains(triggerBody()?['region'], 'us'), 'sftpAmerica', 'sftpDefault'))",
"runAfter": {}
},
"Upload_File": {
"type": "ServiceProvider",
"inputs": {
"serviceProviderConfiguration": {
"connectionName": "@outputs('Resolve_Connection')",
"operationId": "createFile",
"serviceProviderId": "/serviceProviders/Sftp"
},
"parameters": {
"path": "/incoming/data.csv",
"content": "@triggerBody()?['fileContent']"
}
},
"runAfter": {
"Resolve_Connection": ["Succeeded"]
}
}
}
Quais conectores são compatíveis?
Esse padrão funciona com todos os conectores built-in service provider (type: "ServiceProvider"), incluindo:
- SFTP
- SQL
- Service Bus
- Event Hubs
- Azure Blob Storage (built-in)
- SMTP
- Azure Automation
- E qualquer outro conector built-in
Quais são as limitações?
- Code View apenas — O designer não renderiza nem edita nomes de conexão dinâmicos visualmente.
- Built-in connectors apenas — Managed API connections (type: "ApiConnection") não suportam esse padrão.
- Conexão precisa existir — O nome resolvido deve corresponder a uma chave já definida em
connections.json. Conexões não podem ser criadas on-the-fly. - Case-sensitive — O output da expressão deve corresponder exatamente à chave em
connections.json.
Dicas para implementação
- Use referências
@appsetting()noconnections.jsonpara manter credenciais fora do source control. - Armazene senhas no Azure Key Vault referenciado via app settings.
- Use nomes de conexão significativos (ex.:
sftpTeamA,sqlProd,serviceBusDev). - Você pode escalar para qualquer número de conexões — basta adicionar entradas no
connections.jsone app settings correspondentes. - Expressões inline como
@if(...)funcionam diretamente emconnectionName— nem sempre é necessária uma ação separada.
Perguntas Frequentes
-
Essa funcionalidade funciona com todos os conectores do Azure Logic Apps?
- Não. Apenas conectores built-in do tipo ServiceProvider (SFTP, SQL, Service Bus, Event Hubs, Azure Blob Storage, SMTP, Azure Automation) suportam conexões dinâmicas. Managed API connections (tipo ApiConnection) não funcionam com esse padrão.
-
Preciso editar o workflow no designer visual ou só no código?
- Apenas no Code View. O designer visual do Logic Apps Standard ainda não renderiza nem permite editar o campo connectionName com expressões dinâmicas. Toda a configuração deve ser feita diretamente no JSON do workflow.
-
É possível criar conexões em tempo de execução com essa técnica?
- Não. A conexão resolvida pela expressão deve existir previamente no arquivo connections.json. O nome resolvido precisa corresponder exatamente (case-sensitive) a uma chave já definida — não há criação on-the-fly.
-
Como garantir a segurança das credenciais ao usar esse padrão?
- A Microsoft recomenda usar referências @appsetting() no connections.json para manter senhas fora do source control, e armazenar os valores sensíveis no Azure Key Vault, referenciados via Application Settings do Logic App.
-
Quantas conexões posso gerenciar com esse padrão?
- Não há limite documentado. Basta adicionar novas entradas no connections.json com nomes significativos (ex.: sftpTeamA, sqlProd) e criar as Application Settings correspondentes. O workflow resolve dinamicamente qual usar via expressão.
Artigo originalmente publicado em Azure Updates - Latest from Azure Charts.