Connection Setup for DirectClient SDK Validation
This guide documents how to set up and configure API connections for testing DirectClient SDK generated code.
Prerequisites
- Azure CLI installed and logged in (
az login) - Access to an Azure subscription with Logic Apps connections
- A Logic Apps Standard app (for creating connections with runtime URLs)
Quick Start
Use the setup script to automate the entire process:
.\scripts\Setup-Connection.ps1 `
-SubscriptionId "<your-subscription-id>" `
-ResourceGroup "<your-resource-group>" `
-ConnectionName "<your-connection-name>"
For Office365 connections, use a different test path:
.\scripts\Setup-Connection.ps1 `
-SubscriptionId "<your-subscription-id>" `
-ResourceGroup "<your-resource-group>" `
-ConnectionName "office365" `
-TestPath "/Categories"
Connection Types
There are two ways to create connections, depending on whether you use the Connector Namespace:
Option A: Connector Namespace Connection (Recommended for Triggers)
Connector Namespace connections are required for connector triggers (Connector Namespace manages the polling infrastructure). They also work for actions.
A1. Create a Connector Namespace
$subscriptionId = "<your-subscription-id>"
$resourceGroup = "<your-resource-group>"
$connectorNamespace = "<your-connector-namespace>"
$location = "<azure-region>" # e.g., "brazilsouth"
az rest --method PUT `
--uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connectorGateways/$connectorNamespace?api-version=2026-05-01-preview" `
--body "{`"location`":`"$location`",`"properties`":{}}"
A2. Create a Connection in the Connector Namespace
$connectorName = "office365" # API connector name
$connectionName = "office365-test"
az rest --method PUT `
--uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connectorGateways/$connectorNamespace/connections/$connectionName?api-version=2026-05-01-preview" `
--body "{`"properties`":{`"connectorName`":`"$connectorName`"}}"
The connection is created in an unauthenticated state. You must complete OAuth consent.
A3. OAuth Consent via Connector Namespace Manager Portal
- Open the Connector Namespace Manager Portal
- Run the command shown on the portal to get an ARM token, paste it, and save
- Select your Connector Namespace — the connections will appear
- Click Authorize on the connection requiring consent
- Complete the OAuth flow. Status changes from
ErrortoConnected.
Note: The DF consent endpoint (
/login) returns 500. Only the portal UI consent flow works reliably.
A4. Get Connection Runtime URL
After OAuth consent, the connection runtime URL is available:
- In the Connector Namespace Manager Portal: select the connection → copy the runtime URL
- Via ARM API:
$result = az rest --method GET `
--uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connectorGateways/$connectorNamespace/connections/$connectionName?api-version=2026-05-01-preview" `
-o json | ConvertFrom-Json
$result.properties.connectionRuntimeUrl
A5. Add Access Policy for Function App MSI
Grant the Function App's managed identity access to use the connection:
$msiObjectId = "<function-app-msi-object-id>"
$tenantId = "<aad-tenant-id>"
$policyName = "functionapp-msi"
az rest --method PUT `
--uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connectorGateways/$connectorNamespace/connections/$connectionName/accessPolicies/$policyName?api-version=2026-05-01-preview" `
--body "{`"properties`":{`"principal`":{`"type`":`"ActiveDirectory`",`"identity`":{`"objectId`":`"$msiObjectId`",`"tenantId`":`"$tenantId`"}}}}" `
--headers "Content-Type=application/json"
Important: The
Content-Type: application/jsonheader is required — omitting it returns HTTP 415.
The same connection (and access policy) is used for both triggers and actions.
Option B: Standalone ARM Connection (Actions Only)
Standalone connections work for actions but do not support Connector Namespace triggers.
Manual Steps (Option B — Standalone)
If you prefer standalone connections, follow these steps.
Step 1: Create a Connection
The easiest way is through the Azure Portal:
- Open your Logic Apps Standard app
- Go to Connections > Add connection
- Select the connector (e.g., SharePoint, Office365)
- Complete OAuth authorization
Alternatively, connections can be created via ARM templates or CLI.
Step 2: Get Connection Runtime URL
# Replace with your values
$subscriptionId = "<your-subscription-id>"
$resourceGroup = "<your-resource-group>"
$connectionName = "<your-connection-name>"
# Get the runtime URL
az resource show `
--ids "/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connections/$connectionName" `
--query "properties.connectionRuntimeUrl" `
-o tsv
Expected output format:
https://{instance}.{region}.common.logic-{environment}.azure-apihub.net/apim/{connector}/{connection-id}
Note: If the runtime URL is empty, the connection was created as a classic ARM connection, not through a Logic Apps Standard app. You'll need to create a new connection from a Logic Apps Standard app.
Step 3: Check Connection Status
az resource show `
--ids "/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connections/$connectionName" `
--query "properties.statuses[0]" `
-o json
Expected for healthy connection:
If you see invalid_grant or Unauthorized, the connection needs to be re-authorized (see Step 4).
Step 4: Re-authorize an Expired Connection
If the OAuth token has expired:
# Create consent body file
$consentBody = @{
parameters = @(
@{
redirectUrl = "https://portal.azure.com"
parameterName = "token"
}
)
} | ConvertTo-Json -Depth 3
$consentBody | Out-File "$env:TEMP\consent-body.json" -Encoding UTF8
# Get consent link
az resource invoke-action `
--ids "/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connections/$connectionName" `
--action "listConsentLinks" `
--api-version "2018-07-01-preview" `
--request-body "@$env:TEMP\consent-body.json" `
-o json
Open the returned link URL in your browser to complete OAuth authorization.
Step 5: Add Access Policy for CLI/Local Testing
DirectClient SDK uses DefaultAzureCredential which authenticates as your Azure CLI identity. You must grant your identity access to the connection.
Get Your Identity Information
# Get your user object ID
$userObjectId = az ad signed-in-user show --query "id" -o tsv
# Get your tenant ID
$tenantId = az account show --query "tenantId" -o tsv
Create Access Policy
$policyName = "local-dev" # Any unique name
# Create access policy body
$accessPolicyBody = @{
properties = @{
principal = @{
type = "ActiveDirectory"
identity = @{
objectId = $userObjectId
tenantId = $tenantId
}
}
}
} | ConvertTo-Json -Depth 5
$accessPolicyBody | Out-File "$env:TEMP\access-policy.json" -Encoding UTF8
# Add the access policy
az rest --method PUT `
--uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connections/$connectionName/accessPolicies/$policyName?api-version=2018-07-01-preview" `
--body "@$env:TEMP\access-policy.json" `
-o json
Verify Access Policy
az rest --method GET `
--uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connections/$connectionName/accessPolicies?api-version=2018-07-01-preview" `
-o json
Note: ACL propagation can take 1-5 minutes. If you get 403 errors immediately after adding the policy, wait and retry.
Step 6: Test Connection via CLI
Before testing in your application, verify the connection works:
# Replace with your actual runtime URL from Step 2
$runtimeUrl = "<your-runtime-url>"
# Test SharePoint - list available sites
az rest --method GET `
--uri "$runtimeUrl/datasets" `
--resource "https://apihub.azure.com" `
-o json
# Test Office365 - get categories
az rest --method GET `
--uri "$runtimeUrl/Categories" `
--resource "https://apihub.azure.com" `
-o json
Step 7: Configure Your Application
Add the runtime URL to your application settings:
Azure Functions (local.settings.json)
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
"Office365ConnectionRuntimeUrl": "<your-office365-runtime-url>",
"SharepointonlineConnectionRuntimeUrl": "<your-sharepoint-runtime-url>"
}
}
ASP.NET Core (appsettings.json)
{
"ConnectionRuntimeUrls": {
"Office365": "<your-office365-runtime-url>",
"Sharepointonline": "<your-sharepoint-runtime-url>"
}
}
Authentication Modes
The generated connector clients (e.g., Office365Client, TeamsClient) authenticate to API Hub using Azure credentials. Each client offers two constructors that cover three authentication modes:
Mode 1: DefaultAzureCredential (Recommended for Local Development)
Uses the DefaultAzureCredential chain, which tries multiple sources automatically — Azure CLI, Visual Studio, environment variables, and managed identity.
This is the simplest setup. Locally, it authenticates as your Azure CLI identity (az login), which must have an access policy on the connection for local testing. When deployed to Azure, it falls through to the app's managed identity, which must have an access policy on the connection.
Mode 2: System-Assigned Managed Identity
Uses ManagedIdentityCredential with no client ID, targeting the app's system-assigned identity.
Pass null (or an empty string) for managedIdentityClientId. The Function App must have system-assigned managed identity enabled, and the identity must have an access policy on the connection.
Mode 3: User-Assigned Managed Identity
Uses ManagedIdentityCredential with a specific client ID, targeting a user-assigned managed identity attached to the app.
var client = new Office365Client(
connectionRuntimeUrl,
managedIdentityClientId: "<client-id-of-user-assigned-msi>");
Use this when multiple apps share a single identity, or when you need the identity to outlive any single app deployment.
Choosing an Auth Mode
| Mode | When to use | Access policy identity |
|---|---|---|
| DefaultAzureCredential | Local development, quick prototyping | Your Azure CLI user object ID |
| System-assigned MSI | Production deployment, single-app scenarios | Function App's system-assigned MSI object ID |
| User-assigned MSI | Production deployment, shared identity across apps | User-assigned MSI object ID |
Note: All three modes require an access policy granting the identity permission to use the connection. The only difference is which identity's object ID goes into the policy.
Custom Credentials
The primary constructor also accepts any TokenCredential directly, enabling advanced scenarios (e.g., certificate-based auth, chained credentials):
var credential = new ChainedTokenCredential(
new ManagedIdentityCredential(),
new AzurePowerShellCredential());
var client = new Office365Client(connectionRuntimeUrl, credential);
Troubleshooting
Error: "Permission denied due to missing connection ACL"
The access policy hasn't propagated yet, or is missing. Solutions:
- Wait 1-5 minutes for propagation
- Verify the policy exists with the GET accessPolicies command
- Ensure the objectId matches your signed-in identity
Error: "invalid_grant" or token expired
The OAuth token has expired. Re-authorize using Step 4.
Error: "connectionRuntimeUrl is null"
The connection was created as a classic ARM connection. Create a new connection through a Logic Apps Standard app.
Error: 404 on runtime URL
The endpoint path may be incorrect. Check the connector's swagger/OpenAPI spec for valid paths.
See Also
- scripts/Setup-Connection.ps1 - Automated setup script
- ROADMAP.md - Connector generation progress and lessons learned
- DirectConnector Sample - Working example with Office 365, SharePoint, and Teams