Compare commits
6 Commits
4137ac9ad8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d183716f95 | |||
|
|
a099b77bf4 | ||
| cd9682379d | |||
| 1e9c269f07 | |||
| 8efe02cc86 | |||
| 62a4a47373 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,10 +1,14 @@
|
|||||||
# ---> VisualStudioCode
|
# ---> VisualStudioCode
|
||||||
|
.vscode
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/settings.json
|
!.vscode/settings.json
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
!.vscode/*.code-snippets
|
!.vscode/*.code-snippets
|
||||||
|
.idea/*
|
||||||
|
|
||||||
|
launchsettings.json
|
||||||
|
|
||||||
# Local History for Visual Studio Code
|
# Local History for Visual Studio Code
|
||||||
.history/
|
.history/
|
||||||
|
|||||||
75
Azure.Endpoints/ResourceGroup/ActivityLogReader.cs
Normal file
75
Azure.Endpoints/ResourceGroup/ActivityLogReader.cs
Normal file
@@ -0,0 +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;
|
||||||
|
|
||||||
|
public interface IActivityLogReader
|
||||||
|
{
|
||||||
|
Task ScanAppAsync(string appName, CancellationToken cancellationToken, QueryTimeRange? givenTimeRange);
|
||||||
|
Task ScanAllAsync(CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ActivityLogReader : IActivityLogReader
|
||||||
|
{
|
||||||
|
private readonly ILogger<ActivityLogReader> _logger;
|
||||||
|
private readonly ArmClient _armClient;
|
||||||
|
private readonly LogsQueryClient _logsQueryClient;
|
||||||
|
|
||||||
|
public ActivityLogReader(ILogger<ActivityLogReader> 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");
|
||||||
|
await ScanLogsForAppActivityAsync();
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Task canceled");
|
||||||
|
}
|
||||||
|
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<SubscriptionResource> GetDefaultSubscriptionAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sub = await _armClient.GetDefaultSubscriptionAsync(cancellationToken);
|
||||||
|
|
||||||
|
|
||||||
|
return sub;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to retrieve default subscription");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace ProperDI.Azure.Endpoints.ResourceGroup.Models;
|
||||||
|
|
||||||
|
public record AppActivitySummary(string AppName, string PermissionsExercised);
|
||||||
3
Azure.Endpoints/ResourceGroup/Models/TimeRange.cs
Normal file
3
Azure.Endpoints/ResourceGroup/Models/TimeRange.cs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
namespace ProperDI.Azure.Endpoints.ResourceGroup.Models;
|
||||||
|
|
||||||
|
public record TimeRange(DateTime Start, DateTime End);
|
||||||
87
BackgroundService/BackgroundService.cs
Normal file
87
BackgroundService/BackgroundService.cs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using ProperDI.Azure.Endpoints.ResourceGroup;
|
||||||
|
|
||||||
|
public class RoleAssesorBackgroundService : IHostedService, IDisposable
|
||||||
|
{
|
||||||
|
private readonly IActivityLogReader _activityLogReader;
|
||||||
|
private readonly ILogger<RoleAssesorBackgroundService> _logger;
|
||||||
|
private readonly IOptionsMonitor<RoleAssesorBackgroundServiceOptions> _options;
|
||||||
|
private Timer? _timer;
|
||||||
|
private int _runFrequencyInMinutes;
|
||||||
|
|
||||||
|
public RoleAssesorBackgroundService(
|
||||||
|
IActivityLogReader activityLogReader,
|
||||||
|
ILogger<RoleAssesorBackgroundService> logger,
|
||||||
|
IOptionsMonitor<RoleAssesorBackgroundServiceOptions> options)
|
||||||
|
{
|
||||||
|
_activityLogReader = activityLogReader;
|
||||||
|
_logger = logger;
|
||||||
|
_options = options;
|
||||||
|
|
||||||
|
// This sets our initial run frequency
|
||||||
|
_runFrequencyInMinutes = _options.CurrentValue.RunFrequencyInMinutes;
|
||||||
|
|
||||||
|
// Subscribe to any changes in the config file
|
||||||
|
_options.OnChange(UpdateTimerInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateTimerInterval(RoleAssesorBackgroundServiceOptions options)
|
||||||
|
{
|
||||||
|
var newFreq = options.RunFrequencyInMinutes;
|
||||||
|
|
||||||
|
if (newFreq == _runFrequencyInMinutes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_runFrequencyInMinutes = newFreq;
|
||||||
|
_logger.LogInformation("Run frequency updated to {RunFreq} minutes", _runFrequencyInMinutes);
|
||||||
|
_timer?.Change(TimeSpan.Zero, TimeSpan.FromMinutes(_runFrequencyInMinutes));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Role Assessor background service is starting.");
|
||||||
|
|
||||||
|
_timer = new Timer(async _ => await ProcessAsync(cancellationToken), null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ProcessAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Role Assessor background service is running at: {time}", DateTimeOffset.Now);
|
||||||
|
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await _activityLogReader.ScanAppAsync("Test", cancellationToken, null);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Operation was canceled");
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "An error occurred while reading the logs");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Stopping the Role Assessor background service.");
|
||||||
|
|
||||||
|
_timer?.Change(Timeout.Infinite, 0);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
_timer?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
4
BackgroundService/BackgroundServiceOptions.cs
Normal file
4
BackgroundService/BackgroundServiceOptions.cs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
public class RoleAssesorBackgroundServiceOptions
|
||||||
|
{
|
||||||
|
public int RunFrequencyInMinutes { get; set; } = 5; // Defaults to 5 minute.
|
||||||
|
}
|
||||||
30
Program.cs
Normal file
30
Program.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using Azure.Identity;
|
||||||
|
using Microsoft.Extensions.Azure;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using ProperDI.Azure.Endpoints.ResourceGroup;
|
||||||
|
|
||||||
|
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
|
||||||
|
|
||||||
|
builder.Services.AddHttpClient();
|
||||||
|
builder.Services.AddTransient<IActivityLogReader, ActivityLogReader>();
|
||||||
|
builder.Services.AddAzureClients(clientBuilder =>
|
||||||
|
{
|
||||||
|
// var creds = new ClientSecretCredential(
|
||||||
|
// "",
|
||||||
|
// "",
|
||||||
|
// "");
|
||||||
|
clientBuilder.UseCredential(new DefaultAzureCredential());
|
||||||
|
clientBuilder.AddArmClient(builder.Configuration.GetSection("Am95DevSubscriptionId"));
|
||||||
|
clientBuilder.ConfigureDefaults(client =>
|
||||||
|
{
|
||||||
|
client.Retry.MaxRetries = 3;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.Configure<RoleAssesorBackgroundServiceOptions>(builder.Configuration.GetSection("RoleAssessorBackgroundService"));
|
||||||
|
builder.Services.AddHostedService<RoleAssesorBackgroundService>();
|
||||||
|
|
||||||
|
var host = builder.Build();
|
||||||
|
|
||||||
|
await host.RunAsync();
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
# Sentinel
|
# Sentinel
|
||||||
|
|
||||||
Watches over the land and smites and rogue roles!
|
Watches over the land and smites any rogue roles!
|
||||||
21
Sentinel.csproj
Normal file
21
Sentinel.csproj
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Azure.Identity" Version="1.12.1" />
|
||||||
|
<PackageReference Include="Azure.Monitor.Query" Version="1.5.0" />
|
||||||
|
<PackageReference Include="Azure.ResourceManager" Version="1.13.0" />
|
||||||
|
<PackageReference Include="Azure.ResourceManager.Resources" Version="1.9.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.7.6" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
25
Sentinel.sln
Normal file
25
Sentinel.sln
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.11.35327.3
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentinel", "Sentinel.csproj", "{FA937A3C-5191-4DAC-AC62-EBF7EE90BD1F}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{FA937A3C-5191-4DAC-AC62-EBF7EE90BD1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{FA937A3C-5191-4DAC-AC62-EBF7EE90BD1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{FA937A3C-5191-4DAC-AC62-EBF7EE90BD1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FA937A3C-5191-4DAC-AC62-EBF7EE90BD1F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {79EE9B52-D46E-4FBB-8310-32FD5835CF1D}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
0
Utilities/TimerExtensions.cs
Normal file
0
Utilities/TimerExtensions.cs
Normal file
22
appsettings.json
Normal file
22
appsettings.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information",
|
||||||
|
"System.Net": "Warning",
|
||||||
|
"Azure.Core": "Warning",
|
||||||
|
"Azure.Identity": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"RoleAssessorBackgroundService": {
|
||||||
|
"RunFrequencyInMinutes": 12
|
||||||
|
},
|
||||||
|
"Am95DevSubscriptionId": "040698c2-a013-45be-b1fa-e975f46b9d63",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user