create Sentinel API .Net core sample (#1274)

* add Sentinel API sample

* Update launch.json

* Update ActionPayload.json

* Update BookmarkPayload.json

* Update IncidentPayload.json

* Update ScheduledAlertRulePayload.json

* Update SecurityAlertRulePayload.json

* Update SavedSearchPayload.json

* Update DataConnectorPayload.json

* Update FusionAlertRulePayload.json

* Update IncidentCommentPayload.json

* Update README.md

Co-authored-by: ngchi <ngchi@DESKTOP-5TA29SV>
Co-authored-by: Preeti Krishna <preetikr@microsoft.com>
This commit is contained in:
Chi Nguyen 2020-11-10 17:38:10 -08:00 коммит произвёл GitHub
Родитель 6ab2bfe4d0
Коммит a892ab8781
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
77 изменённых файлов: 6276 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,2 @@
.idea
.vs

Просмотреть файл

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30204.135
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureSentinel_ManagementAPI", "AzureSentinel_ManagementAPI\AzureSentinel_ManagementAPI.csproj", "{F5D25CF4-7FA5-43C3-A14A-28C8816701BF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F5D25CF4-7FA5-43C3-A14A-28C8816701BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F5D25CF4-7FA5-43C3-A14A-28C8816701BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5D25CF4-7FA5-43C3-A14A-28C8816701BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F5D25CF4-7FA5-43C3-A14A-28C8816701BF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {540DF26E-3A66-49E5-8560-20C3006303AE}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,366 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
#appsettings.json
../.idea
../.vs

Просмотреть файл

@ -0,0 +1,22 @@
{
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/netcoreapp3.1/AzureSentinel_ManagementAPI.dll",
"args": [],
"cwd": "${workspaceFolder}",
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}

Просмотреть файл

@ -0,0 +1,42 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/AzureSentinel_ManagementAPI.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/AzureSentinel_ManagementAPI.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"${workspaceFolder}/AzureSentinel_ManagementAPI.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}

Просмотреть файл

@ -0,0 +1,266 @@
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using AzureSentinel_ManagementAPI.Actions.Models;
using AzureSentinel_ManagementAPI.Infrastructure.Authentication;
using AzureSentinel_ManagementAPI.Infrastructure.Configuration;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace AzureSentinel_ManagementAPI.Actions
{
public class ActionsController
{
private readonly AzureSentinelApiConfiguration[] azureConfigs;
private readonly AuthenticationService authenticationService;
private bool cliMode;
public ActionsController(
AzureSentinelApiConfiguration[] azureConfig,
IConfigurationRoot rawConfig,
AuthenticationService authenticationService)
{
azureConfigs = azureConfig;
this.authenticationService = authenticationService;
cliMode = rawConfig.GetValue<bool>("Climode");
}
/// <summary>
/// Create action from payload json
/// </summary>
/// <param name="ruleId"></param>
/// <returns></returns>
public async Task CreateAction(string ruleId, int insId, string logicAppResourceId = "")
{
ActionRequestPayload[] actions = Utils.LoadPayload<ActionRequestPayload[]>("ActionPayload.json", cliMode);
foreach (ActionRequestPayload payload in actions)
{
try
{
string subscription = azureConfigs[insId].SubscriptionId;
string resourceGroup = azureConfigs[insId].ResourceGroupName;
payload.PropertiesPayload.LogicAppResourceId = string.Format(payload.PropertiesPayload.LogicAppResourceId, subscription, resourceGroup);
string actionId = Guid.NewGuid().ToString();
string url = $"{azureConfigs[insId].BaseUrl}/alertRules/{ruleId}/actions/{actionId}?api-version={azureConfigs[insId].ApiVersion}";
if (!string.IsNullOrEmpty(logicAppResourceId))
{
payload.PropertiesPayload.LogicAppResourceId = logicAppResourceId;
}
string serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var request = new HttpRequestMessage(HttpMethod.Put, url)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var res = await response.Content.ReadAsStringAsync();
Console.WriteLine(JToken.Parse(res).ToString(Formatting.Indented));
continue;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
}
/// <summary>
/// Delete an action by rule id and action id
/// </summary>
/// <param name="ruleId"></param>
/// <param name="actionId"></param>
/// <returns></returns>
public async Task<string> DeleteAction(string ruleId, string actionId, int insId)
{
try
{
var url = $"{azureConfigs[insId].BaseUrl}/alertRules/{ruleId}/actions/{actionId}?api-version={azureConfigs[insId].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Delete, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
if (response.StatusCode == HttpStatusCode.NotFound)
throw new Exception("Not found, please create a new Action first...");
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Get an action by rule id and action id
/// </summary>
/// <param name="ruleId"></param>
/// <param name="actionId"></param>
/// <returns></returns>
public async Task<string> GetActionById(string ruleId, string actionId, int insId)
{
try
{
var url = $"{azureConfigs[insId].BaseUrl}/alertRules/{ruleId}/actions/{actionId}?api-version={azureConfigs[insId].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Get an action by rule id and action id by instance
/// </summary>
/// <param name="ruleName"></param>
/// <param name="insId"></param>
/// <returns></returns>
public async Task<string> GetAlertRuleByName(string ruleName, int insId)
{
var url = $"{azureConfigs[insId].BaseUrl}/alertRules/{ruleName}?api-version={azureConfigs[insId].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
/// <summary>
/// Get all actions under a rule
/// </summary>
/// <param name="ruleId"></param>
/// <returns></returns>
public async Task<string> GetActionsByRule(string ruleId, int insId)
{
try
{
await GetAlertRuleByName(ruleId, insId);
var url = $"{azureConfigs[insId].BaseUrl}/alertRules/{ruleId}/actions?api-version={azureConfigs[insId].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string res = await response.Content.ReadAsStringAsync();
JObject result = JsonConvert.DeserializeObject<JObject>(res);
var values = result["value"] as JArray;
if (values == null)
{
values = new JArray();
}
int callTimes = 1;
while (result.ContainsKey("nextLink") && callTimes < 100)
{
try
{
var nextLink = result["nextLink"].ToString();
request = new HttpRequestMessage(HttpMethod.Get, nextLink);
await authenticationService.AuthenticateRequest(request, insId);
var nextResponse = await http.SendAsync(request);
if (nextResponse.IsSuccessStatusCode)
{
var newRes = await nextResponse.Content.ReadAsStringAsync();
JObject newResult = JsonConvert.DeserializeObject<JObject>(newRes);
result = newResult;
var newValues = result["value"] as JArray;
if (newValues == null)
{
newValues = new JArray();
}
foreach (var v in newValues)
{
values.Add(v);
}
callTimes++;
}
else
{
var err = await response.Content.ReadAsStringAsync();
Console.WriteLine("Error calling the nextLink: \n" + err);
break;
}
}
catch (Exception ex)
{
Console.WriteLine("Error in parsing nextLink: \n" + ex.Message);
break;
}
}
Utils.WriteJsonStringToFile($"GetActionsByRule_{azureConfigs[insId].InstanceName}.json", cliMode, JsonConvert.SerializeObject(values, Formatting.Indented), false);
return res;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
}
}

Просмотреть файл

@ -0,0 +1,13 @@
using Newtonsoft.Json;
namespace AzureSentinel_ManagementAPI.Actions.Models
{
public class ActionRequestPayload
{
[JsonProperty("etag")]
public string ETag { get; set; }
[JsonProperty("properties")]
public ActionRequestPropertiesPayload PropertiesPayload { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,8 @@
namespace AzureSentinel_ManagementAPI.Actions.Models
{
public class ActionRequestPropertiesPayload
{
public string LogicAppResourceId { get; set; }
public string TriggerUri { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,112 @@
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using AzureSentinel_ManagementAPI.Infrastructure.Authentication;
using AzureSentinel_ManagementAPI.Infrastructure.Configuration;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace AzureSentinel_ManagementAPI.AlertRuleTemplates
{
public class AlertRuleTemplatesController
{
private readonly AuthenticationService authenticationService;
private readonly AzureSentinelApiConfiguration[] azureConfigs;
private bool cliMode;
public AlertRuleTemplatesController(
AuthenticationService authenticationService,
IConfigurationRoot rawConfig,
AzureSentinelApiConfiguration[] azureConfig)
{
this.authenticationService = authenticationService;
azureConfigs = azureConfig;
cliMode = rawConfig.GetValue<bool>("Climode");
}
/// <summary>
/// Get alert rule templates for all instances or for a single instance
/// </summary>
/// <returns></returns>
public async Task GetAlertRuleTemplates(int insId = -1)
{
if (insId != -1)
{
await GetAlertRuleTemplatesByInstance(insId);
}
else
{
for (var i = 0; i < azureConfigs.Length; i++)
{
await GetAlertRuleTemplatesByInstance(i);
}
}
}
/// <summary>
/// Get alert rule templates for a single instance
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
private async Task GetAlertRuleTemplatesByInstance(int i)
{
try
{
var url = $"{azureConfigs[i].BaseUrl}/alertRuleTemplates?api-version={azureConfigs[i].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string res = await response.Content.ReadAsStringAsync();
Utils.WriteJsonStringToFile($"GetAlertRuleTemplates_{azureConfigs[i].InstanceName}.json", cliMode, res);
Console.WriteLine(JToken.Parse(res).ToString(Formatting.Indented));
return;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception($"Something went wrong on {azureConfigs[i].InstanceName}: \n"
+ ex.Message);
}
}
/// <summary>
/// Get alert rule template by id
/// </summary>
/// <param name="ruleTmplId"></param>
/// <returns></returns>
public async Task<string> GetAlertRuleTemplateById(string ruleTmplId, int insId)
{
try
{
var url = $"{azureConfigs[insId].BaseUrl}/alertRuleTemplates/{ruleTmplId}?api-version={azureConfigs[insId].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
}
}

Просмотреть файл

@ -0,0 +1,464 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Security;
using System.Text;
using System.Threading.Tasks;
using AzureSentinel_ManagementAPI.Actions;
using AzureSentinel_ManagementAPI.AlertRules.Models;
using AzureSentinel_ManagementAPI.Infrastructure.Authentication;
using AzureSentinel_ManagementAPI.Infrastructure.Configuration;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace AzureSentinel_ManagementAPI.AlertRules
{
public class AlertRulesController
{
private readonly AzureSentinelApiConfiguration[] azureConfigs;
private readonly AuthenticationService authenticationService;
private bool cliMode;
public AlertRulesController(AzureSentinelApiConfiguration[] azureConfig,
IConfigurationRoot rawConfig,
AuthenticationService authenticationService)
{
azureConfigs = azureConfig;
this.authenticationService = authenticationService;
cliMode = rawConfig.GetValue<bool>("Climode");
}
/// <summary>
/// Create funsion alert rule for all instances or for a single instance
/// </summary>
/// <returns></returns>
public async Task CreateFusionAlertRule(int insId = -1)
{
if (insId != -1)
{
await CreateFusionAlertRuleByInstance(insId);
}
else
{
for (var i = 0; i < azureConfigs.Length; i++)
{
await CreateFusionAlertRuleByInstance(i);
}
}
}
/// <summary>
/// Create funsion alert rule for a single instance
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public async Task CreateFusionAlertRuleByInstance(int i)
{
var alertRules = Utils.LoadPayload<FusionAlertRulePayload[]>("FusionAlertRulePayload.json", cliMode);
foreach (var payload in alertRules)
{
try
{
var ruleId = Guid.NewGuid().ToString();
var url =
$"{azureConfigs[i].BaseUrl}/alertRules/{ruleId}?api-version={azureConfigs[i].ApiVersion}";
var serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var request = new HttpRequestMessage(HttpMethod.Put, url)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var res = await response.Content.ReadAsStringAsync();
Console.WriteLine(JToken.Parse(res).ToString(Formatting.Indented));
continue;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception($"Something went wrong on {azureConfigs[i].InstanceName}: \n"
+ ex.Message);
}
}
}
/// <summary>
/// Create Microsoft security incident creation alert rule for all instances or for a single instance
/// </summary>
/// <returns></returns>
public async Task CreateMicrosoftSecurityIncidentCreationAlertRule(int insId = -1)
{
if (insId != -1)
{
await CreateMSSecurityIncidentAlertRuleByInstance(insId);
}
else
{
for (var i = 0; i < azureConfigs.Length; i++)
{
await CreateMSSecurityIncidentAlertRuleByInstance(i);
}
}
}
/// <summary>
/// Create Microsoft security incident creation alert rule for a single instance
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public async Task CreateMSSecurityIncidentAlertRuleByInstance(int i)
{
var alertRules = Utils.LoadPayload<SecurityIncidentCreationAlertRulePayload[]>("SecurityAlertRulePayload.json", cliMode);
foreach (var payload in alertRules)
{
try
{
var alertRuleId = Guid.NewGuid().ToString();
var url =
$"{azureConfigs[i].BaseUrl}/alertRules/{alertRuleId}?api-version={azureConfigs[i].ApiVersion}";
var serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var request = new HttpRequestMessage(HttpMethod.Put, url)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var res = await response.Content.ReadAsStringAsync();
Console.WriteLine(JToken.Parse(res).ToString(Formatting.Indented));
continue;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception($"Something went wrong on {azureConfigs[i].InstanceName}: \n"
+ ex.Message);
}
}
}
/// <summary>
/// Create scheduled alert rule for all instances or for a single instance
/// </summary>
/// <returns></returns>
public async Task CreateScheduledAlertRule(ActionsController actionsController, int insId = -1)
{
if (insId != -1)
{
await CreateScheduledAlertRuleByInstance(insId, actionsController);
}
else
{
for (var i = 0; i < azureConfigs.Length; i++)
{
await CreateScheduledAlertRuleByInstance(i, actionsController);
}
}
}
/// <summary>
/// Create scheduled alert rule for a single instance
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public async Task CreateScheduledAlertRuleByInstance(int i, ActionsController actionsController)
{
var alertRules = Utils.LoadPayload<ScheduledAlertRulePayload[]>("ScheduledAlertRulePayload.json", cliMode);
foreach (var payload in alertRules)
{
try
{
var alertRuleId = Guid.NewGuid().ToString();
var url =
$"{azureConfigs[i].BaseUrl}/alertRules/{alertRuleId}?api-version={azureConfigs[i].ApiVersion}";
string playbook = payload.Playbook;
string subscription = azureConfigs[i].SubscriptionId;
string resourceGroup = azureConfigs[i].ResourceGroupName;
playbook = string.Format(playbook, subscription, resourceGroup);
payload.Playbook = null;
var serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var request = new HttpRequestMessage(HttpMethod.Put, url)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var res = await response.Content.ReadAsStringAsync();
Console.WriteLine(JToken.Parse(res).ToString(Formatting.Indented));
if (!string.IsNullOrEmpty(playbook))
{
await actionsController.CreateAction(alertRuleId, i, playbook);
}
continue;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception($"Something went wrong on {azureConfigs[i].InstanceName}: \n"
+ ex.Message);
}
}
}
/// <summary>
/// Delete an alert rule by id
/// </summary>
/// <param name="ruleId"></param>
/// <returns></returns>
public async Task<string> DeleteAlertRule(string ruleId, int insId)
{
try
{
var url =
$"{azureConfigs[insId].BaseUrl}/alertRules/{ruleId}?api-version={azureConfigs[insId].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Delete, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
if (response.StatusCode == HttpStatusCode.NotFound)
throw new Exception("Not found, please create a new Alert first...");
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Get funsion alert rule by id
/// </summary>
/// <param name="ruleId"></param>
/// <returns></returns>
public async Task<string> GetFusionAlertRule(string ruleId, int insId)
{
return await GetAlertRuleByName(ruleId, insId);
}
/// <summary>
/// Get Microsoft security incident creation alert rule by id
/// </summary>
/// <param name="ruleId"></param>
/// <returns></returns>
public async Task<string> GetMicrosoftSecurityIdentityCreationAlertRule(string ruleId, int insId)
{
return await GetAlertRuleByName(ruleId, insId);
}
/// <summary>
/// Get scheduled alert rule by id
/// </summary>
/// <param name="ruleId"></param>
/// <returns></returns>
public async Task<string> GetScheduledAlertRule(string ruleId, int insId)
{
return await GetAlertRuleByName(ruleId, insId);
}
/// <summary>
/// Get alert rule by rule id
/// </summary>
/// <param name="ruleName"></param>
/// <returns></returns>
private async Task<string> GetAlertRuleByName(string ruleName, int insId)
{
try
{
var url = $"{azureConfigs[insId].BaseUrl}/alertRules/{ruleName}?api-version={azureConfigs[insId].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Get all alert rules for all instances or for a single instance
/// </summary>
/// <returns></returns>
public async Task GetAlertRules(int insId = -1)
{
if (insId != -1)
{
await GetAlertRulesByInstance(insId);
}
else
{
for (var i = 0; i < azureConfigs.Length; i++)
{
await GetAlertRulesByInstance(i);
}
}
}
/// <summary>
/// Get alert rules for a single instance
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
private async Task GetAlertRulesByInstance(int i)
{
try
{
var url = $"{azureConfigs[i].BaseUrl}/alertRules?api-version={azureConfigs[i].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string res = await response.Content.ReadAsStringAsync();
JObject result = JsonConvert.DeserializeObject<JObject>(res);
var values = result["value"] as JArray;
if (values == null)
{
values = new JArray();
}
int callTimes = 1;
while (result.ContainsKey("nextLink") && callTimes < 100)
{
try
{
var nextLink = result["nextLink"].ToString();
request = new HttpRequestMessage(HttpMethod.Get, nextLink);
await authenticationService.AuthenticateRequest(request, i);
var nextResponse = await http.SendAsync(request);
if (nextResponse.IsSuccessStatusCode)
{
var newRes = await nextResponse.Content.ReadAsStringAsync();
JObject newResult = JsonConvert.DeserializeObject<JObject>(newRes);
result = newResult;
var newValues = result["value"] as JArray;
if (newValues == null)
{
newValues = new JArray();
}
foreach (var v in newValues)
{
values.Add(v);
}
callTimes++;
}
else
{
var err = await response.Content.ReadAsStringAsync();
Console.WriteLine("Error calling the nextLink: \n" + err);
break;
}
}
catch (Exception ex)
{
Console.WriteLine("Error in parsing nextLink: \n" + ex.Message);
break;
}
}
var formattedRes = JsonConvert.SerializeObject(values, Formatting.Indented);
Utils.WriteJsonStringToFile($"GetAlertRules_{azureConfigs[i].InstanceName}.json", cliMode, formattedRes, false);
Console.WriteLine(formattedRes);
return;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception($"Something went wrong on {azureConfigs[i].InstanceName}: \n"
+ ex.Message);
}
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
namespace AzureSentinel_ManagementAPI.AlertRules.Models
{
public enum AlertRuleKind
{
Scheduled,
MicrosoftSecurityIncidentCreation,
Fusion
}
}

Просмотреть файл

@ -0,0 +1,14 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace AzureSentinel_ManagementAPI.AlertRules.Models
{
public abstract class AlertRulePayload
{
public string Etag { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public AlertRuleKind Kind { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,15 @@
using Newtonsoft.Json;
namespace AzureSentinel_ManagementAPI.AlertRules.Models
{
public class FusionAlertRulePayload : AlertRulePayload
{
public FusionAlertRulePayload()
{
Kind = AlertRuleKind.Fusion;
}
[JsonProperty("properties")]
public FusionAlertRulePropertiesPayload PropertiesPayload { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,8 @@
namespace AzureSentinel_ManagementAPI.AlertRules.Models
{
public class FusionAlertRulePropertiesPayload
{
public string AlertRuleTemplateName { get; set; }
public bool Enabled { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,11 @@
namespace AzureSentinel_ManagementAPI.AlertRules.Models
{
public class ProductFilter
{
public const string MicrosoftCloudAppSecurity = "Microsoft Cloud App Security";
public const string AzureSecurityCenter = "Azure Security Center";
public const string AzureAdvancedThreatProtection = "Azure Advanced Threat Protection";
public const string AzureActiveDirectoryIdentityProtection = "Azure Active Directory Identity Protection";
public const string AzureSecurityCenterForIoT = "Azure Security Center for IoT";
}
}

Просмотреть файл

@ -0,0 +1,17 @@
using Newtonsoft.Json;
namespace AzureSentinel_ManagementAPI.AlertRules.Models
{
public class ScheduledAlertRulePayload : AlertRulePayload
{
public ScheduledAlertRulePayload()
{
Kind = AlertRuleKind.Scheduled;
}
[JsonProperty("properties")]
public ScheduledAlertRulePropertiesPayload PropertiesPayload { get; set; }
public string Playbook { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,26 @@
using AzureSentinel_ManagementAPI.Infrastructure.SharedModels.Enums;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace AzureSentinel_ManagementAPI.AlertRules.Models
{
public class ScheduledAlertRulePropertiesPayload
{
public string Query { get; set; }
public string QueryFrequency { get; set; }
public string QueryPeriod { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public Severity Severity { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public TriggerOperator TriggerOperator { get; set; }
public int TriggerThreshold { get; set; }
public string DisplayName { get; set; }
public bool Enabled { get; set; }
public string SuppressionDuration { get; set; }
public bool SuppressionEnabled { get; set; }
public string Description { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,15 @@
using Newtonsoft.Json;
namespace AzureSentinel_ManagementAPI.AlertRules.Models
{
public class SecurityIncidentCreationAlertRulePayload: AlertRulePayload
{
public SecurityIncidentCreationAlertRulePayload()
{
Kind = AlertRuleKind.MicrosoftSecurityIncidentCreation;
}
[JsonProperty("properties")]
public SecurityIncidentCreationAlertRulePropertiesPayload PropertiesPayload { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,9 @@
namespace AzureSentinel_ManagementAPI.AlertRules.Models
{
public class SecurityIncidentCreationAlertRulePropertiesPayload
{
public string ProductFilter { get; set; }
public string DisplayName { get; set; }
public bool Enabled { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,10 @@
namespace AzureSentinel_ManagementAPI.AlertRules.Models
{
public enum TriggerOperator
{
GreaterThan,
LessThan,
Equal,
NotEqual
}
}

Просмотреть файл

@ -0,0 +1,758 @@
using System;
using System.Threading.Tasks;
using AzureSentinel_ManagementAPI.Actions;
using AzureSentinel_ManagementAPI.AlertRules;
using AzureSentinel_ManagementAPI.AlertRuleTemplates;
using AzureSentinel_ManagementAPI.Bookmarks;
using AzureSentinel_ManagementAPI.DataConnectors;
using AzureSentinel_ManagementAPI.Incidents;
using AzureSentinel_ManagementAPI.IncidentRelation;
using AzureSentinel_ManagementAPI.Infrastructure.Authentication;
using AzureSentinel_ManagementAPI.Infrastructure.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using AzureSentinel_ManagementAPI.Infrastructure.SharedModels;
using Microsoft.Extensions.Configuration;
using AzureSentinel_ManagementAPI.Hunting;
namespace AzureSentinel_ManagementAPI
{
class AppHost
{
private readonly AuthenticationService authenticationService;
private readonly AlertRulesController alertRulesController;
private readonly AlertRuleTemplatesController alertRuleTemplatesController;
private readonly IncidentsController incidentsController;
private readonly ActionsController actionsController;
private readonly BookmarksController bookmarksController;
private readonly DataConnectorsController dataConnectorsController;
private readonly IncidentRelationController incidentRelationController;
private readonly SavedSearchController savedSearchController;
private readonly AzureSentinelApiConfiguration[] configurations;
private List<Tuple<string, int>> cmdArgs;
private bool cliMode;
public AppHost(
IConfigurationRoot rawConfig,
AzureSentinelApiConfiguration[] configurations,
AlertRulesController alertRulesController,
AuthenticationService authenticationService,
AlertRuleTemplatesController alertRuleTemplatesController,
IncidentsController incidentsController,
ActionsController actionsController,
BookmarksController bookmarksController,
DataConnectorsController dataConnectorsController,
IncidentRelationController incidentRelationController,
SavedSearchController savedSearchController)
{
this.configurations = configurations;
this.alertRulesController = alertRulesController;
this.authenticationService = authenticationService;
this.alertRuleTemplatesController = alertRuleTemplatesController;
this.incidentsController = incidentsController;
this.actionsController = actionsController;
this.bookmarksController = bookmarksController;
this.dataConnectorsController = dataConnectorsController;
this.incidentRelationController = incidentRelationController;
this.savedSearchController = savedSearchController;
cliMode = rawConfig.GetValue<bool>("Climode");
string exeName = "AzureSentinel_ManagementAPI.exe";
cmdArgs = new TupleList<string, int>
{
{$": {exeName} 1 <actionRuleId> [instanceId]", 3 },
{$": {exeName} 2 <actionRuleId> <actionId> [instanceId]", 4 },
{$": {exeName} 3 <actionRuleId> <actionId> [instanceId]", 4},
{$": {exeName} 4 <actionRuleId> [instanceId]", 3},
{$": {exeName} 5 <alertRuleTemplateId> [instanceId]", 3},
{$": {exeName} 6 [instanceId]", 2},
{$": {exeName} 7 [instanceId]", 2},
{$": {exeName} 8 [instanceId]", 2},
{$": {exeName} 9 [instanceId]", 2},
{$": {exeName} 10 <actionRuleId> [instanceId]", 3},
{$": {exeName} 11 [instanceId]", 2},
{$": {exeName} 12 <fusionRuleId> [instanceId]", 3},
{$": {exeName} 13 <securityRuleId> [instanceId]", 3},
{$": {exeName} 14 <scheduledRuleId> [instanceId]", 3},
{$": {exeName} 15 [instanceId]", 2},
{$": {exeName} 16 <bookmarkId> [instanceId]", 3},
{$": {exeName} 17 <bookmarkId> [instanceId]", 3},
{$": {exeName} 18 [instanceId]", 2},
{$": {exeName} 19 [instanceId]", 2},
{$": {exeName} 20 <bookmarkId> [instanceId]", 3},
{$": {exeName} 21 [instanceId]", 2},
{$": {exeName} 22 [instanceId]", 2},
{$": {exeName} 23 <incidentId> [instanceId]", 3},
{$": {exeName} 24 <incidentId> [instanceId]", 3},
{$": {exeName} 25 [instanceId]", 2},
{$": {exeName} 26 <incidentId> [instanceId]", 3},
{$": {exeName} 27 [instanceId]", 2},
{$": {exeName} 28 <incidentId> [instanceId]", 3},
{$": {exeName} 29 <incidentId> [instanceId]", 3},
{$": {exeName} 30 <incidentId> <commentId> [instanceId]", 4},
{$": {exeName} 31 <incidentId> <bookmarkId> [instanceId]", 4},
{$": {exeName} 32 <incidentId> <relationId> [instanceId]", 4},
{$": {exeName} 33 <incidentId> [instanceId]", 3},
{$": {exeName} 34 <incidentId> <relationId> [instanceId]", 4},
{$": {exeName} 35 <incidentId> [instanceId]", 3},
};
}
private void CommandInvalidFormatError()
{
Console.WriteLine("invalid command line");
PrintCLIMenu();
throw new Exception("invalid command line");
}
public async Task Run(string[] args)
{
if (cliMode)
{
if (args.Length < 1)
{
CommandInvalidFormatError();
}
string option = args[0];
bool isValid = int.TryParse(option, out var index);
isValid = isValid && index > 0 && index < 36;
if (!isValid)
{
CommandInvalidFormatError();
}
if (args.Length < cmdArgs[index - 1].Item2 - 1)
{
CommandInvalidFormatError();
}
int insId = args.Length == cmdArgs[index - 1].Item2 - 1 ?
0 : int.Parse(args[cmdArgs[index - 1].Item2 - 1]);
await RunCommands(cliMode, index, args, insId);
}
else
{
while (true)
{
PrintMenu();
Console.Write(Utils.GetString("Option"));
string option = Console.ReadLine();
bool isValid = int.TryParse(option, out var index);
isValid = isValid && index > 0 && index < 41;
if (!isValid)
{
Console.WriteLine(Utils.GetString("Invalid_Option_Text"));
Console.ReadLine();
continue;
}
await RunCommands(cliMode, index, args);
Console.WriteLine();
Console.WriteLine(Utils.GetString("Continue_Prompt_Text"));
Console.ReadLine();
}
}
}
public async Task RunCommands(bool cliMode, int index, string[] args, int insId = 0)
{
string response = "";
try
{
switch (index)
{
case 1:
{
string ruleId = cliMode ? args[1] : GetNonEmptyInput(Utils.GetString("Action_Rule_Id_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstanceOrApplyAll(configurations);
}
await actionsController.CreateAction(ruleId, insId);
break;
}
case 2:
{
string ruleId = cliMode ? args[1] : GetNonEmptyInput(Utils.GetString("Action_Rule_Id_Prompt_Text"));
string actionId = cliMode ? args[2] : GetNonEmptyInput(Utils.GetString("Action_Id_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await actionsController.DeleteAction(ruleId, actionId, insId);
break;
}
case 3:
{
string ruleId = cliMode ? args[1] : GetNonEmptyInput(Utils.GetString("Action_Rule_Id_Prompt_Text"));
string actionId = cliMode ? args[2] : GetNonEmptyInput(Utils.GetString("Action_Id_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await actionsController.GetActionById(ruleId, actionId, insId);
break;
}
case 4:
{
string ruleId = cliMode ? args[1] : GetNonEmptyInput(Utils.GetString("Action_Rule_Id_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await actionsController.GetActionsByRule(ruleId, insId);
break;
}
case 5:
{
string ruleTmplId = cliMode? args[1]: GetInput(Utils.GetString("Rule_Template_Id_Prompt_Text"),
"57c0cfc-d76d-463b-8755-c781608cdc1a");
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await alertRuleTemplatesController.GetAlertRuleTemplateById(ruleTmplId, insId);
break;
}
case 6:
{
if (!cliMode)
{
insId = Utils.SelectInstanceOrApplyAll(configurations);
}
await alertRuleTemplatesController.GetAlertRuleTemplates(insId);
break;
}
case 7:
{
if (!cliMode)
{
insId = Utils.SelectInstanceOrApplyAll(configurations);
}
await alertRulesController.CreateFusionAlertRule(insId);
break;
}
case 8:
{
if (!cliMode)
{
insId = Utils.SelectInstanceOrApplyAll(configurations);
}
await alertRulesController.CreateMicrosoftSecurityIncidentCreationAlertRule(insId);
break;
}
case 9:
{
if (!cliMode)
{
insId = Utils.SelectInstanceOrApplyAll(configurations);
}
await alertRulesController.CreateScheduledAlertRule(actionsController, insId);
break;
}
case 10:
{
string ruleId = cliMode ? args[1] : GetNonEmptyInput(Utils.GetString("Rule_Id_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await alertRulesController.DeleteAlertRule(ruleId, insId);
break;
}
case 11:
{
if (!cliMode)
{
insId = Utils.SelectInstanceOrApplyAll(configurations);
}
await alertRulesController.GetAlertRules(insId);
break;
}
case 12:
{
string ruleId = cliMode ? args[1] : GetNonEmptyInput(Utils.GetString("Get_Fusion_Rule_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await alertRulesController.GetFusionAlertRule(ruleId, insId);
break;
}
case 13:
{
string ruleId = cliMode ? args[1] : GetInput(Utils.GetString("Get_Incident_Rule_Prompt_Text"), "Microsoft-alert-rule-2");
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await alertRulesController.GetMicrosoftSecurityIdentityCreationAlertRule(ruleId, insId);
break;
}
case 14:
{
string ruleId = cliMode ? args[1] : GetInput(Utils.GetString("Get_Scheduled_Rule_Prompt_Text"), "scheduled-alert-rule-3");
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await alertRulesController.GetScheduledAlertRule(ruleId, insId);
break;
}
case 15:
{
if (!cliMode)
{
insId = Utils.SelectInstanceOrApplyAll(configurations);
}
await bookmarksController.CreateBookmark(insId);
break;
}
case 16:
{
string bookmarkId = cliMode ? args[1] : GetNonEmptyInput(Utils.GetString("Bookmark_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await bookmarksController.DeleteBookmark(bookmarkId, insId);
break;
}
case 17:
{
string bookmarkId = cliMode ? args[1] : GetNonEmptyInput(Utils.GetString("Bookmark_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await bookmarksController.GetBookmarkById(bookmarkId, insId);
break;
}
case 18:
{
if (!cliMode)
{
insId = Utils.SelectInstanceOrApplyAll(configurations);
}
await bookmarksController.GetBookmarks(insId);
break;
}
case 19:
{
if (!cliMode)
{
insId = Utils.SelectInstanceOrApplyAll(configurations);
}
await dataConnectorsController.GetDataConnectors(insId);
break;
}
case 20:
{
string dataConnectorId = GetNonEmptyInput(Utils.GetString("Dataconnector_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await dataConnectorsController.DeleteDataConnector(dataConnectorId, insId);
break;
}
case 21:
{
if (!cliMode)
{
insId = Utils.SelectInstanceOrApplyAll(configurations);
}
await dataConnectorsController.CreateDataConnector(insId);
break;
}
case 22:
{
if (!cliMode)
{
insId = Utils.SelectInstanceOrApplyAll(configurations);
}
await incidentsController.CreateIncident(insId);
break;
}
case 23:
{
string incidentId = GetNonEmptyInput(Utils.GetString("Delete_Incident_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await incidentsController.DeleteIncident(incidentId, insId);
break;
}
case 24:
{
string incidentId = GetNonEmptyInput(Utils.GetString("Get_Incident_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await incidentsController.GetIncidentById(incidentId, insId);
break;
}
case 25:
{
if (!cliMode)
{
insId = Utils.SelectInstanceOrApplyAll(configurations);
}
await incidentsController.GetIncidents(insId);
break;
}
case 26:
{
string incidentId = GetNonEmptyInput(Utils.GetString("Incident_Id_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await incidentsController.UpdateIncident(incidentId, insId);
break;
}
case 27:
{
if (!cliMode)
{
insId = Utils.SelectInstanceOrApplyAll(configurations);
}
await incidentsController.BatchUpdateIncidents(insId);
break;
}
case 28:
{
string incidentId = GetNonEmptyInput(Utils.GetString("Incident_Id_Prompt_Text"));
await incidentsController.CreateIncidentComment(incidentId);
break;
}
case 29:
{
string incidentId = GetNonEmptyInput(Utils.GetString("Incident_Id_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await incidentsController.GetAllIncidentComments(incidentId, insId);
break;
}
case 30:
{
string incidentId = GetNonEmptyInput(Utils.GetString("Incident_Id_Prompt_Text"));
string commentId = GetNonEmptyInput(Utils.GetString("Comment_Id_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await incidentsController.GetIncidentCommentById(incidentId, commentId, insId);
break;
}
case 31:
{
string incidentId = GetNonEmptyInput(Utils.GetString("Incident_Id_Prompt_Text"));
string bookmarkId = GetNonEmptyInput(Utils.GetString("Bookmark_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await incidentRelationController.CreateIncidentRelation(incidentId, bookmarkId, insId);
break;
}
case 32:
{
string incidentId = GetNonEmptyInput(Utils.GetString("Incident_Id_Prompt_Text"));
string relationId = GetNonEmptyInput(Utils.GetString("Relation_Id_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await incidentRelationController.DeleteIncidentRelation(incidentId, relationId, insId);
break;
}
case 33:
{
string incidentId = GetNonEmptyInput(Utils.GetString("Incident_Id_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
await incidentRelationController.GetEntitiesforIncident(incidentId, insId);
break;
}
case 34:
{
string incidentId = GetNonEmptyInput(Utils.GetString("Incident_Id_Prompt_Text"));
string relationId = GetNonEmptyInput(Utils.GetString("Relation_Id_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
await incidentRelationController.GetIncidentRelationByName(incidentId, relationId, insId);
break;
}
case 35:
{
string incidentId = GetNonEmptyInput(Utils.GetString("Incident_Id_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
await incidentRelationController.GetIncidentEntitiesbyEntityType(incidentId, insId);
break;
}
case 36:
{
if (!cliMode)
{
insId = Utils.SelectInstanceOrApplyAll(configurations);
}
await savedSearchController.CreateSavedSearch(insId);
break;
}
case 37:
{
string savedSearchId = GetNonEmptyInput(Utils.GetString("Saved_Search_Id_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await savedSearchController.DeleteSavedSearch(savedSearchId, insId);
break;
}
case 38:
{
string savedSearchId = GetNonEmptyInput(Utils.GetString("Saved_Search_Id_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
response = await savedSearchController.GetSavedSearchById(savedSearchId, insId);
break;
}
case 39:
{
if (!cliMode)
{
insId = Utils.SelectInstanceOrApplyAll(configurations);
}
await savedSearchController.GetSavedSearches(insId);
break;
}
case 40:
{
string savedSearchId = GetNonEmptyInput(Utils.GetString("Saved_Search_Id_Prompt_Text"));
if (!cliMode)
{
insId = Utils.SelectInstance(configurations);
}
await savedSearchController.UpdateSavedSearch(savedSearchId, insId);
break;
}
}
if (response != string.Empty)
{
Console.WriteLine(JToken.Parse(response).ToString(Formatting.Indented));
}
}
catch (JsonReaderException exception)
{
if (string.IsNullOrEmpty(response))
{
Console.WriteLine("Deleted");
Console.WriteLine(Utils.GetString("Continue_Prompt_Text"));
Console.ReadLine();
return;
}
}
catch (Exception exception)
{
ConsoleColor currentColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
await Console.Error.WriteLineAsync(exception.Message);
Console.ForegroundColor = currentColor;
}
Console.WriteLine(response);
}
private string GetNonEmptyInput(string promptText)
{
var id = "";
while (id.Trim() == string.Empty)
{
Console.WriteLine(promptText);
id = Console.ReadLine();
}
return id;
}
private string GetInput(string promptText, string defaultValue = "")
{
Console.WriteLine(promptText);
var input = Console.ReadLine();
if (input.Trim() == string.Empty)
{
input = defaultValue;
}
return input;
}
public void PrintMenu()
{
Console.WriteLine(Utils.GetString("Actions_Menu"));
Console.WriteLine(Utils.GetString("Create_Action_Menu"));
Console.WriteLine(Utils.GetString("Delete_Action_Menu"));
Console.WriteLine(Utils.GetString("Get_Action_By_Id_Menu"));
Console.WriteLine(Utils.GetString("Get_Actions_Menu"));
Console.WriteLine();
Console.WriteLine(Utils.GetString("Alert_Rule_Template_Menu"));
Console.WriteLine(Utils.GetString("Get_Rule_Template_By_Id_Menu"));
Console.WriteLine(Utils.GetString("Get_Rule_Templates_Menu"));
Console.WriteLine();
Console.WriteLine(Utils.GetString("Alert_Rules_Menu"));
Console.WriteLine(Utils.GetString("Create_Fusion_Rule_Menu"));
Console.WriteLine(Utils.GetString("Create_Incident_Rule_Menu"));
Console.WriteLine(Utils.GetString("Create_Scheduled_Rule_Menu"));
Console.WriteLine(Utils.GetString("Delete_Rule_Menu"));
Console.WriteLine(Utils.GetString("Get_Rules_Menu"));
Console.WriteLine(Utils.GetString("Get_Fusion_Rule_Menu"));
Console.WriteLine(Utils.GetString("Get_Incident_Rule_Menu"));
Console.WriteLine(Utils.GetString("Get_Scheduled_Rule_Menu"));
Console.WriteLine();
Console.WriteLine(Utils.GetString("Bookmarks_Menu"));
Console.WriteLine(Utils.GetString("Create_Bookmark_Menu"));
Console.WriteLine(Utils.GetString("Delete_Bookmark_Menu"));
Console.WriteLine(Utils.GetString("Get_Bookmark_By_Id_Menu"));
Console.WriteLine(Utils.GetString("Get_Bookmarks_Menu"));
Console.WriteLine();
Console.WriteLine(Utils.GetString("DataConnectors_Menu"));
Console.WriteLine(Utils.GetString("Get_DataConnectors_Menu"));
Console.WriteLine(Utils.GetString("Delete_DataConnector_Menu"));
Console.WriteLine(Utils.GetString("Create_DataConnector_Menu"));
Console.WriteLine();
Console.WriteLine(Utils.GetString("Incident_Menu"));
Console.WriteLine(Utils.GetString("Create_Incident_Menu"));
Console.WriteLine(Utils.GetString("Delete_Incident_Menu"));
Console.WriteLine(Utils.GetString("Get_Incident_By_Id_Menu"));
Console.WriteLine(Utils.GetString("Get_Incidents_Menu"));
Console.WriteLine(Utils.GetString("Update_Incident_Menu"));
Console.WriteLine(Utils.GetString("Batch_Update_Incidents_Menu"));
Console.WriteLine();
Console.WriteLine(Utils.GetString("Incident_Comments_Menu"));
Console.WriteLine(Utils.GetString("Create_Incident_Comment_Menu"));
Console.WriteLine(Utils.GetString("Get_Incident_Comments_Menu"));
Console.WriteLine(Utils.GetString("Get_Incident_Comment_Menu"));
Console.WriteLine();
Console.WriteLine(Utils.GetString("Incident_Relation_Menu"));
Console.WriteLine(Utils.GetString("Create_Relation_Menu"));
Console.WriteLine(Utils.GetString("Delete_Relation_Menu"));
Console.WriteLine(Utils.GetString("Get_Relations_Menu"));
Console.WriteLine(Utils.GetString("Get_Relation_Menu"));
Console.WriteLine(Utils.GetString("Get_Entities_By_Type_Menu"));
Console.WriteLine();
Console.WriteLine(Utils.GetString("Saved_Searches_Menu"));
Console.WriteLine(Utils.GetString("Create_Saved_Search_Menu"));
Console.WriteLine(Utils.GetString("Delete_Saved_Search_Menu"));
Console.WriteLine(Utils.GetString("Get_Saved_Search_Menu"));
Console.WriteLine(Utils.GetString("Get_Saved_Searches_Menu"));
Console.WriteLine(Utils.GetString("Update_Saved_Search_Menu"));
Console.WriteLine();
Console.WriteLine(Utils.GetString("Exit_Text"));
}
public void PrintCLIMenu()
{
Console.WriteLine(Utils.GetString("Actions_Menu"));
Console.WriteLine(Utils.GetString("Create_Action_Menu") + cmdArgs[0].Item1);
Console.WriteLine(Utils.GetString("Delete_Action_Menu") + cmdArgs[1].Item1);
Console.WriteLine(Utils.GetString("Get_Action_By_Id_Menu") + cmdArgs[2].Item1);
Console.WriteLine(Utils.GetString("Get_Actions_Menu") + cmdArgs[3].Item1);
Console.WriteLine();
Console.WriteLine(Utils.GetString("Alert_Rule_Template_Menu"));
Console.WriteLine(Utils.GetString("Get_Rule_Template_By_Id_Menu") + cmdArgs[4].Item1);
Console.WriteLine(Utils.GetString("Get_Rule_Templates_Menu") + cmdArgs[5].Item1);
Console.WriteLine();
Console.WriteLine(Utils.GetString("Alert_Rules_Menu"));
Console.WriteLine(Utils.GetString("Create_Fusion_Rule_Menu") + cmdArgs[6].Item1);
Console.WriteLine(Utils.GetString("Create_Incident_Rule_Menu") + cmdArgs[7].Item1);
Console.WriteLine(Utils.GetString("Create_Scheduled_Rule_Menu") + cmdArgs[8].Item1);
Console.WriteLine(Utils.GetString("Delete_Rule_Menu") + cmdArgs[9].Item1);
Console.WriteLine(Utils.GetString("Get_Rules_Menu") + cmdArgs[10].Item1);
Console.WriteLine(Utils.GetString("Get_Fusion_Rule_Menu") + cmdArgs[11].Item1);
Console.WriteLine(Utils.GetString("Get_Incident_Rule_Menu") + cmdArgs[12].Item1);
Console.WriteLine(Utils.GetString("Get_Scheduled_Rule_Menu") + cmdArgs[13].Item1);
Console.WriteLine();
Console.WriteLine(Utils.GetString("Bookmarks_Menu"));
Console.WriteLine(Utils.GetString("Create_Bookmark_Menu") + cmdArgs[14].Item1);
Console.WriteLine(Utils.GetString("Delete_Bookmark_Menu") + cmdArgs[15].Item1);
Console.WriteLine(Utils.GetString("Get_Bookmark_By_Id_Menu") + cmdArgs[16].Item1);
Console.WriteLine(Utils.GetString("Get_Bookmarks_Menu") + cmdArgs[17].Item1);
Console.WriteLine();
Console.WriteLine(Utils.GetString("DataConnectors_Menu"));
Console.WriteLine(Utils.GetString("Get_DataConnectors_Menu") + cmdArgs[18].Item1);
Console.WriteLine(Utils.GetString("Delete_DataConnector_Menu") + cmdArgs[19].Item1);
Console.WriteLine(Utils.GetString("Create_DataConnector_Menu") + cmdArgs[20].Item1);
Console.WriteLine();
Console.WriteLine(Utils.GetString("Incident_Menu"));
Console.WriteLine(Utils.GetString("Create_Incident_Menu") + cmdArgs[21].Item1);
Console.WriteLine(Utils.GetString("Delete_Incident_Menu") + cmdArgs[22].Item1);
Console.WriteLine(Utils.GetString("Get_Incident_By_Id_Menu") + cmdArgs[23].Item1);
Console.WriteLine(Utils.GetString("Get_Incidents_Menu") + cmdArgs[24].Item1);
Console.WriteLine(Utils.GetString("Update_Incident_Menu") + cmdArgs[25].Item1);
Console.WriteLine(Utils.GetString("Batch_Update_Incidents_Menu") + cmdArgs[26].Item1);
Console.WriteLine();
Console.WriteLine(Utils.GetString("Incident_Comments_Menu"));
Console.WriteLine(Utils.GetString("Create_Incident_Comment_Menu") + cmdArgs[27].Item1);
Console.WriteLine(Utils.GetString("Get_Incident_Comments_Menu") + cmdArgs[28].Item1);
Console.WriteLine(Utils.GetString("Get_Incident_Comment_Menu") + cmdArgs[29].Item1);
Console.WriteLine();
Console.WriteLine(Utils.GetString("Incident_Relation_Menu"));
Console.WriteLine(Utils.GetString("Create_Relation_Menu") + cmdArgs[30].Item1);
Console.WriteLine(Utils.GetString("Delete_Relation_Menu") + cmdArgs[31].Item1);
Console.WriteLine(Utils.GetString("Get_Relations_Menu") + cmdArgs[32].Item1);
Console.WriteLine(Utils.GetString("Get_Relation_Menu") + cmdArgs[33].Item1);
Console.WriteLine(Utils.GetString("Get_Entities_By_Type_Menu") + cmdArgs[34].Item1);
Console.WriteLine();
}
}
}

Просмотреть файл

@ -0,0 +1,45 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="3.1.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.5" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="5.2.8" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="AlertRuleTemplates\Models" />
<Folder Include="Results\" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resource1.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resource1.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resource1.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resource1.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ProjectExtensions><VisualStudio><UserProperties appsettings_1json__JsonSchema="https://json.schemastore.org/azure-iot-edge-deployment-template-2.0" /></VisualStudio></ProjectExtensions>
</Project>

Просмотреть файл

@ -0,0 +1,274 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using AzureSentinel_ManagementAPI.Bookmarks.Models;
using AzureSentinel_ManagementAPI.Infrastructure.Authentication;
using AzureSentinel_ManagementAPI.Infrastructure.Configuration;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace AzureSentinel_ManagementAPI.Bookmarks
{
public class BookmarksController
{
private readonly AzureSentinelApiConfiguration[] azureConfigs;
private readonly AuthenticationService authenticationService;
private bool cliMode;
public BookmarksController(
AzureSentinelApiConfiguration[] azureConfig,
IConfigurationRoot rawConfig,
AuthenticationService authenticationService)
{
azureConfigs = azureConfig;
this.authenticationService = authenticationService;
cliMode = rawConfig.GetValue<bool>("Climode");
}
/// <summary>
/// Create a bookmark for all instances or for a single instance
/// </summary>
/// <returns></returns>
public async Task CreateBookmark(int insId = -1)
{
if (insId != -1)
{
await CreateBookmarkByInstance(insId);
}
else
{
for (var i = 0; i < azureConfigs.Length; i++)
{
await CreateBookmarkByInstance(i);
}
}
}
/// <summary>
/// Create a bookmark for a single instance
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
private async Task CreateBookmarkByInstance(int i)
{
var bookmarks = Utils.LoadPayload<BookmarkPayload[]>("BookmarkPayload.json", cliMode);
foreach (var payload in bookmarks)
{
try
{
var bookmarkId = Guid.NewGuid().ToString();
var url = $"{azureConfigs[i].BaseUrl}/bookmarks/{bookmarkId}?api-version={azureConfigs[i].ApiVersion}";
var serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var request = new HttpRequestMessage(HttpMethod.Put, url)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var res = await response.Content.ReadAsStringAsync();
Console.WriteLine(JToken.Parse(res).ToString(Formatting.Indented));
continue;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception($"Something went wrong on {azureConfigs[i].InstanceName}: \n"
+ ex.Message);
}
}
}
/// <summary>
/// Delete a bookmark by id
/// </summary>
/// <param name="bookmarkId"></param>
/// <returns></returns>
public async Task<string> DeleteBookmark(string bookmarkId, int insId)
{
try
{
var url = $"{azureConfigs[insId].BaseUrl}/bookmarks/{bookmarkId}?api-version={azureConfigs[insId].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Delete, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
if (response.StatusCode == HttpStatusCode.NotFound)
throw new Exception("Not found, please create a new Bookmark first...");
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Get a bookmark by id
/// </summary>
/// <param name="bookmarkId"></param>
/// <returns></returns>
public async Task<string> GetBookmarkById(string bookmarkId, int insId)
{
try
{
var url = $"{azureConfigs[insId].BaseUrl}/bookmarks/{bookmarkId}?api-version={azureConfigs[insId].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Get all bookmarks for all instances or for a single instance
/// </summary>
/// <param name="insId"></param>
/// <returns></returns>
public async Task GetBookmarks(int insId = -1)
{
if (insId != -1)
{
await GetBookmarksByInstance(insId);
}
else
{
for (var i = 0; i < azureConfigs.Length; i++)
{
await GetBookmarksByInstance(i);
}
}
}
/// <summary>
/// Get bookmarks for a single instance
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
private async Task GetBookmarksByInstance(int i)
{
try
{
var url = $"{azureConfigs[i].BaseUrl}/bookmarks?api-version={azureConfigs[i].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string res = await response.Content.ReadAsStringAsync();
JObject result = JsonConvert.DeserializeObject<JObject>(res);
var values = result["value"] as JArray;
if (values == null)
{
values = new JArray();
}
int callTimes = 1;
while (result.ContainsKey("nextLink") && callTimes < 100)
{
try
{
var nextLink = result["nextLink"].ToString();
request = new HttpRequestMessage(HttpMethod.Get, nextLink);
await authenticationService.AuthenticateRequest(request, i);
var nextResponse = await http.SendAsync(request);
if (nextResponse.IsSuccessStatusCode)
{
var newRes = await nextResponse.Content.ReadAsStringAsync();
JObject newResult = JsonConvert.DeserializeObject<JObject>(newRes);
result = newResult;
var newValues = result["value"] as JArray;
if (newValues == null)
{
newValues = new JArray();
}
foreach (var v in newValues)
{
values.Add(v);
}
callTimes++;
}
else
{
var err = await response.Content.ReadAsStringAsync();
Console.WriteLine("Error calling the nextLink: \n" + err);
break;
}
}
catch (Exception ex)
{
Console.WriteLine("Error in parsing nextLink: \n" + ex.Message);
break;
}
}
var formattedRes = JsonConvert.SerializeObject(values, Formatting.Indented);
Utils.WriteJsonStringToFile($"GetBookmarks_{azureConfigs[i].InstanceName}.json", cliMode, formattedRes, false);
Console.WriteLine(formattedRes);
return;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception($"Something went wrong on {azureConfigs[i].InstanceName}: \n"
+ ex.Message);
}
}
}
}

Просмотреть файл

@ -0,0 +1,13 @@
using Newtonsoft.Json;
namespace AzureSentinel_ManagementAPI.Bookmarks.Models
{
public class BookmarkPayload
{
[JsonProperty("etag")]
public string ETag { get; set; }
[JsonProperty("properties")]
public BookmarkPropertiesPayload PropertiesPayload { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace AzureSentinel_ManagementAPI.Bookmarks.Models
{
public class BookmarkPropertiesPayload
{
public string DisplayName { get; set; }
public string Query { get; set; }
public string Notes { get; set; }
public List<string> Labels { get; set; }
public string QueryResult { get; set; }
public IncidentInfo IncidentInfo { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,16 @@
using AzureSentinel_ManagementAPI.Infrastructure.SharedModels.Enums;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace AzureSentinel_ManagementAPI.Bookmarks.Models
{
public class IncidentInfo
{
public string IncidentId { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public Severity Severity { get; set; }
public string Title { get; set; }
public string RelationName { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,245 @@
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using AzureSentinel_ManagementAPI.DataConnectors.Models;
using AzureSentinel_ManagementAPI.Infrastructure.Authentication;
using AzureSentinel_ManagementAPI.Infrastructure.Configuration;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace AzureSentinel_ManagementAPI.DataConnectors
{
public class DataConnectorsController
{
private readonly AzureSentinelApiConfiguration[] azureConfigs;
private readonly AuthenticationService authenticationService;
private bool cliMode;
public DataConnectorsController(
AzureSentinelApiConfiguration[] azureConfig,
IConfigurationRoot rawConfig,
AuthenticationService authenticationService
)
{
azureConfigs = azureConfig;
this.authenticationService = authenticationService;
cliMode = rawConfig.GetValue<bool>("Climode");
}
/// <summary>
/// Get all data connectors for all instances or for a single instance
/// </summary>
/// <returns></returns>
public async Task GetDataConnectors(int insId)
{
if (insId != -1)
{
await GetDataConnectorsByInstance(insId);
}
else
{
for (var i = 0; i < azureConfigs.Length; i++)
{
await GetDataConnectorsByInstance(i);
}
}
}
/// <summary>
/// Get all data connectors for a single instance
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
private async Task GetDataConnectorsByInstance(int i)
{
try
{
var url = $"{azureConfigs[i].BaseUrl}/dataConnectors?api-version={azureConfigs[i].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string res = await response.Content.ReadAsStringAsync();
JObject result = JsonConvert.DeserializeObject<JObject>(res);
var values = result["value"] as JArray;
if (values == null)
{
values = new JArray();
}
int callTimes = 1;
while (result.ContainsKey("nextLink") && callTimes < 100)
{
try
{
var nextLink = result["nextLink"].ToString();
request = new HttpRequestMessage(HttpMethod.Get, nextLink);
await authenticationService.AuthenticateRequest(request, i);
var nextResponse = await http.SendAsync(request);
if (nextResponse.IsSuccessStatusCode)
{
var newRes = await nextResponse.Content.ReadAsStringAsync();
JObject newResult = JsonConvert.DeserializeObject<JObject>(newRes);
result = newResult;
var newValues = result["value"] as JArray;
if (newValues == null)
{
newValues = new JArray();
}
foreach (var v in newValues)
{
values.Add(v);
}
callTimes++;
}
else
{
var err = await response.Content.ReadAsStringAsync();
Console.WriteLine("Error calling the nextLink: \n" + err);
break;
}
}
catch (Exception ex)
{
Console.WriteLine("Error in parsing nextLink: \n" + ex.Message);
break;
}
}
var formattedRes = JsonConvert.SerializeObject(values, Formatting.Indented);
Utils.WriteJsonStringToFile($"GetDataConnectors_{azureConfigs[i].InstanceName}.json", cliMode, formattedRes, false);
Console.WriteLine(formattedRes);
return;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception($"Something went wrong on {azureConfigs[i].InstanceName}: \n"
+ ex.Message);
}
}
/// <summary>
/// Create a data connector for all instances or for a single instance
/// </summary>
/// <returns></returns>
public async Task CreateDataConnector(int insId)
{
if (insId != -1)
{
await CreateDataConnectorByInstance(insId);
}
else
{
for (var i = 0; i < azureConfigs.Length; i++)
{
await CreateDataConnectorByInstance(i);
}
}
}
/// <summary>
/// Create a data connector for a single instance
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
private async Task CreateDataConnectorByInstance(int i)
{
var dataConnectors = Utils.LoadPayload<ASCDataConnectorPayload[]>("DataConnectorPayload.json", cliMode);
foreach (var payload in dataConnectors)
{
try
{
var dataConnectorId = Guid.NewGuid().ToString();
var url =
$"{azureConfigs[i].BaseUrl}/dataConnectors/{dataConnectorId}?api-version={azureConfigs[i].ApiVersion}";
var serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var request = new HttpRequestMessage(HttpMethod.Put, url)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var res = await response.Content.ReadAsStringAsync();
Console.WriteLine(JToken.Parse(res).ToString(Formatting.Indented));
continue;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception($"Something went wrong on {azureConfigs[i].InstanceName}: \n"
+ ex.Message);
}
}
}
/// <summary>
/// Delete a data connector by id
/// </summary>
/// <param name="dataConnectorId"></param>
/// <returns></returns>
public async Task<string> DeleteDataConnector(string dataConnectorId, int insId)
{
try
{
var url = $"{azureConfigs[insId].BaseUrl}/dataConnectors/{dataConnectorId}?api-version={azureConfigs[insId].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Delete, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
if (response.StatusCode == HttpStatusCode.NotFound)
throw new Exception("Not found, please create a new DataConnector first...");
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
}
}

Просмотреть файл

@ -0,0 +1,7 @@
namespace AzureSentinel_ManagementAPI.DataConnectors.Models
{
public class ASCDataConnectorDataTypesPayload
{
public DataTypeConnectionStatePayload Alerts { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,9 @@
using Newtonsoft.Json;
namespace AzureSentinel_ManagementAPI.DataConnectors.Models
{
public class ASCDataConnectorPayload : DataConnectorPayload
{
[JsonProperty("properties")] public ASCDataConnectorPropertiesPayload PropertiesPayload { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,14 @@
using Newtonsoft.Json;
namespace AzureSentinel_ManagementAPI.DataConnectors.Models
{
public class ASCDataConnectorPropertiesPayload
{
public string SubscriptionId { get; set; }
public string TenantId { get; set; }
[JsonProperty("dataTypes")]
public ASCDataConnectorDataTypesPayload DataTypesPayload { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,8 @@
namespace AzureSentinel_ManagementAPI.DataConnectors.Models
{
public enum DataConnectionState
{
Enabled,
Disabled
}
}

Просмотреть файл

@ -0,0 +1,14 @@
namespace AzureSentinel_ManagementAPI.DataConnectors.Models
{
public enum DataConnectorKind
{
AzureActiveDirectory,
AzureSecurityCenter,
MicrosoftCloudAppSecurity,
ThreatIntelligence,
Office365,
AmazonWebServicesCloudTrail,
AzureAdvancedThreatProtection,
MicrosoftDefenderAdvancedThreatProtection
}
}

Просмотреть файл

@ -0,0 +1,14 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace AzureSentinel_ManagementAPI.DataConnectors.Models
{
public abstract class DataConnectorPayload
{
[JsonProperty("etag")]
public string ETag { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public DataConnectorKind Kind { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,11 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace AzureSentinel_ManagementAPI.DataConnectors.Models
{
public class DataTypeConnectionStatePayload
{
[JsonConverter(typeof(StringEnumConverter))]
public DataConnectionState State { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,16 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
namespace AzureSentinel_ManagementAPI.Hunting.Models
{
public class SavedSearchPayload
{
[JsonProperty("etag")]
public string Etag { get; set; }
[JsonProperty("properties")]
public SavedSearchPropertiesPayload PropertiesPayload { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace AzureSentinel_ManagementAPI.Hunting.Models
{
public class SavedSearchPropertiesPayload
{
public string Category { get; set; }
public string DisplayName { get; set; }
public int Version { get; set; }
public string FunctionAlias { get; set; }
public string FunctionParameters { get; set; }
public List<SavedSearchTag> Tags { get; set; }
public string Query { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace AzureSentinel_ManagementAPI.Hunting.Models
{
public class SavedSearchTag
{
public string Name { get; set; }
public string Value { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,326 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using AzureSentinel_ManagementAPI.Hunting.Models;
using AzureSentinel_ManagementAPI.Infrastructure.Authentication;
using AzureSentinel_ManagementAPI.Infrastructure.Configuration;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace AzureSentinel_ManagementAPI.Hunting
{
public class SavedSearchController
{
private readonly AzureSentinelApiConfiguration[] azureConfigs;
private readonly AuthenticationService authenticationService;
private bool cliMode;
private const string savedSearchApiVersion = "2020-08-01";
public SavedSearchController(
AzureSentinelApiConfiguration[] azureConfig,
IConfigurationRoot rawConfig,
AuthenticationService authenticationService
)
{
azureConfigs = azureConfig;
this.authenticationService = authenticationService;
cliMode = rawConfig.GetValue<bool>("Climode");
}
/// <summary>
/// Create saved search
/// </summary>
/// <param name="insId"></param>
/// <returns></returns>
public async Task CreateSavedSearch(int insId)
{
if (insId != -1)
{
await CreateSavedSearchByInstance(insId);
}
else
{
for (var i = 0; i < azureConfigs.Length; i++)
{
await CreateSavedSearchByInstance(i);
}
}
}
/// <summary>
/// Create saved search by instance
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public async Task CreateSavedSearchByInstance(int i)
{
var savedSearches = Utils.LoadPayload<SavedSearchPayload[]>("SavedSearchPayload.json", cliMode);
foreach (var payload in savedSearches)
{
try
{
var savedSearchId = Guid.NewGuid().ToString();
var url =
$"{azureConfigs[i].OperationInsightBaseUrl}/savedSearches/{savedSearchId}?api-version={savedSearchApiVersion}";
var serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var request = new HttpRequestMessage(HttpMethod.Put, url)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var res = await response.Content.ReadAsStringAsync();
Console.WriteLine(JToken.Parse(res).ToString(Formatting.Indented));
continue;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception($"Something went wrong on {azureConfigs[i].InstanceName}: \n"
+ ex.Message);
}
}
}
/// <summary>
/// Update saved search data by instance
/// </summary>
/// <param name="savedSearchId"></param>
/// <param name="insId"></param>
/// <returns></returns>
public async Task<string> UpdateSavedSearch(string savedSearchId, int insId)
{
var savedSearch = await GetSavedSearchById(savedSearchId, insId);
try
{
JObject jobject = JObject.Parse(savedSearch);
var savedSearchObj = JsonConvert.DeserializeObject<SavedSearchPayload>(jobject.ToString());
var savedSearchs = Utils.LoadPayload<SavedSearchPayload[]>("SavedSearchPayload.json", cliMode);
// Get the first one to update
var payload = savedSearchs[0];
payload.Etag = savedSearchObj.Etag;
var url = $"{azureConfigs[insId].OperationInsightBaseUrl}/savedSearches/{savedSearchId}?api-version={savedSearchApiVersion}";
var serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var request = new HttpRequestMessage(HttpMethod.Put, url)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Delete saved search by id and instance
/// </summary>
/// <param name="savedSearchId"></param>
/// <param name="insId"></param>
/// <returns></returns>
public async Task<string> DeleteSavedSearch(string savedSearchId, int insId)
{
try
{
var url = $"{azureConfigs[insId].OperationInsightBaseUrl}/savedSearches/{savedSearchId}?api-version={savedSearchApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Delete, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
if (response.StatusCode == HttpStatusCode.NotFound)
throw new Exception("Not found, please create a new SavedSearch first...");
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Get a specific saved search by id and instance
/// </summary>
/// <param name="savedSearchId"></param>
/// <param name="i"></param>
/// <returns></returns>
public async Task<string> GetSavedSearchById(string savedSearchId, int i)
{
try
{
var url = $"{azureConfigs[i].OperationInsightBaseUrl}/savedSearches/{savedSearchId}?api-version={savedSearchApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
public async Task GetSavedSearches(int insId)
{
if (insId != -1)
{
await GetSavedSearchesByInstance(insId);
}
else
{
for (var i = 0; i < azureConfigs.Length; i++)
{
await GetSavedSearchesByInstance(i);
}
}
}
/// <summary>
/// Get all saved searches by instance
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
private async Task GetSavedSearchesByInstance(int i)
{
try
{
var url = $"{azureConfigs[i].OperationInsightBaseUrl}/savedSearches?api-version={savedSearchApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string res = await response.Content.ReadAsStringAsync();
JObject result = JsonConvert.DeserializeObject<JObject>(res);
var values = result["value"] as JArray;
if (values == null)
{
values = new JArray();
}
int callTimes = 1;
while (result.ContainsKey("nextLink") && callTimes < 100)
{
try
{
var nextLink = result["nextLink"].ToString();
request = new HttpRequestMessage(HttpMethod.Get, nextLink);
await authenticationService.AuthenticateRequest(request, i);
var nextResponse = await http.SendAsync(request);
if (nextResponse.IsSuccessStatusCode)
{
var newRes = await nextResponse.Content.ReadAsStringAsync();
JObject newResult = JsonConvert.DeserializeObject<JObject>(newRes);
result = newResult;
var newValues = result["value"] as JArray;
if (newValues == null)
{
newValues = new JArray();
}
foreach (var v in newValues)
{
values.Add(v);
}
callTimes++;
}
else
{
var err = await response.Content.ReadAsStringAsync();
Console.WriteLine("Error calling the nextLink: \n" + err);
break;
}
}
catch (Exception ex)
{
Console.WriteLine("Error in parsing nextLink: \n" + ex.Message);
break;
}
}
var formattedRes = JsonConvert.SerializeObject(values, Formatting.Indented);
Utils.WriteJsonStringToFile($"GetSavedSearches_{azureConfigs[i].InstanceName}.json", cliMode, formattedRes, false);
Console.WriteLine(formattedRes);
return;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception($"Something went wrong on {azureConfigs[i].InstanceName}: \n"
+ ex.Message);
}
}
}
}

Просмотреть файл

@ -0,0 +1,480 @@
using System;
using System.Reflection;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using AzureSentinel_ManagementAPI.IncidentRelation.Models;
using AzureSentinel_ManagementAPI.Infrastructure.Authentication;
using AzureSentinel_ManagementAPI.Infrastructure.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
namespace AzureSentinel_ManagementAPI.IncidentRelation
{
class IncidentRelationController
{
private readonly AzureSentinelApiConfiguration[] azureConfigs;
private readonly AuthenticationService authenticationService;
private const string Domain = "https://management.azure.com";
private bool cliMode;
public IncidentRelationController(
AzureSentinelApiConfiguration[] azureConfig,
IConfigurationRoot rawConfig,
AuthenticationService authenticationService
)
{
azureConfigs = azureConfig;
this.authenticationService = authenticationService;
cliMode = rawConfig.GetValue<bool>("Climode");
}
/// <summary>
/// Create an incident relation for a single instance, connecting incident and bookmark
/// </summary>
/// <param name="incidentId"></param>
/// <param name="bookmarkId"></param>
/// <returns></returns>
public async Task<string> CreateIncidentRelation(string incidentId, string bookmarkId, int insId)
{
try
{
var payload = new RelationPayload
{
PropertiesPayload = new RelationPropertiesPayload
{
RelatedResourceId = $"{azureConfigs[insId].BaseUrl}/bookmarks/{bookmarkId}"
}
};
var relationId = Guid.NewGuid().ToString();
var url =
$"{azureConfigs[insId].BaseUrl}/incidents/{incidentId}/relations/{relationId}?api-version={azureConfigs[insId].PreviewApiVersion}";
var serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var request = new HttpRequestMessage(HttpMethod.Put, url)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Delete an incident relation by id
/// </summary>
/// <param name="incidentId"></param>
/// <param name="relationId"></param>
/// <returns></returns>
public async Task<string> DeleteIncidentRelation(string incidentId, string relationId, int insId)
{
try
{
var url = $"{azureConfigs[insId].BaseUrl}/incidents/{incidentId}/relations/{relationId}?api-version={azureConfigs[insId].PreviewApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Delete, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
if (response.StatusCode == HttpStatusCode.NotFound)
throw new Exception("Not found, please create a new incident relation first...");
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Get an incident relation by id
/// </summary>
/// <param name="incidentId"></param>
/// <param name="relationId"></param>
/// <returns></returns>
public async Task GetIncidentRelationByName(string incidentId, string relationId, int insId)
{
try
{
var url = $"{azureConfigs[insId].BaseUrl}/incidents/{incidentId}/relations/{relationId}?api-version={azureConfigs[insId].PreviewApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string res = await response.Content.ReadAsStringAsync();
JObject value = JsonConvert.DeserializeObject<JObject>(res);
Console.WriteLine(JsonConvert.SerializeObject(value, Formatting.Indented));
var resources = new JObject();
resources.Add("incidentRelationId", relationId);
var properties = value["properties"];
var relatedResourceId = properties != null ? properties["relatedResourceId"] : null;
var resourceDataStr = string.Empty;
if (relatedResourceId != null)
{
var resourceType = properties["relatedResourceType"].ToString();
if (resourceType == "Microsoft.SecurityInsights/bookmarks")
{
var resourceUrl = $"{Domain}{relatedResourceId}?api-version={azureConfigs[insId].ApiVersion}";
var resourcerequest = new HttpRequestMessage(HttpMethod.Get, resourceUrl);
await authenticationService.AuthenticateRequest(resourcerequest, insId);
var resourceHttp = new HttpClient();
var resourceRes = await resourceHttp.SendAsync(resourcerequest);
if (resourceRes.IsSuccessStatusCode)
{
resourceDataStr = await resourceRes.Content.ReadAsStringAsync();
}
}
else if (resourceType == "Microsoft.SecurityInsights/entities")
{
var resourceUrl = $"{Domain}{relatedResourceId}/expand?api-version={azureConfigs[insId].PreviewApiVersion}";
var resourceKind = properties["relatedResourceKind"].ToString();
var expansionId = GetExpansionId(resourceKind);
var payload = new RelationEntityPayload
{
ExpansionId = expansionId
};
var serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var resourcerequest = new HttpRequestMessage(HttpMethod.Post, resourceUrl)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(resourcerequest, insId);
var resourceHttp = new HttpClient();
var resourceRes = await resourceHttp.SendAsync(resourcerequest);
if (resourceRes.IsSuccessStatusCode)
{
resourceDataStr = await resourceRes.Content.ReadAsStringAsync();
}
}
if (resourceDataStr != string.Empty)
{
JObject resourceData = JsonConvert.DeserializeObject<JObject>(resourceDataStr);
resources.Merge(resourceData);
}
}
Console.WriteLine("related resources:");
var serializedResources = JsonConvert.SerializeObject(resources, Formatting.Indented);
Console.WriteLine(serializedResources);
Utils.WriteJsonStringToFile($"GetIncidentRelationByNameResources_{azureConfigs[insId].InstanceName}.json",
cliMode, serializedResources, false);
return;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Get all incident relations under an incident, and get all entities under the incident
/// </summary>
/// <param name="incidentId"></param>
/// <returns></returns>
public async Task GetEntitiesforIncident(string incidentId, int insId)
{
await GetIncidentRelationsAndEntities(incidentId, EntityKind.SecurityAlert, true, insId);
}
/// <summary>
/// Get all incident relations under an incident, and get all entities under the incident
/// </summary>
/// <param name="incidentId"></param>
/// <returns></returns>
public async Task GetIncidentEntitiesbyEntityType(string incidentId, int insId)
{
// Console.WriteLine(Utils.GetString("Select_Entity_Kind"));
var entityKind = Utils.SelectKind<EntityKind>();
await GetIncidentRelationsAndEntities(incidentId, entityKind, false, insId);
}
/// <summary>
/// Get all incident relations and entities under an incident
/// </summary>
/// <param name="incidentId"></param>
/// <param name="entityKind"></param>
/// <param name="allKind"></param>
/// <returns></returns>
public async Task GetIncidentRelationsAndEntities(string incidentId, EntityKind entityKind, bool allKind, int insId)
{
try
{
var url = $"{azureConfigs[insId].BaseUrl}/incidents/{incidentId}/relations?api-version={azureConfigs[insId].PreviewApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string res = await response.Content.ReadAsStringAsync();
JObject result = JsonConvert.DeserializeObject<JObject>(res);
var values = result["value"] as JArray;
if (values == null)
{
values = new JArray();
}
int callTimes = 1;
while (result.ContainsKey("nextLink") && callTimes < 100)
{
try
{
var nextLink = result["nextLink"].ToString();
request = new HttpRequestMessage(HttpMethod.Get, nextLink);
await authenticationService.AuthenticateRequest(request, insId);
var nextResponse = await http.SendAsync(request);
if (nextResponse.IsSuccessStatusCode)
{
var newRes = await nextResponse.Content.ReadAsStringAsync();
JObject newResult = JsonConvert.DeserializeObject<JObject>(newRes);
result = newResult;
var newValues = result["value"] as JArray;
if (newValues == null)
{
newValues = new JArray();
}
foreach (var v in newValues)
{
values.Add(v);
}
callTimes++;
}
else
{
var err = await response.Content.ReadAsStringAsync();
Console.WriteLine("Error calling the nextLink: \n" + err);
break;
}
}
catch (Exception ex)
{
Console.WriteLine("Error in parsing nextLink: \n" + ex.Message);
break;
}
}
var resources = new JArray();
foreach (JToken value in values)
{
var properties = value["properties"];
var relatedResourceId = properties != null ? properties["relatedResourceId"] : null;
var resourceDataStr = string.Empty;
if (relatedResourceId != null)
{
var resourceType = properties["relatedResourceType"].ToString();
if (resourceType == "Microsoft.SecurityInsights/bookmarks" && (allKind || entityKind == EntityKind.Bookmark))
{
var resourceUrl = $"{Domain}{relatedResourceId}?api-version={azureConfigs[insId].ApiVersion}";
var resourcerequest = new HttpRequestMessage(HttpMethod.Get, resourceUrl);
await authenticationService.AuthenticateRequest(resourcerequest, insId);
var resourceHttp = new HttpClient();
var resourceRes = await resourceHttp.SendAsync(resourcerequest);
if (resourceRes.IsSuccessStatusCode)
{
resourceDataStr = await resourceRes.Content.ReadAsStringAsync();
}
}
else if (resourceType == "Microsoft.SecurityInsights/entities")
{
var resourceUrl = $"{Domain}{relatedResourceId}/expand?api-version={azureConfigs[insId].PreviewApiVersion}";
var resourceKind = properties["relatedResourceKind"].ToString();
var expansionId = GetExpansionId(resourceKind);
var payload = new RelationEntityPayload
{
ExpansionId = expansionId
};
var serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var resourcerequest = new HttpRequestMessage(HttpMethod.Post, resourceUrl)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(resourcerequest, insId);
var resourceHttp = new HttpClient();
var resourceRes = await resourceHttp.SendAsync(resourcerequest);
if (resourceRes.IsSuccessStatusCode)
{
resourceDataStr = await resourceRes.Content.ReadAsStringAsync();
}
}
if (resourceDataStr != string.Empty)
{
JObject resourceData = JsonConvert.DeserializeObject<JObject>(resourceDataStr);
if (resourceType == "Microsoft.SecurityInsights/bookmarks" && allKind)
{
resources.Add(resourceData);
}
else
{
var resourceValue = resourceData["value"];
var entities = resourceValue != null ? resourceValue["entities"] : null;
if (entities != null)
{
foreach (var entity in entities)
{
if (allKind || entity["kind"].ToString() == entityKind.ToString())
{
resources.Add(entity);
}
}
}
}
}
}
}
Utils.WriteJsonStringToFile($"GetIncidentRelations_{azureConfigs[insId].InstanceName}.json",
cliMode, JsonConvert.SerializeObject(values, Formatting.Indented), false);
Console.WriteLine(JsonConvert.SerializeObject(values, Formatting.Indented));
Console.WriteLine(Utils.GetString("Related_Entities"));
var serializedResources = JsonConvert.SerializeObject(resources, Formatting.Indented);
Console.WriteLine(serializedResources);
var fileNamePrefix = allKind ? "GetEntitiesforIncident" : $"GetIncidentEntitiesbyEntityType_{entityKind.ToString()}";
Utils.WriteJsonStringToFile($"{fileNamePrefix}_{azureConfigs[insId].InstanceName}.json", cliMode,
serializedResources, false);
return;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Get entities expansion id by kind
/// </summary>
/// <param name="kind"></param>
/// <returns></returns>
private string GetExpansionId(string kind)
{
switch (kind)
{
case "SecurityAlert":
return "98b974fd-cc64-48b8-9bd0-3a209f5b944b";
case "Bookmark":
return "27f76e63-c41b-480f-bb18-12ad2e011d49";
case "Account":
return "a77992f3-25e9-4d01-99a4-5ff606cc410a";
case "AzureResource":
return "4a014a1b-c5a1-499f-9f54-3f7b99b0a675";
case "CloudApplication":
return "f74ad13a-ae93-47b9-8782-b1142b95d046";
case "DnsResolution":
return "80218599-45b4-4402-95cc-86f9929dd43d";
case "File":
return "0f0bccef-4512-4530-a866-27056a39dcd6";
case "FileHash":
return "b6eaa3ad-e69b-437e-9c13-bb5273dd34ab";
case "Host":
return "055a5692-555f-42bd-ac17-923a5a9994ed";
case "Ip":
return "58c1516f-b78a-4d78-9e71-77c40849c27b";
case "Malware":
return "b8407195-b9a3-4565-bf08-7b23e5c57e3a";
case "Process":
return "63a4fa2f-f89d-4cf5-96a2-cb2479e49731";
case "RegistryKey":
return "d788cd65-a7ef-448e-aa34-81185ac0e611";
case "RegistryValue":
return "3a45a7e3-80e0-4e05-84db-b97bd1ae452b";
case "Url":
return "7b61d5e2-4b66-40a7-bb0f-9145b445104e";
case "IoTDevice":
return "4daeed0e-0e74-4f2d-990c-a958210e9dd7";
default:
return string.Empty;
}
}
}
}

Просмотреть файл

@ -0,0 +1,22 @@
namespace AzureSentinel_ManagementAPI.IncidentRelation.Models
{
public enum EntityKind
{
SecurityAlert,
Bookmark,
Account,
AzureResource,
CloudApplication,
DnsResolution,
File,
FileHash,
Host,
Ip,
Malware,
Process,
RegistryKey,
RegistryValue,
Url,
IoTDevice
}
}

Просмотреть файл

@ -0,0 +1,13 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
namespace AzureSentinel_ManagementAPI.IncidentRelation.Models
{
public class RelationEntityPayload
{
[JsonProperty("expansionId")]
public string ExpansionId { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,12 @@
using Newtonsoft.Json;
namespace AzureSentinel_ManagementAPI.IncidentRelation.Models
{
public class RelationPayload
{
public RelationPayload()
{
}
[JsonProperty("properties")] public RelationPropertiesPayload PropertiesPayload { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace AzureSentinel_ManagementAPI.IncidentRelation.Models
{
public class RelationPropertiesPayload
{
public string RelatedResourceId { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,648 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using AzureSentinel_ManagementAPI.Incidents.Models;
using AzureSentinel_ManagementAPI.Incidents.Models.Comments;
using AzureSentinel_ManagementAPI.Infrastructure.Authentication;
using AzureSentinel_ManagementAPI.Infrastructure.Configuration;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace AzureSentinel_ManagementAPI.Incidents
{
public class IncidentsController
{
public const string INCIDENT_NAME = "incident-1";
private const string INCIDENT_COMMENT_NAME = "incident-comment-1";
private string incidentId = Guid.NewGuid().ToString();
private string commentId = Guid.NewGuid().ToString();
private readonly AzureSentinelApiConfiguration[] azureConfigs;
private readonly AuthenticationService authenticationService;
private bool cliMode;
public IncidentsController(
AzureSentinelApiConfiguration[] azureConfig,
IConfigurationRoot rawConfig,
AuthenticationService authenticationService)
{
azureConfigs = azureConfig;
this.authenticationService = authenticationService;
cliMode = rawConfig.GetValue<bool>("Climode");
}
/// <summary>
/// Create an incident for all instances or for a single instance
/// </summary>
/// <returns></returns>
public async Task CreateIncident(int insId = -1)
{
if (insId != -1)
{
await CreateIncidentByInstance(insId);
}
else
{
for (var i = 0; i < azureConfigs.Length; i++)
{
await CreateIncidentByInstance(i);
}
}
}
/// <summary>
/// Create an incident for a single instance
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
private async Task CreateIncidentByInstance(int i)
{
var incidents = Utils.LoadPayload<IncidentPayload[]>("IncidentPayload.json", cliMode);
foreach (var payload in incidents)
{
try
{
incidentId = Guid.NewGuid().ToString();
var url = $"{azureConfigs[i].BaseUrl}/incidents/{incidentId}?api-version={azureConfigs[i].ApiVersion}";
var serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var request = new HttpRequestMessage(HttpMethod.Put, url)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var res = await response.Content.ReadAsStringAsync();
Console.WriteLine(JToken.Parse(res).ToString(Formatting.Indented));
continue;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception($"Something went wrong on {azureConfigs[i].InstanceName}: \n"
+ ex.Message);
}
}
}
/// <summary>
/// Update an incident by id
/// </summary>
/// <param name="incidentId"></param>
/// <returns></returns>
public async Task<string> UpdateIncident(string incidentId, int insId)
{
var incident = await GetIncidentById(incidentId, insId);
try
{
JObject jobject = JObject.Parse(incident);
var incidentObj = JsonConvert.DeserializeObject<IncidentPayload>(jobject.ToString());
var incidents = Utils.LoadPayload<IncidentPayload[]>("IncidentPayload.json", cliMode);
// Get the first one to update
var payload = incidents[0];
payload.Etag = incidentObj.Etag;
var url = $"{azureConfigs[insId].BaseUrl}/incidents/{incidentId}?api-version={azureConfigs[insId].ApiVersion}";
var serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var request = new HttpRequestMessage(HttpMethod.Put, url)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Batch update incidents for all instances or for a single instance
/// </summary>
/// <returns></returns>
public async Task BatchUpdateIncidents(int insId)
{
if (insId != -1)
{
await BatchUpdateIncidentsByInstance(insId);
}
else
{
for (var i = 0; i < azureConfigs.Length; i++)
{
await BatchUpdateIncidentsByInstance(i);
}
}
}
/// <summary>
/// Batch update incidents for a single instance
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
private async Task BatchUpdateIncidentsByInstance(int i)
{
var incidentUpdates = Utils.LoadPayload<IncidentPayload[]>("IncidentPayload.json", cliMode);
var insName = azureConfigs[i].InstanceName;
try
{
var incidents = await GetIncidentsByInstance(i, azureConfigs[i].FilterQuery);
foreach (var incidentObj in incidents)
{
// Get the first one to update
var payload = incidentUpdates[0];
var etag = (string)incidentObj["etag"];
payload.Etag = etag;
var incidentId = (string)incidentObj["name"];
var url = $"{azureConfigs[i].BaseUrl}/incidents/{incidentId}?api-version={azureConfigs[i].ApiVersion}";
var serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var request = new HttpRequestMessage(HttpMethod.Put, url)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var res = await response.Content.ReadAsStringAsync();
Console.WriteLine(JToken.Parse(res).ToString(Formatting.Indented));
continue;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
Console.WriteLine("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
}
catch (Exception ex)
{
throw new Exception($"Something went wrong on {insName}: \n"
+ ex.Message);
}
}
/// <summary>
/// Batch update incidents from json payload file
/// </summary>
/// <returns></returns>
public async Task BatchUpdateIncidentsFromJson()
{
var insId = Utils.SelectInstance(azureConfigs);
var incidentUpdates = Utils.LoadPayload<IncidentPayload[]>("GetIncidents0.json", cliMode);
var incidentIds = incidentUpdates.Select(x => x.Name);
Console.WriteLine("Updating incidents:");
Console.WriteLine("[{0}]", string.Join(", ", incidentIds));
foreach (var payload in incidentUpdates)
{
try
{
var incidentId = payload.Name;
var url = $"{azureConfigs[insId].BaseUrl}/incidents/{incidentId}?api-version={azureConfigs[insId].ApiVersion}";
var serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var request = new HttpRequestMessage(HttpMethod.Put, url)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var res = await response.Content.ReadAsStringAsync();
Console.WriteLine(JToken.Parse(res).ToString(Formatting.Indented));
continue;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
Console.WriteLine("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
Console.WriteLine("Something went wrong: \n" + ex.Message);
}
}
}
/// <summary>
/// Delete an incident by id
/// </summary>
/// <param name="incidentId"></param>
/// <returns></returns>
public async Task<string> DeleteIncident(string incidentId, int insId)
{
try
{
var url = $"{azureConfigs[insId].BaseUrl}/incidents/{incidentId}?api-version={azureConfigs[insId].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Delete, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
if (response.StatusCode == HttpStatusCode.NotFound)
throw new Exception("Not found, please create a new Incident first...");
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Get an incident by id
/// </summary>
/// <param name="incidentId"></param>
/// <param name="i"></param>
/// <returns></returns>
public async Task<string> GetIncidentById(string incidentId, int i)
{
try
{
var url = $"{azureConfigs[i].BaseUrl}/incidents/{incidentId}?api-version={azureConfigs[i].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Get all incidents for all instances or for a single instance
/// </summary>
/// <returns></returns>
public async Task GetIncidents(int insId)
{
if (insId != -1)
{
var values = await GetIncidentsByInstance(insId, azureConfigs[insId].FilterQuery);
var formattedRes = JsonConvert.SerializeObject(values, Formatting.Indented);
Utils.WriteJsonStringToFile($"GetIncidents_{azureConfigs[insId].InstanceName}.json", cliMode,formattedRes, false);
Console.WriteLine(formattedRes);
}
else
{
for (var i = 0; i < azureConfigs.Length; i++)
{
var values = await GetIncidentsByInstance(i, azureConfigs[i].FilterQuery);
var formattedRes = JsonConvert.SerializeObject(values, Formatting.Indented);
Utils.WriteJsonStringToFile($"GetIncidents_{azureConfigs[i].InstanceName}.json", cliMode, formattedRes, false);
Console.WriteLine(formattedRes);
continue;
}
}
}
/// <summary>
/// Get all incidents for a single instance
/// </summary>
/// <param name="i"></param>
/// <param name="filter"></param>
/// <returns></returns>
public async Task<JArray> GetIncidentsByInstance(int i, string filter = "")
{
try
{
var insName = azureConfigs[i].InstanceName;
var url = $"{azureConfigs[i].BaseUrl}/incidents?api-version={azureConfigs[i].ApiVersion}";
if (!string.IsNullOrEmpty(filter))
{
url = $"{url}{filter}";
}
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, i);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string res = await response.Content.ReadAsStringAsync();
JObject result = JsonConvert.DeserializeObject<JObject>(res);
var values = result["value"] as JArray;
if (values == null)
{
values = new JArray();
}
int callTimes = 1;
while (result.ContainsKey("nextLink") && callTimes < 100)
{
try
{
var nextLink = result["nextLink"].ToString();
request = new HttpRequestMessage(HttpMethod.Get, nextLink);
await authenticationService.AuthenticateRequest(request, i);
var nextResponse = await http.SendAsync(request);
if (nextResponse.IsSuccessStatusCode)
{
var newRes = await nextResponse.Content.ReadAsStringAsync();
JObject newResult = JsonConvert.DeserializeObject<JObject>(newRes);
result = newResult;
var newValues = result["value"] as JArray;
if (newValues == null)
{
newValues = new JArray();
}
foreach (var v in newValues)
{
values.Add(v);
}
callTimes++;
}
else
{
var err = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Error calling the nextLink on {insName}: \n" + err);
break;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error in parsing nextLink on {insName}: \n" + ex.Message);
break;
}
}
return values;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException($"Error calling the API {insName}: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception($"Something went wrong on {azureConfigs[i].InstanceName}: \n"
+ ex.Message);
}
}
/// <summary>
/// Create an incident comment for an incident
/// </summary>
/// <param name="incidentId"></param>
/// <returns></returns>
public async Task CreateIncidentComment(string incidentId)
{
var insId = Utils.SelectInstance(azureConfigs);
var comments = Utils.LoadPayload<IncidentCommentPayload[]>("IncidentCommentPayload.json", cliMode);
foreach (var payload in comments)
{
try
{
commentId = Guid.NewGuid().ToString();
var url = $"{azureConfigs[insId].BaseUrl}/incidents/{incidentId}/comments/{commentId}?api-version={azureConfigs[insId].ApiVersion}";
var serialized = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
});
var request = new HttpRequestMessage(HttpMethod.Put, url)
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var res = await response.Content.ReadAsStringAsync();
Console.WriteLine(JToken.Parse(res).ToString(Formatting.Indented));
continue;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
}
/// <summary>
/// Get an incident comment by id
/// </summary>
/// <param name="incidentId"></param>
/// <param name="commentId"></param>
/// <returns></returns>
public async Task<string> GetIncidentCommentById(string incidentId, string commentId, int insId)
{
try
{
var url = $"{azureConfigs[insId].BaseUrl}/incidents/{incidentId}/comments/{commentId}?api-version={azureConfigs[insId].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsStringAsync();
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
/// <summary>
/// Get all incident comments under an incident
/// </summary>
/// <param name="incidentId"></param>
/// <returns></returns>
public async Task<string> GetAllIncidentComments(string incidentId, int insId)
{
try
{
var url = $"{azureConfigs[insId].BaseUrl}/incidents/{incidentId}/comments?api-version={azureConfigs[insId].ApiVersion}";
var request = new HttpRequestMessage(HttpMethod.Get, url);
await authenticationService.AuthenticateRequest(request, insId);
var http = new HttpClient();
var response = await http.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string res = await response.Content.ReadAsStringAsync();
JObject result = JsonConvert.DeserializeObject<JObject>(res);
var values = result["value"] as JArray;
if (values == null)
{
values = new JArray();
}
int callTimes = 1;
while (result.ContainsKey("nextLink") && callTimes < 100)
{
try
{
var nextLink = result["nextLink"].ToString();
request = new HttpRequestMessage(HttpMethod.Get, nextLink);
await authenticationService.AuthenticateRequest(request, insId);
var nextResponse = await http.SendAsync(request);
if (nextResponse.IsSuccessStatusCode)
{
var newRes = await nextResponse.Content.ReadAsStringAsync();
JObject newResult = JsonConvert.DeserializeObject<JObject>(newRes);
result = newResult;
var newValues = result["value"] as JArray;
if (newValues == null)
{
newValues = new JArray();
}
foreach (var v in newValues)
{
values.Add(v);
}
callTimes++;
}
else
{
var err = await response.Content.ReadAsStringAsync();
Console.WriteLine("Error calling the nextLink: \n" + err);
break;
}
}
catch (Exception ex)
{
Console.WriteLine("Error in parsing nextLink: \n" + ex.Message);
break;
}
}
Utils.WriteJsonStringToFile($"GetAllIncidentComments_{azureConfigs[insId].InstanceName}.json", cliMode, JsonConvert.SerializeObject(values, Formatting.Indented), false);
return res;
}
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error calling the API: \n" +
JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
catch (Exception ex)
{
throw new Exception("Something went wrong: \n" + ex.Message);
}
}
}
}

Просмотреть файл

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace AzureSentinel_ManagementAPI.Incidents.Models.Comments
{
public class IncidentCommentPayload
{
[JsonProperty("properties")]
public IncidentCommentPropertiesPayload PropertiesPayload { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,7 @@
namespace AzureSentinel_ManagementAPI.Incidents.Models.Comments
{
public class IncidentCommentPropertiesPayload
{
public string Message { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace AzureSentinel_ManagementAPI.Incidents.Models
{
public class IncidentAdditionalData
{
public int AlertsCount { get; set; }
public int BookmarksCount { get; set; }
public int CommentsCount { get; set; }
public string[] AlertProductNames { get; set; }
public string[] Tactics { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace AzureSentinel_ManagementAPI.Incidents.Models
{
public class IncidentOwner
{
public string ObjectId { get; set; }
public string Email { get; set; }
public string AssignedTo { get; set; }
public string UserPrincipalName { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,17 @@
using Newtonsoft.Json;
using System.Diagnostics.Tracing;
namespace AzureSentinel_ManagementAPI.Incidents.Models
{
public class IncidentPayload
{
[JsonProperty("properties")]
public IncidentPropertiesPayload PropertiesPayload { get; set; }
[JsonProperty("etag")]
public string Etag { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,28 @@
using AzureSentinel_ManagementAPI.Infrastructure.SharedModels.Enums;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace AzureSentinel_ManagementAPI.Incidents.Models
{
public class IncidentPropertiesPayload
{
[JsonConverter(typeof(StringEnumConverter))]
public Severity Severity { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public IncidentStatus Status { get; set; }
public string Title { get; set; }
public string Classification { get; set; }
public string ClassificationComment { get; set; }
public string ClassificationReason { get; set; }
public string Description { get; set; }
[JsonProperty("owner")]
public IncidentOwner Owner { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,9 @@
namespace AzureSentinel_ManagementAPI.Incidents.Models
{
public enum IncidentStatus
{
New,
Active,
Closed
}
}

Просмотреть файл

@ -0,0 +1,55 @@
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using AzureSentinel_ManagementAPI.Infrastructure.Configuration;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
namespace AzureSentinel_ManagementAPI.Infrastructure.Authentication
{
//Access token class to authenticate and obtain AAD Token for future calls
public class AuthenticationService
{
private ClientCredential credential;
private AuthenticationContext authContext;
private readonly AzureSentinelApiConfiguration[] azureConfigs;
public AuthenticationService(AzureSentinelApiConfiguration[] azureConfig)
{
azureConfigs = azureConfig;
}
/// <summary>
/// Get token by instance id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<AuthenticationResult> GetToken(int id)
{
try
{
var azureConfig = azureConfigs[id];
authContext = new AuthenticationContext("https://login.microsoftonline.com/" + azureConfig.TenantId);
credential = new ClientCredential(azureConfig.AppId, azureConfig.AppSecret);
return
await authContext.AcquireTokenAsync("https://management.azure.com", credential);
}
catch (Exception ex)
{
throw new Exception("Error Acquiring Access Token: \n" + ex.Message);
}
}
/// <summary>
/// Get authorization token by instance and make the request authorized
/// </summary>
/// <param name="request"></param>
/// <param name="id"></param>
/// <returns></returns>
public async Task AuthenticateRequest(HttpRequestMessage request, int id)
{
var token = await GetToken(id);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);
}
}
}

Просмотреть файл

@ -0,0 +1,27 @@
namespace AzureSentinel_ManagementAPI.Infrastructure.Configuration
{
public class AzureSentinelApiConfiguration
{
public string InstanceName { get; set; }
public string TenantId { get; set; }
public string AppId { get; set; }
public string AppSecret { get; set; }
public string SubscriptionId { get; set; }
public string ResourceGroupName { get; set; }
public string WorkspaceName { get; set; }
public string ApiVersion { get; set; }
public string PreviewApiVersion { get; set; }
public string UrlTemplate { get; set; }
public string OperationInsightUrlTemplate { get; set; }
public string BaseUrl => string.Format(UrlTemplate, SubscriptionId, ResourceGroupName, WorkspaceName);
public string OperationInsightBaseUrl => string.Format(OperationInsightUrlTemplate, SubscriptionId, ResourceGroupName, WorkspaceName);
public string WorkflowId { get; set; }
public string FilterQuery { get; set; }
public string LastCreatedAction { get; set; }
public string LastCreatedAlertRule { get; set; }
public string LastCreatedBookmark { get; set; }
public string LastCreatedIncident { get; set; }
public string LastCreatedDataConnector { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,10 @@
namespace AzureSentinel_ManagementAPI.Infrastructure.SharedModels.Enums
{
public enum Severity
{
High,
Medium,
Low,
Informational
}
}

Просмотреть файл

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace AzureSentinel_ManagementAPI.Infrastructure.SharedModels
{
public class TupleList<T1, T2> : List<Tuple<T1, T2>>
{
public void Add(T1 item, T2 item2)
{
Add(new Tuple<T1, T2>(item, item2));
}
}
}

Просмотреть файл

@ -0,0 +1,44 @@
using System;
using System.Threading.Tasks;
using AzureSentinel_ManagementAPI.Actions;
using AzureSentinel_ManagementAPI.AlertRules;
using AzureSentinel_ManagementAPI.AlertRuleTemplates;
using AzureSentinel_ManagementAPI.Bookmarks;
using AzureSentinel_ManagementAPI.DataConnectors;
using AzureSentinel_ManagementAPI.Incidents;
using AzureSentinel_ManagementAPI.IncidentRelation;
using AzureSentinel_ManagementAPI.Infrastructure.Authentication;
using AzureSentinel_ManagementAPI.Infrastructure.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using AzureSentinel_ManagementAPI.Hunting;
namespace AzureSentinel_ManagementAPI
{
class Program
{
public static async Task Main(string[] args)
{
var rawConfig = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
var configurations = rawConfig.GetSection("Instances").Get<AzureSentinelApiConfiguration[]>();
var serviceProvider = new ServiceCollection()
.AddSingleton<AppHost>()
.AddSingleton<IConfigurationRoot>(rawConfig)
.AddSingleton<AzureSentinelApiConfiguration[]>(configurations)
.AddTransient<AlertRulesController>()
.AddTransient<AuthenticationService>()
.AddTransient<AlertRuleTemplatesController>()
.AddTransient<IncidentsController>()
.AddTransient<ActionsController>()
.AddTransient<BookmarksController>()
.AddTransient<DataConnectorsController>()
.AddTransient<IncidentRelationController>()
.AddTransient<SavedSearchController>()
.BuildServiceProvider();
await serviceProvider.GetService<AppHost>().Run(args);
}
}
}

Просмотреть файл

@ -0,0 +1,693 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace AzureSentinel_ManagementAPI {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resource1 {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resource1() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AzureSentinel_ManagementAPI.Resource1", typeof(Resource1).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to please input action id: .
/// </summary>
internal static string Action_Id_Prompt_Text {
get {
return ResourceManager.GetString("Action_Id_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to please input existing action rule id:.
/// </summary>
internal static string Action_Rule_Id_Prompt_Text {
get {
return ResourceManager.GetString("Action_Rule_Id_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Actions:.
/// </summary>
internal static string Actions_Menu {
get {
return ResourceManager.GetString("Actions_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Alert rule templates:.
/// </summary>
internal static string Alert_Rule_Template_Menu {
get {
return ResourceManager.GetString("Alert_Rule_Template_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Alert rules:.
/// </summary>
internal static string Alert_Rules_Menu {
get {
return ResourceManager.GetString("Alert_Rules_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 27.- BatchUpdateIncidents.
/// </summary>
internal static string Batch_Update_Incidents_Menu {
get {
return ResourceManager.GetString("Batch_Update_Incidents_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Please input existing bookmark id:.
/// </summary>
internal static string Bookmark_Prompt_Text {
get {
return ResourceManager.GetString("Bookmark_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Bookmarks:.
/// </summary>
internal static string Bookmarks_Menu {
get {
return ResourceManager.GetString("Bookmarks_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to please input comment id:.
/// </summary>
internal static string Comment_Id_Prompt_Text {
get {
return ResourceManager.GetString("Comment_Id_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Enter to continue.
/// </summary>
internal static string Continue_Prompt_Text {
get {
return ResourceManager.GetString("Continue_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 1.- CreateActionOfAlertRule - Attach a playbook to an analytic rule.
/// </summary>
internal static string Create_Action_Menu {
get {
return ResourceManager.GetString("Create_Action_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 15.- CreateBookmark.
/// </summary>
internal static string Create_Bookmark_Menu {
get {
return ResourceManager.GetString("Create_Bookmark_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 21.- CreateDataConnector.
/// </summary>
internal static string Create_DataConnector_Menu {
get {
return ResourceManager.GetString("Create_DataConnector_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 7.- CreateFusionAlertRule.
/// </summary>
internal static string Create_Fusion_Rule_Menu {
get {
return ResourceManager.GetString("Create_Fusion_Rule_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 28.- CreateIncidentComment.
/// </summary>
internal static string Create_Incident_Comment_Menu {
get {
return ResourceManager.GetString("Create_Incident_Comment_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 22.- CreateIncident.
/// </summary>
internal static string Create_Incident_Menu {
get {
return ResourceManager.GetString("Create_Incident_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 8.- CreateMicrosoftSecurityIncidentCreationAlertRule.
/// </summary>
internal static string Create_Incident_Rule_Menu {
get {
return ResourceManager.GetString("Create_Incident_Rule_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 31.- CreateIncidentRelation.
/// </summary>
internal static string Create_Relation_Menu {
get {
return ResourceManager.GetString("Create_Relation_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 36.- CreateSavedSearch.
/// </summary>
internal static string Create_Saved_Search_Menu {
get {
return ResourceManager.GetString("Create_Saved_Search_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 9.- CreateScheduledAlertRule.
/// </summary>
internal static string Create_Scheduled_Rule_Menu {
get {
return ResourceManager.GetString("Create_Scheduled_Rule_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to please input data connector id:.
/// </summary>
internal static string Dataconnector_Prompt_Text {
get {
return ResourceManager.GetString("Dataconnector_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DataConnectors:.
/// </summary>
internal static string DataConnectors_Menu {
get {
return ResourceManager.GetString("DataConnectors_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 2.- DeleteActionOfAlertRule2.
/// </summary>
internal static string Delete_Action_Menu {
get {
return ResourceManager.GetString("Delete_Action_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 16.- DeleteBookmark.
/// </summary>
internal static string Delete_Bookmark_Menu {
get {
return ResourceManager.GetString("Delete_Bookmark_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 20.- DeleteDataConnector.
/// </summary>
internal static string Delete_DataConnector_Menu {
get {
return ResourceManager.GetString("Delete_DataConnector_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 23.- DeleteIncident.
/// </summary>
internal static string Delete_Incident_Menu {
get {
return ResourceManager.GetString("Delete_Incident_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to please input incident id (the incident&apos;s &apos;name&apos; field, aka Internal Incident Id) you want to delete:.
/// </summary>
internal static string Delete_Incident_Prompt_Text {
get {
return ResourceManager.GetString("Delete_Incident_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 32.- DeleteIncidentRelation.
/// </summary>
internal static string Delete_Relation_Menu {
get {
return ResourceManager.GetString("Delete_Relation_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 10.- DeleteAlertRule.
/// </summary>
internal static string Delete_Rule_Menu {
get {
return ResourceManager.GetString("Delete_Rule_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 37.- DeleteSavedSearch.
/// </summary>
internal static string Delete_Saved_Search_Menu {
get {
return ResourceManager.GetString("Delete_Saved_Search_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Ctrl + C to exit.
/// </summary>
internal static string Exit_Text {
get {
return ResourceManager.GetString("Exit_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 3.- GetActionOfAlertRuleById.
/// </summary>
internal static string Get_Action_By_Id_Menu {
get {
return ResourceManager.GetString("Get_Action_By_Id_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 4.- GetAllActionsByAlertRule.
/// </summary>
internal static string Get_Actions_Menu {
get {
return ResourceManager.GetString("Get_Actions_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 17.- GetBookmarkById.
/// </summary>
internal static string Get_Bookmark_By_Id_Menu {
get {
return ResourceManager.GetString("Get_Bookmark_By_Id_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 18.- GetBookmarks.
/// </summary>
internal static string Get_Bookmarks_Menu {
get {
return ResourceManager.GetString("Get_Bookmarks_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 19.- GetDataConnectors.
/// </summary>
internal static string Get_DataConnectors_Menu {
get {
return ResourceManager.GetString("Get_DataConnectors_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 35.- GetIncidentEntitiesbyEntityType.
/// </summary>
internal static string Get_Entities_By_Type_Menu {
get {
return ResourceManager.GetString("Get_Entities_By_Type_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 12.- GetFusionAlertRule.
/// </summary>
internal static string Get_Fusion_Rule_Menu {
get {
return ResourceManager.GetString("Get_Fusion_Rule_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to please input fusion alert rule id:.
/// </summary>
internal static string Get_Fusion_Rule_Prompt_Text {
get {
return ResourceManager.GetString("Get_Fusion_Rule_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 24.- GetIncidentByInternalId.
/// </summary>
internal static string Get_Incident_By_Id_Menu {
get {
return ResourceManager.GetString("Get_Incident_By_Id_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 30.- GetIncidentCommentById.
/// </summary>
internal static string Get_Incident_Comment_Menu {
get {
return ResourceManager.GetString("Get_Incident_Comment_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 29.- GetAllIncidentComments.
/// </summary>
internal static string Get_Incident_Comments_Menu {
get {
return ResourceManager.GetString("Get_Incident_Comments_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to please input incident id (the incident&apos;s &apos;name&apos; field, aka Internal Incident Id) you want to get:.
/// </summary>
internal static string Get_Incident_Prompt_Text {
get {
return ResourceManager.GetString("Get_Incident_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 13.- GetMicrosoftSecurityIncidentCreationAlertRule.
/// </summary>
internal static string Get_Incident_Rule_Menu {
get {
return ResourceManager.GetString("Get_Incident_Rule_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to please input microsoft security incident alert rule id:.
/// </summary>
internal static string Get_Incident_Rule_Prompt_Text {
get {
return ResourceManager.GetString("Get_Incident_Rule_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 25.- GetIncidents.
/// </summary>
internal static string Get_Incidents_Menu {
get {
return ResourceManager.GetString("Get_Incidents_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 34.- GetIncidentRelationByName.
/// </summary>
internal static string Get_Relation_Menu {
get {
return ResourceManager.GetString("Get_Relation_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 33.- GetAllIncidentRelationsAndEntities.
/// </summary>
internal static string Get_Relations_Menu {
get {
return ResourceManager.GetString("Get_Relations_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 5.- GetAlertRuleTemplateById.
/// </summary>
internal static string Get_Rule_Template_By_Id_Menu {
get {
return ResourceManager.GetString("Get_Rule_Template_By_Id_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 6.- GetAlertRuleTemplates.
/// </summary>
internal static string Get_Rule_Templates_Menu {
get {
return ResourceManager.GetString("Get_Rule_Templates_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 11.- GetAllAlertRules.
/// </summary>
internal static string Get_Rules_Menu {
get {
return ResourceManager.GetString("Get_Rules_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 38.- GetSavedSearchById.
/// </summary>
internal static string Get_Saved_Search_Menu {
get {
return ResourceManager.GetString("Get_Saved_Search_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 39.- GetSavedSearches.
/// </summary>
internal static string Get_Saved_Searches_Menu {
get {
return ResourceManager.GetString("Get_Saved_Searches_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 14.- GetScheduledAlertRule.
/// </summary>
internal static string Get_Scheduled_Rule_Menu {
get {
return ResourceManager.GetString("Get_Scheduled_Rule_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to please input scheduled alert rule id:.
/// </summary>
internal static string Get_Scheduled_Rule_Prompt_Text {
get {
return ResourceManager.GetString("Get_Scheduled_Rule_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Incident comments:.
/// </summary>
internal static string Incident_Comments_Menu {
get {
return ResourceManager.GetString("Incident_Comments_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to please input existing incident id ((the incident&apos;s &apos;name&apos; field, aka Internal Incident Id):.
/// </summary>
internal static string Incident_Id_Prompt_Text {
get {
return ResourceManager.GetString("Incident_Id_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Incidents:.
/// </summary>
internal static string Incident_Menu {
get {
return ResourceManager.GetString("Incident_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Incident relations:.
/// </summary>
internal static string Incident_Relation_Menu {
get {
return ResourceManager.GetString("Incident_Relation_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid option... please, press enter to continue....
/// </summary>
internal static string Invalid_Option_Text {
get {
return ResourceManager.GetString("Invalid_Option_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Option:.
/// </summary>
internal static string Option {
get {
return ResourceManager.GetString("Option", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to related entities:.
/// </summary>
internal static string Related_Entities {
get {
return ResourceManager.GetString("Related_Entities", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to please input existing incident relation id:.
/// </summary>
internal static string Relation_Id_Prompt_Text {
get {
return ResourceManager.GetString("Relation_Id_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to please input alert rule id: .
/// </summary>
internal static string Rule_Id_Prompt_Text {
get {
return ResourceManager.GetString("Rule_Id_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to please input alert rule template id(Default is 157c0cfc-d76d-463b-8755-c781608cdc1a):.
/// </summary>
internal static string Rule_Template_Id_Prompt_Text {
get {
return ResourceManager.GetString("Rule_Template_Id_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to please input saved search id:.
/// </summary>
internal static string Saved_Search_Id_Prompt_Text {
get {
return ResourceManager.GetString("Saved_Search_Id_Prompt_Text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to SavedSearches/Hunting queries:.
/// </summary>
internal static string Saved_Searches_Menu {
get {
return ResourceManager.GetString("Saved_Searches_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Please an entity kind:.
/// </summary>
internal static string Select_Entity_Kind {
get {
return ResourceManager.GetString("Select_Entity_Kind", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 26.- UpdateIncident.
/// </summary>
internal static string Update_Incident_Menu {
get {
return ResourceManager.GetString("Update_Incident_Menu", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 40.- UpdateSavedSearch.
/// </summary>
internal static string Update_Saved_Search_Menu {
get {
return ResourceManager.GetString("Update_Saved_Search_Menu", resourceCulture);
}
}
}
}

Просмотреть файл

@ -0,0 +1,330 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Actions_Menu" xml:space="preserve">
<value>Actions:</value>
</data>
<data name="Action_Id_Prompt_Text" xml:space="preserve">
<value>please input action id: </value>
</data>
<data name="Action_Rule_Id_Prompt_Text" xml:space="preserve">
<value>please input existing action rule id:</value>
</data>
<data name="Alert_Rules_Menu" xml:space="preserve">
<value>Alert rules:</value>
</data>
<data name="Alert_Rule_Template_Menu" xml:space="preserve">
<value>Alert rule templates:</value>
</data>
<data name="Batch_Update_Incidents_Menu" xml:space="preserve">
<value>27.- BatchUpdateIncidents</value>
</data>
<data name="Bookmarks_Menu" xml:space="preserve">
<value>Bookmarks:</value>
</data>
<data name="Bookmark_Prompt_Text" xml:space="preserve">
<value>Please input existing bookmark id:</value>
</data>
<data name="Comment_Id_Prompt_Text" xml:space="preserve">
<value>please input comment id:</value>
</data>
<data name="Continue_Prompt_Text" xml:space="preserve">
<value>Enter to continue</value>
</data>
<data name="Create_Action_Menu" xml:space="preserve">
<value>1.- CreateActionOfAlertRule - Attach a playbook to an analytic rule</value>
</data>
<data name="Create_Bookmark_Menu" xml:space="preserve">
<value>15.- CreateBookmark</value>
</data>
<data name="Create_DataConnector_Menu" xml:space="preserve">
<value>21.- CreateDataConnector</value>
</data>
<data name="Create_Fusion_Rule_Menu" xml:space="preserve">
<value>7.- CreateFusionAlertRule</value>
</data>
<data name="Create_Incident_Comment_Menu" xml:space="preserve">
<value>28.- CreateIncidentComment</value>
</data>
<data name="Create_Incident_Menu" xml:space="preserve">
<value>22.- CreateIncident</value>
</data>
<data name="Create_Incident_Rule_Menu" xml:space="preserve">
<value>8.- CreateMicrosoftSecurityIncidentCreationAlertRule</value>
</data>
<data name="Create_Relation_Menu" xml:space="preserve">
<value>31.- CreateIncidentRelation</value>
</data>
<data name="Create_Saved_Search_Menu" xml:space="preserve">
<value>36.- CreateSavedSearch</value>
</data>
<data name="Create_Scheduled_Rule_Menu" xml:space="preserve">
<value>9.- CreateScheduledAlertRule</value>
</data>
<data name="DataConnectors_Menu" xml:space="preserve">
<value>DataConnectors:</value>
</data>
<data name="Dataconnector_Prompt_Text" xml:space="preserve">
<value>please input data connector id:</value>
</data>
<data name="Delete_Action_Menu" xml:space="preserve">
<value>2.- DeleteActionOfAlertRule2</value>
</data>
<data name="Delete_Bookmark_Menu" xml:space="preserve">
<value>16.- DeleteBookmark</value>
</data>
<data name="Delete_DataConnector_Menu" xml:space="preserve">
<value>20.- DeleteDataConnector</value>
</data>
<data name="Delete_Incident_Menu" xml:space="preserve">
<value>23.- DeleteIncident</value>
</data>
<data name="Delete_Incident_Prompt_Text" xml:space="preserve">
<value>please input incident id (the incident's 'name' field, aka Internal Incident Id) you want to delete:</value>
</data>
<data name="Delete_Relation_Menu" xml:space="preserve">
<value>32.- DeleteIncidentRelation</value>
</data>
<data name="Delete_Rule_Menu" xml:space="preserve">
<value>10.- DeleteAlertRule</value>
</data>
<data name="Delete_Saved_Search_Menu" xml:space="preserve">
<value>37.- DeleteSavedSearch</value>
</data>
<data name="Exit_Text" xml:space="preserve">
<value>Ctrl + C to exit</value>
</data>
<data name="Get_Actions_Menu" xml:space="preserve">
<value>4.- GetAllActionsByAlertRule</value>
</data>
<data name="Get_Action_By_Id_Menu" xml:space="preserve">
<value>3.- GetActionOfAlertRuleById</value>
</data>
<data name="Get_Bookmarks_Menu" xml:space="preserve">
<value>18.- GetBookmarks</value>
</data>
<data name="Get_Bookmark_By_Id_Menu" xml:space="preserve">
<value>17.- GetBookmarkById</value>
</data>
<data name="Get_DataConnectors_Menu" xml:space="preserve">
<value>19.- GetDataConnectors</value>
</data>
<data name="Get_Entities_By_Type_Menu" xml:space="preserve">
<value>35.- GetIncidentEntitiesbyEntityType</value>
</data>
<data name="Get_Fusion_Rule_Menu" xml:space="preserve">
<value>12.- GetFusionAlertRule</value>
</data>
<data name="Get_Fusion_Rule_Prompt_Text" xml:space="preserve">
<value>please input fusion alert rule id:</value>
</data>
<data name="Get_Incidents_Menu" xml:space="preserve">
<value>25.- GetIncidents</value>
</data>
<data name="Get_Incident_By_Id_Menu" xml:space="preserve">
<value>24.- GetIncidentByInternalId</value>
</data>
<data name="Get_Incident_Comments_Menu" xml:space="preserve">
<value>29.- GetAllIncidentComments</value>
</data>
<data name="Get_Incident_Comment_Menu" xml:space="preserve">
<value>30.- GetIncidentCommentById</value>
</data>
<data name="Get_Incident_Prompt_Text" xml:space="preserve">
<value>please input incident id (the incident's 'name' field, aka Internal Incident Id) you want to get:</value>
</data>
<data name="Get_Incident_Rule_Menu" xml:space="preserve">
<value>13.- GetMicrosoftSecurityIncidentCreationAlertRule</value>
</data>
<data name="Get_Incident_Rule_Prompt_Text" xml:space="preserve">
<value>please input microsoft security incident alert rule id:</value>
</data>
<data name="Get_Relations_Menu" xml:space="preserve">
<value>33.- GetAllIncidentRelationsAndEntities</value>
</data>
<data name="Get_Relation_Menu" xml:space="preserve">
<value>34.- GetIncidentRelationByName</value>
</data>
<data name="Get_Rules_Menu" xml:space="preserve">
<value>11.- GetAllAlertRules</value>
</data>
<data name="Get_Rule_Templates_Menu" xml:space="preserve">
<value>6.- GetAlertRuleTemplates</value>
</data>
<data name="Get_Rule_Template_By_Id_Menu" xml:space="preserve">
<value>5.- GetAlertRuleTemplateById</value>
</data>
<data name="Get_Saved_Searches_Menu" xml:space="preserve">
<value>39.- GetSavedSearches</value>
</data>
<data name="Get_Saved_Search_Menu" xml:space="preserve">
<value>38.- GetSavedSearchById</value>
</data>
<data name="Get_Scheduled_Rule_Menu" xml:space="preserve">
<value>14.- GetScheduledAlertRule</value>
</data>
<data name="Get_Scheduled_Rule_Prompt_Text" xml:space="preserve">
<value>please input scheduled alert rule id:</value>
</data>
<data name="Incident_Comments_Menu" xml:space="preserve">
<value>Incident comments:</value>
</data>
<data name="Incident_Id_Prompt_Text" xml:space="preserve">
<value>please input existing incident id ((the incident's 'name' field, aka Internal Incident Id):</value>
</data>
<data name="Incident_Menu" xml:space="preserve">
<value>Incidents:</value>
</data>
<data name="Incident_Relation_Menu" xml:space="preserve">
<value>Incident relations:</value>
</data>
<data name="Invalid_Option_Text" xml:space="preserve">
<value>Invalid option... please, press enter to continue...</value>
</data>
<data name="Option" xml:space="preserve">
<value>Option:</value>
</data>
<data name="Related_Entities" xml:space="preserve">
<value>related entities:</value>
</data>
<data name="Relation_Id_Prompt_Text" xml:space="preserve">
<value>please input existing incident relation id:</value>
</data>
<data name="Rule_Id_Prompt_Text" xml:space="preserve">
<value>please input alert rule id: </value>
</data>
<data name="Rule_Template_Id_Prompt_Text" xml:space="preserve">
<value>please input alert rule template id(Default is 157c0cfc-d76d-463b-8755-c781608cdc1a):</value>
</data>
<data name="Saved_Searches_Menu" xml:space="preserve">
<value>SavedSearches/Hunting queries:</value>
</data>
<data name="Saved_Search_Id_Prompt_Text" xml:space="preserve">
<value>please input saved search id:</value>
</data>
<data name="Select_Entity_Kind" xml:space="preserve">
<value>Please an entity kind:</value>
</data>
<data name="Update_Incident_Menu" xml:space="preserve">
<value>26.- UpdateIncident</value>
</data>
<data name="Update_Saved_Search_Menu" xml:space="preserve">
<value>40.- UpdateSavedSearch</value>
</data>
</root>

Просмотреть файл

@ -0,0 +1,8 @@
[
{
"properties": {
"triggerUri": "",
"logicAppResourceId": "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Logic/workflows/<enter_your_playbook_name>"
}
}
]

Просмотреть файл

@ -0,0 +1,26 @@
[
{
"properties": {
"displayName": "My bookmark",
"notes": "Found a suspicious activity",
"labels": [
"Tag1",
"Tag2"
],
"query": "SecurityEvent | where TimeGenerated > ago(1d) and TimeGenerated < ago(2d)",
"queryResult": "Security Event query result"
}
},
{
"properties": {
"displayName": "My bookmark2",
"notes": "Found a suspicious activity",
"labels": [
"Tag1",
"Tag2"
],
"query": "SecurityEvent | where TimeGenerated > ago(1d) and TimeGenerated < ago(2d)",
"queryResult": "Security Event query result"
}
}
]

Просмотреть файл

@ -0,0 +1,16 @@
[
{
"kind": "Office365",
"properties": {
"tenantId": "",
"dataTypes": {
"sharePoint": {
"state": "Enabled"
},
"exchange": {
"state": "Enabled"
}
}
}
}
]

Просмотреть файл

@ -0,0 +1,9 @@
[
{
"kind": "Fusion",
"properties": {
"enabled": true,
"alertRuleTemplateName": "f71aba3d-28fb-450b-b192-4e76a83015c8"
}
}
]

Просмотреть файл

@ -0,0 +1,7 @@
[
{
"properties": {
"message": "Some message"
}
}
]

Просмотреть файл

@ -0,0 +1,9 @@
[
{
"properties": {
"severity": "Low",
"status": "New",
"title": "Test Incident"
}
}
]

Просмотреть файл

@ -0,0 +1,20 @@
[
{
"properties": {
"Category": "Hunting Queries",
"DisplayName": "HuntingRule 1",
"Query": "SecurityEvent | where EventID == \"4688\" | where CommandLine contains \"-noni -ep bypass $\"",
"Tags": [
{
"Name": "Description",
"Value": "Test",
"Hunting": "Query"
},
{
"Name": "Tactics",
"Value": "Execution, Discovery"
}
]
}
}
]

Просмотреть файл

@ -0,0 +1,23 @@
[
{
"kind": "Scheduled",
"properties": {
"displayName": "Test Rule",
"description": "",
"severity": "High",
"enabled": true,
"tactics": [
"Persistence",
"LateralMovement"
],
"query": "AzureActivity | where isnotempty(OperationName)",
"queryFrequency": "PT1H",
"queryPeriod": "P2DT1H30M",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0,
"suppressionDuration": "PT1H",
"suppressionEnabled": false
},
"playbook": "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Logic/workflows/<enter_your_playbook_name>"
}
]

Просмотреть файл

@ -0,0 +1,10 @@
[
{
"kind": "MicrosoftSecurityIncidentCreation",
"properties": {
"productFilter": "Microsoft Cloud App Security",
"displayName": "MCAS alert rule",
"enabled": true
}
}
]

Просмотреть файл

@ -0,0 +1,190 @@
using AzureSentinel_ManagementAPI.Infrastructure.Configuration;
using Newtonsoft.Json;
using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Resources;
namespace AzureSentinel_ManagementAPI
{
static class Utils
{
private static ResourceManager resource = new ResourceManager("AzureSentinel_ManagementAPI.Resource1", Assembly.GetExecutingAssembly());
/// <summary>
/// Select one from enum type
/// </summary>
/// <typeparam name="TEnum"></typeparam>
/// <returns></returns>
public static TEnum SelectKind<TEnum>()
{
string[] kinds = System.Enum.GetNames(typeof(TEnum));
for (int i = 0; i < kinds.Length; i++)
{
Console.WriteLine($" {(i + 1).ToString()}. {kinds[i]}");
}
var len = kinds.Length;
var isValid = false;
var index = -1;
while (!isValid)
{
Console.WriteLine($"Please type in a number between 1 and {len.ToString()}");
var option = Console.ReadLine();
isValid = int.TryParse(option, out index);
isValid = isValid && index > 0 && index < len;
}
TEnum kind = (TEnum)Enum.Parse(typeof(TEnum), kinds[index - 1], true);
return kind;
}
/// <summary>
/// Get user input, return default value if no user input
/// </summary>
/// <param name="promptText"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
public static string GetInput(string promptText, string defaultValue = "")
{
Console.WriteLine(promptText);
var input = Console.ReadLine();
if (input.Trim() == string.Empty)
{
input = defaultValue;
}
return input;
}
/// <summary>
/// Select a specific instance
/// </summary>
/// <param name="configs"></param>
/// <returns></returns>
public static int SelectInstance(AzureSentinelApiConfiguration[] configs)
{
var isValid = false;
var index = -1;
int len = configs.Length;
if (len == 1)
return 0;
while (!isValid)
{
Console.WriteLine($"Please select instance, Please type in a number between 1 and {len.ToString()}:");
for(int i = 0; i < len; i++)
{
Console.WriteLine($"{i + 1}. {configs[i].InstanceName}");
}
var option = Console.ReadLine();
isValid = int.TryParse(option, out index);
isValid = isValid && index > 0 && index <= len;
}
return index-1;
}
/// <summary>
/// Select a specific instance to apply to all instances
/// </summary>
/// <param name="configs"></param>
/// <returns></returns>
public static int SelectInstanceOrApplyAll(AzureSentinelApiConfiguration[] configs)
{
int len = configs.Length;
if (len == 1)
return 0;
Console.WriteLine($"Please select an instance, Please type in a number between 1 and {len.ToString()}, Otherwise it would apply to all:");
for (int i = 0; i < len; i++)
{
Console.WriteLine($"{i + 1}. {configs[i].InstanceName}");
}
var option = Console.ReadLine();
int index;
bool isValid = int.TryParse(option, out index);
isValid = isValid && index > 0 && index <= len;
return isValid ? index - 1: -1;
}
/// <summary>
/// Write json output string to a json file
/// </summary>
/// <param name="fileName"></param>
/// <param name="jsonData"></param>
/// <param name="format"></param>
public static void WriteJsonStringToFile(string fileName, bool cliMode, string jsonData, bool format = true)
{
try
{
if (format)
{
var jsonObj = JsonConvert.DeserializeObject(jsonData);
jsonData = JsonConvert.SerializeObject(jsonObj, Formatting.Indented);
}
string projectPath = cliMode ? Directory.GetCurrentDirectory() :
Path.GetDirectoryName(Path.GetDirectoryName(
Path.GetDirectoryName(Directory.GetCurrentDirectory())));
string resultFolder = "Results";
string filePath = Path.Combine(projectPath, resultFolder, fileName);
System.IO.File.WriteAllText(filePath, jsonData);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
/// <summary>
/// Load payload from json file
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fileName"></param>
/// <returns></returns>
public static T LoadPayload<T>(string fileName, bool cliMode)
{
try {
Console.WriteLine($"Request body from {fileName}:");
string projectPath = cliMode ? Directory.GetCurrentDirectory() :
Path.GetDirectoryName(Path.GetDirectoryName(
Path.GetDirectoryName(Directory.GetCurrentDirectory())));
string fileFolderName = Path.GetFileNameWithoutExtension(fileName);
string resultFolder = Path.Combine("Templates", fileFolderName);
string filePath = Path.Combine(projectPath, resultFolder, fileName);
var data = System.IO.File.ReadAllText(filePath);
Console.WriteLine(data);
var incidentObj = JsonConvert.DeserializeObject<T>(data);
return incidentObj;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return default;
}
}
/// <summary>
/// Get localized text from resource file by key
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static string GetString(string key)
{
return resource.GetString(key, CultureInfo.CurrentCulture);
}
}
}

Просмотреть файл

@ -0,0 +1,40 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"Climode": false,
"Instances": [
{
"InstanceName": "",
"TenantId": "",
"AppId": "",
"AppSecret": "",
"SubscriptionId": "",
"ResourceGroupName": "",
"WorkspaceName": "",
"ApiVersion": "2020-01-01",
"PreviewApiVersion": "2019-01-01-preview",
"FilterQuery": "",
"UrlTemplate": "https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.OperationalInsights/workspaces/{2}/providers/Microsoft.SecurityInsights",
"OperationInsightUrlTemplate": "https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.OperationalInsights/workspaces/{2}"
},
{
"InstanceName": "",
"TenantId": "",
"AppId": "",
"AppSecret": "",
"SubscriptionId": "",
"ResourceGroupName": "",
"WorkspaceName": "",
"ApiVersion": "2020-01-01",
"PreviewApiVersion": "2019-01-01-preview",
"FilterQuery": "",
"UrlTemplate": "https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.OperationalInsights/workspaces/{2}/providers/Microsoft.SecurityInsights",
"OperationInsightUrlTemplate": "https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.OperationalInsights/workspaces/{2}"
}
]
}

Просмотреть файл

@ -0,0 +1,2 @@
REM xcopy "Templates\*.*" "bin\Debug\netcoreapp3.1\Templates" /K /D /H /E
xcopy "Templates\*.*" "bin\Release\netcoreapp3.1\publish\Templates" /K /D /H /E

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 69 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 88 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 38 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 77 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 87 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 53 KiB

Просмотреть файл

@ -0,0 +1,189 @@
# Azure Sentinel Management API C# Sample
Author: Chi Nguyen
## Description
This repo contains a C# .NET Core 3.1 console application to demonstrate how you can leverage the Azure Sentinel Management API to programmatically manage your Azure Sentinel workspace(s).
The application uses OAuth 2.0 client credentials flow on Microsoft Identity Platform for authentication. Essentially, the flow permits the application to use its own credentials, instead of impersonating a user, to authenticate when calling the Azure Sentinel API. Please refer to [OAuth 2.0 client credentials flow](https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow) for more details on the authentication mechanism.
What can you use this solution for?
1. For starters, it serves as a quickstart to help you set up a custom application to call the Azure Sentinel REST API to automate your management tasks on Azure Sentinel. Some examples include importing and exporting analytic rules, disable and enable an analytic rules, updating multiple incidents at once across workspaces, and many other use cases. The app can handle multiple Azure Sentinel workspaces cross-tenant at once, so if you manage multiple workspaces for multiple clients as a MSSP, this can be a solution.
2. Additionally, the solution can be combined and integrated with other tools within your organization to achieve a more comprehensive security solution.
Please refer to the documentation and specs below for more details on Azure Sentinel API.
* [Sentinel API documentation](https://docs.microsoft.com/rest/api/securityinsights/)
* [Saved Searches/Hunting queries API documentation](https://docs.microsoft.com/rest/api/loganalytics/savedsearches)
* [Sentinel API specs - Stable: 2020-01-01 version](https://github.com/Azure/azure-rest-api-specs/blob/master/specification/securityinsights/resource-manager/Microsoft.SecurityInsights/stable/2020-01-01/SecurityInsights.json)
* [Sentinel API specs - Preview: 2019-01-01 version](https://github.com/Azure/azure-rest-api-specs/blob/master/specification/securityinsights/resource-manager/Microsoft.SecurityInsights/preview/2019-01-01-preview/SecurityInsights.json)
This sample provides examples of the following Sentinel API operation groups.
| Entity | Operation | API version |
| -----------|-----------|--------|
| Alert Rule | Get, Create, Update, Delete | Stable |
| Alert Rule Templates | Get | Stable |
| Data Connector | Get, Create, Delete | Stable |
| Incidents | Get, Create, Update, Delete | Stable|
| Incident Comments | Get, Create | Stable |
| Incident Relation | Get, Create, Update, Delete | Preview |
| Bookmarks | Get, Create, Delete | Stable|
| Playbooks | Get, Create, Delete| Stable|
| Hunting Queries | Get, Create, Update, Delete | Stable |
## Prerequisites
To configure the tool, the following assembly is required to authenticate and make requests to the Azure Sentinel Management API.
### _Azure Sentinel_
1. **Active Azure Subscription**, if you don't have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin.
2. **Log Analytics workspace**. If you don't have one, [create a Log Analytics workspace](https://docs.microsoft.com/azure/azure-monitor/learn/quick-create-workspace).
3. Obtain **WorkSpaceId** and **WorkspaceKey** following these steps. Copy this workspace Id and Key as you will need them later to run the application.
1. In the Azure portal, search for and select **Log Analytics workspaces**
1. In your list of Log Analytics workspaces, select the workspace you intend on configuring the agent to report to.
1. Select **Advanced Settings**.
4. To enable Azure Sentinel, you need **Contributor** permissions to the subscription in which the Azure Sentinel workspace resides. Learn more to [onboard Azure Sentinel](https://docs.microsoft.com/azure/sentinel/quickstart-onboard#enable-azure-sentinel-).
5. To use Azure Sentinel, you need either **contributor** or **reader** permissions on the resource group that the workspace belongs to.
### _AAD Application Registration_
To configure the sample, you'll need to register a new Azure Active Directory application (Service Principal) in the Microsoft [Application Registration Portal](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps).
Follow these steps to register a new application:
1. Sign in to the [Application Registration Portal](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps) using either your personal or work or school account.
2. Choose **New registration**.
3. Enter an application name, and choose **Register**.
4. Open the **Overview** page of your app. Copy and save the **Application Id** field. You will need it later to complete the configuration process.
5. Under **Certificates & secrets**, choose **New client secret** and add a quick description. A new secret will be displayed in the **Value** column. Copy this password. You will need it later to complete the configuration process and it will not be shown again.
### _Permissions_
Adhering to the principle of least privilege, always grant the lowest possible permissions required to your API.
1. Azure Sentinel permissions
1. To access your Azure Sentinel workspace, your app needs **Sentinel Contributor / Azure Sentinel Responder / Azure Sentinel Reader**” permissions. If you have multiple Azure Sentinel workspaces, repeat these steps for each of the workspaces.
1. In the Resource Group where Azure Sentinel has been built, open **Access Control (IAM)** setting.
2. Select **Add a role assignment**.
3. Under **Role** search box, search for and select one of the roles above.
4. Under **Select** search box, search for your app name and select it.
5. Select **Save** to finish the role assignment.
![Azure Sentinel permission](./Images/AzureSentinel-permission.png)
2. Logic Apps permissions
1. To get/enable/disable a security playbook associated with an analytic rule, your app needs **Logic App Contributor / Logic App Operator** permission. If you have multiple Azure Sentinel workspaces, repeat these steps for each of the workspaces.
1. In the Resource Group where Azure Sentinel has been built, open **Access Control (IAM)** setting.
2. Select **Add a role assignment**.
3. Under **Role** search box, search for and select one of the roles above.
4. Under **Select** search box, search for your app name and select it.
5. Select **Save** to finish the role assignment.
![Logic Apps permission](./Images/LogicApp-permission.png)
3. If you have Azure Sentinel workspaces in multiple tenants, then follow these additional steps to grant other tenants access to your app.
1. Provide your Administrator your **Application Id** that you get in the previous steps. Your organizations Admin (or other user authorized to grant consent for organizational resources) is required to grant consent to the application.
2. As the tenant Admin for your organization, open a browser window and craft the following URL in the address bar. Make sure to replace APPLICATION_ID with the **application Id** of your app, then select **Accept**.
```https://login.microsoftonline.com/common/adminconsent?client_id=APPLICATION_ID```
3. After logging in, the tenant Admin will be presented with a dialog like the following (depending on which permissions the application is requesting):
![Scope consent dialog](./Images/admin-consent.png)
4. When the tenant Admin agrees to this dialog, he/she is granting consent for all users of their organization to use this application. If a message like the following screenshot appears, then ignore it. That is expected for a daemon app without a Redirect URI.
![After admin consent dialog](./Images/After-consent.png)
### _Azure Key Vault_
Although Azure Key Vault is an optional component, we highly recommend it.
TODO: Wire up Azure Key Vault to manage application and Azure Sentinel credentials in this solution.
## Setup
1. Create a new folder called **Samples** in your local machine.
2. Open a command prompt.
3. In the command prompt, navigate to this **Samples** folder. Clone Azure Sentinel repository to the folder by running this command: git clone https://github.com/Azure/Azure-Sentinel.git
4. In explorer, navigate to the cloned repo, then navigate to the **Tools** directory, select **Sample Code**. Then in **AzureSentinel-ManagementAPICsharp** folder, open **AzureSentinel_ManagementAPI.sln** in Visual Studio 2017 or later.
5. Install necessary dependencies: In Visual Studio, right click the **AzureSentinel_ManagementAPI** solution, then select **Restore NuGet Packages**.
6. Open **Appsettings.json** file, fill in the values of the following variables using the information you've saved from the [Prerequisites](#Prerequisites) section.
1. **InstanceName**: a name of your choice for your Azure Sentinel.
2. **TenantId**: ID value of your tenant where your app resides.
3. **AppId**: your application ID.
4. **AppSecret**: your application secret.
5. **SubscriptionId**: ID value of your subscription where your app resides.
6. **ResourceGroupName**: name of your resource group where you've granted Azure Sentinel and Logic Apps permissions to your app.
7. **WorkspaceName**: name of your Azure Sentinel workspace.
8. **FilterQuery**: customize the filter clause to your needs. In the following example, I filter incidents by incident **LastModifiedDateTime**, **Status**, and an analyst who the incident's assigned to.
9. Repeat step 1-8 above if you have multiple Azure Sentinel instances.
```
"Instances": [
{
"InstanceName": "<enter_your_AzureSentinel_instance_name>",
"TenantId": "<enter_your_tenantID>",
"AppId": "<enter_your_appID>",
"AppSecret": "<enter_your_appSecret>",
"SubscriptionId": "<enter_your_subscriptionID>",
"ResourceGroupName": "<enter_your_resourcegroup_name>",
"WorkspaceName": "<enter_your_AzureSentinel_workspace_name>",
"ApiVersion": "2020-01-01",
"PreviewApiVersion": "2019-01-01-preview",
"FilterQuery": "&$filter=properties/lastModifiedTimeUtc gt 2020-08-10T07:00:00.000000Z and properties/status eq 'Active' and properties/owner/assignedTo eq 'analyst@contoso.com'",
"UrlTemplate": "https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.OperationalInsights/workspaces/{2}/providers/Microsoft.SecurityInsights"
},
```
## Run The Application
Follow these steps to run the application in Visual Studio.
1. The **Templates** folder contains templates of the request body or payload for **CREATE/UPDATE** API requests in json format. Modify the payload for each entity you'd like to make a request. For example, **IncidentPayload.json** file is where you can edit your payload you'd like to send when making for CreateIncidents request.
2. Choose the **AzureSentinel_ManagementAPI** button on the toolbar to run the application in Debug mode. (Or, you can press F5.)
![Start application](./Images/start-button.png)
3. Once the app is running, a menu like the folowing screenshot appears in the console window.
1. Each API call is represented by each option in the menu. Enter a number corresponding with your API request in the console. For example, next to the **Option**, enter number **22** to create an incident. The content of the incident comes from the **IncidentPayload.json** file you've filled in.
2. Press ENTER or Ctrl + C if you want to close the application.
![View Console](./Images/console-menu.png)
4. The **Results** folder contains responses from all GET requests you make. For example, when you make a GetIncidents request, the app will export the incidents returned from the request to a file starting with **GetIncidents.json** in the Results folder.
## Important Notes
The API currently has some limitations, and here are a few things to note regarding this sample.
1. Data Connectors: For CREATE data connector request to enable a data connection in Azure Sentinel, the API currently supports **User-Delegated** mode of authentication only, which requires a user sign-in. This solution is using **Application-Only** authentication, as the app is a daemon service running in the background without a user sign-in, so it isn't currently working if you enable a data connector via this solution.
2. Incidents: To close an incident (option 26 or 27 in the option menu), these following fields must be filled with values. Go to **Templates** folder, open **IncidentPayload.json** file, and make the modification accordingly.
"classification": "",
"classificationComment": "",
"classificationReason": ""
3. Actions: To select a playbook for an analytic rule, you can either use Option 1 (Create Action) or option 9 (Create Alert rule) in the menu. Open your **ActionPayload.json** file in **templates** folder and ill in values of these fields:
1. **triggerUri**: Callback URL for your playbook trigger. To get this URL, make a POST request to [WorkflowTrigger CallbackUrl](https://docs.microsoft.com/rest/api/logic/workflowtriggers/listcallbackurl). You can easily obtain the value using this [code-try](https://docs.microsoft.com/rest/api/logic/workflowtriggers/listcallbackurl#code-try-0). The **value** field in the response should be the URL.
2. **logicAppResourceId**: Azure ARM resource ID of your logic app. Fill in the Subscription Id, Resource Group Name, and Playbook Name in the value.
```[
{
"properties": {
"triggerUri": "<enter_Trigger_URI_of_your_playbook>",
"logicAppResourceId": "/subscriptions/<subscrition-ID>/resourceGroups/<resource-group-name>/providers/Microsoft.Logic/workflows/<logic-app-name>"
}
},
```
### Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
### Copyright
Copyright (c) 2020 Microsoft. All rights reserved.