I am trying to send logs from my application to an Azure Log Analytics Workspace, in order to do that I develop the following code based on what I found in https://docs.microsoft.com/en-us/azure/azure-monitor/logs/data-collector-api
using maintenance.messaging;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace maintenance.dataaccessobjects
{
public class LogAnalyticsWorkspaceDAO
{
private static LogAnalyticsWorkspaceDAO _Instance { get; set; }
private String WorkspaceId { get; set; } = AzureKeyVaultDAO.Instance.GetSecret("WorkspaceId"); //Get WorkspaceId from KeyVault
private String SharedKey { get; set; } = AzureKeyVaultDAO.Instance.GetSecret("SharedKey"); //Get SharedKey from KeyVault
private String ApiVersion { get; set; } = AzureKeyVaultDAO.Instance.GetSecret("LAWApiVersion"); //Get API Version from KeyVault 2016-04-01
private String LogType { get; set; } = AzureKeyVaultDAO.Instance.GetSecret("LogType"); //Get LogType from KeyVault ApplicationLog
private LogAnalyticsWorkspaceDAO()
{
}
public static LogAnalyticsWorkspaceDAO Instance
{
get
{
if (_Instance == null)
{
_Instance = new LogAnalyticsWorkspaceDAO();
}
return _Instance;
}
}
private string GetSignature(String Method, Int32 ContentLength, String ContentType, DateTime Date, String Resource)
{
string Message = $"{Method}\n{ContentLength}\n{ContentType}\nx-ms-date:{Date}\n{Resource}";
byte[] Bytes = Encoding.UTF8.GetBytes(Message);
HMACSHA256 Encryptor = new HMACSHA256(Convert.FromBase64String(SharedKey));
return $"SharedKey {WorkspaceId}:{Convert.ToBase64String(Encryptor.ComputeHash(Bytes))}";
}
public async Task<String> Post(String Message)
{
DateTime Date = DateTime.UtcNow;
Dictionary<String, String> Headers = new Dictionary<String, String>();
MessageSender MessageSender = new MessageSender(new Uri($"https://{WorkspaceId}.ods.opinsights.azure.com/api/logs?api-version={ApiVersion}"));
Headers.Add("Method", "POST");
Headers.Add("Log-Type", LogType);
Headers.Add("x-ms-date", Date.ToString("r"));
Headers.Add("Authorization", GetSignature("POST", Message.Length, "application/json", Date, "/api/logs"));
return await MessageSender.Post(MessageSender.Message(Headers, Message));
}
}
}
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace maintenance.messaging
{
public class MessageSender : IDisposable
{
private readonly HttpClient Client;
private Uri Url { get; set; }
public MessageSender(Uri Url)
{
this.Client = new HttpClient();
this.Url = Url;
}
public HttpRequestMessage Message(Dictionary<String, String> Headers, String Message)
{
HttpRequestMessage Request = new HttpRequestMessage(HttpMethod.Post, this.Url);
Request.Content = new StringContent(Message, Encoding.UTF8, "application/json");
foreach (KeyValuePair<String, String> Header in Headers)
{
Request.Headers.Add(Header.Key, Header.Value);
}
return Request;
}
public async Task<String> Post(HttpRequestMessage Request)
{
HttpResponseMessage Response = await Client.SendAsync(Request);
Response.EnsureSuccessStatusCode();
return await Response.Content.ReadAsStringAsync();
}
public void Dispose()
{
Client?.Dispose();
}
}
}
However I always fall under a 403 Forbiden, I guess the error should be in the Authorization header (Signature generation)
Do you know what I am missing? I tried looking for other signature generations but didn't find anything new
I may be wrong, but as far as I can see SharedKey is not Base64 encoded, so I just try with
HMACSHA256 Encryptor = new HMACSHA256(Encoding.UTF8.GetBytes(SharedKey));
But get the same error 403 Forbidden