diff --git a/DirectConnector/ArmFunctions.cs b/DirectConnector/ArmFunctions.cs new file mode 100644 index 0000000..4c5d9e2 --- /dev/null +++ b/DirectConnector/ArmFunctions.cs @@ -0,0 +1,262 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System.Net; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.Arm; +using Azure.Connectors.Sdk.Arm.Models; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.Extensions.Logging; + +namespace DirectConnector; + +/// +/// Azure Functions demonstrating Azure Resource Manager operations using the generated +/// from the Azure Connectors SDK. +/// +/// +/// ARM connector uses OAuth (user-delegated Azure AD token). +/// The connection requires OAuth consent via the consent link flow. +/// +public class ArmFunctions +{ + private readonly ILogger _logger; + private readonly ArmClient _armClient; + + public ArmFunctions( + ILogger logger, + ArmClient armClient) + { + this._logger = logger; + this._armClient = armClient; + } + + /// + /// Lists all subscriptions accessible to the authenticated user. + /// + [Function("ArmListSubscriptions")] + public async Task ListSubscriptionsAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "arm/subscriptions")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("ListSubscriptions: Using generated ArmClient."); + + try + { + var subscriptions = new List(); + await foreach (var subscription in this._armClient + .SubscriptionsListAsync() + .WithCancellation(cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false)) + { + subscriptions.Add(subscription); + } + + this._logger.LogInformation("Found '{Count}' subscriptions.", subscriptions.Count); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, count = subscriptions.Count, subscriptions }) + .ConfigureAwait(continueOnCapturedContext: false); + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "ARM connector error: '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }) + .ConfigureAwait(continueOnCapturedContext: false); + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in ListSubscriptions."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + return errorResponse; + } + } + + /// + /// Lists resource groups in a subscription. + /// + [Function("ArmListResourceGroups")] + public async Task ListResourceGroupsAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "arm/subscriptions/{subscriptionId}/resourcegroups")] HttpRequestData request, + string subscriptionId, + CancellationToken cancellationToken) + { + this._logger.LogInformation("ListResourceGroups: subscription='{SubscriptionId}'.", subscriptionId); + + try + { + var resourceGroups = new List(); + await foreach (var resourceGroup in this._armClient + .ResourceGroupsListAsync(subscription: subscriptionId) + .WithCancellation(cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false)) + { + resourceGroups.Add(resourceGroup); + } + + this._logger.LogInformation("Found '{Count}' resource groups.", resourceGroups.Count); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, count = resourceGroups.Count, resourceGroups }) + .ConfigureAwait(continueOnCapturedContext: false); + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "ARM connector error: '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }) + .ConfigureAwait(continueOnCapturedContext: false); + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in ListResourceGroups."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + return errorResponse; + } + } + + /// + /// Reads a specific resource by its resource group, provider, and short resource ID. + /// + [Function("ArmReadResource")] + public async Task ReadResourceAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "arm/resource")] HttpRequestData request, + CancellationToken cancellationToken) + { + var subscriptionId = request.Query["subscriptionId"]; + var resourceGroup = request.Query["resourceGroup"]; + var provider = request.Query["provider"]; + var shortResourceId = request.Query["shortResourceId"]; + var apiVersion = request.Query["apiVersion"]; + + if (string.IsNullOrWhiteSpace(subscriptionId) || + string.IsNullOrWhiteSpace(resourceGroup) || + string.IsNullOrWhiteSpace(provider) || + string.IsNullOrWhiteSpace(shortResourceId) || + string.IsNullOrWhiteSpace(apiVersion)) + { + var badRequest = request.CreateResponse(HttpStatusCode.BadRequest); + await badRequest + .WriteAsJsonAsync(new { error = "Query parameters 'subscriptionId', 'resourceGroup', 'provider', 'shortResourceId', and 'apiVersion' are required." }) + .ConfigureAwait(continueOnCapturedContext: false); + return badRequest; + } + + this._logger.LogInformation("ReadResource: '{Provider}/{ShortResourceId}' in '{ResourceGroup}'.", provider, shortResourceId, resourceGroup); + + try + { + var resource = await this._armClient + .ResourcesGetByIdAsync( + subscription: subscriptionId, + resourceGroup: resourceGroup, + resourceProvider: provider, + shortResourceId: shortResourceId, + clientApiVersion: apiVersion, + cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + this._logger.LogInformation("Read resource '{Name}' of type '{Type}'.", resource.Name, resource.Type); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, resource }) + .ConfigureAwait(continueOnCapturedContext: false); + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "ARM connector error: '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }) + .ConfigureAwait(continueOnCapturedContext: false); + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in ReadResource."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + return errorResponse; + } + } + + /// + /// Lists resources in a resource group. + /// + [Function("ArmListResources")] + public async Task ListResourcesByResourceGroupAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "arm/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/resources")] HttpRequestData request, + string subscriptionId, + string resourceGroupName, + CancellationToken cancellationToken) + { + this._logger.LogInformation("ListResourcesByResourceGroup: subscription='{SubscriptionId}', resourceGroup='{ResourceGroup}'.", subscriptionId, resourceGroupName); + + try + { + var resources = new List(); + await foreach (var resource in this._armClient + .ResourceGroupsListResourcesAsync(subscription: subscriptionId, resourceGroup: resourceGroupName) + .WithCancellation(cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false)) + { + resources.Add(resource); + } + + this._logger.LogInformation("Found '{Count}' resources.", resources.Count); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, count = resources.Count, resources }) + .ConfigureAwait(continueOnCapturedContext: false); + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "ARM connector error: '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }) + .ConfigureAwait(continueOnCapturedContext: false); + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in ListResourcesByResourceGroup."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + return errorResponse; + } + } +} diff --git a/DirectConnector/AzureBlobFunctions.cs b/DirectConnector/AzureBlobFunctions.cs index 4dc8c38..79d8962 100644 --- a/DirectConnector/AzureBlobFunctions.cs +++ b/DirectConnector/AzureBlobFunctions.cs @@ -3,8 +3,9 @@ //------------------------------------------------------------ using System.Net; -using Microsoft.Azure.Connectors.DirectClient.Azureblob; -using Microsoft.Azure.Connectors.Sdk; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.AzureBlob; +using Azure.Connectors.Sdk.AzureBlob.Models; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; using Microsoft.Extensions.Logging; @@ -13,7 +14,7 @@ namespace DirectConnector; /// /// Azure Functions demonstrating Azure Blob Storage operations using the generated -/// from the DirectClient SDK. +/// from the Azure Connectors SDK. /// /// /// Azure Blob Storage uses key-based auth (accountName + accessKey), not OAuth. @@ -22,18 +23,18 @@ namespace DirectConnector; public class AzureBlobFunctions { private readonly ILogger _logger; - private readonly AzureblobClient _azureBlobClient; + private readonly AzureBlobClient _azureBlobClient; public AzureBlobFunctions( ILogger logger, - AzureblobClient azureBlobClient) + AzureBlobClient azureBlobClient) { this._logger = logger; this._azureBlobClient = azureBlobClient; } /// - /// Gets blob metadata using the generated . + /// Gets blob metadata using the generated . /// Exercises the response type. /// [Function("GetBlobMetadata")] @@ -41,7 +42,7 @@ public async Task GetBlobMetadataAsync( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "blob/metadata")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("GetBlobMetadata: Using generated AzureblobClient."); + this._logger.LogInformation("GetBlobMetadata: Using generated AzureBlobClient."); var storageAccount = request.Query["account"]; var blobPath = request.Query["path"]; @@ -71,13 +72,13 @@ await response .ConfigureAwait(continueOnCapturedContext: false); return response; } - catch (AzureblobConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "Azure Blob connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "Azure Blob connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; } @@ -94,7 +95,7 @@ await errorResponse } /// - /// Downloads blob content using the generated . + /// Downloads blob content using the generated . /// Exercises the byte[] return path. /// [Function("DownloadBlob")] @@ -102,7 +103,7 @@ public async Task DownloadBlobAsync( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "blob/download")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("DownloadBlob: Using generated AzureblobClient byte[] response path."); + this._logger.LogInformation("DownloadBlob: Using generated AzureBlobClient byte[] response path."); var storageAccount = request.Query["account"]; var blobPath = request.Query["path"]; @@ -140,13 +141,13 @@ await response.Body return response; } - catch (AzureblobConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "Azure Blob connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "Azure Blob connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; } @@ -163,7 +164,7 @@ await errorResponse } /// - /// Uploads a blob using the generated . + /// Uploads a blob using the generated . /// Exercises the byte[] input path with response. /// [Function("UploadBlob")] @@ -171,7 +172,7 @@ public async Task UploadBlobAsync( [HttpTrigger(AuthorizationLevel.Function, "post", Route = "blob/upload")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("UploadBlob: Using generated AzureblobClient."); + this._logger.LogInformation("UploadBlob: Using generated AzureBlobClient."); var storageAccount = request.Query["account"]; var folder = request.Query["folder"]; @@ -210,13 +211,13 @@ await response .ConfigureAwait(continueOnCapturedContext: false); return response; } - catch (AzureblobConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "Azure Blob connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "Azure Blob connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; } @@ -233,7 +234,7 @@ await errorResponse } /// - /// Deletes a blob using the generated . + /// Deletes a blob using the generated . /// Exercises the void (no response body) path. /// [Function("DeleteBlob")] @@ -241,7 +242,7 @@ public async Task DeleteBlobAsync( [HttpTrigger(AuthorizationLevel.Function, "delete", Route = "blob/delete")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("DeleteBlob: Using generated AzureblobClient."); + this._logger.LogInformation("DeleteBlob: Using generated AzureBlobClient."); var storageAccount = request.Query["account"]; var blobId = request.Query["id"]; @@ -271,13 +272,13 @@ await response .ConfigureAwait(continueOnCapturedContext: false); return response; } - catch (AzureblobConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "Azure Blob connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "Azure Blob connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; } diff --git a/DirectConnector/AzureLogAnalyticsFunctions.cs b/DirectConnector/AzureLogAnalyticsFunctions.cs index 04f3492..1e0ed20 100644 --- a/DirectConnector/AzureLogAnalyticsFunctions.cs +++ b/DirectConnector/AzureLogAnalyticsFunctions.cs @@ -3,8 +3,9 @@ //------------------------------------------------------------ using System.Net; -using Microsoft.Azure.Connectors.DirectClient.Azureloganalytics; -using Microsoft.Azure.Connectors.Sdk; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.Azuremonitorlogs; +using Azure.Connectors.Sdk.Azuremonitorlogs.Models; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; using Microsoft.Extensions.Logging; @@ -12,17 +13,23 @@ namespace DirectConnector; /// -/// Azure Functions demonstrating Azure Log Analytics operations using the generated -/// from the DirectClient SDK. +/// Azure Functions demonstrating Azure Monitor Logs operations using the generated +/// from the Azure Connectors SDK. /// +/// +/// This connector replaces the deprecated Azure Log Analytics connector. +/// The file retains the "LogAnalytics" name in routes for backward compatibility. +/// public class AzureLogAnalyticsFunctions { + private const string OperationalInsightsWorkspaceResourceType = "Microsoft.OperationalInsights/workspaces"; + private readonly ILogger _logger; - private readonly AzureloganalyticsClient _logAnalyticsClient; + private readonly AzuremonitorlogsClient _logAnalyticsClient; public AzureLogAnalyticsFunctions( ILogger logger, - AzureloganalyticsClient logAnalyticsClient) + AzuremonitorlogsClient logAnalyticsClient) { this._logger = logger; this._logAnalyticsClient = logAnalyticsClient; @@ -37,7 +44,7 @@ public async Task ListSubscriptionsAsync( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "loganalytics/subscriptions")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("ListLogAnalyticsSubscriptions: Using generated AzureloganalyticsClient from SDK."); + this._logger.LogInformation("ListLogAnalyticsSubscriptions: Using generated AzuremonitorlogsClient from SDK."); try { @@ -57,13 +64,13 @@ await response return response; } - catch (AzureloganalyticsConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "ListLogAnalyticsSubscriptions failed with status '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "ListLogAnalyticsSubscriptions failed with status '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }, cancellationToken) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; @@ -94,7 +101,7 @@ public async Task ListWorkspacesAsync( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "loganalytics/workspaces")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("ListLogAnalyticsWorkspaces: Using generated AzureloganalyticsClient from SDK."); + this._logger.LogInformation("ListLogAnalyticsWorkspaces: Using generated AzuremonitorlogsClient from SDK."); var subscription = request.Query["subscription"]; var resourceGroup = request.Query["resourceGroup"]; @@ -111,30 +118,30 @@ await badRequest try { - // Note: SDK returns ResourceGroup type for workspace entries per the connector API schema - var workspaces = new List(); - await foreach (var workspace in this._logAnalyticsClient - .ListWorkspaceNamesAsync(subscription: subscription, resourceGroup: resourceGroup) + // Note: SDK returns ResourceItem for resource entries per the connector API schema + var resources = new List(); + await foreach (var resource in this._logAnalyticsClient + .ListResourcesAsync(subscription: subscription, resourceGroup: resourceGroup, resourceType: AzureLogAnalyticsFunctions.OperationalInsightsWorkspaceResourceType) .WithCancellation(cancellationToken) .ConfigureAwait(continueOnCapturedContext: false)) { - workspaces.Add(workspace); + resources.Add(resource); } var response = request.CreateResponse(HttpStatusCode.OK); await response - .WriteAsJsonAsync(new { success = true, count = workspaces.Count, workspaces }, cancellationToken) + .WriteAsJsonAsync(new { success = true, count = resources.Count, workspaces = resources }, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); return response; } - catch (AzureloganalyticsConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "ListLogAnalyticsWorkspaces failed with status '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "ListLogAnalyticsWorkspaces failed with status '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }, cancellationToken) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; diff --git a/DirectConnector/ConnectorFunctions.cs b/DirectConnector/ConnectorFunctions.cs index 81b52a1..f3dbf0c 100644 --- a/DirectConnector/ConnectorFunctions.cs +++ b/DirectConnector/ConnectorFunctions.cs @@ -5,20 +5,23 @@ using System.Net; using System.Text; using System.Text.Json; -using Microsoft.Azure.Connectors.DirectClient.Office365; -using Microsoft.Azure.Connectors.DirectClient.Sharepointonline; -using Microsoft.Azure.Connectors.DirectClient.Teams; -using Microsoft.Azure.Connectors.Sdk; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.Office365; +using Azure.Connectors.Sdk.Office365.Models; +using Azure.Connectors.Sdk.SharePointOnline; +using Azure.Connectors.Sdk.SharePointOnline.Models; +using Azure.Connectors.Sdk.Teams; +using Azure.Connectors.Sdk.Teams.Models; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; using Microsoft.Extensions.Logging; -using SharePointBlobMetadata = Microsoft.Azure.Connectors.DirectClient.Sharepointonline.BlobMetadata; +using SharePointBlobMetadata = Azure.Connectors.Sdk.SharePointOnline.Models.BlobMetadata; namespace DirectConnector; /// -/// Azure Functions that use the generated , , -/// and from the DirectClient SDK. +/// Azure Functions that use the generated , , +/// and from the Azure Connectors SDK. /// /// /// Demonstrates DI-based lifetime management, JSON deserialization for structured responses, @@ -56,7 +59,7 @@ public class ConnectorFunctions private readonly ILogger _logger; private readonly Office365Client _office365Client; - private readonly SharepointonlineClient _sharePointClient; + private readonly SharePointOnlineClient _sharePointClient; private readonly TeamsClient _teamsClient; /// @@ -69,7 +72,7 @@ public class ConnectorFunctions public ConnectorFunctions( ILogger logger, Office365Client office365Client, - SharepointonlineClient sharePointClient, + SharePointOnlineClient sharePointClient, TeamsClient teamsClient) { this._logger = logger; @@ -148,9 +151,9 @@ await response return response; } - catch (Office365ConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "Connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "Connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -158,7 +161,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -212,9 +215,9 @@ await response return response; } - catch (Office365ConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "Connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "Connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -222,7 +225,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode + statusCode = ex.Status }) .ConfigureAwait(continueOnCapturedContext: false); @@ -246,7 +249,7 @@ await errorResponse } /// - /// Gets all SharePoint lists and libraries for a site using the generated . + /// Gets all SharePoint lists and libraries for a site using the generated . /// /// The HTTP request containing the site address. /// The cancellation token. @@ -255,7 +258,7 @@ public async Task GetSharePointListsAsync( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "sharepoint/lists")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("GetSharePointLists: Using generated SharepointonlineClient from SDK."); + this._logger.LogInformation("GetSharePointLists: Using generated SharePointOnlineClient from SDK."); var siteAddress = request.Query["site"]; if (string.IsNullOrEmpty(siteAddress)) @@ -290,9 +293,9 @@ await response return response; } - catch (SharepointonlineConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "SharePoint connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "SharePoint connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -300,7 +303,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -325,7 +328,7 @@ await errorResponse } /// - /// Lists files in a SharePoint folder using the generated . + /// Lists files in a SharePoint folder using the generated . /// /// /// Exercises the model for folder browsing. @@ -337,7 +340,7 @@ public async Task ListFolderAsync( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "sharepoint/files")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("ListFolder: Using generated SharepointonlineClient from SDK."); + this._logger.LogInformation("ListFolder: Using generated SharePointOnlineClient from SDK."); var siteAddress = request.Query["site"]; if (string.IsNullOrEmpty(siteAddress)) @@ -386,9 +389,9 @@ await response return response; } - catch (SharepointonlineConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "SharePoint connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "SharePoint connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -396,7 +399,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -420,7 +423,7 @@ await errorResponse /// Downloads file content from SharePoint as binary bytes. /// /// - /// Exercises the byte[] response path in . + /// Exercises the byte[] response path in . /// The generated CallConnectorAsync detects byte[] as the response type and uses /// ReadAsByteArrayAsync instead of JSON deserialization. /// @@ -431,7 +434,7 @@ public async Task DownloadFileAsync( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "sharepoint/download")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("DownloadFile: Using generated SharepointonlineClient byte[] response path."); + this._logger.LogInformation("DownloadFile: Using generated SharePointOnlineClient byte[] response path."); var siteAddress = request.Query["site"]; var filePath = request.Query["path"]; @@ -471,9 +474,9 @@ await response.Body return response; } - catch (SharepointonlineConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "SharePoint connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "SharePoint connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -481,7 +484,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -505,7 +508,7 @@ await errorResponse /// Uploads a file to a SharePoint document library. /// /// - /// Exercises the byte[] input path in . + /// Exercises the byte[] input path in . /// Accepts a JSON body with base64-encoded content or plain text, and uploads it to /// the specified SharePoint folder. /// @@ -516,7 +519,7 @@ public async Task UploadFileAsync( [HttpTrigger(AuthorizationLevel.Function, "post", Route = "sharepoint/upload")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("UploadFile: Using generated SharepointonlineClient byte[] input path."); + this._logger.LogInformation("UploadFile: Using generated SharePointOnlineClient byte[] input path."); try { @@ -593,9 +596,9 @@ await response return response; } - catch (SharepointonlineConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "SharePoint connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "SharePoint connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -603,7 +606,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -670,9 +673,9 @@ await response.Body return response; } - catch (Office365ConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "Connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "Connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -680,7 +683,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode + statusCode = ex.Status }) .ConfigureAwait(continueOnCapturedContext: false); @@ -928,9 +931,9 @@ await response return response; } - catch (TeamsConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "Teams connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "Teams connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -938,7 +941,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -1005,9 +1008,9 @@ await response return response; } - catch (TeamsConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "Teams connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "Teams connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -1015,7 +1018,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -1098,9 +1101,9 @@ await response return response; } - catch (TeamsConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "Teams connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "Teams connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -1108,7 +1111,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -1178,7 +1181,7 @@ await badRequest // The actual message body properties are determined at runtime by the connector's schema // discovery endpoint. With [JsonExtensionData] on AdditionalProperties, arbitrary properties // are now serialized correctly. Populate the dictionary with the expected message fields. - var messageRequest = new Microsoft.Azure.Connectors.DirectClient.Teams.DynamicPostMessageRequest(); + var messageRequest = new Azure.Connectors.Sdk.Teams.Models.DynamicPostMessageRequest(); messageRequest.AdditionalProperties["recipient"] = JsonSerializer.SerializeToElement( new { @@ -1210,9 +1213,9 @@ await response return response; } - catch (TeamsConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "Teams connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "Teams connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -1220,7 +1223,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -1323,9 +1326,9 @@ await response return response; } - catch (Office365ConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "Connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "Connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -1333,7 +1336,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); diff --git a/DirectConnector/ConnectorTriggerMetadataAttribute.cs b/DirectConnector/ConnectorTriggerMetadataAttribute.cs index fd4ac8e..988ba0c 100644 --- a/DirectConnector/ConnectorTriggerMetadataAttribute.cs +++ b/DirectConnector/ConnectorTriggerMetadataAttribute.cs @@ -28,13 +28,13 @@ namespace DirectConnector; public sealed class ConnectorTriggerMetadataAttribute : Attribute { /// - /// The connector API name. Use constants from . + /// The connector API name. Use constants from . /// public string ConnectorName { get; set; } = ""; /// /// The trigger operation name. Use constants from the connector's *TriggerOperations class - /// (e.g., ). + /// (e.g., ). /// public string OperationName { get; set; } = ""; diff --git a/DirectConnector/DirectConnector.csproj b/DirectConnector/DirectConnector.csproj index 25131d0..5f49bed 100644 --- a/DirectConnector/DirectConnector.csproj +++ b/DirectConnector/DirectConnector.csproj @@ -14,7 +14,7 @@ - + diff --git a/DirectConnector/ExceptionExtensions.cs b/DirectConnector/ExceptionExtensions.cs new file mode 100644 index 0000000..736cf1a --- /dev/null +++ b/DirectConnector/ExceptionExtensions.cs @@ -0,0 +1,28 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System.Runtime.InteropServices; + +namespace DirectConnector; + +/// +/// Extension methods for exception handling. +/// Provides the guard that was available in earlier SDK versions +/// but made internal in Azure.Connectors.Sdk 0.9.0. +/// +internal static class ExceptionExtensions +{ + /// + /// Determines whether the exception is fatal and should not be caught. + /// + /// The exception to check. + public static bool IsFatal(this Exception exception) + { + return exception is OutOfMemoryException or + StackOverflowException or + AccessViolationException or + SEHException or + ThreadAbortException; + } +} diff --git a/DirectConnector/MqFunctions.cs b/DirectConnector/MqFunctions.cs index f5a1918..09f93db 100644 --- a/DirectConnector/MqFunctions.cs +++ b/DirectConnector/MqFunctions.cs @@ -4,8 +4,9 @@ using System.Net; using System.Text.Json; -using Microsoft.Azure.Connectors.DirectClient.Mq; -using Microsoft.Azure.Connectors.Sdk; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.Mq; +using Azure.Connectors.Sdk.Mq.Models; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; using Microsoft.Extensions.Logging; @@ -14,7 +15,7 @@ namespace DirectConnector; /// /// Azure Functions demonstrating IBM MQ operations using the generated -/// from the DirectClient SDK. +/// from the Azure Connectors SDK. /// /// /// IBM MQ uses parameter-based auth (server, queue manager, channel, credentials). @@ -85,13 +86,13 @@ await response return response; } - catch (MqConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "MQ send failed with status '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "MQ send failed with status '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }, cancellationToken) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; @@ -156,13 +157,13 @@ await response return response; } - catch (MqConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "MQ browse failed with status '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "MQ browse failed with status '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }, cancellationToken) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; @@ -231,13 +232,13 @@ await response return response; } - catch (MqConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "MQ browse batch failed with status '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "MQ browse batch failed with status '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }, cancellationToken) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; @@ -302,13 +303,13 @@ await response return response; } - catch (MqConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "MQ receive failed with status '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "MQ receive failed with status '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }, cancellationToken) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; @@ -378,13 +379,13 @@ await response return response; } - catch (MqConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "MQ delete failed with status '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "MQ delete failed with status '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }, cancellationToken) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; diff --git a/DirectConnector/MsGraphFunctions.cs b/DirectConnector/MsGraphFunctions.cs index 7be6cbb..837e1a7 100644 --- a/DirectConnector/MsGraphFunctions.cs +++ b/DirectConnector/MsGraphFunctions.cs @@ -3,8 +3,9 @@ //------------------------------------------------------------ using System.Net; -using Microsoft.Azure.Connectors.DirectClient.Msgraphgroupsanduser; -using Microsoft.Azure.Connectors.Sdk; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.MsGraphGroupsAndUsers; +using Azure.Connectors.Sdk.MsGraphGroupsAndUsers.Models; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; using Microsoft.Extensions.Logging; @@ -13,7 +14,7 @@ namespace DirectConnector; /// /// Azure Functions demonstrating MS Graph Groups & Users operations using the generated -/// from the DirectClient SDK. +/// from the Azure Connectors SDK. /// /// /// Exercises user listing, group search, and group property retrieval. @@ -21,7 +22,7 @@ namespace DirectConnector; public class MsGraphFunctions { private readonly ILogger _logger; - private readonly MsgraphgroupsanduserClient _msGraphClient; + private readonly MsGraphGroupsAndUsersClient _msGraphClient; /// /// Initializes a new instance of the class. @@ -30,14 +31,14 @@ public class MsGraphFunctions /// The DI-injected MS Graph Groups & Users client (disposed by the host). public MsGraphFunctions( ILogger logger, - MsgraphgroupsanduserClient msGraphClient) + MsGraphGroupsAndUsersClient msGraphClient) { this._logger = logger; this._msGraphClient = msGraphClient; } /// - /// Lists a page of users in the tenant using the generated . + /// Lists a page of users in the tenant using the generated . /// /// The HTTP request. /// The cancellation token. @@ -46,7 +47,7 @@ public async Task ListGraphUsersAsync( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "graph/users")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("ListGraphUsers: Using generated MsgraphgroupsanduserClient from SDK."); + this._logger.LogInformation("ListGraphUsers: Using generated MsGraphGroupsAndUsersClient from SDK."); try { @@ -66,9 +67,9 @@ await response return response; } - catch (MsgraphgroupsanduserConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "MS Graph connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "MS Graph connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -76,7 +77,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -97,7 +98,7 @@ await errorResponse } /// - /// Searches for groups by display name using the generated . + /// Searches for groups by display name using the generated . /// /// The HTTP request with optional 'search' query parameter. /// The cancellation token. @@ -106,7 +107,7 @@ public async Task ListGraphGroupsAsync( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "graph/groups")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("ListGraphGroups: Using generated MsgraphgroupsanduserClient from SDK."); + this._logger.LogInformation("ListGraphGroups: Using generated MsGraphGroupsAndUsersClient from SDK."); var rawSearch = request.Query["search"]; var search = string.IsNullOrWhiteSpace(rawSearch) ? null : rawSearch; @@ -130,9 +131,9 @@ await response return response; } - catch (MsgraphgroupsanduserConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "MS Graph connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "MS Graph connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -140,7 +141,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -161,7 +162,7 @@ await errorResponse } /// - /// Gets properties of a specific group using the generated . + /// Gets properties of a specific group using the generated . /// /// The HTTP request with 'groupId' query parameter. /// The cancellation token. @@ -170,7 +171,7 @@ public async Task GetGraphGroupPropertiesAsync( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "graph/groups/properties")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("GetGraphGroupProperties: Using generated MsgraphgroupsanduserClient from SDK."); + this._logger.LogInformation("GetGraphGroupProperties: Using generated MsGraphGroupsAndUsersClient from SDK."); var rawGroupId = request.Query["groupId"]; var groupId = rawGroupId?.Trim(); @@ -206,9 +207,9 @@ await response return response; } - catch (MsgraphgroupsanduserConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "MS Graph connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "MS Graph connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -216,7 +217,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); diff --git a/DirectConnector/Office365UsersFunctions.cs b/DirectConnector/Office365UsersFunctions.cs index d6d4d41..5dc6a86 100644 --- a/DirectConnector/Office365UsersFunctions.cs +++ b/DirectConnector/Office365UsersFunctions.cs @@ -3,8 +3,9 @@ //------------------------------------------------------------ using System.Net; -using Microsoft.Azure.Connectors.DirectClient.Office365users; -using Microsoft.Azure.Connectors.Sdk; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.Office365users; +using Azure.Connectors.Sdk.Office365users.Models; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; using Microsoft.Extensions.Logging; @@ -13,7 +14,7 @@ namespace DirectConnector; /// /// Azure Functions demonstrating Office 365 Users operations using the generated -/// from the DirectClient SDK. +/// from the Azure Connectors SDK. /// public class Office365UsersFunctions { @@ -48,13 +49,13 @@ await response return response; } - catch (Office365usersConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "GetMyProfile failed with status '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "GetMyProfile failed with status '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }, cancellationToken) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; @@ -97,13 +98,13 @@ await response return response; } - catch (Office365usersConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "GetUserProfile failed with status '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "GetUserProfile failed with status '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }, cancellationToken) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; @@ -146,13 +147,13 @@ await response return response; } - catch (Office365usersConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "GetManager failed with status '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "GetManager failed with status '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }, cancellationToken) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; @@ -195,13 +196,13 @@ await response return response; } - catch (Office365usersConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "GetDirectReports failed with status '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "GetDirectReports failed with status '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }, cancellationToken) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; @@ -260,13 +261,13 @@ await response return response; } - catch (Office365usersConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "SearchUsers failed with status '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "SearchUsers failed with status '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse - .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, details = ex.ResponseBody }, cancellationToken) + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); return errorResponse; diff --git a/DirectConnector/OneDriveFunctions.cs b/DirectConnector/OneDriveFunctions.cs index f46dbdc..3460486 100644 --- a/DirectConnector/OneDriveFunctions.cs +++ b/DirectConnector/OneDriveFunctions.cs @@ -5,18 +5,19 @@ using System.Net; using System.Text; using System.Text.Json; -using Microsoft.Azure.Connectors.DirectClient.Onedriveforbusiness; -using Microsoft.Azure.Connectors.Sdk; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.OneDriveForBusiness; +using Azure.Connectors.Sdk.OneDriveForBusiness.Models; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; using Microsoft.Extensions.Logging; -using OneDriveBlobMetadata = Microsoft.Azure.Connectors.DirectClient.Onedriveforbusiness.BlobMetadata; +using OneDriveBlobMetadata = Azure.Connectors.Sdk.OneDriveForBusiness.Models.BlobMetadata; namespace DirectConnector; /// /// Azure Functions demonstrating OneDrive for Business operations using the generated -/// from the DirectClient SDK. +/// from the Azure Connectors SDK. /// /// /// Exercises folder listing, file download/upload, search, sharing links, @@ -49,7 +50,7 @@ public class OneDriveFunctions }; private readonly ILogger _logger; - private readonly OnedriveforbusinessClient _oneDriveClient; + private readonly OneDriveForBusinessClient _oneDriveClient; /// /// Initializes a new instance of the class. @@ -58,7 +59,7 @@ public class OneDriveFunctions /// The DI-injected OneDrive for Business client (disposed by the host). public OneDriveFunctions( ILogger logger, - OnedriveforbusinessClient oneDriveClient) + OneDriveForBusinessClient oneDriveClient) { this._logger = logger; this._oneDriveClient = oneDriveClient; @@ -74,7 +75,7 @@ public async Task ListOneDriveRootAsync( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "onedrive/root")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("ListOneDriveRoot: Using generated OnedriveforbusinessClient from SDK."); + this._logger.LogInformation("ListOneDriveRoot: Using generated OneDriveForBusinessClient from SDK."); try { @@ -103,9 +104,9 @@ await response return response; } - catch (OnedriveforbusinessConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "OneDrive connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "OneDrive connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -113,7 +114,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -137,7 +138,7 @@ await errorResponse /// Lists files in a specific OneDrive folder. /// /// - /// Exercises which returns an + /// Exercises which returns an /// that automatically follows pagination across all pages. /// /// The HTTP request containing the folder identifier. @@ -147,7 +148,7 @@ public async Task ListOneDriveFolderAsync( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "onedrive/files")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("ListOneDriveFolder: Using generated OnedriveforbusinessClient from SDK."); + this._logger.LogInformation("ListOneDriveFolder: Using generated OneDriveForBusinessClient from SDK."); var folderId = request.Query["folder"]; if (string.IsNullOrEmpty(folderId)) @@ -194,9 +195,9 @@ await response return response; } - catch (OnedriveforbusinessConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "OneDrive connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "OneDrive connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -204,7 +205,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -228,7 +229,7 @@ await errorResponse /// Downloads a file from OneDrive for Business as binary bytes. /// /// - /// Exercises the byte[] response path in . + /// Exercises the byte[] response path in . /// /// The HTTP request containing the file path. /// The cancellation token. @@ -237,7 +238,7 @@ public async Task DownloadOneDriveFileAsync( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "onedrive/download")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("DownloadOneDriveFile: Using generated OnedriveforbusinessClient byte[] response path."); + this._logger.LogInformation("DownloadOneDriveFile: Using generated OneDriveForBusinessClient byte[] response path."); var filePath = request.Query["path"]; if (string.IsNullOrEmpty(filePath)) @@ -272,9 +273,9 @@ await response.Body return response; } - catch (OnedriveforbusinessConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "OneDrive connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "OneDrive connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -282,7 +283,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -306,7 +307,7 @@ await errorResponse /// Uploads a file to OneDrive for Business. /// /// - /// Exercises the byte[] input path in . + /// Exercises the byte[] input path in . /// /// The HTTP request containing upload details. /// The cancellation token. @@ -315,7 +316,7 @@ public async Task UploadOneDriveFileAsync( [HttpTrigger(AuthorizationLevel.Function, "post", Route = "onedrive/upload")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("UploadOneDriveFile: Using generated OnedriveforbusinessClient byte[] input path."); + this._logger.LogInformation("UploadOneDriveFile: Using generated OneDriveForBusinessClient byte[] input path."); try { @@ -391,9 +392,9 @@ await response return response; } - catch (OnedriveforbusinessConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "OneDrive connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "OneDrive connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -401,7 +402,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -431,7 +432,7 @@ public async Task SearchOneDriveFilesAsync( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "onedrive/search")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("SearchOneDriveFiles: Using generated OnedriveforbusinessClient from SDK."); + this._logger.LogInformation("SearchOneDriveFiles: Using generated OneDriveForBusinessClient from SDK."); var query = request.Query["query"]; if (string.IsNullOrEmpty(query)) @@ -477,9 +478,9 @@ await response return response; } - catch (OnedriveforbusinessConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "OneDrive connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "OneDrive connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -487,7 +488,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -517,7 +518,7 @@ public async Task CreateOneDriveShareLinkAsync( [HttpTrigger(AuthorizationLevel.Function, "post", Route = "onedrive/share")] HttpRequestData request, CancellationToken cancellationToken) { - this._logger.LogInformation("CreateOneDriveShareLink: Using generated OnedriveforbusinessClient from SDK."); + this._logger.LogInformation("CreateOneDriveShareLink: Using generated OneDriveForBusinessClient from SDK."); try { @@ -577,9 +578,9 @@ await response return response; } - catch (OnedriveforbusinessConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "OneDrive connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "OneDrive connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -587,7 +588,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }) .ConfigureAwait(continueOnCapturedContext: false); @@ -627,7 +628,7 @@ await errorResponse /// Object body (OnNewFilesV2 / OnUpdatedFilesV2) /// /// The body field is a {"value":[...]} object with items. - /// The payload deserializes to . + /// The payload deserializes to . /// /// /// @@ -636,8 +637,8 @@ await errorResponse /// The cancellation token. [Function("OneDriveTriggerCallback")] [ConnectorTriggerMetadata( - ConnectorName = ConnectorNames.Onedriveforbusiness, - OperationName = OnedriveforbusinessTriggerOperations.OnNewFiles, + ConnectorName = ConnectorNames.OneDriveForBusiness, + OperationName = OneDriveForBusinessTriggerOperations.OnNewFiles, Connection = "Connectors:OneDrive")] public async Task OneDriveTriggerCallbackAsync( [HttpTrigger(AuthorizationLevel.Function, "post", Route = "onedriveTriggerCallback")] HttpRequestData request, @@ -758,7 +759,7 @@ await binaryResponse // NOTE: OnNewFilesV2 / OnUpdatedFilesV2 (properties-only trigger). // The "body" field is a {"value":[...]} object with BlobMetadata items. - var payload = JsonSerializer.Deserialize( + var payload = JsonSerializer.Deserialize( body, OneDriveFunctions.JsonOptions); diff --git a/DirectConnector/Program.cs b/DirectConnector/Program.cs index e88984c..15d9a97 100644 --- a/DirectConnector/Program.cs +++ b/DirectConnector/Program.cs @@ -2,22 +2,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ -using DirectConnector.Configuration; -using Microsoft.Azure.Connectors.DirectClient.Azureblob; -using Microsoft.Azure.Connectors.DirectClient.Azureloganalytics; -using Microsoft.Azure.Connectors.DirectClient.Mq; -using Microsoft.Azure.Connectors.DirectClient.Msgraphgroupsanduser; -using Microsoft.Azure.Connectors.DirectClient.Office365; -using Microsoft.Azure.Connectors.DirectClient.Office365users; -using Microsoft.Azure.Connectors.DirectClient.Onedriveforbusiness; -using Microsoft.Azure.Connectors.DirectClient.Sharepointonline; -using Microsoft.Azure.Connectors.DirectClient.Smtp; -using Microsoft.Azure.Connectors.DirectClient.Teams; +using Azure.Connectors.Sdk; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Options; var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults() @@ -25,137 +14,27 @@ { var configuration = hostContext.Configuration; - // NOTE: Bind connector options from configuration. - // This uses the Options pattern for hierarchical, validated configuration. - // See: https://learn.microsoft.com/dotnet/core/extensions/options - services.AddOptions() - .Bind(configuration.GetSection(ConnectorOptions.SectionName)) - .ValidateDataAnnotations() - .ValidateOnStart(); - - services.AddHttpClient(); - - // NOTE: Register generated connector clients as singletons. - // The factory overload lets DI own the instance lifetime and call Dispose, - // which exercises the ownership-based disposal pattern: the client will - // dispose its internally-created HttpClient and DefaultAzureCredential. - // NOTE: Validation of ConnectionRuntimeUrl is handled by - // [Required] attribute + ValidateOnStart() at host initialization. - services.AddSingleton(serviceProvider => - { - var options = serviceProvider.GetRequiredService>().Value; - - // NOTE: When ManagedIdentityClientId is null, use the default constructor so - // the client relies on DefaultAzureCredential. When ManagedIdentityClientId is non-null - // (empty string = system-assigned MSI, non-empty = user-assigned MSI), use the MSI constructor. - return options.Office365.ManagedIdentityClientId != null - ? new Office365Client( - options.Office365.ConnectionRuntimeUrl, - options.Office365.ManagedIdentityClientId) - : new Office365Client(options.Office365.ConnectionRuntimeUrl); - }); - - services.AddSingleton(serviceProvider => - { - var options = serviceProvider.GetRequiredService>().Value; - - // NOTE: When ManagedIdentityClientId is null, use the default constructor so - // the client relies on DefaultAzureCredential. When ManagedIdentityClientId is non-null - // (empty string = system-assigned MSI, non-empty = user-assigned MSI), use the MSI constructor. - return options.SharePoint.ManagedIdentityClientId != null - ? new SharepointonlineClient( - options.SharePoint.ConnectionRuntimeUrl, - options.SharePoint.ManagedIdentityClientId) - : new SharepointonlineClient(options.SharePoint.ConnectionRuntimeUrl); - }); - - services.AddSingleton(serviceProvider => - { - var options = serviceProvider.GetRequiredService>().Value; - - return options.Teams.ManagedIdentityClientId != null - ? new TeamsClient( - options.Teams.ConnectionRuntimeUrl, - options.Teams.ManagedIdentityClientId) - : new TeamsClient(options.Teams.ConnectionRuntimeUrl); - }); - - services.AddSingleton(serviceProvider => - { - var options = serviceProvider.GetRequiredService>().Value; - - return options.OneDrive.ManagedIdentityClientId != null - ? new OnedriveforbusinessClient( - options.OneDrive.ConnectionRuntimeUrl, - options.OneDrive.ManagedIdentityClientId) - : new OnedriveforbusinessClient(options.OneDrive.ConnectionRuntimeUrl); - }); - - services.AddSingleton(serviceProvider => - { - var options = serviceProvider.GetRequiredService>().Value; - - return options.MsGraph.ManagedIdentityClientId != null - ? new MsgraphgroupsanduserClient( - options.MsGraph.ConnectionRuntimeUrl, - options.MsGraph.ManagedIdentityClientId) - : new MsgraphgroupsanduserClient(options.MsGraph.ConnectionRuntimeUrl); - }); - - services.AddSingleton(serviceProvider => - { - var options = serviceProvider.GetRequiredService>().Value; - - return options.AzureBlob.ManagedIdentityClientId != null - ? new AzureblobClient( - options.AzureBlob.ConnectionRuntimeUrl, - options.AzureBlob.ManagedIdentityClientId) - : new AzureblobClient(options.AzureBlob.ConnectionRuntimeUrl); - }); - - services.AddSingleton(serviceProvider => - { - var options = serviceProvider.GetRequiredService>().Value; - - return options.Smtp.ManagedIdentityClientId != null - ? new SmtpClient( - options.Smtp.ConnectionRuntimeUrl, - options.Smtp.ManagedIdentityClientId) - : new SmtpClient(options.Smtp.ConnectionRuntimeUrl); - }); - - services.AddSingleton(serviceProvider => - { - var options = serviceProvider.GetRequiredService>().Value; - - return options.Mq.ManagedIdentityClientId != null - ? new MqClient( - options.Mq.ConnectionRuntimeUrl, - options.Mq.ManagedIdentityClientId) - : new MqClient(options.Mq.ConnectionRuntimeUrl); - }); - - services.AddSingleton(serviceProvider => - { - var options = serviceProvider.GetRequiredService>().Value; - - return options.Office365Users.ManagedIdentityClientId != null - ? new Office365usersClient( - options.Office365Users.ConnectionRuntimeUrl, - options.Office365Users.ManagedIdentityClientId) - : new Office365usersClient(options.Office365Users.ConnectionRuntimeUrl); - }); - - services.AddSingleton(serviceProvider => - { - var options = serviceProvider.GetRequiredService>().Value; - - return options.AzureLogAnalytics.ManagedIdentityClientId != null - ? new AzureloganalyticsClient( - options.AzureLogAnalytics.ConnectionRuntimeUrl, - options.AzureLogAnalytics.ManagedIdentityClientId) - : new AzureloganalyticsClient(options.AzureLogAnalytics.ConnectionRuntimeUrl); - }); + // NOTE: Register a TokenCredential for connector clients. + // In Development, use DefaultAzureCredential (supports CLI, env vars, etc.). + // In Production, the SDK defaults to system-assigned managed identity + // when no TokenCredential is registered in DI. + if (hostContext.HostingEnvironment.IsDevelopment()) + { + services.AddSingleton( + new Azure.Identity.DefaultAzureCredential()); + } + + services.AddOffice365Client(configuration.GetSection("Connectors:Office365")); + services.AddSharePointOnlineClient(configuration.GetSection("Connectors:SharePoint")); + services.AddTeamsClient(configuration.GetSection("Connectors:Teams")); + services.AddOneDriveForBusinessClient(configuration.GetSection("Connectors:OneDrive")); + services.AddMsGraphGroupsAndUsersClient(configuration.GetSection("Connectors:MsGraph")); + services.AddAzureBlobClient(configuration.GetSection("Connectors:AzureBlob")); + services.AddSmtpClient(configuration.GetSection("Connectors:Smtp")); + services.AddMqClient(configuration.GetSection("Connectors:Mq")); + services.AddOffice365usersClient(configuration.GetSection("Connectors:Office365Users")); + services.AddAzuremonitorlogsClient(configuration.GetSection("Connectors:AzureMonitorLogs")); + services.AddArmClient(configuration.GetSection("Connectors:Arm")); }) .Build(); diff --git a/DirectConnector/SmtpFunctions.cs b/DirectConnector/SmtpFunctions.cs index d84a1ce..3d6a35d 100644 --- a/DirectConnector/SmtpFunctions.cs +++ b/DirectConnector/SmtpFunctions.cs @@ -4,8 +4,9 @@ using System.Net; using System.Text.Json; -using Microsoft.Azure.Connectors.DirectClient.Smtp; -using Microsoft.Azure.Connectors.Sdk; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.Smtp; +using Azure.Connectors.Sdk.Smtp.Models; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; using Microsoft.Extensions.Logging; @@ -14,7 +15,7 @@ namespace DirectConnector; /// /// Azure Functions demonstrating SMTP operations using the generated -/// from the DirectClient SDK. +/// from the Azure Connectors SDK. /// public class SmtpFunctions { @@ -91,9 +92,9 @@ await response return response; } - catch (SmtpConnectorException ex) + catch (ConnectorException ex) { - this._logger.LogError(ex, "SMTP connector error: '{StatusCode}'.", ex.StatusCode); + this._logger.LogError(ex, "SMTP connector error: '{StatusCode}'.", ex.Status); var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); await errorResponse @@ -101,7 +102,7 @@ await errorResponse { success = false, error = ex.Message, - statusCode = ex.StatusCode, + statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); diff --git a/DirectConnector/local.settings.json.template b/DirectConnector/local.settings.json.template index e068f83..f8cc022 100644 --- a/DirectConnector/local.settings.json.template +++ b/DirectConnector/local.settings.json.template @@ -40,8 +40,12 @@ "__comment_Office365Users_MSI": "To use managed identity, remove the __ prefix from the key below. Use empty string for system-assigned MSI, or a client ID GUID for user-assigned MSI.", "__Connectors:Office365Users:ManagedIdentityClientId": "", - "Connectors:AzureLogAnalytics:ConnectionRuntimeUrl": "https://YOUR-INSTANCE.azure-apihub.net/apim/azureloganalytics/YOUR-CONNECTION-ID", - "__comment_AzureLogAnalytics_MSI": "To use managed identity, remove the __ prefix from the key below. Use empty string for system-assigned MSI, or a client ID GUID for user-assigned MSI.", - "__Connectors:AzureLogAnalytics:ManagedIdentityClientId": "" + "Connectors:AzureMonitorLogs:ConnectionRuntimeUrl": "https://YOUR-INSTANCE.azure-apihub.net/apim/azuremonitorlogs/YOUR-CONNECTION-ID", + "__comment_AzureMonitorLogs_MSI": "To use managed identity, remove the __ prefix from the key below. Use empty string for system-assigned MSI, or a client ID GUID for user-assigned MSI.", + "__Connectors:AzureMonitorLogs:ManagedIdentityClientId": "", + + "Connectors:Arm:ConnectionRuntimeUrl": "https://YOUR-INSTANCE.azure-apihub.net/apim/arm/YOUR-CONNECTION-ID", + "__comment_Arm_MSI": "To use managed identity, remove the __ prefix from the key below. Use empty string for system-assigned MSI, or a client ID GUID for user-assigned MSI.", + "__Connectors:Arm:ManagedIdentityClientId": "" } } diff --git a/README.md b/README.md index 1388b16..1e02a39 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Sample Azure Functions demonstrating the [Azure Connectors .NET SDK](https://git ## What's Inside -The `DirectConnector/` project is an Azure Functions (isolated worker) app with 40 sample functions across 10 connectors. Newer connectors have dedicated Functions classes; the original three (Office 365, SharePoint, Teams) share `ConnectorFunctions.cs`: +The `DirectConnector/` project is an Azure Functions (isolated worker) app with sample functions across 11 connectors. Newer connectors have dedicated Functions classes; the original three (Office 365, SharePoint, Teams) share `ConnectorFunctions.cs`: | File | Connector | Sample Operations | |------|-----------|-------------------| @@ -28,7 +28,8 @@ The `DirectConnector/` project is an Azure Functions (isolated worker) app with | MqFunctions.cs | IBM MQ | Send, browse, receive, delete messages | | SmtpFunctions.cs | SMTP | Send email via SMTP | | AzureBlobFunctions.cs | Azure Blob Storage | Upload, download, get metadata, delete blobs | -| AzureLogAnalyticsFunctions.cs | Azure Log Analytics | List subscriptions, list workspaces | +| AzureLogAnalyticsFunctions.cs | Azure Monitor Logs | List subscriptions, list resources | +| ArmFunctions.cs | Azure Resource Manager (ARM) | List subscriptions, resource groups, read resources | ### Key Patterns Demonstrated