From 4b55d4e0c3cff9267012b715f8a3cacce2748370 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:34:19 +0000 Subject: [PATCH 1/7] Initial plan From 68e9f2da95cbef5df3cd0ca3075930bf820acaac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:37:15 +0000 Subject: [PATCH 2/7] Initial plan for Azure Application Insights integration Co-authored-by: BenjaminMichaelis <22186029+BenjaminMichaelis@users.noreply.github.com> --- dotnet-sdk.tar.gz | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 dotnet-sdk.tar.gz diff --git a/dotnet-sdk.tar.gz b/dotnet-sdk.tar.gz new file mode 100644 index 00000000..e69de29b From d76b7b1f3bea38b6e82a356520c708730589b3fe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:39:22 +0000 Subject: [PATCH 3/7] Add Azure Application Insights packages and basic configuration Co-authored-by: BenjaminMichaelis <22186029+BenjaminMichaelis@users.noreply.github.com> --- Directory.Packages.props | 2 ++ .../EssentialCSharp.Web.csproj | 2 ++ EssentialCSharp.Web/Program.cs | 20 ++++++++++++------- dotnet-sdk.tar.gz | 0 4 files changed, 17 insertions(+), 7 deletions(-) delete mode 100644 dotnet-sdk.tar.gz diff --git a/Directory.Packages.props b/Directory.Packages.props index 083c26db..182f7e19 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -18,6 +18,8 @@ + + diff --git a/EssentialCSharp.Web/EssentialCSharp.Web.csproj b/EssentialCSharp.Web/EssentialCSharp.Web.csproj index cb8f0cd1..4cb85e02 100644 --- a/EssentialCSharp.Web/EssentialCSharp.Web.csproj +++ b/EssentialCSharp.Web/EssentialCSharp.Web.csproj @@ -15,6 +15,7 @@ + @@ -22,6 +23,7 @@ + diff --git a/EssentialCSharp.Web/Program.cs b/EssentialCSharp.Web/Program.cs index 134c57be..dad309cf 100644 --- a/EssentialCSharp.Web/Program.cs +++ b/EssentialCSharp.Web/Program.cs @@ -1,10 +1,11 @@ -using EssentialCSharp.Web.Areas.Identity.Data; -using EssentialCSharp.Web.Areas.Identity.Services.PasswordValidators; +using EssentialCSharp.Web.Areas.Identity.Data; +using EssentialCSharp.Web.Areas.Identity.Services.PasswordValidators; using EssentialCSharp.Web.Data; -using EssentialCSharp.Web.Extensions; -using EssentialCSharp.Web.Middleware; +using EssentialCSharp.Web.Extensions; +using EssentialCSharp.Web.Middleware; using EssentialCSharp.Web.Services; -using EssentialCSharp.Web.Services.Referrals; +using EssentialCSharp.Web.Services.Referrals; +using Azure.Monitor.OpenTelemetry.AspNetCore; using Mailjet.Client; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Identity; @@ -34,8 +35,13 @@ private static void Main(string[] args) ConfigurationManager configuration = builder.Configuration; string connectionString = builder.Configuration.GetConnectionString("EssentialCSharpWebContextConnection") ?? throw new InvalidOperationException("Connection string 'EssentialCSharpWebContextConnection' not found."); - builder.Logging.AddConsole(); - builder.Services.AddHealthChecks(); + builder.Logging.AddConsole(); + builder.Services.AddHealthChecks(); + + // Configure Azure Application Insights with OpenTelemetry + builder.Services.AddOpenTelemetry().UseAzureMonitor(); + builder.Services.AddApplicationInsightsTelemetry(); + builder.Services.AddServiceProfiler(); builder.Services.AddDbContext(options => options.UseSqlServer(connectionString)); builder.Services.AddDefaultIdentity(options => diff --git a/dotnet-sdk.tar.gz b/dotnet-sdk.tar.gz deleted file mode 100644 index e69de29b..00000000 From 41069c1fb2a20998e0472a59e41aa755dfc863b4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:44:32 +0000 Subject: [PATCH 4/7] Complete Azure Application Insights integration with tests and documentation Co-authored-by: BenjaminMichaelis <22186029+BenjaminMichaelis@users.noreply.github.com> --- .../ApplicationInsightsTests.cs | 31 +++++++++++++++++++ README.md | 3 +- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 EssentialCSharp.Web.Tests/ApplicationInsightsTests.cs diff --git a/EssentialCSharp.Web.Tests/ApplicationInsightsTests.cs b/EssentialCSharp.Web.Tests/ApplicationInsightsTests.cs new file mode 100644 index 00000000..9b70b9f5 --- /dev/null +++ b/EssentialCSharp.Web.Tests/ApplicationInsightsTests.cs @@ -0,0 +1,31 @@ +using Microsoft.ApplicationInsights.Extensibility; +using Microsoft.Extensions.DependencyInjection; + +namespace EssentialCSharp.Web.Tests; + +public class ApplicationInsightsTests +{ + [Fact] + public void WhenTheApplicationStarts_ApplicationInsightsIsRegistered() + { + using WebApplicationFactory factory = new(); + + // Verify that Application Insights services are registered + var services = factory.Services; + + // Check if TelemetryConfiguration is registered (core Application Insights service) + var telemetryConfiguration = services.GetService(); + Assert.NotNull(telemetryConfiguration); + } + + [Fact] + public async Task WhenTheApplicationStarts_HealthCheckIsAvailable() + { + using WebApplicationFactory factory = new(); + + HttpClient client = factory.CreateClient(); + using HttpResponseMessage response = await client.GetAsync("/healthz"); + + Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode); + } +} \ No newline at end of file diff --git a/README.md b/README.md index c06bf130..13a5319b 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ For any bugs, questions, or anything else with specifically the code found insid ## What You Will Need - [Visual Studio](https://visualstudio.microsoft.com/) (or your preferred IDE) -- [.NET 8.0 SDK](https://dotnet.microsoft.com/download) +- [.NET 9.0 SDK](https://dotnet.microsoft.com/download) - If you already have .NET installed you can check the version by typing `dotnet --info` into cmd to make sure you have the right version ## Startup Steps @@ -35,6 +35,7 @@ Authentication:github:clientSecret = anotherimportantclientsecret Authentication:github:clientId = anotherimportantclientid HCaptcha:SiteKey = captchaSiteKey HCaptcha:SecretKey = captchaSecretKey +APPLICATIONINSIGHTS_CONNECTION_STRING = "InstrumentationKey=your-instrumentation-key-here;IngestionEndpoint=https://region.in.applicationinsights.azure.com/;LiveEndpoint=https://region.livediagnostics.monitor.azure.com/" Testing Secret Values: Some Value Secrets for Testing/Development Purposes: From 0ff60433ce5c5c4b8081b726cfaf5501443be838 Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Thu, 26 Jun 2025 19:29:37 -0700 Subject: [PATCH 5/7] Conditionally configures Azure Monitor Ensures Azure Application Insights and Service Profiler are only configured in non-development environments to avoid conflicts and resource consumption during local development. --- EssentialCSharp.Web/Program.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/EssentialCSharp.Web/Program.cs b/EssentialCSharp.Web/Program.cs index dad309cf..10d524ec 100644 --- a/EssentialCSharp.Web/Program.cs +++ b/EssentialCSharp.Web/Program.cs @@ -1,3 +1,4 @@ +using Azure.Monitor.OpenTelemetry.AspNetCore; using EssentialCSharp.Web.Areas.Identity.Data; using EssentialCSharp.Web.Areas.Identity.Services.PasswordValidators; using EssentialCSharp.Web.Data; @@ -5,7 +6,6 @@ using EssentialCSharp.Web.Middleware; using EssentialCSharp.Web.Services; using EssentialCSharp.Web.Services.Referrals; -using Azure.Monitor.OpenTelemetry.AspNetCore; using Mailjet.Client; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Identity; @@ -37,11 +37,14 @@ private static void Main(string[] args) builder.Logging.AddConsole(); builder.Services.AddHealthChecks(); - - // Configure Azure Application Insights with OpenTelemetry - builder.Services.AddOpenTelemetry().UseAzureMonitor(); - builder.Services.AddApplicationInsightsTelemetry(); - builder.Services.AddServiceProfiler(); + + if (!builder.Environment.IsDevelopment()) + { + // Configure Azure Application Insights with OpenTelemetry + builder.Services.AddOpenTelemetry().UseAzureMonitor(); + builder.Services.AddApplicationInsightsTelemetry(); + builder.Services.AddServiceProfiler(); + } builder.Services.AddDbContext(options => options.UseSqlServer(connectionString)); builder.Services.AddDefaultIdentity(options => From 3d6de459a4f97392dcf895b6e2d38360c7ea0ed5 Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Thu, 26 Jun 2025 19:34:47 -0700 Subject: [PATCH 6/7] Adds Application Insights connection string Adds the Application Insights connection string to the container app configuration for both staging and production environments. This allows collecting telemetry data from the application. --- .github/workflows/Build-Test-And-Deploy.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/Build-Test-And-Deploy.yml b/.github/workflows/Build-Test-And-Deploy.yml index 5bd54e25..3375413b 100644 --- a/.github/workflows/Build-Test-And-Deploy.yml +++ b/.github/workflows/Build-Test-And-Deploy.yml @@ -146,11 +146,12 @@ jobs: msft-clientsecret=keyvaultref:$KEYVAULTURI/secrets/authentication-microsoft-clientsecret,identityref:$MANAGEDIDENTITYID emailsender-apikey=keyvaultref:$KEYVAULTURI/secrets/authmessagesender-apikey,identityref:$MANAGEDIDENTITYID \ emailsender-secret=keyvaultref:$KEYVAULTURI/secrets/authmessagesender-secretkey,identityref:$MANAGEDIDENTITYID emailsender-name=keyvaultref:$KEYVAULTURI/secrets/authmessagesender-sendfromname,identityref:$MANAGEDIDENTITYID \ emailsender-email=keyvaultref:$KEYVAULTURI/secrets/authmessagesender-sendfromemail,identityref:$MANAGEDIDENTITYID connectionstring=keyvaultref:$KEYVAULTURI/secrets/connectionstrings-essentialcsharpwebcontextconnection,identityref:$MANAGEDIDENTITYID \ - captcha-sitekey=keyvaultref:$KEYVAULTURI/secrets/captcha-sitekey,identityref:$MANAGEDIDENTITYID captcha-secretkey=keyvaultref:$KEYVAULTURI/secrets/captcha-secretkey,identityref:$MANAGEDIDENTITYID + captcha-sitekey=keyvaultref:$KEYVAULTURI/secrets/captcha-sitekey,identityref:$MANAGEDIDENTITYID captcha-secretkey=keyvaultref:$KEYVAULTURI/secrets/captcha-secretkey,identityref:$MANAGEDIDENTITYID \ + appinsights-connectionstring=keyvaultref:$KEYVAULTURI/secrets/applicationinsights-connectionstring,identityref:$MANAGEDIDENTITYID az containerapp update --name $CONTAINER_APP_NAME --resource-group $RESOURCEGROUP --replace-env-vars Authentication__github__clientId=secretref:github-clientid Authentication__github__clientSecret=secretref:github-clientsecret \ Authentication__microsoft__clientId=secretref:msft-clientid Authentication__microsoft__clientSecret=secretref:msft-clientsecret AuthMessageSender__ApiKey=secretref:emailsender-apikey AuthMessageSender__SecretKey=secretref:emailsender-secret \ AuthMessageSender__SendFromName=secretref:emailsender-name AuthMessageSender__SendFromEmail=secretref:emailsender-email ConnectionStrings__EssentialCSharpWebContextConnection=secretref:connectionstring ASPNETCORE_ENVIRONMENT=Staging \ - AZURE_CLIENT_ID=$AZURECLIENTID HCaptcha__SiteKey=secretref:captcha-sitekey HCaptcha__SecretKey=secretref:captcha-secretkey + AZURE_CLIENT_ID=$AZURECLIENTID HCaptcha__SiteKey=secretref:captcha-sitekey HCaptcha__SecretKey=secretref:captcha-secretkey ApplicationInsights__ConnectionString=secretref:appinsights-connectionstring - name: Logout of Azure CLI if: "always()" @@ -231,11 +232,12 @@ jobs: msft-clientsecret=keyvaultref:$KEYVAULTURI/secrets/authentication-microsoft-clientsecret,identityref:$MANAGEDIDENTITYID emailsender-apikey=keyvaultref:$KEYVAULTURI/secrets/authmessagesender-apikey,identityref:$MANAGEDIDENTITYID \ emailsender-secret=keyvaultref:$KEYVAULTURI/secrets/authmessagesender-secretkey,identityref:$MANAGEDIDENTITYID emailsender-name=keyvaultref:$KEYVAULTURI/secrets/authmessagesender-sendfromname,identityref:$MANAGEDIDENTITYID \ emailsender-email=keyvaultref:$KEYVAULTURI/secrets/authmessagesender-sendfromemail,identityref:$MANAGEDIDENTITYID connectionstring=keyvaultref:$KEYVAULTURI/secrets/connectionstrings-essentialcsharpwebcontextconnection,identityref:$MANAGEDIDENTITYID \ - captcha-sitekey=keyvaultref:$KEYVAULTURI/secrets/captcha-sitekey,identityref:$MANAGEDIDENTITYID captcha-secretkey=keyvaultref:$KEYVAULTURI/secrets/captcha-secretkey,identityref:$MANAGEDIDENTITYID + captcha-sitekey=keyvaultref:$KEYVAULTURI/secrets/captcha-sitekey,identityref:$MANAGEDIDENTITYID captcha-secretkey=keyvaultref:$KEYVAULTURI/secrets/captcha-secretkey,identityref:$MANAGEDIDENTITYID \ + appinsights-connectionstring=keyvaultref:$KEYVAULTURI/secrets/applicationinsights-connectionstring,identityref:$MANAGEDIDENTITYID az containerapp update --name $CONTAINER_APP_NAME --resource-group $RESOURCEGROUP --replace-env-vars Authentication__github__clientId=secretref:github-clientid Authentication__github__clientSecret=secretref:github-clientsecret \ Authentication__microsoft__clientId=secretref:msft-clientid Authentication__microsoft__clientSecret=secretref:msft-clientsecret AuthMessageSender__ApiKey=secretref:emailsender-apikey AuthMessageSender__SecretKey=secretref:emailsender-secret \ AuthMessageSender__SendFromName=secretref:emailsender-name AuthMessageSender__SendFromEmail=secretref:emailsender-email ConnectionStrings__EssentialCSharpWebContextConnection=secretref:connectionstring ASPNETCORE_ENVIRONMENT=Production \ - AZURE_CLIENT_ID=$AZURECLIENTID HCaptcha__SiteKey=secretref:captcha-sitekey HCaptcha__SecretKey=secretref:captcha-secretkey + AZURE_CLIENT_ID=$AZURECLIENTID HCaptcha__SiteKey=secretref:captcha-sitekey HCaptcha__SecretKey=secretref:captcha-secretkey ApplicationInsights__ConnectionString=secretref:appinsights-connectionstring - name: Logout of Azure CLI if: "always()" From 906d9929d3d727270e7b84009fac75a5ad76180b Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Thu, 26 Jun 2025 19:37:14 -0700 Subject: [PATCH 7/7] Removes Application Insights tests Removes the Application Insights tests, which appear unnecessary at this stage. Adds a functional test for the health check endpoint. --- .../ApplicationInsightsTests.cs | 31 ------------------- EssentialCSharp.Web.Tests/FunctionalTests.cs | 3 +- 2 files changed, 2 insertions(+), 32 deletions(-) delete mode 100644 EssentialCSharp.Web.Tests/ApplicationInsightsTests.cs diff --git a/EssentialCSharp.Web.Tests/ApplicationInsightsTests.cs b/EssentialCSharp.Web.Tests/ApplicationInsightsTests.cs deleted file mode 100644 index 9b70b9f5..00000000 --- a/EssentialCSharp.Web.Tests/ApplicationInsightsTests.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.ApplicationInsights.Extensibility; -using Microsoft.Extensions.DependencyInjection; - -namespace EssentialCSharp.Web.Tests; - -public class ApplicationInsightsTests -{ - [Fact] - public void WhenTheApplicationStarts_ApplicationInsightsIsRegistered() - { - using WebApplicationFactory factory = new(); - - // Verify that Application Insights services are registered - var services = factory.Services; - - // Check if TelemetryConfiguration is registered (core Application Insights service) - var telemetryConfiguration = services.GetService(); - Assert.NotNull(telemetryConfiguration); - } - - [Fact] - public async Task WhenTheApplicationStarts_HealthCheckIsAvailable() - { - using WebApplicationFactory factory = new(); - - HttpClient client = factory.CreateClient(); - using HttpResponseMessage response = await client.GetAsync("/healthz"); - - Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode); - } -} \ No newline at end of file diff --git a/EssentialCSharp.Web.Tests/FunctionalTests.cs b/EssentialCSharp.Web.Tests/FunctionalTests.cs index a989643d..18b7d75d 100644 --- a/EssentialCSharp.Web.Tests/FunctionalTests.cs +++ b/EssentialCSharp.Web.Tests/FunctionalTests.cs @@ -1,4 +1,4 @@ -using System.Net; +using System.Net; namespace EssentialCSharp.Web.Tests; @@ -9,6 +9,7 @@ public class FunctionalTests [InlineData("/hello-world")] [InlineData("/hello-world#hello-world")] [InlineData("/guidelines")] + [InlineData("/healthz")] public async Task WhenTheApplicationStarts_ItCanLoadLoadPages(string relativeUrl) { using WebApplicationFactory factory = new();