diff --git a/src/HealthChecks.Azure.Messaging.EventGrid/AzureEventGridHealthCheck.cs b/src/HealthChecks.Azure.Messaging.EventGrid/AzureEventGridHealthCheck.cs
new file mode 100644
index 0000000000..00d16bc502
--- /dev/null
+++ b/src/HealthChecks.Azure.Messaging.EventGrid/AzureEventGridHealthCheck.cs
@@ -0,0 +1,33 @@
+namespace HealthChecks.Azure.Messaging.EventGrid;
+
+public sealed class AzureEventGridHealthCheck : IHealthCheck
+{
+ private readonly EventGridPublisherClient _client;
+
+ public AzureEventGridHealthCheck(EventGridPublisherClient client)
+ {
+ _client = Guard.ThrowIfNull(client);
+ }
+
+ ///
+ public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
+ {
+ try
+ {
+ // Send a ping event to verify connectivity
+ var healthCheckEvent = new EventGridEvent(
+ "HealthCheck",
+ "HealthCheck.Ping",
+ "1.0",
+ new { Timestamp = DateTimeOffset.UtcNow });
+
+ await _client.SendEventAsync(healthCheckEvent, cancellationToken).ConfigureAwait(false);
+
+ return HealthCheckResult.Healthy();
+ }
+ catch (Exception ex)
+ {
+ return new HealthCheckResult(context.Registration.FailureStatus, exception: ex);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/HealthChecks.Azure.Messaging.EventGrid/DependencyInjection/AzureEventGridHealthChecksBuilderExtensions.cs b/src/HealthChecks.Azure.Messaging.EventGrid/DependencyInjection/AzureEventGridHealthChecksBuilderExtensions.cs
new file mode 100644
index 0000000000..8b697d006d
--- /dev/null
+++ b/src/HealthChecks.Azure.Messaging.EventGrid/DependencyInjection/AzureEventGridHealthChecksBuilderExtensions.cs
@@ -0,0 +1,41 @@
+namespace Microsoft.Extensions.DependencyInjection;
+
+///
+/// Extension methods to configure .
+///
+public static class AzureEventGridHealthChecksBuilderExtensions
+{
+ private const string NAME = "azure_event_grid";
+
+ ///
+ /// Add a health check for Azure Event Grid by registering for given .
+ ///
+ /// The to add to.
+ ///
+ /// An optional factory to obtain instance.
+ /// When not provided, is simply resolved from .
+ ///
+ /// The health check name. Optional. If null the name 'azure_event_grid' will be used.
+ ///
+ /// The that should be reported when the health check fails. Optional. If null then
+ /// the default status of will be reported.
+ ///
+ /// A list of tags that can be used to filter sets of health checks. Optional.
+ /// An optional representing the timeout of the check.
+ /// The specified .
+ public static IHealthChecksBuilder AddAzureEventGrid(
+ this IHealthChecksBuilder builder,
+ Func? clientFactory = default,
+ string? name = NAME,
+ HealthStatus? failureStatus = default,
+ IEnumerable? tags = default,
+ TimeSpan? timeout = default)
+ {
+ return builder.Add(new HealthCheckRegistration(
+ name ?? NAME,
+ sp => new AzureEventGridHealthCheck(clientFactory?.Invoke(sp) ?? sp.GetRequiredService()),
+ failureStatus,
+ tags,
+ timeout));
+ }
+}
\ No newline at end of file
diff --git a/src/HealthChecks.Azure.Messaging.EventGrid/GlobalUsings.cs b/src/HealthChecks.Azure.Messaging.EventGrid/GlobalUsings.cs
new file mode 100644
index 0000000000..ae825a6edd
--- /dev/null
+++ b/src/HealthChecks.Azure.Messaging.EventGrid/GlobalUsings.cs
@@ -0,0 +1,9 @@
+global using System;
+global using System.Threading;
+global using System.Threading.Tasks;
+global using System.Runtime.CompilerServices;
+
+global using Azure.Messaging.EventGrid;
+
+global using Microsoft.Extensions.DependencyInjection;
+global using Microsoft.Extensions.Diagnostics.HealthChecks;
\ No newline at end of file
diff --git a/src/HealthChecks.Azure.Messaging.EventGrid/HealthChecks.Azure.Messaging.EventGrid.csproj b/src/HealthChecks.Azure.Messaging.EventGrid/HealthChecks.Azure.Messaging.EventGrid.csproj
new file mode 100644
index 0000000000..7894d00b26
--- /dev/null
+++ b/src/HealthChecks.Azure.Messaging.EventGrid/HealthChecks.Azure.Messaging.EventGrid.csproj
@@ -0,0 +1,16 @@
+
+
+
+ net6.0;net7.0;net8.0
+ $(PackageTags);Azure;EventGrid
+ HealthChecks.Azure.Messaging.EventGrid is the health check package for Azure Event Grid.
+ enable
+ enable
+
+
+
+
+
+
+
+]]>
\ No newline at end of file
diff --git a/src/HealthChecks.Azure.Messaging.EventGrid/README.md b/src/HealthChecks.Azure.Messaging.EventGrid/README.md
new file mode 100644
index 0000000000..1d4cf64877
--- /dev/null
+++ b/src/HealthChecks.Azure.Messaging.EventGrid/README.md
@@ -0,0 +1,51 @@
+# Azure Event Grid Health Check
+
+This health check verifies the ability to communicate with [Azure Event Grid](https://azure.microsoft.com/services/event-grid/). It uses the provided [EventGridPublisherClient](https://learn.microsoft.com/dotnet/api/azure.messaging.eventgrid.eventgridpublisherclient) to send a ping event to verify connectivity.
+
+## Implementation
+
+The health check makes an actual call to Event Grid by sending a simple ping event. This verifies that:
+1. The client is properly configured
+2. The connection to Event Grid service is working
+3. The topic exists and is accessible
+4. The credentials are valid and not expired
+
+## Setup
+
+```csharp
+public void ConfigureServices(IServiceCollection services)
+{
+ // Register the EventGridPublisherClient
+ services.AddSingleton(sp =>
+ {
+ return new EventGridPublisherClient(
+ new Uri("https://"),
+ new AzureKeyCredential(""));
+ });
+
+ services
+ .AddHealthChecks()
+ .AddAzureEventGrid();
+}
+```
+
+You can also register the health check by providing a factory method for the client:
+
+```csharp
+services
+ .AddHealthChecks()
+ .AddAzureEventGrid(sp =>
+ {
+ return new EventGridPublisherClient(
+ new Uri("https://"),
+ new AzureKeyCredential(""));
+ });
+```
+
+## Parameters
+
+- `clientFactory`: An optional factory to obtain the EventGridPublisherClient instance. When not provided, EventGridPublisherClient is resolved from IServiceProvider.
+- `name`: The health check name. Optional. If null the name 'azure_event_grid' will be used.
+- `failureStatus`: The HealthStatus that should be reported when the health check fails. Optional. If null then the default status of HealthStatus.Unhealthy will be reported.
+- `tags`: A list of tags that can be used to filter sets of health checks.
+- `timeout`: An optional TimeSpan representing the timeout of the check.
\ No newline at end of file
diff --git a/src/HealthChecks.Azure.Messaging.EventGrid/Utils/Guard.cs b/src/HealthChecks.Azure.Messaging.EventGrid/Utils/Guard.cs
new file mode 100644
index 0000000000..2e300abc4d
--- /dev/null
+++ b/src/HealthChecks.Azure.Messaging.EventGrid/Utils/Guard.cs
@@ -0,0 +1,15 @@
+namespace HealthChecks.Azure.Messaging.EventGrid;
+
+internal static class Guard
+{
+ public static T ThrowIfNull(T value, [CallerArgumentExpression(nameof(value))] string? parameterName = null)
+ where T : class
+ {
+ if (value is null)
+ {
+ throw new ArgumentNullException(parameterName);
+ }
+
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/test/HealthChecks.Azure.Messaging.EventGrid.Tests/EventGridConformanceTests.cs b/test/HealthChecks.Azure.Messaging.EventGrid.Tests/EventGridConformanceTests.cs
new file mode 100644
index 0000000000..ebb78b1d72
--- /dev/null
+++ b/test/HealthChecks.Azure.Messaging.EventGrid.Tests/EventGridConformanceTests.cs
@@ -0,0 +1,24 @@
+using Azure.Identity;
+using Azure.Messaging.EventGrid;
+
+namespace HealthChecks.Azure.Messaging.EventGrid.Tests;
+
+public class EventGridConformanceTests : ConformanceTests
+{
+ protected override IHealthChecksBuilder AddHealthCheck(IHealthChecksBuilder builder, Func? clientFactory = null, Func? optionsFactory = null, string? healthCheckName = null, HealthStatus? failureStatus = null, IEnumerable? tags = null, TimeSpan? timeout = null)
+ => builder.AddAzureEventGrid(clientFactory, healthCheckName, failureStatus, tags, timeout);
+
+ protected override EventGridPublisherClient CreateClientForNonExistingEndpoint()
+ => new(new Uri("https://non-existing-topic.region.eventgrid.azure.net"), new AzureCliCredential());
+
+ protected override AzureEventGridHealthCheck CreateHealthCheck(EventGridPublisherClient client, UnusedOptions? options)
+ => new(client);
+
+ protected override UnusedOptions CreateHealthCheckOptions()
+ => new();
+}
+
+// AzureEventGridHealthCheck does not use any options, the type exists only to meet ConformanceTests<,,> criteria
+public sealed class UnusedOptions
+{
+}
\ No newline at end of file
diff --git a/test/HealthChecks.Azure.Messaging.EventGrid.Tests/GlobalUsings.cs b/test/HealthChecks.Azure.Messaging.EventGrid.Tests/GlobalUsings.cs
new file mode 100644
index 0000000000..7bbea44071
--- /dev/null
+++ b/test/HealthChecks.Azure.Messaging.EventGrid.Tests/GlobalUsings.cs
@@ -0,0 +1,9 @@
+global using System;
+global using System.Threading;
+global using System.Threading.Tasks;
+global using System.Collections.Generic;
+
+global using Microsoft.Extensions.DependencyInjection;
+global using Microsoft.Extensions.Diagnostics.HealthChecks;
+
+global using HealthChecks.Tests.Common;
\ No newline at end of file
diff --git a/test/HealthChecks.Azure.Messaging.EventGrid.Tests/HealthChecks.Azure.Messaging.EventGrid.Tests.csproj b/test/HealthChecks.Azure.Messaging.EventGrid.Tests/HealthChecks.Azure.Messaging.EventGrid.Tests.csproj
new file mode 100644
index 0000000000..2bf300e582
--- /dev/null
+++ b/test/HealthChecks.Azure.Messaging.EventGrid.Tests/HealthChecks.Azure.Messaging.EventGrid.Tests.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net8.0
+ false
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+]]>
\ No newline at end of file
diff --git a/test/HealthChecks.Azure.Messaging.EventGrid.Tests/HealthChecks.Azure.Messaging.EventGrid.approved.txt b/test/HealthChecks.Azure.Messaging.EventGrid.Tests/HealthChecks.Azure.Messaging.EventGrid.approved.txt
new file mode 100644
index 0000000000..1ef74225b2
--- /dev/null
+++ b/test/HealthChecks.Azure.Messaging.EventGrid.Tests/HealthChecks.Azure.Messaging.EventGrid.approved.txt
@@ -0,0 +1,15 @@
+namespace HealthChecks.Azure.Messaging.EventGrid
+{
+ public sealed class AzureEventGridHealthCheck : Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck
+ {
+ public AzureEventGridHealthCheck(Azure.Messaging.EventGrid.EventGridPublisherClient client) { }
+ public System.Threading.Tasks.Task CheckHealthAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckContext context, System.Threading.CancellationToken cancellationToken = default) { }
+ }
+}
+namespace Microsoft.Extensions.DependencyInjection
+{
+ public static class AzureEventGridHealthChecksBuilderExtensions
+ {
+ public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAzureEventGrid(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, System.Func? clientFactory = null, string? name = "azure_event_grid", Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default, System.Collections.Generic.IEnumerable? tags = null, System.TimeSpan? timeout = default) { }
+ }
+}
\ No newline at end of file