Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
267 changes: 267 additions & 0 deletions DirectConnector/ArmFunctions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

using System.Net;
using System.Text.Json;
using Microsoft.Azure.Connectors.Sdk.Arm;
using Microsoft.Azure.Connectors.Sdk;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;

namespace DirectConnector;

/// <summary>
/// Azure Functions demonstrating Azure Resource Manager operations using the generated
/// <see cref="ArmClient"/> from the DirectClient SDK.
/// </summary>
/// <remarks>
/// ARM connector uses OAuth (user-delegated Azure AD token).
/// The connection requires OAuth consent via the consent link flow.
/// </remarks>
public class ArmFunctions
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true
};

private readonly ILogger<ArmFunctions> _logger;
private readonly ArmClient _armClient;

public ArmFunctions(
ILogger<ArmFunctions> logger,
ArmClient armClient)
{
this._logger = logger;
this._armClient = armClient;
}

/// <summary>
/// Lists all subscriptions accessible to the authenticated user.
/// </summary>
[Function("ListSubscriptions")]
public async Task<HttpResponseData> ListSubscriptionsAsync(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "arm/subscriptions")] HttpRequestData request,
CancellationToken cancellationToken)
{
this._logger.LogInformation("ListSubscriptions: Using generated ArmClient.");

try
{
var subscriptions = new List<Subscription>();
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(subscriptions)
.ConfigureAwait(continueOnCapturedContext: false);
return response;
}
catch (ConnectorException ex)
{
this._logger.LogError(ex, "ARM connector error: '{StatusCode}'.", ex.StatusCode);

var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway);
await errorResponse
.WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, 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;
}
}

/// <summary>
/// Lists resource groups in a subscription.
/// </summary>
[Function("ListResourceGroups")]
public async Task<HttpResponseData> 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<ResourceGroup>();
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(resourceGroups)
.ConfigureAwait(continueOnCapturedContext: false);
return response;
}
catch (ConnectorException ex)
{
this._logger.LogError(ex, "ARM connector error: '{StatusCode}'.", ex.StatusCode);

var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway);
await errorResponse
.WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, 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;
}
}

/// <summary>
/// Reads a specific resource by its resource group, provider, and short resource ID.
/// </summary>
[Function("ReadResource")]
public async Task<HttpResponseData> 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(resource)
.ConfigureAwait(continueOnCapturedContext: false);
return response;
}
catch (ConnectorException ex)
{
this._logger.LogError(ex, "ARM connector error: '{StatusCode}'.", ex.StatusCode);

var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway);
await errorResponse
.WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, 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;
}
}

/// <summary>
/// Lists resources in a resource group.
/// </summary>
[Function("ListResourcesByResourceGroup")]
public async Task<HttpResponseData> 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<GenericResource>();
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(resources)
.ConfigureAwait(continueOnCapturedContext: false);
return response;
}
catch (ConnectorException ex)
{
this._logger.LogError(ex, "ARM connector error: '{StatusCode}'.", ex.StatusCode);

var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway);
await errorResponse
.WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.StatusCode, 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;
}
}
}
10 changes: 5 additions & 5 deletions DirectConnector/AzureBlobFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//------------------------------------------------------------

using System.Net;
using Microsoft.Azure.Connectors.DirectClient.Azureblob;
using Microsoft.Azure.Connectors.Sdk.Azureblob;
using Microsoft.Azure.Connectors.Sdk;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
Expand Down Expand Up @@ -71,7 +71,7 @@ await response
.ConfigureAwait(continueOnCapturedContext: false);
return response;
}
catch (AzureblobConnectorException ex)
catch (ConnectorException ex)
{
this._logger.LogError(ex, "Azure Blob connector error: '{StatusCode}'.", ex.StatusCode);

Expand Down Expand Up @@ -140,7 +140,7 @@ await response.Body

return response;
}
catch (AzureblobConnectorException ex)
catch (ConnectorException ex)
{
this._logger.LogError(ex, "Azure Blob connector error: '{StatusCode}'.", ex.StatusCode);

Expand Down Expand Up @@ -210,7 +210,7 @@ await response
.ConfigureAwait(continueOnCapturedContext: false);
return response;
}
catch (AzureblobConnectorException ex)
catch (ConnectorException ex)
{
this._logger.LogError(ex, "Azure Blob connector error: '{StatusCode}'.", ex.StatusCode);

Expand Down Expand Up @@ -271,7 +271,7 @@ await response
.ConfigureAwait(continueOnCapturedContext: false);
return response;
}
catch (AzureblobConnectorException ex)
catch (ConnectorException ex)
{
this._logger.LogError(ex, "Azure Blob connector error: '{StatusCode}'.", ex.StatusCode);

Expand Down
22 changes: 11 additions & 11 deletions DirectConnector/AzureLogAnalyticsFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//------------------------------------------------------------

using System.Net;
using Microsoft.Azure.Connectors.DirectClient.Azureloganalytics;
using Microsoft.Azure.Connectors.Sdk.Azuremonitorlogs;
using Microsoft.Azure.Connectors.Sdk;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
Expand All @@ -13,16 +13,16 @@ namespace DirectConnector;

/// <summary>
/// Azure Functions demonstrating Azure Log Analytics operations using the generated
/// <see cref="AzureloganalyticsClient"/> from the DirectClient SDK.
/// <see cref="AzuremonitorlogsClient"/> from the DirectClient SDK.
/// </summary>
public class AzureLogAnalyticsFunctions
{
private readonly ILogger<AzureLogAnalyticsFunctions> _logger;
private readonly AzureloganalyticsClient _logAnalyticsClient;
private readonly AzuremonitorlogsClient _logAnalyticsClient;

public AzureLogAnalyticsFunctions(
ILogger<AzureLogAnalyticsFunctions> logger,
AzureloganalyticsClient logAnalyticsClient)
AzuremonitorlogsClient logAnalyticsClient)
{
this._logger = logger;
this._logAnalyticsClient = logAnalyticsClient;
Expand All @@ -37,7 +37,7 @@ public async Task<HttpResponseData> 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
{
Expand All @@ -57,7 +57,7 @@ await response

return response;
}
catch (AzureloganalyticsConnectorException ex)
catch (ConnectorException ex)
{
this._logger.LogError(ex, "ListLogAnalyticsSubscriptions failed with status '{StatusCode}'.", ex.StatusCode);

Expand Down Expand Up @@ -94,7 +94,7 @@ public async Task<HttpResponseData> 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"];
Expand All @@ -111,10 +111,10 @@ await badRequest

try
{
// Note: SDK returns ResourceGroup type for workspace entries per the connector API schema
var workspaces = new List<ResourceGroup>();
// Note: SDK returns ResourceItem type for workspace entries per the connector API schema
var workspaces = new List<ResourceItem>();
await foreach (var workspace in this._logAnalyticsClient
.ListWorkspaceNamesAsync(subscription: subscription, resourceGroup: resourceGroup)
.ListResourcesAsync(subscription: subscription, resourceGroup: resourceGroup, resourceType: "Microsoft.OperationalInsights/workspaces")
.WithCancellation(cancellationToken)
.ConfigureAwait(continueOnCapturedContext: false))
{
Expand All @@ -128,7 +128,7 @@ await response

return response;
}
catch (AzureloganalyticsConnectorException ex)
catch (ConnectorException ex)
{
this._logger.LogError(ex, "ListLogAnalyticsWorkspaces failed with status '{StatusCode}'.", ex.StatusCode);

Expand Down
Loading
Loading