From cd9682379d03977a2b37453d4b23705f6d705ff2 Mon Sep 17 00:00:00 2001 From: Robby Date: Sun, 13 Oct 2024 20:49:50 -0500 Subject: [PATCH] v3 --- .gitignore | 3 + .../ResourceGroup/ActivityLogReader.cs | 64 +++++++++++++---- .../Models/AppActivitySummary.cs | 3 + .../ResourceGroup/Models/TimeRange.cs | 3 + BackgroundService/BackgroundService.cs | 4 +- Program.cs | 68 +++++++++++++------ Sentinel.csproj | 1 + Utilities/TimerExtensions.cs | 0 appsettings.json | 11 ++- 9 files changed, 118 insertions(+), 39 deletions(-) create mode 100644 Azure.Endpoints/ResourceGroup/Models/AppActivitySummary.cs create mode 100644 Azure.Endpoints/ResourceGroup/Models/TimeRange.cs create mode 100644 Utilities/TimerExtensions.cs diff --git a/.gitignore b/.gitignore index 94837e5..e7cae7f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # ---> VisualStudioCode +.vscode .vscode/* !.vscode/settings.json !.vscode/tasks.json @@ -7,6 +8,8 @@ !.vscode/*.code-snippets .idea/* +launchsettings.json + # Local History for Visual Studio Code .history/ diff --git a/Azure.Endpoints/ResourceGroup/ActivityLogReader.cs b/Azure.Endpoints/ResourceGroup/ActivityLogReader.cs index 4789e8c..9d7bb7d 100644 --- a/Azure.Endpoints/ResourceGroup/ActivityLogReader.cs +++ b/Azure.Endpoints/ResourceGroup/ActivityLogReader.cs @@ -1,37 +1,75 @@ +using Azure; +using Azure.Monitor.Query; +using Azure.Monitor.Query.Models; using Azure.ResourceManager; +using Azure.ResourceManager.Resources; using Microsoft.Extensions.Logging; +using ProperDI.Azure.Endpoints.ResourceGroup.Models; -namespace ProperDI.Azure.Endpoints.ResourceGroup.LogLooker; +namespace ProperDI.Azure.Endpoints.ResourceGroup; public interface IActivityLogReader { - Task Scan(CancellationToken cancellationToken); + Task ScanAppAsync(string appName, CancellationToken cancellationToken, QueryTimeRange? givenTimeRange); + Task ScanAllAsync(CancellationToken cancellationToken); } -public class ActivityLogReader( - ILogger logger, - ArmClient armClient) - : IActivityLogReader +public class ActivityLogReader : IActivityLogReader { - private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - private readonly ArmClient _armClient = armClient ?? throw new ArgumentNullException(nameof(armClient)); + private readonly ILogger _logger; + private readonly ArmClient _armClient; + private readonly LogsQueryClient _logsQueryClient; - public async Task Scan(CancellationToken cancellationToken) + public ActivityLogReader(ILogger logger, ArmClient armClient, LogsQueryClient logsQueryClient) { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _armClient = armClient ?? throw new ArgumentNullException(nameof(armClient)); + _logsQueryClient = logsQueryClient ?? throw new ArgumentNullException(nameof(logsQueryClient)); + } + + public async Task ScanAppAsync(string appName, CancellationToken cancellationToken, QueryTimeRange? givenTimeRange = default) + { + var timeRange = givenTimeRange ?? new QueryTimeRange(DateTime.UtcNow.AddDays(-7), DateTime.UtcNow); + try { _logger.LogInformation("Scanning for resources"); - var resp = await _armClient.GetDefaultSubscriptionAsync(cancellationToken); - _logger.LogInformation("Found default subscription {sub}", resp.Data.DisplayName); + await ScanLogsForAppActivityAsync(); } catch (TaskCanceledException) { _logger.LogWarning("Task canceled"); } - catch (System.Exception ex) + catch (Exception ex) { _logger.LogError(ex, "Http request failed"); throw; } - } + } + + public async Task ScanAllAsync(CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public async Task ScanLogsForAppActivityAsync() + { + } + + + private async Task GetDefaultSubscriptionAsync(CancellationToken cancellationToken) + { + try + { + var sub = await _armClient.GetDefaultSubscriptionAsync(cancellationToken); + + + return sub; + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to retrieve default subscription"); + throw; + } + } } \ No newline at end of file diff --git a/Azure.Endpoints/ResourceGroup/Models/AppActivitySummary.cs b/Azure.Endpoints/ResourceGroup/Models/AppActivitySummary.cs new file mode 100644 index 0000000..2cfb142 --- /dev/null +++ b/Azure.Endpoints/ResourceGroup/Models/AppActivitySummary.cs @@ -0,0 +1,3 @@ +namespace ProperDI.Azure.Endpoints.ResourceGroup.Models; + +public record AppActivitySummary(string AppName, string PermissionsExercised); diff --git a/Azure.Endpoints/ResourceGroup/Models/TimeRange.cs b/Azure.Endpoints/ResourceGroup/Models/TimeRange.cs new file mode 100644 index 0000000..4f07ee7 --- /dev/null +++ b/Azure.Endpoints/ResourceGroup/Models/TimeRange.cs @@ -0,0 +1,3 @@ +namespace ProperDI.Azure.Endpoints.ResourceGroup.Models; + +public record TimeRange(DateTime Start, DateTime End); \ No newline at end of file diff --git a/BackgroundService/BackgroundService.cs b/BackgroundService/BackgroundService.cs index 8a20f9f..59d7254 100644 --- a/BackgroundService/BackgroundService.cs +++ b/BackgroundService/BackgroundService.cs @@ -1,7 +1,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using ProperDI.Azure.Endpoints.ResourceGroup.LogLooker; +using ProperDI.Azure.Endpoints.ResourceGroup; public class RoleAssesorBackgroundService : IHostedService, IDisposable { @@ -57,7 +57,7 @@ public class RoleAssesorBackgroundService : IHostedService, IDisposable if (cancellationToken.IsCancellationRequested) return; - await _activityLogReader.Scan(cancellationToken); + await _activityLogReader.ScanAppAsync("Test", cancellationToken, null); } catch (OperationCanceledException) { diff --git a/Program.cs b/Program.cs index 2b1d5f8..6e778f6 100644 --- a/Program.cs +++ b/Program.cs @@ -1,32 +1,56 @@ using Azure.Identity; -using Azure.ResourceManager; -using Azure.ResourceManager.Resources; using Microsoft.Extensions.Azure; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using ProperDI.Azure.Endpoints.ResourceGroup.LogLooker; +using Microsoft.Extensions.Configuration; +using ProperDI.Azure.Endpoints.ResourceGroup; -HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); - -builder.Services.AddHttpClient(); -builder.Services.AddTransient(); -builder.Services.AddAzureClients(clientBuilder => +public class Program { - // var creds = new ClientSecretCredential( - // "", - // "", - // ""); - clientBuilder.UseCredential(new DefaultAzureCredential()); - clientBuilder.AddArmClient("2690a5f1-155b-4fa8-896f-92c6bcb62bee"); - clientBuilder.ConfigureDefaults(client => + public static async Task Main(string[] args) { - client.Retry.MaxRetries = 3; - }); -}); + var host = CreateHostBuilder(args).Build(); + await host.RunAsync(); + } -builder.Services.Configure(builder.Configuration.GetSection("RoleAssessorBackgroundService")); -builder.Services.AddHostedService(); + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.AddEnvironmentVariables(); + + // Optionally, you can add JSON configuration if needed + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); + // config.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true); + }) + .ConfigureServices((hostContext, services) => + { + services.AddHttpClient(); + services.AddTransient(); -var host = builder.Build(); + services.AddAzureClients(clientBuilder => + { + var configuration = hostContext.Configuration; + + // Read Azure credentials from environment variables + var tenantId = configuration["AzureTenantId"]; + var clientId = configuration["AzureClientId"]; + var clientSecret = configuration["AzureClientSecret"]; + var subscriptionId = configuration["AzureSubscriptionId"]; + System.Console.WriteLine($"Tenant id: {tenantId}\nClientID: {clientId}\nClientSecret: {clientSecret}"); + var credential = new ClientSecretCredential(tenantId, clientId, clientSecret); -await host.RunAsync(); \ No newline at end of file + clientBuilder.UseCredential(credential); + clientBuilder.AddArmClient(subscriptionId); + clientBuilder.ConfigureDefaults(client => + { + client.Retry.MaxRetries = 3; + }); + clientBuilder.AddLogsQueryClient(); + }); + + services.Configure( + hostContext.Configuration.GetSection("RoleAssessorBackgroundService")); + services.AddHostedService(); + }); +} \ No newline at end of file diff --git a/Sentinel.csproj b/Sentinel.csproj index 825b962..bba5992 100644 --- a/Sentinel.csproj +++ b/Sentinel.csproj @@ -9,6 +9,7 @@ + diff --git a/Utilities/TimerExtensions.cs b/Utilities/TimerExtensions.cs new file mode 100644 index 0000000..e69de29 diff --git a/appsettings.json b/appsettings.json index dbba275..fb0de21 100644 --- a/appsettings.json +++ b/appsettings.json @@ -4,11 +4,18 @@ "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information", - "System.Net": "Warning" + "System.Net": "Warning", + "Azure.Core": "Warning", + "Azure.Identity": "Warning" } }, "RoleAssessorBackgroundService": { "RunFrequencyInMinutes": 12 }, - "DefaultSubscriptionId": "2690a5f1-155b-4fa8-896f-92c6bcb62bee" + "DefaultSubscriptionId": "2690a5f1-155b-4fa8-896f-92c6bcb62bee", + "ASPNETCORE_ENVIRONMENT": "Development", + "AzureSubscriptionId": "2690a5f1-155b-4fa8-896f-92c6bcb62bee", + "AzureTenantId": "11ffe5d3-5e72-4414-8dfd-1eb209497679", + "AzureClientId": "d0870778-24dd-4c87-a449-2c36674035d1", + "AzureClientSecret": "EWX8Q~jpfKab8hB1TzujorzKIeJEV5NKEfCuOaFC" } \ No newline at end of file