diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/.gitignore b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/.gitignore new file mode 100644 index 0000000000..a39c1ad538 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/.gitignore @@ -0,0 +1,2 @@ +.idea +.vs \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI.sln b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI.sln new file mode 100644 index 0000000000..404aac89c3 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI.sln @@ -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 diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/.gitignore b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/.gitignore new file mode 100644 index 0000000000..6e6ebbe817 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/.gitignore @@ -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 diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/.vscode/launch.json b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/.vscode/launch.json new file mode 100644 index 0000000000..82b16db3ff --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/.vscode/launch.json @@ -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}" + } + ] +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/.vscode/tasks.json b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/.vscode/tasks.json new file mode 100644 index 0000000000..64c4bad693 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/.vscode/tasks.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Actions/ActionsController.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Actions/ActionsController.cs new file mode 100644 index 0000000000..8af2c40988 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Actions/ActionsController.cs @@ -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("Climode"); + } + + /// + /// Create action from payload json + /// + /// + /// + public async Task CreateAction(string ruleId, int insId, string logicAppResourceId = "") + { + ActionRequestPayload[] actions = Utils.LoadPayload("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); + } + } + } + + /// + /// Delete an action by rule id and action id + /// + /// + /// + /// + public async Task 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); + } + } + + /// + /// Get an action by rule id and action id + /// + /// + /// + /// + public async Task 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); + } + } + + /// + /// Get an action by rule id and action id by instance + /// + /// + /// + /// + public async Task 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)); + } + + /// + /// Get all actions under a rule + /// + /// + /// + public async Task 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(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(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); + } + } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Actions/Models/ActionRequestPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Actions/Models/ActionRequestPayload.cs new file mode 100644 index 0000000000..541d368644 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Actions/Models/ActionRequestPayload.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Actions/Models/ActionRequestPropertiesPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Actions/Models/ActionRequestPropertiesPayload.cs new file mode 100644 index 0000000000..0fdd90d473 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Actions/Models/ActionRequestPropertiesPayload.cs @@ -0,0 +1,8 @@ +namespace AzureSentinel_ManagementAPI.Actions.Models +{ + public class ActionRequestPropertiesPayload + { + public string LogicAppResourceId { get; set; } + public string TriggerUri { get; set; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRuleTemplates/AlertRuleTemplatesController.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRuleTemplates/AlertRuleTemplatesController.cs new file mode 100644 index 0000000000..0b51d8d57b --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRuleTemplates/AlertRuleTemplatesController.cs @@ -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("Climode"); + } + + /// + /// Get alert rule templates for all instances or for a single instance + /// + /// + 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); + } + } + } + + /// + /// Get alert rule templates for a single instance + /// + /// + /// + 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); + } + + } + + /// + /// Get alert rule template by id + /// + /// + /// + public async Task 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); + } + } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/AlertRulesController.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/AlertRulesController.cs new file mode 100644 index 0000000000..53e6695a91 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/AlertRulesController.cs @@ -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("Climode"); + } + + /// + /// Create funsion alert rule for all instances or for a single instance + /// + /// + 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); + } + } + } + + /// + /// Create funsion alert rule for a single instance + /// + /// + /// + public async Task CreateFusionAlertRuleByInstance(int i) + { + var alertRules = Utils.LoadPayload("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); + } + } + } + + /// + /// Create Microsoft security incident creation alert rule for all instances or for a single instance + /// + /// + 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); + } + } + } + + /// + /// Create Microsoft security incident creation alert rule for a single instance + /// + /// + /// + public async Task CreateMSSecurityIncidentAlertRuleByInstance(int i) + { + var alertRules = Utils.LoadPayload("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); + } + } + } + + /// + /// Create scheduled alert rule for all instances or for a single instance + /// + /// + 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); + } + } + } + + /// + /// Create scheduled alert rule for a single instance + /// + /// + /// + public async Task CreateScheduledAlertRuleByInstance(int i, ActionsController actionsController) + { + var alertRules = Utils.LoadPayload("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); + } + } + } + + /// + /// Delete an alert rule by id + /// + /// + /// + public async Task 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); + } + } + + /// + /// Get funsion alert rule by id + /// + /// + /// + public async Task GetFusionAlertRule(string ruleId, int insId) + { + return await GetAlertRuleByName(ruleId, insId); + } + + /// + /// Get Microsoft security incident creation alert rule by id + /// + /// + /// + public async Task GetMicrosoftSecurityIdentityCreationAlertRule(string ruleId, int insId) + { + return await GetAlertRuleByName(ruleId, insId); + } + + /// + /// Get scheduled alert rule by id + /// + /// + /// + public async Task GetScheduledAlertRule(string ruleId, int insId) + { + return await GetAlertRuleByName(ruleId, insId); + } + + /// + /// Get alert rule by rule id + /// + /// + /// + private async Task 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); + } + } + + /// + /// Get all alert rules for all instances or for a single instance + /// + /// + 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); + } + } + } + + /// + /// Get alert rules for a single instance + /// + /// + /// + 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(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(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); + } + } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/AlertRuleKind.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/AlertRuleKind.cs new file mode 100644 index 0000000000..f8dccbc8e3 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/AlertRuleKind.cs @@ -0,0 +1,9 @@ +namespace AzureSentinel_ManagementAPI.AlertRules.Models +{ + public enum AlertRuleKind + { + Scheduled, + MicrosoftSecurityIncidentCreation, + Fusion + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/AlertRulePayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/AlertRulePayload.cs new file mode 100644 index 0000000000..d07774a07e --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/AlertRulePayload.cs @@ -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; } + + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/FusionAlertRulePayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/FusionAlertRulePayload.cs new file mode 100644 index 0000000000..46f790be82 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/FusionAlertRulePayload.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/FusionAlertRulePropertiesPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/FusionAlertRulePropertiesPayload.cs new file mode 100644 index 0000000000..a200a41832 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/FusionAlertRulePropertiesPayload.cs @@ -0,0 +1,8 @@ +namespace AzureSentinel_ManagementAPI.AlertRules.Models +{ + public class FusionAlertRulePropertiesPayload + { + public string AlertRuleTemplateName { get; set; } + public bool Enabled { get; set; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/ProductFilter.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/ProductFilter.cs new file mode 100644 index 0000000000..278281cd82 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/ProductFilter.cs @@ -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"; + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/ScheduledAlertRulePayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/ScheduledAlertRulePayload.cs new file mode 100644 index 0000000000..06615b4638 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/ScheduledAlertRulePayload.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/ScheduledAlertRulePropertiesPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/ScheduledAlertRulePropertiesPayload.cs new file mode 100644 index 0000000000..ea2081bc7d --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/ScheduledAlertRulePropertiesPayload.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/SecurityIncidentCreationAlertRulePayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/SecurityIncidentCreationAlertRulePayload.cs new file mode 100644 index 0000000000..f33528868b --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/SecurityIncidentCreationAlertRulePayload.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/SecurityIncidentCreationAlertRulePropertiesPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/SecurityIncidentCreationAlertRulePropertiesPayload.cs new file mode 100644 index 0000000000..255f495d59 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/SecurityIncidentCreationAlertRulePropertiesPayload.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/TriggerOperator.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/TriggerOperator.cs new file mode 100644 index 0000000000..77be3a71b6 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AlertRules/Models/TriggerOperator.cs @@ -0,0 +1,10 @@ +namespace AzureSentinel_ManagementAPI.AlertRules.Models +{ + public enum TriggerOperator + { + GreaterThan, + LessThan, + Equal, + NotEqual + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AppHost.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AppHost.cs new file mode 100644 index 0000000000..22aeec6974 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AppHost.cs @@ -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> 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("Climode"); + + string exeName = "AzureSentinel_ManagementAPI.exe"; + cmdArgs = new TupleList + { + {$": {exeName} 1 [instanceId]", 3 }, + {$": {exeName} 2 [instanceId]", 4 }, + {$": {exeName} 3 [instanceId]", 4}, + {$": {exeName} 4 [instanceId]", 3}, + {$": {exeName} 5 [instanceId]", 3}, + {$": {exeName} 6 [instanceId]", 2}, + {$": {exeName} 7 [instanceId]", 2}, + {$": {exeName} 8 [instanceId]", 2}, + {$": {exeName} 9 [instanceId]", 2}, + {$": {exeName} 10 [instanceId]", 3}, + {$": {exeName} 11 [instanceId]", 2}, + {$": {exeName} 12 [instanceId]", 3}, + {$": {exeName} 13 [instanceId]", 3}, + {$": {exeName} 14 [instanceId]", 3}, + {$": {exeName} 15 [instanceId]", 2}, + {$": {exeName} 16 [instanceId]", 3}, + {$": {exeName} 17 [instanceId]", 3}, + {$": {exeName} 18 [instanceId]", 2}, + {$": {exeName} 19 [instanceId]", 2}, + {$": {exeName} 20 [instanceId]", 3}, + {$": {exeName} 21 [instanceId]", 2}, + {$": {exeName} 22 [instanceId]", 2}, + {$": {exeName} 23 [instanceId]", 3}, + {$": {exeName} 24 [instanceId]", 3}, + {$": {exeName} 25 [instanceId]", 2}, + {$": {exeName} 26 [instanceId]", 3}, + {$": {exeName} 27 [instanceId]", 2}, + {$": {exeName} 28 [instanceId]", 3}, + {$": {exeName} 29 [instanceId]", 3}, + {$": {exeName} 30 [instanceId]", 4}, + {$": {exeName} 31 [instanceId]", 4}, + {$": {exeName} 32 [instanceId]", 4}, + {$": {exeName} 33 [instanceId]", 3}, + {$": {exeName} 34 [instanceId]", 4}, + {$": {exeName} 35 [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(); + } + } +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AzureSentinel_ManagementAPI.csproj b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AzureSentinel_ManagementAPI.csproj new file mode 100644 index 0000000000..a7fff16b49 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/AzureSentinel_ManagementAPI.csproj @@ -0,0 +1,45 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + + Always + + + + + + + + + + + True + True + Resource1.resx + + + + + + ResXFileCodeGenerator + Resource1.Designer.cs + + + + + + diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Bookmarks/BookmarksController.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Bookmarks/BookmarksController.cs new file mode 100644 index 0000000000..d89533821c --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Bookmarks/BookmarksController.cs @@ -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("Climode"); + } + + /// + /// Create a bookmark for all instances or for a single instance + /// + /// + 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); + } + } + } + + /// + /// Create a bookmark for a single instance + /// + /// + /// + private async Task CreateBookmarkByInstance(int i) + { + var bookmarks = Utils.LoadPayload("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); + } + } + } + + /// + /// Delete a bookmark by id + /// + /// + /// + public async Task 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); + } + } + + /// + /// Get a bookmark by id + /// + /// + /// + public async Task 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); + } + } + + /// + /// Get all bookmarks for all instances or for a single instance + /// + /// + /// + 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); + } + } + } + + /// + /// Get bookmarks for a single instance + /// + /// + /// + 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(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(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); + } + } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Bookmarks/Models/BookmarkPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Bookmarks/Models/BookmarkPayload.cs new file mode 100644 index 0000000000..1aaa94a6d0 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Bookmarks/Models/BookmarkPayload.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Bookmarks/Models/BookmarkPropertiesPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Bookmarks/Models/BookmarkPropertiesPayload.cs new file mode 100644 index 0000000000..c0952c39d6 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Bookmarks/Models/BookmarkPropertiesPayload.cs @@ -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 Labels { get; set; } + public string QueryResult { get; set; } + + public IncidentInfo IncidentInfo { get; set; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Bookmarks/Models/IncidentInfo.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Bookmarks/Models/IncidentInfo.cs new file mode 100644 index 0000000000..51663a0281 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Bookmarks/Models/IncidentInfo.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/DataConnectorsController.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/DataConnectorsController.cs new file mode 100644 index 0000000000..780110d9eb --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/DataConnectorsController.cs @@ -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("Climode"); + } + + /// + /// Get all data connectors for all instances or for a single instance + /// + /// + public async Task GetDataConnectors(int insId) + { + if (insId != -1) + { + await GetDataConnectorsByInstance(insId); + } + else + { + for (var i = 0; i < azureConfigs.Length; i++) + { + await GetDataConnectorsByInstance(i); + } + } + } + + /// + /// Get all data connectors for a single instance + /// + /// + /// + 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(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(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); + } + } + + /// + /// Create a data connector for all instances or for a single instance + /// + /// + public async Task CreateDataConnector(int insId) + { + if (insId != -1) + { + await CreateDataConnectorByInstance(insId); + } + else + { + for (var i = 0; i < azureConfigs.Length; i++) + { + await CreateDataConnectorByInstance(i); + } + } + } + + /// + /// Create a data connector for a single instance + /// + /// + /// + private async Task CreateDataConnectorByInstance(int i) + { + var dataConnectors = Utils.LoadPayload("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); + } + } + } + + /// + /// Delete a data connector by id + /// + /// + /// + public async Task 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); + } + } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/ASCDataConnectorDataTypesPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/ASCDataConnectorDataTypesPayload.cs new file mode 100644 index 0000000000..a31c85b921 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/ASCDataConnectorDataTypesPayload.cs @@ -0,0 +1,7 @@ +namespace AzureSentinel_ManagementAPI.DataConnectors.Models +{ + public class ASCDataConnectorDataTypesPayload + { + public DataTypeConnectionStatePayload Alerts { get; set; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/ASCDataConnectorPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/ASCDataConnectorPayload.cs new file mode 100644 index 0000000000..3da73eb485 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/ASCDataConnectorPayload.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace AzureSentinel_ManagementAPI.DataConnectors.Models +{ + public class ASCDataConnectorPayload : DataConnectorPayload + { + [JsonProperty("properties")] public ASCDataConnectorPropertiesPayload PropertiesPayload { get; set; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/ASCDataConnectorPropertiesPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/ASCDataConnectorPropertiesPayload.cs new file mode 100644 index 0000000000..8a26531259 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/ASCDataConnectorPropertiesPayload.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/DataConnectionState.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/DataConnectionState.cs new file mode 100644 index 0000000000..80b70226d6 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/DataConnectionState.cs @@ -0,0 +1,8 @@ +namespace AzureSentinel_ManagementAPI.DataConnectors.Models +{ + public enum DataConnectionState + { + Enabled, + Disabled + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/DataConnectorKind.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/DataConnectorKind.cs new file mode 100644 index 0000000000..d26dbb8b78 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/DataConnectorKind.cs @@ -0,0 +1,14 @@ +namespace AzureSentinel_ManagementAPI.DataConnectors.Models +{ + public enum DataConnectorKind + { + AzureActiveDirectory, + AzureSecurityCenter, + MicrosoftCloudAppSecurity, + ThreatIntelligence, + Office365, + AmazonWebServicesCloudTrail, + AzureAdvancedThreatProtection, + MicrosoftDefenderAdvancedThreatProtection + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/DataConnectorPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/DataConnectorPayload.cs new file mode 100644 index 0000000000..07467a0e74 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/DataConnectorPayload.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/DataTypeConnectionStatePayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/DataTypeConnectionStatePayload.cs new file mode 100644 index 0000000000..5d205ed8e1 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/DataConnectors/Models/DataTypeConnectionStatePayload.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Hunting/Models/SavedSearchPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Hunting/Models/SavedSearchPayload.cs new file mode 100644 index 0000000000..dfe27b93ec --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Hunting/Models/SavedSearchPayload.cs @@ -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; } + } +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Hunting/Models/SavedSearchPropertiesPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Hunting/Models/SavedSearchPropertiesPayload.cs new file mode 100644 index 0000000000..ff54a20435 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Hunting/Models/SavedSearchPropertiesPayload.cs @@ -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 Tags { get; set; } + public string Query { get; set; } + } +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Hunting/Models/SavedSearchTag.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Hunting/Models/SavedSearchTag.cs new file mode 100644 index 0000000000..344a6757e8 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Hunting/Models/SavedSearchTag.cs @@ -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; } + } +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Hunting/SavedSearchController.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Hunting/SavedSearchController.cs new file mode 100644 index 0000000000..bf1547f3be --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Hunting/SavedSearchController.cs @@ -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("Climode"); + } + + /// + /// Create saved search + /// + /// + /// + public async Task CreateSavedSearch(int insId) + { + if (insId != -1) + { + await CreateSavedSearchByInstance(insId); + } + else + { + for (var i = 0; i < azureConfigs.Length; i++) + { + await CreateSavedSearchByInstance(i); + } + } + } + + /// + /// Create saved search by instance + /// + /// + /// + public async Task CreateSavedSearchByInstance(int i) + { + var savedSearches = Utils.LoadPayload("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); + } + } + } + + /// + /// Update saved search data by instance + /// + /// + /// + /// + public async Task UpdateSavedSearch(string savedSearchId, int insId) + { + var savedSearch = await GetSavedSearchById(savedSearchId, insId); + + try + { + JObject jobject = JObject.Parse(savedSearch); + var savedSearchObj = JsonConvert.DeserializeObject(jobject.ToString()); + + var savedSearchs = Utils.LoadPayload("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); + } + } + + /// + /// Delete saved search by id and instance + /// + /// + /// + /// + public async Task 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); + } + } + + /// + /// Get a specific saved search by id and instance + /// + /// + /// + /// + public async Task 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); + } + } + } + + /// + /// Get all saved searches by instance + /// + /// + /// + 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(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(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); + } + } + } +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/IncidentRelation/IncidentRelationController.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/IncidentRelation/IncidentRelationController.cs new file mode 100644 index 0000000000..30602a172a --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/IncidentRelation/IncidentRelationController.cs @@ -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("Climode"); + } + + /// + /// Create an incident relation for a single instance, connecting incident and bookmark + /// + /// + /// + /// + public async Task 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); + } + } + + /// + /// Delete an incident relation by id + /// + /// + /// + /// + public async Task 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); + } + } + + /// + /// Get an incident relation by id + /// + /// + /// + /// + 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(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(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); + } + } + + /// + /// Get all incident relations under an incident, and get all entities under the incident + /// + /// + /// + public async Task GetEntitiesforIncident(string incidentId, int insId) + { + await GetIncidentRelationsAndEntities(incidentId, EntityKind.SecurityAlert, true, insId); + } + + /// + /// Get all incident relations under an incident, and get all entities under the incident + /// + /// + /// + public async Task GetIncidentEntitiesbyEntityType(string incidentId, int insId) + { + // Console.WriteLine(Utils.GetString("Select_Entity_Kind")); + var entityKind = Utils.SelectKind(); + await GetIncidentRelationsAndEntities(incidentId, entityKind, false, insId); + } + + /// + /// Get all incident relations and entities under an incident + /// + /// + /// + /// + /// + 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(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(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(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); + } + } + + /// + /// Get entities expansion id by kind + /// + /// + /// + 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; + } + } + } +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/IncidentRelation/Models/EntityKind.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/IncidentRelation/Models/EntityKind.cs new file mode 100644 index 0000000000..2c9a27192b --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/IncidentRelation/Models/EntityKind.cs @@ -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 + } +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/IncidentRelation/Models/RelationEntityPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/IncidentRelation/Models/RelationEntityPayload.cs new file mode 100644 index 0000000000..e602dc77d0 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/IncidentRelation/Models/RelationEntityPayload.cs @@ -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; } + } +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/IncidentRelation/Models/RelationPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/IncidentRelation/Models/RelationPayload.cs new file mode 100644 index 0000000000..122542ad39 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/IncidentRelation/Models/RelationPayload.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace AzureSentinel_ManagementAPI.IncidentRelation.Models +{ + public class RelationPayload + { + public RelationPayload() + { + } + [JsonProperty("properties")] public RelationPropertiesPayload PropertiesPayload { get; set; } + } +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/IncidentRelation/Models/RelationPropertiesPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/IncidentRelation/Models/RelationPropertiesPayload.cs new file mode 100644 index 0000000000..f6c5f2d009 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/IncidentRelation/Models/RelationPropertiesPayload.cs @@ -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; } + } +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/IncidentsController.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/IncidentsController.cs new file mode 100644 index 0000000000..5cdcb45654 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/IncidentsController.cs @@ -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("Climode"); + } + + /// + /// Create an incident for all instances or for a single instance + /// + /// + 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); + } + } + } + + /// + /// Create an incident for a single instance + /// + /// + /// + private async Task CreateIncidentByInstance(int i) + { + var incidents = Utils.LoadPayload("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); + } + } + } + + /// + /// Update an incident by id + /// + /// + /// + public async Task UpdateIncident(string incidentId, int insId) + { + var incident = await GetIncidentById(incidentId, insId); + + try + { + JObject jobject = JObject.Parse(incident); + var incidentObj = JsonConvert.DeserializeObject(jobject.ToString()); + + var incidents = Utils.LoadPayload("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); + } + } + + /// + /// Batch update incidents for all instances or for a single instance + /// + /// + public async Task BatchUpdateIncidents(int insId) + { + if (insId != -1) + { + await BatchUpdateIncidentsByInstance(insId); + } + else + { + for (var i = 0; i < azureConfigs.Length; i++) + { + await BatchUpdateIncidentsByInstance(i); + } + } + } + + /// + /// Batch update incidents for a single instance + /// + /// + /// + private async Task BatchUpdateIncidentsByInstance(int i) + { + var incidentUpdates = Utils.LoadPayload("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); + } + } + + /// + /// Batch update incidents from json payload file + /// + /// + public async Task BatchUpdateIncidentsFromJson() + { + var insId = Utils.SelectInstance(azureConfigs); + var incidentUpdates = Utils.LoadPayload("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); + } + } + } + + /// + /// Delete an incident by id + /// + /// + /// + public async Task 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); + } + } + + /// + /// Get an incident by id + /// + /// + /// + /// + public async Task 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); + } + } + + /// + /// Get all incidents for all instances or for a single instance + /// + /// + 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; + } + } + } + + /// + /// Get all incidents for a single instance + /// + /// + /// + /// + public async Task 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(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(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); + } + } + + /// + /// Create an incident comment for an incident + /// + /// + /// + public async Task CreateIncidentComment(string incidentId) + { + var insId = Utils.SelectInstance(azureConfigs); + + var comments = Utils.LoadPayload("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); + } + } + } + + /// + /// Get an incident comment by id + /// + /// + /// + /// + public async Task 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); + } + } + + /// + /// Get all incident comments under an incident + /// + /// + /// + public async Task 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(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(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); + } + } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/Comments/IncidentCommentPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/Comments/IncidentCommentPayload.cs new file mode 100644 index 0000000000..936b5ad5ef --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/Comments/IncidentCommentPayload.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace AzureSentinel_ManagementAPI.Incidents.Models.Comments +{ + public class IncidentCommentPayload + { + [JsonProperty("properties")] + public IncidentCommentPropertiesPayload PropertiesPayload { get; set; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/Comments/IncidentCommentPropertiesPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/Comments/IncidentCommentPropertiesPayload.cs new file mode 100644 index 0000000000..069c3987f0 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/Comments/IncidentCommentPropertiesPayload.cs @@ -0,0 +1,7 @@ +namespace AzureSentinel_ManagementAPI.Incidents.Models.Comments +{ + public class IncidentCommentPropertiesPayload + { + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/IncidentAdditionalData.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/IncidentAdditionalData.cs new file mode 100644 index 0000000000..819979e465 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/IncidentAdditionalData.cs @@ -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; } + } +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/IncidentOwner.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/IncidentOwner.cs new file mode 100644 index 0000000000..953408e330 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/IncidentOwner.cs @@ -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; } + } +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/IncidentPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/IncidentPayload.cs new file mode 100644 index 0000000000..dcf7a10220 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/IncidentPayload.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/IncidentPropertiesPayload.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/IncidentPropertiesPayload.cs new file mode 100644 index 0000000000..c3b60deb7c --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/IncidentPropertiesPayload.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/IncidentStatus.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/IncidentStatus.cs new file mode 100644 index 0000000000..1c85f73111 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Incidents/Models/IncidentStatus.cs @@ -0,0 +1,9 @@ +namespace AzureSentinel_ManagementAPI.Incidents.Models +{ + public enum IncidentStatus + { + New, + Active, + Closed + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Infrastructure/Authentication/AuthenticationService.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Infrastructure/Authentication/AuthenticationService.cs new file mode 100644 index 0000000000..7e1af0a5e0 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Infrastructure/Authentication/AuthenticationService.cs @@ -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; + } + + /// + /// Get token by instance id + /// + /// + /// + public async Task 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); + } + } + + /// + /// Get authorization token by instance and make the request authorized + /// + /// + /// + /// + public async Task AuthenticateRequest(HttpRequestMessage request, int id) + { + var token = await GetToken(id); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken); + } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Infrastructure/Configuration/AzureSentinelApiConfiguration.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Infrastructure/Configuration/AzureSentinelApiConfiguration.cs new file mode 100644 index 0000000000..e177293d6c --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Infrastructure/Configuration/AzureSentinelApiConfiguration.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Infrastructure/SharedModels/Enums/Severity.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Infrastructure/SharedModels/Enums/Severity.cs new file mode 100644 index 0000000000..4c45458775 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Infrastructure/SharedModels/Enums/Severity.cs @@ -0,0 +1,10 @@ +namespace AzureSentinel_ManagementAPI.Infrastructure.SharedModels.Enums +{ + public enum Severity + { + High, + Medium, + Low, + Informational + } +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Infrastructure/SharedModels/TupleList.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Infrastructure/SharedModels/TupleList.cs new file mode 100644 index 0000000000..f51b081847 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Infrastructure/SharedModels/TupleList.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AzureSentinel_ManagementAPI.Infrastructure.SharedModels +{ + public class TupleList : List> + { + public void Add(T1 item, T2 item2) + { + Add(new Tuple(item, item2)); + } + } +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Program.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Program.cs new file mode 100644 index 0000000000..73410c2281 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Program.cs @@ -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(); + + var serviceProvider = new ServiceCollection() + .AddSingleton() + .AddSingleton(rawConfig) + .AddSingleton(configurations) + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .BuildServiceProvider(); + + await serviceProvider.GetService().Run(args); + } + } + +} \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Resource1.Designer.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Resource1.Designer.cs new file mode 100644 index 0000000000..4bc6e2b493 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Resource1.Designer.cs @@ -0,0 +1,693 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace AzureSentinel_ManagementAPI { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to please input action id: . + /// + internal static string Action_Id_Prompt_Text { + get { + return ResourceManager.GetString("Action_Id_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to please input existing action rule id:. + /// + internal static string Action_Rule_Id_Prompt_Text { + get { + return ResourceManager.GetString("Action_Rule_Id_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Actions:. + /// + internal static string Actions_Menu { + get { + return ResourceManager.GetString("Actions_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Alert rule templates:. + /// + internal static string Alert_Rule_Template_Menu { + get { + return ResourceManager.GetString("Alert_Rule_Template_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Alert rules:. + /// + internal static string Alert_Rules_Menu { + get { + return ResourceManager.GetString("Alert_Rules_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 27.- BatchUpdateIncidents. + /// + internal static string Batch_Update_Incidents_Menu { + get { + return ResourceManager.GetString("Batch_Update_Incidents_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please input existing bookmark id:. + /// + internal static string Bookmark_Prompt_Text { + get { + return ResourceManager.GetString("Bookmark_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bookmarks:. + /// + internal static string Bookmarks_Menu { + get { + return ResourceManager.GetString("Bookmarks_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to please input comment id:. + /// + internal static string Comment_Id_Prompt_Text { + get { + return ResourceManager.GetString("Comment_Id_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter to continue. + /// + internal static string Continue_Prompt_Text { + get { + return ResourceManager.GetString("Continue_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 1.- CreateActionOfAlertRule - Attach a playbook to an analytic rule. + /// + internal static string Create_Action_Menu { + get { + return ResourceManager.GetString("Create_Action_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 15.- CreateBookmark. + /// + internal static string Create_Bookmark_Menu { + get { + return ResourceManager.GetString("Create_Bookmark_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 21.- CreateDataConnector. + /// + internal static string Create_DataConnector_Menu { + get { + return ResourceManager.GetString("Create_DataConnector_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 7.- CreateFusionAlertRule. + /// + internal static string Create_Fusion_Rule_Menu { + get { + return ResourceManager.GetString("Create_Fusion_Rule_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 28.- CreateIncidentComment. + /// + internal static string Create_Incident_Comment_Menu { + get { + return ResourceManager.GetString("Create_Incident_Comment_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 22.- CreateIncident. + /// + internal static string Create_Incident_Menu { + get { + return ResourceManager.GetString("Create_Incident_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 8.- CreateMicrosoftSecurityIncidentCreationAlertRule. + /// + internal static string Create_Incident_Rule_Menu { + get { + return ResourceManager.GetString("Create_Incident_Rule_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 31.- CreateIncidentRelation. + /// + internal static string Create_Relation_Menu { + get { + return ResourceManager.GetString("Create_Relation_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 36.- CreateSavedSearch. + /// + internal static string Create_Saved_Search_Menu { + get { + return ResourceManager.GetString("Create_Saved_Search_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 9.- CreateScheduledAlertRule. + /// + internal static string Create_Scheduled_Rule_Menu { + get { + return ResourceManager.GetString("Create_Scheduled_Rule_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to please input data connector id:. + /// + internal static string Dataconnector_Prompt_Text { + get { + return ResourceManager.GetString("Dataconnector_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to DataConnectors:. + /// + internal static string DataConnectors_Menu { + get { + return ResourceManager.GetString("DataConnectors_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 2.- DeleteActionOfAlertRule2. + /// + internal static string Delete_Action_Menu { + get { + return ResourceManager.GetString("Delete_Action_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 16.- DeleteBookmark. + /// + internal static string Delete_Bookmark_Menu { + get { + return ResourceManager.GetString("Delete_Bookmark_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 20.- DeleteDataConnector. + /// + internal static string Delete_DataConnector_Menu { + get { + return ResourceManager.GetString("Delete_DataConnector_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 23.- DeleteIncident. + /// + internal static string Delete_Incident_Menu { + get { + return ResourceManager.GetString("Delete_Incident_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to please input incident id (the incident's 'name' field, aka Internal Incident Id) you want to delete:. + /// + internal static string Delete_Incident_Prompt_Text { + get { + return ResourceManager.GetString("Delete_Incident_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 32.- DeleteIncidentRelation. + /// + internal static string Delete_Relation_Menu { + get { + return ResourceManager.GetString("Delete_Relation_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 10.- DeleteAlertRule. + /// + internal static string Delete_Rule_Menu { + get { + return ResourceManager.GetString("Delete_Rule_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 37.- DeleteSavedSearch. + /// + internal static string Delete_Saved_Search_Menu { + get { + return ResourceManager.GetString("Delete_Saved_Search_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ctrl + C to exit. + /// + internal static string Exit_Text { + get { + return ResourceManager.GetString("Exit_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 3.- GetActionOfAlertRuleById. + /// + internal static string Get_Action_By_Id_Menu { + get { + return ResourceManager.GetString("Get_Action_By_Id_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 4.- GetAllActionsByAlertRule. + /// + internal static string Get_Actions_Menu { + get { + return ResourceManager.GetString("Get_Actions_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 17.- GetBookmarkById. + /// + internal static string Get_Bookmark_By_Id_Menu { + get { + return ResourceManager.GetString("Get_Bookmark_By_Id_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 18.- GetBookmarks. + /// + internal static string Get_Bookmarks_Menu { + get { + return ResourceManager.GetString("Get_Bookmarks_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 19.- GetDataConnectors. + /// + internal static string Get_DataConnectors_Menu { + get { + return ResourceManager.GetString("Get_DataConnectors_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 35.- GetIncidentEntitiesbyEntityType. + /// + internal static string Get_Entities_By_Type_Menu { + get { + return ResourceManager.GetString("Get_Entities_By_Type_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 12.- GetFusionAlertRule. + /// + internal static string Get_Fusion_Rule_Menu { + get { + return ResourceManager.GetString("Get_Fusion_Rule_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to please input fusion alert rule id:. + /// + internal static string Get_Fusion_Rule_Prompt_Text { + get { + return ResourceManager.GetString("Get_Fusion_Rule_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 24.- GetIncidentByInternalId. + /// + internal static string Get_Incident_By_Id_Menu { + get { + return ResourceManager.GetString("Get_Incident_By_Id_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 30.- GetIncidentCommentById. + /// + internal static string Get_Incident_Comment_Menu { + get { + return ResourceManager.GetString("Get_Incident_Comment_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 29.- GetAllIncidentComments. + /// + internal static string Get_Incident_Comments_Menu { + get { + return ResourceManager.GetString("Get_Incident_Comments_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to please input incident id (the incident's 'name' field, aka Internal Incident Id) you want to get:. + /// + internal static string Get_Incident_Prompt_Text { + get { + return ResourceManager.GetString("Get_Incident_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 13.- GetMicrosoftSecurityIncidentCreationAlertRule. + /// + internal static string Get_Incident_Rule_Menu { + get { + return ResourceManager.GetString("Get_Incident_Rule_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to please input microsoft security incident alert rule id:. + /// + internal static string Get_Incident_Rule_Prompt_Text { + get { + return ResourceManager.GetString("Get_Incident_Rule_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 25.- GetIncidents. + /// + internal static string Get_Incidents_Menu { + get { + return ResourceManager.GetString("Get_Incidents_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 34.- GetIncidentRelationByName. + /// + internal static string Get_Relation_Menu { + get { + return ResourceManager.GetString("Get_Relation_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 33.- GetAllIncidentRelationsAndEntities. + /// + internal static string Get_Relations_Menu { + get { + return ResourceManager.GetString("Get_Relations_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 5.- GetAlertRuleTemplateById. + /// + internal static string Get_Rule_Template_By_Id_Menu { + get { + return ResourceManager.GetString("Get_Rule_Template_By_Id_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 6.- GetAlertRuleTemplates. + /// + internal static string Get_Rule_Templates_Menu { + get { + return ResourceManager.GetString("Get_Rule_Templates_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 11.- GetAllAlertRules. + /// + internal static string Get_Rules_Menu { + get { + return ResourceManager.GetString("Get_Rules_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 38.- GetSavedSearchById. + /// + internal static string Get_Saved_Search_Menu { + get { + return ResourceManager.GetString("Get_Saved_Search_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 39.- GetSavedSearches. + /// + internal static string Get_Saved_Searches_Menu { + get { + return ResourceManager.GetString("Get_Saved_Searches_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 14.- GetScheduledAlertRule. + /// + internal static string Get_Scheduled_Rule_Menu { + get { + return ResourceManager.GetString("Get_Scheduled_Rule_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to please input scheduled alert rule id:. + /// + internal static string Get_Scheduled_Rule_Prompt_Text { + get { + return ResourceManager.GetString("Get_Scheduled_Rule_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Incident comments:. + /// + internal static string Incident_Comments_Menu { + get { + return ResourceManager.GetString("Incident_Comments_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to please input existing incident id ((the incident's 'name' field, aka Internal Incident Id):. + /// + internal static string Incident_Id_Prompt_Text { + get { + return ResourceManager.GetString("Incident_Id_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Incidents:. + /// + internal static string Incident_Menu { + get { + return ResourceManager.GetString("Incident_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Incident relations:. + /// + internal static string Incident_Relation_Menu { + get { + return ResourceManager.GetString("Incident_Relation_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid option... please, press enter to continue.... + /// + internal static string Invalid_Option_Text { + get { + return ResourceManager.GetString("Invalid_Option_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Option:. + /// + internal static string Option { + get { + return ResourceManager.GetString("Option", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to related entities:. + /// + internal static string Related_Entities { + get { + return ResourceManager.GetString("Related_Entities", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to please input existing incident relation id:. + /// + internal static string Relation_Id_Prompt_Text { + get { + return ResourceManager.GetString("Relation_Id_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to please input alert rule id: . + /// + internal static string Rule_Id_Prompt_Text { + get { + return ResourceManager.GetString("Rule_Id_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to please input alert rule template id(Default is 157c0cfc-d76d-463b-8755-c781608cdc1a):. + /// + internal static string Rule_Template_Id_Prompt_Text { + get { + return ResourceManager.GetString("Rule_Template_Id_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to please input saved search id:. + /// + internal static string Saved_Search_Id_Prompt_Text { + get { + return ResourceManager.GetString("Saved_Search_Id_Prompt_Text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to SavedSearches/Hunting queries:. + /// + internal static string Saved_Searches_Menu { + get { + return ResourceManager.GetString("Saved_Searches_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please an entity kind:. + /// + internal static string Select_Entity_Kind { + get { + return ResourceManager.GetString("Select_Entity_Kind", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 26.- UpdateIncident. + /// + internal static string Update_Incident_Menu { + get { + return ResourceManager.GetString("Update_Incident_Menu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 40.- UpdateSavedSearch. + /// + internal static string Update_Saved_Search_Menu { + get { + return ResourceManager.GetString("Update_Saved_Search_Menu", resourceCulture); + } + } + } +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Resource1.resx b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Resource1.resx new file mode 100644 index 0000000000..73aec42df7 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Resource1.resx @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Actions: + + + please input action id: + + + please input existing action rule id: + + + Alert rules: + + + Alert rule templates: + + + 27.- BatchUpdateIncidents + + + Bookmarks: + + + Please input existing bookmark id: + + + please input comment id: + + + Enter to continue + + + 1.- CreateActionOfAlertRule - Attach a playbook to an analytic rule + + + 15.- CreateBookmark + + + 21.- CreateDataConnector + + + 7.- CreateFusionAlertRule + + + 28.- CreateIncidentComment + + + 22.- CreateIncident + + + 8.- CreateMicrosoftSecurityIncidentCreationAlertRule + + + 31.- CreateIncidentRelation + + + 36.- CreateSavedSearch + + + 9.- CreateScheduledAlertRule + + + DataConnectors: + + + please input data connector id: + + + 2.- DeleteActionOfAlertRule2 + + + 16.- DeleteBookmark + + + 20.- DeleteDataConnector + + + 23.- DeleteIncident + + + please input incident id (the incident's 'name' field, aka Internal Incident Id) you want to delete: + + + 32.- DeleteIncidentRelation + + + 10.- DeleteAlertRule + + + 37.- DeleteSavedSearch + + + Ctrl + C to exit + + + 4.- GetAllActionsByAlertRule + + + 3.- GetActionOfAlertRuleById + + + 18.- GetBookmarks + + + 17.- GetBookmarkById + + + 19.- GetDataConnectors + + + 35.- GetIncidentEntitiesbyEntityType + + + 12.- GetFusionAlertRule + + + please input fusion alert rule id: + + + 25.- GetIncidents + + + 24.- GetIncidentByInternalId + + + 29.- GetAllIncidentComments + + + 30.- GetIncidentCommentById + + + please input incident id (the incident's 'name' field, aka Internal Incident Id) you want to get: + + + 13.- GetMicrosoftSecurityIncidentCreationAlertRule + + + please input microsoft security incident alert rule id: + + + 33.- GetAllIncidentRelationsAndEntities + + + 34.- GetIncidentRelationByName + + + 11.- GetAllAlertRules + + + 6.- GetAlertRuleTemplates + + + 5.- GetAlertRuleTemplateById + + + 39.- GetSavedSearches + + + 38.- GetSavedSearchById + + + 14.- GetScheduledAlertRule + + + please input scheduled alert rule id: + + + Incident comments: + + + please input existing incident id ((the incident's 'name' field, aka Internal Incident Id): + + + Incidents: + + + Incident relations: + + + Invalid option... please, press enter to continue... + + + Option: + + + related entities: + + + please input existing incident relation id: + + + please input alert rule id: + + + please input alert rule template id(Default is 157c0cfc-d76d-463b-8755-c781608cdc1a): + + + SavedSearches/Hunting queries: + + + please input saved search id: + + + Please an entity kind: + + + 26.- UpdateIncident + + + 40.- UpdateSavedSearch + + \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/ActionPayload/ActionPayload.json b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/ActionPayload/ActionPayload.json new file mode 100644 index 0000000000..f0cffec0a2 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/ActionPayload/ActionPayload.json @@ -0,0 +1,8 @@ +[ + { + "properties": { + "triggerUri": "", + "logicAppResourceId": "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Logic/workflows/" + } + } +] diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/BookmarkPayload/BookmarkPayload.json b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/BookmarkPayload/BookmarkPayload.json new file mode 100644 index 0000000000..d7017f4919 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/BookmarkPayload/BookmarkPayload.json @@ -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" + } + } +] diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/DataConnectorPayload/DataConnectorPayload.json b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/DataConnectorPayload/DataConnectorPayload.json new file mode 100644 index 0000000000..68db8b5161 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/DataConnectorPayload/DataConnectorPayload.json @@ -0,0 +1,16 @@ +[ + { + "kind": "Office365", + "properties": { + "tenantId": "", + "dataTypes": { + "sharePoint": { + "state": "Enabled" + }, + "exchange": { + "state": "Enabled" + } + } + } + } +] diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/FusionAlertRulePayload/FusionAlertRulePayload.json b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/FusionAlertRulePayload/FusionAlertRulePayload.json new file mode 100644 index 0000000000..4296af9771 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/FusionAlertRulePayload/FusionAlertRulePayload.json @@ -0,0 +1,9 @@ +[ + { + "kind": "Fusion", + "properties": { + "enabled": true, + "alertRuleTemplateName": "f71aba3d-28fb-450b-b192-4e76a83015c8" + } + } +] diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/IncidentCommentPayload/IncidentCommentPayload.json b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/IncidentCommentPayload/IncidentCommentPayload.json new file mode 100644 index 0000000000..b9e95513b7 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/IncidentCommentPayload/IncidentCommentPayload.json @@ -0,0 +1,7 @@ +[ + { + "properties": { + "message": "Some message" + } + } +] diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/IncidentPayload/IncidentPayload.json b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/IncidentPayload/IncidentPayload.json new file mode 100644 index 0000000000..a667dbc437 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/IncidentPayload/IncidentPayload.json @@ -0,0 +1,9 @@ +[ + { + "properties": { + "severity": "Low", + "status": "New", + "title": "Test Incident" + } + } +] diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/SavedSearchPayload/SavedSearchPayload.json b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/SavedSearchPayload/SavedSearchPayload.json new file mode 100644 index 0000000000..502420cdfe --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/SavedSearchPayload/SavedSearchPayload.json @@ -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" + } + ] + } +} +] diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/ScheduledAlertRulePayload/ScheduledAlertRulePayload.json b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/ScheduledAlertRulePayload/ScheduledAlertRulePayload.json new file mode 100644 index 0000000000..02720d7de2 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/ScheduledAlertRulePayload/ScheduledAlertRulePayload.json @@ -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/" + } +] diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/SecurityAlertRulePayload/SecurityAlertRulePayload.json b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/SecurityAlertRulePayload/SecurityAlertRulePayload.json new file mode 100644 index 0000000000..2cd03eb43e --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Templates/SecurityAlertRulePayload/SecurityAlertRulePayload.json @@ -0,0 +1,10 @@ +[ + { + "kind": "MicrosoftSecurityIncidentCreation", + "properties": { + "productFilter": "Microsoft Cloud App Security", + "displayName": "MCAS alert rule", + "enabled": true + } + } +] diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Utils.cs b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Utils.cs new file mode 100644 index 0000000000..179c062e9f --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/Utils.cs @@ -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()); + + /// + /// Select one from enum type + /// + /// + /// + public static TEnum SelectKind() + { + 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; + } + + /// + /// Get user input, return default value if no user input + /// + /// + /// + /// + public static string GetInput(string promptText, string defaultValue = "") + { + Console.WriteLine(promptText); + var input = Console.ReadLine(); + + if (input.Trim() == string.Empty) + { + input = defaultValue; + } + + return input; + } + + /// + /// Select a specific instance + /// + /// + /// + 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; + } + + /// + /// Select a specific instance to apply to all instances + /// + /// + /// + 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; + } + + /// + /// Write json output string to a json file + /// + /// + /// + /// + 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); + } + } + + /// + /// Load payload from json file + /// + /// + /// + /// + public static T LoadPayload(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(data); + return incidentObj; + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + return default; + } + } + + /// + /// Get localized text from resource file by key + /// + /// + /// + public static string GetString(string key) + { + return resource.GetString(key, CultureInfo.CurrentCulture); + } + } +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/appsettings.json b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/appsettings.json new file mode 100644 index 0000000000..b91b49ceea --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/appsettings.json @@ -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}" + } + ] +} diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/copydata.bat b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/copydata.bat new file mode 100644 index 0000000000..c34e7a53ea --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/AzureSentinel_ManagementAPI/copydata.bat @@ -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 \ No newline at end of file diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/After-consent.PNG b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/After-consent.PNG new file mode 100644 index 0000000000..8f651f1612 Binary files /dev/null and b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/After-consent.PNG differ diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/AzureSentinel-permission.PNG b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/AzureSentinel-permission.PNG new file mode 100644 index 0000000000..b295cd8d2f Binary files /dev/null and b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/AzureSentinel-permission.PNG differ diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/Console-Menu.PNG b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/Console-Menu.PNG new file mode 100644 index 0000000000..66aff7257e Binary files /dev/null and b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/Console-Menu.PNG differ diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/LogicApp-permission.PNG b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/LogicApp-permission.PNG new file mode 100644 index 0000000000..689e35bcf7 Binary files /dev/null and b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/LogicApp-permission.PNG differ diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/Start-button.PNG b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/Start-button.PNG new file mode 100644 index 0000000000..3d77bdb925 Binary files /dev/null and b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/Start-button.PNG differ diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/admin-consent.PNG b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/admin-consent.PNG new file mode 100644 index 0000000000..4f5fea226e Binary files /dev/null and b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/Images/admin-consent.PNG differ diff --git a/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/README.md b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/README.md new file mode 100644 index 0000000000..d979ac82a9 --- /dev/null +++ b/Tools/Sample Code/AzureSentinel-ManagementAPICsharp/README.md @@ -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 organization’s 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": "", + "TenantId": "", + "AppId": "", + "AppSecret": "", + "SubscriptionId": "", + "ResourceGroupName": "", + "WorkspaceName": "", + "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": "", + "logicAppResourceId": "/subscriptions//resourceGroups//providers/Microsoft.Logic/workflows/" + } + }, + ``` + +### 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.