Azure functions querying log analytics workspace

Maxime Callebaut 20 Reputation points
2025-05-05T13:41:31.13+00:00

Hello,

My company got a log analytics workspace (law) that has certain data. we want to copy this into our database.

When i run this code locally we are able to fetch data from the law. when the function is deployed we get the error. The azure funciton has a system assigned identity. this identity has log analytics contributor

"The workspace could not be found\nStatus: 404 (Not Found)\nErrorCode: WorkspaceNotFoundError\n\nContent:\n{"error":{"message":"The workspace could not be found","code":"WorkspaceNotFoundError","correlationId":"f378ac49-4f3e-4034-b911-24c4cc45ca75"}}\n\nHeaders:\nDate: Mon, 05 May 2025 13:31:26 GMT\nConnection: keep-alive\nVia: REDACTED\nX-Content-Type-Options: REDACTED\nAccess-Control-Allow-Origin: REDACTED\nAccess-Control-Expose-Headers: REDACTED\nVary: REDACTED\nStrict-Transport-Security: REDACTED\nContent-Type: application/json; charset=utf-8\nContent-Length: 143\n"

using Microsoft.Extensions.Hosting;
using Azure.Identity;
using Azure.Monitor.Query;
using System.Text.Json;
using TobaHR.Models;
using TobaHR.Services;
using Azure.Core;
using System.Text.Json.Nodes;

var host = Host.CreateDefaultBuilder()
    .ConfigureFunctionsWebApplication() // <-- NEW: ASP.NET Core integration
    .ConfigureServices(services =>
    {
        services.AddLogging();

        _ = services.AddSingleton<LogAnalyticsSettings>(provider =>
        {
            var json = JsonNode.Parse("{\"WorkspaceId\":\"x\"}");
            //var json = Environment.GetEnvironmentVariable("LogAnalyticsConfig");
            //if (string.IsNullOrWhiteSpace(json))
            //{
                //throw new InvalidOperationException("Missing LogAnalyticsConfig environment variable.");
            //}
            return JsonSerializer.Deserialize<LogAnalyticsSettings>(json) ?? throw new InvalidOperationException("Invalid LogAnalyticsConfig JSON.");
        });

        services.AddSingleton(new LogsQueryClient(new DefaultAzureCredential()));
        services.AddSingleton<TokenCredential>(new DefaultAzureCredential());
        services.AddSingleton<ILogAnalyticsService, LogAnalyticsService>();
        services.AddSingleton<IDatabaseService, DatabaseService>();
    })
    .Build();

host.Run();


public class LogAnalyticsService(LogsQueryClient queryClient, LogAnalyticsSettings settings, ILogger<LogAnalyticsService> logger) : ILogAnalyticsService
    {
        private readonly LogsQueryClient _queryClient = queryClient;
        private readonly LogAnalyticsSettings _settings = settings;
        private readonly ILogger<LogAnalyticsService> _logger = logger;

        public async Task<LogsQueryResult> QueryLogsAsync(string kql, DateTimeOffset startTime, DateTimeOffset endTime)
        {
            var timeRange = new QueryTimeRange(startTime, endTime);
            _logger.LogInformation("Fetching query {kql} from {startTime} untill {endTime}", kql, startTime, endTime);
            return await _queryClient.QueryWorkspaceAsync(_settings.WorkspaceId, kql, timeRange);
        }
    }

Azure Functions
Azure Functions
An Azure service that provides an event-driven serverless compute platform.
5,741 questions
{count} votes

2 answers

Sort by: Most helpful
  1. RithwikBojja 2,165 Reputation points Microsoft External Staff Moderator
    2025-05-06T06:56:31.1633333+00:00

    Hi @Maxime Callebaut ,

    I am able to get the table using below code In Azure Function App after deployment:

    Program.cs:

    using Azure.Identity;
    using Azure.Monitor.Query;
    using Azure.Monitor.Query.Models;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Logging;
    using System.Text.Json;
    
    var rith = Host.CreateDefaultBuilder()
        .ConfigureFunctionsWebApplication()
        .ConfigureAppConfiguration((context, config) =>
        {
            config.SetBasePath(context.HostingEnvironment.ContentRootPath)
                .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                .AddEnvironmentVariables();
        })
        .ConfigureServices(services =>
        {
            services.AddLogging();
    
            services.AddSingleton<LogAnalyticsSettings>(provider =>
            {
                var json = Environment.GetEnvironmentVariable("LogAnalyticsConfig");
                if (string.IsNullOrWhiteSpace(json))
                {
                    throw new InvalidOperationException("Missing LogAnalyticsConfig environment variable.");
                }
    
                return JsonSerializer.Deserialize<LogAnalyticsSettings>(json)
                       ?? throw new InvalidOperationException("Invalid LogAnalyticsConfig JSON.");
            });
    
            var credential = new DefaultAzureCredential();
    
            services.AddSingleton(new LogsQueryClient(credential));
            services.AddSingleton<ILogAnalyticsService, LogAnalyticsService>();
            services.AddSingleton<IDatabaseService, DatabaseService>();
        })
        .Build();
    
    rith.Run();
    public interface ILogAnalyticsService
    {
        Task<LogsQueryResult> QueryLogsAsync(string kql, DateTimeOffset startTime, DateTimeOffset endTime);
    }
    
    public interface IDatabaseService
    {
    }
    
    public class LogAnalyticsSettings
    {
        public string WorkspaceId { get; set; } = string.Empty;
    }
    
    public class LogAnalyticsService : ILogAnalyticsService
    {
        private readonly LogsQueryClient _queryClient;
        private readonly LogAnalyticsSettings _settings;
        private readonly ILogger<LogAnalyticsService> _logger;
    
        public LogAnalyticsService(LogsQueryClient queryClient, LogAnalyticsSettings settings, ILogger<LogAnalyticsService> logger)
        {
            _queryClient = queryClient;
            _settings = settings;
            _logger = logger;
        }
    
        public async Task<LogsQueryResult> QueryLogsAsync(string kql, DateTimeOffset startTime, DateTimeOffset endTime)
        {
            var timeRange = new QueryTimeRange(startTime, endTime);
            _logger.LogInformation("Running KQL: {Kql} from {StartTime} to {EndTime}", kql, startTime, endTime);
            return await _queryClient.QueryWorkspaceAsync(_settings.WorkspaceId, kql, timeRange);
        }
    }
    
    public class DatabaseService : IDatabaseService
    {
    }
    

    Function.cs:

    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Azure.Functions.Worker;
    using Microsoft.Extensions.Logging;
    
    
    namespace FunctionApp15
    {
        public class Function1
        {
            private readonly ILogger<Function1> ri_lg;
            private readonly ILogAnalyticsService _logAnalyticsService;
    
            public Function1(ILogger<Function1> logger, ILogAnalyticsService logAnalyticsService)
            {
                ri_lg = logger;
                _logAnalyticsService = logAnalyticsService;
            }
    
            [Function("Function1")]
            public async Task<IActionResult> Run(
                [HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequest req)
            {
                ri_lg.LogInformation("Function1 triggered to query Log Analytics.");
                string kql = "AppTraces | limit 10";
    
                try
                {
                    var result = await _logAnalyticsService.QueryLogsAsync(
                        kql,
                        DateTimeOffset.UtcNow.AddDays(-1),
                        DateTimeOffset.UtcNow);
    
    \                var table = result.Table;
                    var rows = table.Rows.Select(row =>
                    {
                        var dict = new Dictionary<string, object>();
                        for (int i = 0; i < table.Columns.Count; i++)
                        {
                            dict[table.Columns[i].Name] = row[i];
                        }
                        return dict;
                    });
    
                    return new OkObjectResult(rows);
                }
                catch (Exception ex)
                {
                    ri_lg.LogError(ex, "Failed to query Log Analytics.");
                    return new ObjectResult("Error querying logs.") { StatusCode = 500 };
                }
            }
        }
    }
    
    

    csproj:

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        <AzureFunctionsVersion>v4</AzureFunctionsVersion>
        <OutputType>Exe</OutputType>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
      </PropertyGroup>
      <ItemGroup>
        <FrameworkReference Include="Microsoft.AspNetCore.App" />
        <PackageReference Include="Azure.Identity" Version="1.13.2" />
        <PackageReference Include="Azure.Monitor.Query" Version="1.6.0" />
        <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.0.0" />
        <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.2.0" />
        <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.0.0" />
        <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.0" />
      </ItemGroup>
      <ItemGroup>
        <None Update="host.json">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </None>
        <None Update="local.settings.json">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
          <CopyToPublishDirectory>Never</CopyToPublishDirectory>
        </None>
      </ItemGroup>
      <ItemGroup>
        <Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext" />
      </ItemGroup>
    </Project>
    

    After Deploying In Function App added the below environment variable in Settings Section in Environment variables as below:

     {
        "name": "LogAnalyticsConfig",
        "value": "{\"WorkspaceId\": \"59ca9hhhhb583c79\"}",
        "slotSetting": false
      }
    

    Screenshot 2025-05-06 121655

    Then Enabled Managed Identity as below:

    enter image description here

    Then given Monitoring Contributor Role to the managed identity of the function app in Log analytics Workspace:

    enter image description here

    Output:

    enter image description here

    If this answer was helpful, please click "Accept the answer" and mark Yes, as this can help other community members.

    enter image description here

    If you have any other questions or are still experiencing issues, feel free to ask in the "comments" section, and I'd be happy to help.

    1 person found this answer helpful.

  2. Ranashekar Guda 1,360 Reputation points Microsoft External Staff Moderator
    2025-05-05T22:21:39.4933333+00:00

    Hello @Maxime Callebaut,

    The error message, "The workspace could not be found," indicates that your Azure Function is unable to locate the specified Log Analytics workspace. To resolve this issue, start by verifying that the Workspace ID used in your code is correct and matches the ID of the intended Log Analytics workspace. Although the function has a system-assigned identity with Log Analytics Contributor permissions, ensure this identity has been granted access to that specific workspace.

    If you are using environment variables to configure the Workspace ID, double-check that they are correctly set in the Azure Function’s application settings. Also, be aware that deployment configurations might differ from your local settings, so confirm that all required settings are properly configured in the Azure environment. Additionally, verify that you are using the correct API version currently v1 for any requests to the Log Analytics API.

    I hope this helps resolve your issue. Feel free to reach out if you have further concerns.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.