Closed gagwithgaffer closed 1 year ago
Got it working, below is a summary of how I did it:
Note in my environment, I'm using a user assigned managed identity (not system assigned managed identity) although I expect the process is pretty much the same.
First Step - Once creating an SignalR resource in Azure, I went to the Access Control (IAM) blade for the SignalR resource and added myself and my chosen user assigned managed identity. The Role assignment required for the two users is "SignalR App Server" (MS docs covers some info on this configuration --> https://learn.microsoft.com/en-us/azure/azure-signalr/signalr-howto-authorize-application)
App Code Configuration (using .NET Core 7)
appsettings.json file:
"AzureSignalR": {
"ServiceEndpoint": "https://myservicename-azuresignalr.service.signalr.net"
}
Program.cs file
// Add the Background Services
builder.Services.AddHostedService<AzureSignalRService>();
I then created a .NET Background service which I use to initiate the HubContext, I shoved this background service file into a class library so my other backend server apps can enjoy the same configuration:
/// <summary>
/// See https://github.com/aspnet/AzureSignalR-samples/tree/main/samples/Management/MessagePublisher
/// See https://learn.microsoft.com/en-us/azure/azure-signalr/signalr-howto-authorize-application
/// </summary>
public class AzureSignalRService : BackgroundService
{
private ServiceHubContext? _hubContext;
private readonly string? _transportType;
private readonly string? _serviceEndpoint;
private readonly IConfiguration _configuration;
public readonly ILogExtensionRepo _logExtensionRepo;
private ServiceTransportType ServiceTransportType { get; set; }
public static ServiceHubContext? EventsMonitorHubContext { get; private set; }
public AzureSignalRService(
IConfiguration configuration,
ILogExtensionRepo logExtensionRepo)
{
_configuration = configuration;
_logExtensionRepo = logExtensionRepo;
_serviceEndpoint = _configuration.GetSection("AzureSignalR").GetValue<string>("ServiceEndpoint");
_transportType = _configuration.GetSection("AzureSignalR").GetValue<string>("TransportType");
}
private static string? LogData { get; set; }
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
EventsMonitorHubContext = await InitAsync("EventsMonitorHub");
}
private async Task<ServiceHubContext> InitAsync(string hubName)
{
if (string.IsNullOrEmpty(_transportType))
{
// Set the default transport type
ServiceTransportType = ServiceTransportType.Persistent;
}
else
{
// Set the default transport type from configuration.
ServiceTransportType = Enum.Parse<ServiceTransportType>(_transportType, true);
}
// Create a service manager instance.
var serviceManager = new ServiceManagerBuilder().WithOptions(option =>
{
//option.ConnectionString = _connectionString;
option.ServiceEndpoints = new ServiceEndpoint[]
{
//// Note: this is just a demonstration of how to set options.Endpoints
//// Having ConnectionStrings explicitly set inside the code is not encouraged
//// You can fetch it from a safe place such as Azure KeyVault or use ManagedIdentityCredential
//new ServiceEndpoint("<ConnectionString0>"),
//new ServiceEndpoint("<ConnectionString1>", type: EndpointType.Primary, name: "east-region-a"),
//new ServiceEndpoint("<ConnectionString2>", type: EndpointType.Primary, name: "east-region-b"),
//new ServiceEndpoint("<ConnectionString3>", type: EndpointType.Secondary, name: "backup"),
// USE THIS ONE!
new ServiceEndpoint(new Uri(_serviceEndpoint!), new DefaultAzureCredential()),
};
option.ServiceTransportType = ServiceTransportType;
option.UseJsonObjectSerializer(new JsonObjectSerializer(new JsonSerializerOptions
{
// VERY IMPORTANT --> Set to null otherwise Azure SignalR will
// automatically set the first character to lowercase for each
// log event property when serializing the string for transportation.
// See https://github.com/Azure/azure-signalr/blob/dev/docs/advanced-topics/json-object-serializer.md
PropertyNamingPolicy = null
}));
})
// Uncomment the following line to get more logs
//.WithLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole()))
.BuildServiceManager();
try
{
_hubContext = await serviceManager.CreateHubContextAsync(hubName, default);
return _hubContext;
}
catch (Exception ex)
{
LogData = "My-API encountered an exception when initializing a SignalR Hub with name [" + hubName + "]. SignalR returned an error with message: [" + ex.Message + "]";
await _logExtensionRepo.WriteLogEvent<AzureSignalRService>("Error", "SignalR Service", "Initialize Hub Connection", hubName!, LogData, ex);
return null!;
}
}
}
The final step is sending a message to SignalR. I first check that the Hub Context is alive before sending the message to SignalR, given some of my apps may not require the SignalR service.
// Send the messageto SignalR Hub.
if (AzureSignalRService.EventsMonitorHubContext != null)
{
await AzureSignalRService.EventsMonitorHubContext!.Clients.Group("allEvents").SendAsync("Receive", "allEvents", le);
await AzureSignalRService.EventsMonitorHubContext!.Clients.Group(groupName).SendAsync("Receive", groupName, le);
}
Hello,
I am wanting to move away from using any private keys for connections to Azure services in my app, and equally want to avoid using Azure Key Vault. Managed Identity is the preferred solution. I cant find any code samples for how to include the ManagedIdentityCredential.
Does the code sample below look correct for my requirements?