From 6ccb3ef684c9d5d3e47c076df68737c1bac4a0bc Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Wed, 29 Jan 2020 17:12:30 +0100 Subject: [PATCH 01/53] Release '0.6.2' (#31) * updating get alert and hunting rule function * updated error handling * Create Get-PlayBook.ps1 * cleaning up --- AzSentinel/AzSentinel.psd1 | 2 +- AzSentinel/Classes/AlertRule.ps1 | 50 ++++++------------- AzSentinel/Public/Get-AzSentinelAlertRule.ps1 | 19 +++++-- .../Public/Get-AzSentinelHuntingRule.ps1 | 23 ++++++--- AzSentinel/Public/Get-AzSentinelIncident.ps1 | 16 ++++-- .../Public/Import-AzSentinelAlertRule.ps1 | 33 +++++------- .../Public/Import-AzSentinelHuntingRule.ps1 | 19 +++---- AzSentinel/Public/New-AzSentinelAlertRule.ps1 | 17 ++----- .../Public/New-AzSentinelHuntingRule.ps1 | 16 ++---- .../Public/Remove-AzSentinelAlertRule.ps1 | 20 ++++++-- .../Public/Remove-AzSentinelHuntingRule.ps1 | 24 ++++++--- AzSentinel/Public/Set-AzSentinel.ps1 | 12 ++--- AzSentinel/enums/TriggerOperator.ps1 | 10 ++-- 13 files changed, 132 insertions(+), 129 deletions(-) diff --git a/AzSentinel/AzSentinel.psd1 b/AzSentinel/AzSentinel.psd1 index 3648a1d..66e5572 100644 --- a/AzSentinel/AzSentinel.psd1 +++ b/AzSentinel/AzSentinel.psd1 @@ -12,7 +12,7 @@ RootModule = 'AzSentinel.psm1' # Version number of this module. - ModuleVersion = '0.6.1' + ModuleVersion = '0.6.2' # Supported PSEditions CompatiblePSEditions = 'Core', 'Desktop' diff --git a/AzSentinel/Classes/AlertRule.ps1 b/AzSentinel/Classes/AlertRule.ps1 index 4d2ede0..c9a6682 100644 --- a/AzSentinel/Classes/AlertRule.ps1 +++ b/AzSentinel/Classes/AlertRule.ps1 @@ -1,54 +1,42 @@ class AlertProp { - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] [guid] $Name - [Parameter(Mandatory)] [string] $DisplayName - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] [string] $Description - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] [Severity] $Severity - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] [bool] $Enabled - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] [string] $Query - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] [string] $QueryFrequency - [ValidateNotNullOrEmpty()] [string] $QueryPeriod - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [TriggerOperator] $TriggerOperator + [TriggerOperator]$TriggerOperator - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] [Int] $TriggerThreshold - [Parameter(Mandatory)] - [AllowEmptyString()] [string] $SuppressionDuration - [Parameter(Mandatory)] [bool] $SuppressionEnabled - [Parameter(Mandatory)] - [AllowEmptyCollection()] [Tactics[]] $Tactics + static [string] TriggerOperatorSwitch([string]$value) { + switch ($value) { + "gt" { $value = "GreaterThan" } + "lt" { $value = "LessThan" } + "eq" { $value = "Equal" } + "ne" { $value = "NotEqual" } + default { $value } + } + return $value + } + AlertProp ($Name, $DisplayName, $Description, $Severity, $Enabled, $Query, $QueryFrequency, $QueryPeriod, $TriggerOperator, $TriggerThreshold, $suppressionDuration, $suppressionEnabled, $Tactics) { $this.name = $Name $this.DisplayName = $DisplayName @@ -56,29 +44,23 @@ class AlertProp { $this.Severity = $Severity $this.Enabled = $Enabled $this.Query = $Query - $this.QueryFrequency = ("PT" + $QueryFrequency).ToUpper() - $this.QueryPeriod = ("PT" + $QueryPeriod).ToUpper() - $this.TriggerOperator = $TriggerOperator + $this.QueryFrequency = if ($QueryFrequency -like "PT*") { $QueryFrequency.ToUpper() } else { ("PT" + $QueryFrequency).ToUpper() } + $this.QueryPeriod = if ($QueryPeriod -like "PT*") { $QueryPeriod.ToUpper() } else { ("PT" + $QueryPeriod).ToUpper() } + $this.TriggerOperator = [AlertProp]::TriggerOperatorSwitch($TriggerOperator) $this.TriggerThreshold = $TriggerThreshold - $this.SuppressionDuration = if (! ($null -eq $suppressionDuration) -or ! ($null -eq $suppressionEnabled)) { ("PT" + $suppressionDuration).ToUpper() } else { "PT1H" } + $this.SuppressionDuration = if ((! $null -eq $suppressionDuration) -or ( $false -eq $suppressionEnabled)) { if ($suppressionDuration -like "PT*") { $suppressionDuration.ToUpper() } else { ("PT" + $suppressionDuration).ToUpper() } } else { "PT1H" } $this.SuppressionEnabled = if ($suppressionEnabled) { $suppressionEnabled } else { $false } $this.Tactics = $Tactics } } class AlertRule { - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] [guid] $Name - [Parameter(Mandatory)] [string] $Etag - [Parameter(Mandatory = $false)] [string]$type - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] [AlertProp]$Properties [Parameter(Mandatory)] diff --git a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 index 2016e7e..90477d5 100644 --- a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 @@ -57,14 +57,23 @@ function Get-AzSentinelAlertRule { $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules?api-version=2019-01-01-preview" Write-Verbose -Message "Using URI: $($uri)" - $alertRules = Invoke-webrequest -Uri $uri -Method get -Headers $script:authHeader - Write-Verbose "Found $((($alertRules.Content | ConvertFrom-Json).value).count) Alert rules" + + try { + $alertRules = Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader + } + catch { + Write-Verbose $_ + Write-Error "Unable to get alert rules with error code: $($_.Exception.Message)" -ErrorAction Stop + } + $return = @() - if ($alertRules) { + if ($alertRules.value) { + Write-Verbose "Found $($alertRules.value.count) Alert rules" + if ($RuleName.Count -ge 1) { foreach ($rule in $RuleName) { - [PSCustomObject]$temp = ($alertRules.Content | ConvertFrom-Json).value | Where-Object { $_.properties.displayName -eq $rule } + [PSCustomObject]$temp = $alertRules.value | Where-Object { $_.properties.displayName -eq $rule } if ($null -ne $temp) { $temp.properties | Add-Member -NotePropertyName name -NotePropertyValue $temp.name -Force $temp.properties | Add-Member -NotePropertyName etag -NotePropertyValue $temp.etag -Force @@ -79,7 +88,7 @@ function Get-AzSentinelAlertRule { return $return } else { - ($alertRules.Content | ConvertFrom-Json).value | ForEach-Object { + $alertRules.value | ForEach-Object { $_.properties | Add-Member -NotePropertyName name -NotePropertyValue $_.name -Force return $_.properties } diff --git a/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 index e54e550..2f6e495 100644 --- a/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 @@ -38,7 +38,7 @@ function Get-AzSentinelHuntingRule { [string[]]$RuleName, [Parameter(Mandatory = $false)] - [validateset("HuntingQueries", "GeneralExploration", "LogManagement")] + [validateset("Hunting Queries", "GeneralExploration", "LogManagement")] [string]$Filter ) @@ -65,14 +65,21 @@ function Get-AzSentinelHuntingRule { $uri = "$script:baseUri/savedSearches?api-version=2017-04-26-preview" Write-Verbose -Message "Using URI: $($uri)" - $alertRules = Invoke-webrequest -Uri $uri -Method get -Headers $script:authHeader - Write-Verbose "Found $((($alertRules.Content | ConvertFrom-Json).value).count) Alert rules" + try { + $huntingRules = (Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader | Where-Object $_.Category -eq $Filter) + } + catch { + Write-Verbose $_ + Write-Error "Unable to get hunting rules with error code: $($_.Exception.Message)" -ErrorAction Stop + } + $return = @() - if ($alertRules) { + if ($huntingRules.value) { + Write-Verbose "Found $($huntingRules.value.count) hunting rules" if ($RuleName.Count -ge 1) { foreach ($rule in $RuleName) { - [PSCustomObject]$temp = ($alertRules.Content | ConvertFrom-Json).value | Where-Object {$_.properties.displayName -eq $rule} + [PSCustomObject]$temp = $huntingRules.value | Where-Object { $_.properties.displayName -eq $rule } if ($null -ne $temp) { $temp.properties | Add-Member -NotePropertyName name -NotePropertyValue $temp.name -Force $temp.properties | Add-Member -NotePropertyName id -NotePropertyValue $temp.id -Force @@ -81,13 +88,13 @@ function Get-AzSentinelHuntingRule { $return += $temp.Properties } else { - Write-Warning "Unable to find Rule: $rule" + Write-Warning "Unable to find hunting rule: $rule" } } return $return } else { - ($alertRules.Content | ConvertFrom-Json).value | ForEach-Object { + $huntingRules.value | ForEach-Object { $_.properties | Add-Member -NotePropertyName name -NotePropertyValue $_.name -Force $_.properties | Add-Member -NotePropertyName id -NotePropertyValue $_.id -Force $_.properties | Add-Member -NotePropertyName etag -NotePropertyValue $_.etag -Force @@ -96,7 +103,7 @@ function Get-AzSentinelHuntingRule { } } else { - Write-Warning "No rules found on $($WorkspaceName)" + Write-Warning "No hunting rules found on $($WorkspaceName)" } } } diff --git a/AzSentinel/Public/Get-AzSentinelIncident.ps1 b/AzSentinel/Public/Get-AzSentinelIncident.ps1 index 2fa69cf..72dbf13 100644 --- a/AzSentinel/Public/Get-AzSentinelIncident.ps1 +++ b/AzSentinel/Public/Get-AzSentinelIncident.ps1 @@ -71,14 +71,22 @@ function Get-AzSentinelIncident { $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/Cases?api-version=2019-01-01-preview" Write-Verbose -Message "Using URI: $($uri)" - $incident = Invoke-webrequest -Uri $uri -Method get -Headers $script:authHeader - Write-Verbose "Found $((($incident.Content | ConvertFrom-Json).value).count) incidents" + + try { + $incident = Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader + } + catch { + Write-Verbose $_ + Write-Error "Unable to get incidents with error code: $($_.Exception.Message)" -ErrorAction Stop + } + $return = @() if ($incident) { + Write-Verbose "Found $($incident.value.count) incidents" if ($IncidentName.Count -ge 1) { foreach ($rule in $IncidentName) { - [PSCustomObject]$temp = ($incident.Content | ConvertFrom-Json).value | Where-Object { $_.properties.title -eq $rule } + [PSCustomObject]$temp = $incident.value | Where-Object { $_.properties.title -eq $rule } if ($null -ne $temp) { $return += $temp.properties } @@ -90,7 +98,7 @@ function Get-AzSentinelIncident { } elseif ($CaseNumber.Count -ge 1) { foreach ($rule in $CaseNumber) { - [PSCustomObject]$temp = ($incident.Content | ConvertFrom-Json).value | Where-Object { $_.properties.caseNumber -eq $rule } + [PSCustomObject]$temp = $incident.value | Where-Object { $_.properties.caseNumber -eq $rule } if ($null -ne $temp) { $return += $temp.properties } diff --git a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 index 1b4332e..b4cc111 100644 --- a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 @@ -62,7 +62,6 @@ function Import-AzSentinelAlertRule { } Get-LogAnalyticWorkspace @arguments - $errorResult = '' if ($SettingsFile.Extension -eq '.json') { try { @@ -96,7 +95,7 @@ function Import-AzSentinelAlertRule { $content = Get-AzSentinelAlertRule @arguments -RuleName $($item.displayName) -ErrorAction SilentlyContinue if ($content) { - Write-Verbose -Message "Rule $($item.displayName) exists in Azure Sentinel" + Write-Host -Message "Rule $($item.displayName) exists in Azure Sentinel" $item | Add-Member -NotePropertyName name -NotePropertyValue $content.name -Force $item | Add-Member -NotePropertyName etag -NotePropertyValue $content.etag -Force @@ -114,10 +113,8 @@ function Import-AzSentinelAlertRule { } } catch { - $errorReturn = $_ - $errorResult = ($errorReturn | ConvertFrom-Json ).error Write-Verbose $_ - Write-Error "Unable to connect to APi to get Analytic rules with message: $($errorResult.message)" -ErrorAction Stop + Write-Error "Unable to connect to APi to get Analytic rules with message: $($_.Exception.Message)" -ErrorAction Stop } try { @@ -145,29 +142,27 @@ function Import-AzSentinelAlertRule { if ($content) { $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name) if ($compareResult) { - Write-Output "Found Differences for rule: $($item.displayName)" - Write-Output ($compareResult | Format-Table | Out-String) + Write-Host "Found Differences for rule: $($item.displayName)" -ForegroundColor Yellow + Write-Host ($compareResult | Format-Table | Out-String) if ($PSCmdlet.ShouldProcess("Do you want to update profile: $($body.Properties.DisplayName)")) { try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -EnumsAsStrings) - Write-Output "Successfully updated rule: $($item.displayName) with status: $($result.StatusDescription)" + Write-Host "Successfully updated rule: $($item.displayName) with status: $($result.StatusDescription)" -ForegroundColor Green Write-Output ($body.Properties | Format-List | Format-Table | Out-String) } catch { - $errorReturn = $_ - $errorResult = ($errorReturn | ConvertFrom-Json ).error - Write-Verbose $_.Exception.Message - Write-Error "Unable to invoke webrequest with error message: $($errorResult.message)" -ErrorAction Continue + Write-Verbose $_ + Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Continue } } else { - Write-Output "No change have been made for rule $($item.displayName), deployment aborted" + Write-Host "No change have been made for rule $($item.displayName), deployment aborted" } } else { - Write-Output "Rule $($item.displayName) is compliance, nothing to do" - Write-Output ($body.Properties | Format-List | Format-Table | Out-String) + Write-Host "Rule $($item.displayName) is compliance, nothing to do" + Write-Host ($body.Properties | Format-List | Format-Table | Out-String) } } else { @@ -175,14 +170,12 @@ function Import-AzSentinelAlertRule { try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -EnumsAsStrings) - Write-Output "Successfully created rule: $($item.displayName) with status: $($result.StatusDescription)" + Write-Host "Successfully created rule: $($item.displayName) with status: $($result.StatusDescription)" -ForegroundColor Green Write-Output ($body.Properties | Format-List | Format-Table | Out-String) } catch { - $errorReturn = $_ - $errorResult = ($errorReturn | ConvertFrom-Json ).error - Write-Verbose $_.Exception.Message - Write-Error "Unable to invoke webrequest with error message: $($errorResult.message)" -ErrorAction Continue + Write-Verbose $_ + Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Continue } } } diff --git a/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 index 2a5df2d..be86b45 100644 --- a/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 @@ -62,7 +62,6 @@ function Import-AzSentinelHuntingRule { } Get-LogAnalyticWorkspace @arguments - $errorResult = '' $item = @{ } if ($SettingsFile.Extension -eq '.json') { @@ -88,7 +87,7 @@ function Import-AzSentinelHuntingRule { } foreach ($item in $analytics) { - Write-Verbose -Message "Started with Hunting rule: $($item.displayName)" + Write-Host -Message "Started with Hunting rule: $($item.displayName)" try { Write-Verbose -Message "Get rule $($item.description)" @@ -116,10 +115,8 @@ function Import-AzSentinelHuntingRule { } } catch { - $errorReturn = $_ - $errorResult = ($errorReturn | ConvertFrom-Json ).error Write-Verbose $_ - Write-Error "Unable to connect to APi to get Analytic rules with message: $($errorResult.message)" -ErrorAction Stop + Write-Error "Unable to connect to APi to get Analytic rules with message: $($_.Exception.Message)" -ErrorAction Stop } [PSCustomObject]$body = @{ @@ -167,10 +164,8 @@ function Import-AzSentinelHuntingRule { Write-Output ($body.Properties | Format-List | Format-Table | Out-String) } catch { - $errorReturn = $_ - $errorResult = ($errorReturn | ConvertFrom-Json ).error - Write-Verbose $_.Exception.Message - Write-Error "Unable to invoke webrequest with error message: $($errorResult.message)" -ErrorAction Continue + Write-Verbose $_ + Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Continue } } else { @@ -191,10 +186,8 @@ function Import-AzSentinelHuntingRule { Write-Output ($body.Properties | Format-List | Format-Table | Out-String) } catch { - $errorReturn = $_ - $errorResult = ($errorReturn | ConvertFrom-Json ).error - Write-Verbose $_.Exception.Message - Write-Error "Unable to invoke webrequest with error message: $($errorResult.message)" -ErrorAction Continue + Write-Verbose $_ + Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Continue } } } diff --git a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 index 7e38df0..dc5cc13 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 @@ -116,7 +116,6 @@ function New-AzSentinelAlertRule { } Get-LogAnalyticWorkspace @arguments - $errorResult = '' $item = @{ } Write-Verbose -Message "Creating new rule: $($DisplayName)" @@ -146,10 +145,8 @@ function New-AzSentinelAlertRule { } } catch { - $errorReturn = $_ - $errorResult = ($errorReturn | ConvertFrom-Json ).error Write-Verbose $_ - Write-Error "Unable to connect to APi to get Analytic rules with message: $($errorResult.message)" -ErrorAction Stop + Write-Error "Unable to connect to APi to get Analytic rules with message: $($_.Exception.Message)" -ErrorAction Stop } try { @@ -187,10 +184,8 @@ function New-AzSentinelAlertRule { Write-Output ($body.Properties | Format-List | Format-Table | Out-String) } catch { - $errorReturn = $_ - $errorResult = ($errorReturn | ConvertFrom-Json ).error - Write-Verbose $_.Exception.Message - Write-Error "Unable to invoke webrequest with error message: $($errorResult.message)" -ErrorAction Stop + Write-Verbose $_ + Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Stop } } else { @@ -211,10 +206,8 @@ function New-AzSentinelAlertRule { Write-Output ($body.Properties | Format-List | Format-Table | Out-String) } catch { - $errorReturn = $_ - $errorResult = ($errorReturn | ConvertFrom-Json ).error - Write-Verbose $_.Exception.Message - Write-Error "Unable to invoke webrequest with error message: $($errorResult.message)" -ErrorAction Stop + Write-Verbose $_ + Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Stop } } } diff --git a/AzSentinel/Public/New-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/New-AzSentinelHuntingRule.ps1 index be37736..3a5132a 100644 --- a/AzSentinel/Public/New-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelHuntingRule.ps1 @@ -107,10 +107,8 @@ function New-AzSentinelHuntingRule { } } catch { - $errorReturn = $_ - $errorResult = ($errorReturn | ConvertFrom-Json ).error Write-Verbose $_ - Write-Error "Unable to connect to APi to get Analytic rules with message: $($errorResult.message)" -ErrorAction Stop + Write-Error "Unable to connect to APi to get Analytic rules with message: $($_.Exception.Message)" -ErrorAction Stop } [PSCustomObject]$body = @{ @@ -160,10 +158,8 @@ function New-AzSentinelHuntingRule { Write-Output "Successfully updated hunting rule: $($DisplayName) with status: $($result.StatusDescription)" } catch { - $errorReturn = $_ - $errorResult = ($errorReturn | ConvertFrom-Json).error - Write-Verbose $_.Exception.Message - Write-Error "Unable to invoke webrequest with error message: $($errorResult.message)" -ErrorAction Stop + Write-Verbose $_ + Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Stop } } else { @@ -185,10 +181,8 @@ function New-AzSentinelHuntingRule { Write-Output ($body.properties | Format-Table) } catch { - $errorReturn = $_ - $errorResult = ($errorReturn | ConvertFrom-Json ).error - Write-Verbose $_.Exception.Message - Write-Error "Unable to invoke webrequest with error message: $($errorResult.message)" -ErrorAction Stop + Write-Verbose $_ + Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Stop } } } diff --git a/AzSentinel/Public/Remove-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Remove-AzSentinelAlertRule.ps1 index fbcd076..381302e 100644 --- a/AzSentinel/Public/Remove-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Remove-AzSentinelAlertRule.ps1 @@ -70,8 +70,14 @@ function Remove-AzSentinelAlertRule { if ($PSCmdlet.ShouldProcess("Do you want to remove: $rule")) { Write-Output $item - $result = Invoke-WebRequest -Uri $uri -Method DELETE -Headers $script:authHeader - Write-Output "Successfully removed rule: $($rule) with status: $($result.StatusDescription)" + try { + $result = Invoke-WebRequest -Uri $uri -Method DELETE -Headers $script:authHeader + Write-Output "Successfully removed rule: $($rule) with status: $($result.StatusDescription)" + } + catch { + Write-Verbose $_ + Write-Error "Unable to remove rule: $($rule) with error message: $($_.Exception.Message)" -ErrorAction Continue + } } else { Write-Output "No change have been made for rule: $rule" @@ -88,8 +94,14 @@ function Remove-AzSentinelAlertRule { $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($_.name)?api-version=2019-01-01-preview" if ($PSCmdlet.ShouldProcess("Do you want to remove: $($_.displayName)")) { - $result = Invoke-WebRequest -Uri $uri -Method DELETE -Headers $script:authHeader - Write-Output "Successfully removed rule: $($_.displayName) with status: $($result.StatusDescription)" + try { + $result = Invoke-WebRequest -Uri $uri -Method DELETE -Headers $script:authHeader + Write-Output "Successfully removed rule: $($_.displayName) with status: $($result.StatusDescription)" + } + catch { + Write-Verbose $_ + Write-Error "Unable to remove rule: $($_.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue + } } else { Write-Output "No change have been made for rule: $($_.displayName)" diff --git a/AzSentinel/Public/Remove-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/Remove-AzSentinelHuntingRule.ps1 index 5f69b0e..3549ee7 100644 --- a/AzSentinel/Public/Remove-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/Remove-AzSentinelHuntingRule.ps1 @@ -64,14 +64,20 @@ function Remove-AzSentinelHuntingRule { if ($RuleName) { # remove defined rules foreach ($rule in $RuleName) { - $item = Get-AzSentinelHuntingRule @arguments -RuleName $rule + $item = Get-AzSentinelHuntingRule @arguments -Filter 'HuntingQueries' -RuleName $rule if ($item) { $uri = "$script:baseUri/savedSearches/$($item.name)?api-version=2017-04-26-preview" if ($PSCmdlet.ShouldProcess("Do you want to remove: $rule")) { Write-Output $item - $result = Invoke-WebRequest -Uri $uri -Method DELETE -Headers $script:authHeader - Write-Output "Successfully removed hunting rule: $($rule) with status: $($result.StatusDescription)" + try { + $result = Invoke-WebRequest -Uri $uri -Method DELETE -Headers $script:authHeader + Write-Output "Successfully removed hunting rule: $($rule) with status: $($result.StatusDescription)" + } + catch { + Write-Verbose $_ + Write-Error "Unable to remove rule: $($rule) with error message: $($_.Exception.Message)" -ErrorAction Continue + } } else { Write-Output "No change have been made for hunting rule: $rule" @@ -84,11 +90,17 @@ function Remove-AzSentinelHuntingRule { } else { Write-Warning "No hunting rule selected, All hunting rules will be removed one by one!" - Get-AzSentinelHuntingRule @arguments | ForEach-Object { + Get-AzSentinelHuntingRule @arguments -Filter 'HuntingQueries' | ForEach-Object { $uri = "$script:baseUri/savedSearches/$($_.name)?api-version=2017-04-26-preview" if ($PSCmdlet.ShouldProcess("Do you want to remove: $($_.displayName)")) { - $result = Invoke-WebRequest -Uri $uri -Method DELETE -Headers $script:authHeader - Write-Output "Successfully removed hunting rule: $($_.displayName) with status: $($result.StatusDescription)" + try { + $result = Invoke-WebRequest -Uri $uri -Method DELETE -Headers $script:authHeader + Write-Output "Successfully removed hunting rule: $($_.displayName) with status: $($result.StatusDescription)" + } + catch { + Write-Verbose $_ + Write-Error "Unable to remove rule: $($_.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue + } } else { Write-Output "No change have been made for hunting rule: $($_.displayName)" diff --git a/AzSentinel/Public/Set-AzSentinel.ps1 b/AzSentinel/Public/Set-AzSentinel.ps1 index 7613969..0b98c1f 100644 --- a/AzSentinel/Public/Set-AzSentinel.ps1 +++ b/AzSentinel/Public/Set-AzSentinel.ps1 @@ -51,8 +51,6 @@ function Set-AzSentinel { # Variables $errorResult = '' - Write-Output $Script:workspace - if ($workspaceResult.properties.provisioningState -eq 'Succeeded') { $body = @{ 'id' = '' @@ -84,23 +82,21 @@ function Set-AzSentinel { try { if ($PSCmdlet.ShouldProcess("Do you want to enable Sentinel for Workspace: $workspace")) { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json) - Write-Output "Successfully enabled Sentinel on workspae: $WorkspaceName" - return $result + Write-Output "Successfully enabled Sentinel on workspae: $WorkspaceName with result code $($result.StatusDescription)" } else { Write-Output "No change have been made for rule $WorkspaceName, deployment aborted" } } catch { - $errorReturn = $_ - $errorResult = ($errorReturn | ConvertFrom-Json ).error - Write-Error "Unable to enable Sentinel on $WorkspaceName with error message: $($errorResult.message)" + Write-Verbose $_ + Write-Error "Unable to enable Sentinel on $WorkspaceName with error message: $($_.Exception.Message)" } } else { Write-Verbose $_ - Write-Error "Unable to invoke webrequest with error message: $($errorResult.message)" -ErrorAction Stop + Write-Error "Unable to Azure Sentinel with error message: $($_.Exception.Message)" -ErrorAction Stop } } diff --git a/AzSentinel/enums/TriggerOperator.ps1 b/AzSentinel/enums/TriggerOperator.ps1 index cf6018b..cf181f3 100644 --- a/AzSentinel/enums/TriggerOperator.ps1 +++ b/AzSentinel/enums/TriggerOperator.ps1 @@ -1,6 +1,10 @@ enum TriggerOperator { GreaterThan - FewerThan - EqualTo - NotEqualTo + LessThan + Equal + NotEqual + gt + lt + eq + ne } From 6f80f4d4e79d36a5b4184bb293ce08b9f51a754b Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Thu, 20 Feb 2020 20:10:15 +0100 Subject: [PATCH 02/53] Release Update Incident function (#37) * init release update incident function * cleaning up * updating * updating incident function * code cleanup * Cleaning up and ready for release * updating final docs folder --- .gitignore | 2 + AzSentinel/AzSentinel.psd1 | 3 +- AzSentinel/Public/Get-AzSentinelIncident.ps1 | 6 + .../Public/Update-AzSentinelIncident.ps1 | 166 +++++++++++++ ...t.md => Get-AzSentinelHuntingRule copy.md} | 38 ++- AzSentinel/docs/Update-AzSentinelIncident.md | 227 ++++++++++++++++++ AzSentinel/enums/CloseReason.ps1 | 4 + AzSentinel/enums/Status.ps1 | 5 + .../Update-AzSentinelIncident.tests.ps1 | 0 docs/Update-AzSentinelIncident.md | 227 ++++++++++++++++++ 10 files changed, 654 insertions(+), 24 deletions(-) create mode 100644 AzSentinel/Public/Update-AzSentinelIncident.ps1 rename AzSentinel/docs/{Get-AzSentinelIncident.md => Get-AzSentinelHuntingRule copy.md} (65%) create mode 100644 AzSentinel/docs/Update-AzSentinelIncident.md create mode 100644 AzSentinel/enums/CloseReason.ps1 create mode 100644 AzSentinel/enums/Status.ps1 create mode 100644 AzSentinel/tests/Unit/public/Update-AzSentinelIncident.tests.ps1 create mode 100644 docs/Update-AzSentinelIncident.md diff --git a/.gitignore b/.gitignore index e69de29..d9021eb 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,2 @@ +temp/* +BuildOutput/* diff --git a/AzSentinel/AzSentinel.psd1 b/AzSentinel/AzSentinel.psd1 index 66e5572..c76e462 100644 --- a/AzSentinel/AzSentinel.psd1 +++ b/AzSentinel/AzSentinel.psd1 @@ -89,7 +89,8 @@ 'New-AzSentinelHuntingRule', 'Import-AzSentinelHuntingRule', 'Remove-AzSentinelHuntingRule', - 'Get-AzSentinelIncident' + 'Get-AzSentinelIncident', + 'Update-AzSentinelIncident' ) # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. diff --git a/AzSentinel/Public/Get-AzSentinelIncident.ps1 b/AzSentinel/Public/Get-AzSentinelIncident.ps1 index 72dbf13..71ebbb7 100644 --- a/AzSentinel/Public/Get-AzSentinelIncident.ps1 +++ b/AzSentinel/Public/Get-AzSentinelIncident.ps1 @@ -88,6 +88,8 @@ function Get-AzSentinelIncident { foreach ($rule in $IncidentName) { [PSCustomObject]$temp = $incident.value | Where-Object { $_.properties.title -eq $rule } if ($null -ne $temp) { + $temp.properties | Add-Member -NotePropertyName etag -NotePropertyValue $temp.etag -Force + $temp.properties | Add-Member -NotePropertyName name -NotePropertyValue $temp.name -Force $return += $temp.properties } else { @@ -100,6 +102,8 @@ function Get-AzSentinelIncident { foreach ($rule in $CaseNumber) { [PSCustomObject]$temp = $incident.value | Where-Object { $_.properties.caseNumber -eq $rule } if ($null -ne $temp) { + $temp.properties | Add-Member -NotePropertyName etag -NotePropertyValue $temp.etag -Force + $temp.properties | Add-Member -NotePropertyName name -NotePropertyValue $temp.name -Force $return += $temp.properties } else { @@ -110,6 +114,8 @@ function Get-AzSentinelIncident { } else { ($incident.Content | ConvertFrom-Json).value | ForEach-Object { + $_.properties | Add-Member -NotePropertyName etag -NotePropertyValue $_.etag -Force + $_.properties | Add-Member -NotePropertyName name -NotePropertyValue $_.name -Force return $_.properties } } diff --git a/AzSentinel/Public/Update-AzSentinelIncident.ps1 b/AzSentinel/Public/Update-AzSentinelIncident.ps1 new file mode 100644 index 0000000..e4029ee --- /dev/null +++ b/AzSentinel/Public/Update-AzSentinelIncident.ps1 @@ -0,0 +1,166 @@ +#requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} +#requires -version 6.2 + +function Update-AzSentinelIncident { + <# + .SYNOPSIS + Update Azure Sentinel Incident + .DESCRIPTION + With this function you can update existing Azure Sentinel Incident. + .PARAMETER SubscriptionId + Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + .PARAMETER WorkspaceName + Enter the Workspace name + .PARAMETER CaseNumber + Enter the case number to get specfiek details of a open case + .PARAMETER Severity + Enter the Severity, you can choose from Medium, High, Low and Informational + .PARAMETER Status + Enter the Status of the incident, you can choose from New, InProgress and Closed + .PARAMETER Comment + Enter Comment tekst to add comment to the incident + .PARAMETER Labels + Add Lebels to the incident, current configured Labels will be added to the existing Labels + .PARAMETER CloseReason + When Status is equil to Closed, CloseReason is required. You can select from: TruePositive, FalsePositive + .PARAMETER ClosedReasonText + When Status is equil to Closed, ClosedReasonText is required to be filled in. + .EXAMPLE + Update-AzSentinelIncident -WorkspaceName "" + Get a list of all open Incidents + .EXAMPLE + Update-AzSentinelIncident -WorkspaceName '' -CaseNumber 42291 -Labels "NewLabel" + Add a new Label to list of Labels for a Incident + .EXAMPLE + Update-AzSentinelIncident -WorkspaceName '' -CaseNumber 42293 -Status Closed -CloseReason FalsePositive -ClosedReasonText "Your input" + Close the Incidnet using status Closed, when status closed is selected then CloseReason and ClosedReasonText prperty are required to be filled in + #> + + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory = $false, + ParameterSetName = "Sub")] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$WorkspaceName, + + [Parameter(Mandatory, + ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [int]$CaseNumber, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$Severity, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [Status]$Status, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$Comment, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string[]]$Labels, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [CloseReason]$CloseReason, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$ClosedReasonText + ) + + begin { + precheck + } + + process { + switch ($PsCmdlet.ParameterSetName) { + Sub { + $arguments = @{ + WorkspaceName = $WorkspaceName + SubscriptionId = $SubscriptionId + } + } + default { + $arguments = @{ + WorkspaceName = $WorkspaceName + } + } + } + Write-Verbose -Message "Using URI: $($uri)" + + $incident = Get-AzSentinelIncident @arguments -CaseNumber $CaseNumber + + if ($incident) { + if ($Comment) { + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/Cases/$($incident.name)/comments/$(New-Guid)?api-version=2019-01-01-preview" + $body = @{ + "properties" = @{ + "message" = $Comment + } + } + } + else { + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/Cases/$($incident.name)?api-version=2019-01-01-preview" + $LabelsUnique = $incident.labels + $Labels | Select-Object -Unique + $body = @{ + "etag" = $($incident.etag) + "properties" = @{ + "caseNumber" = $CaseNumber + "createdTimeUtc" = $($incident.incidentcreatedTimeUtc) + "endTimeUtc" = $($incident.endTimeUtc) + "lastUpdatedTimeUtc" = $($incident.lastUpdatedTimeUtc) + "lastComment" = "" + "totalComments" = 0 + "metrics" = @{ } + [pscustomobject]"relatedAlertIds" = @( + ) + [pscustomobject]"relatedAlertProductNames" = @( + ) + "severity" = if ($Severity) { $Severity } else { $incident.severity } + "startTimeUtc" = $($incident.startTimeUtc) + "status" = if ($Status) { $Status } else { $incident.status } + "closeReason" = if ($Status -eq 'Closed') { if ($null -ne [CloseReason]$CloseReason) { $CloseReason } else { Write-Error "No Close Reasen provided" -ErrorAction Stop } } else { $null } + "closedReasonText" = if ($Status -eq 'Closed') { if ($ClosedReasonText) { $ClosedReasonText } else { Write-Error 'No closed comment provided' } } else { $null } + [pscustomobject]"labels" = @( $LabelsUnique) + "title" = $($incident.title) + "description" = "" + "firstAlertTimeGenerated" = "" + "lastAlertTimeGenerated" = "" + "owner" = @{ + "name" = $null + "email" = $null + "objectId" = $null + } + } + } + } + + Write-Output "Found incident with case number: $($incident.caseNumber)" + + if ($PSCmdlet.ShouldProcess("Do you want to update Incident: $($body.Properties.DisplayName)")) { + try { + $return = Invoke-WebRequest -Uri $uri -Method Put -Body ($body | ConvertTo-Json -Depth 99 -EnumsAsStrings) -Headers $script:authHeader + return ($return.Content | ConvertFrom-Json).properties + } + catch { + $return = $_.Exception.Message + Write-Verbose $_ + Write-Error "Unable to update Incident $($incident.caseNumber) with error message $return" + return $return + } + } + else { + Write-Output "No change have been made for Incident $($incident.caseNumber), update aborted" + } + } + } +} diff --git a/AzSentinel/docs/Get-AzSentinelIncident.md b/AzSentinel/docs/Get-AzSentinelHuntingRule copy.md similarity index 65% rename from AzSentinel/docs/Get-AzSentinelIncident.md rename to AzSentinel/docs/Get-AzSentinelHuntingRule copy.md index ab4aa18..060c6a5 100644 --- a/AzSentinel/docs/Get-AzSentinelIncident.md +++ b/AzSentinel/docs/Get-AzSentinelHuntingRule copy.md @@ -5,44 +5,36 @@ online version: schema: 2.0.0 --- -# Get-AzSentinelIncident +# Get-AzSentinelHuntingRule ## SYNOPSIS -Get Azure Sentinel Incident +Get Azure Sentinel Hunting rule ## SYNTAX ``` -Get-AzSentinelIncident [-SubscriptionId ] -WorkspaceName [-IncidentName ] - [-CaseNumber ] [-WhatIf] [-Confirm] [] +Get-AzSentinelHuntingRule [-SubscriptionId ] -WorkspaceName [-RuleName ] + [-Filter ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION -With this function you can get a list of open incidents from Azure Sentinel. -You can can also filter to Incident with speciefiek case namber or Case name +With this function you can get the configuration of the Azure Sentinel Hunting rule from Azure Sentinel ## EXAMPLES ### EXAMPLE 1 ``` -Get-AzSentinelIncident -WorkspaceName "" +Get-AzSentinelHuntingRule -WorkspaceName "" -RuleName "","" ``` -Get a list of all open Incidents +In this example you can get configuration of multiple Hunting rules ### EXAMPLE 2 ``` -Get-AzSentinelIncident -WorkspaceName "" -CaseNumber +Get-AzSentinelHuntingRule -WorkspaceName "" ``` -Get information of a specifiek incident with providing the casenumber - -### EXAMPLE 3 -``` -Get-AzSentinelIncident -WorkspaceName "" -IncidentName "","" -``` - -Get information of one or more incidents with providing a incident name, this is the name of the alert rule that triggered the incident +In this example you can get a list of all the Hunting rules in once ## PARAMETERS @@ -76,8 +68,8 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -IncidentName -Enter incident name, this is the same name as the alert rule that triggered the incident +### -RuleName +Enter the name of the Hunting rule name ```yaml Type: String[] @@ -91,18 +83,18 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` -### -CaseNumber -Enter the case number to get specfiek details of a open case +### -Filter +{{ Fill Filter Description }} ```yaml -Type: Int32[] +Type: String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None -Accept pipeline input: True (ByValue) +Accept pipeline input: False Accept wildcard characters: False ``` diff --git a/AzSentinel/docs/Update-AzSentinelIncident.md b/AzSentinel/docs/Update-AzSentinelIncident.md new file mode 100644 index 0000000..dbbe94b --- /dev/null +++ b/AzSentinel/docs/Update-AzSentinelIncident.md @@ -0,0 +1,227 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Update-AzSentinelIncident + +## SYNOPSIS +Update Azure Sentinel Incident + +## SYNTAX + +``` +Update-AzSentinelIncident [-SubscriptionId ] -WorkspaceName -CaseNumber + [-Severity ] [-Status ] [-Comment ] [-Labels ] [-CloseReason ] + [-ClosedReasonText ] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +With this function you can update existing Azure Sentinel Incident. + +## EXAMPLES + +### EXAMPLE 1 +``` +Update-AzSentinelIncident -WorkspaceName "" +``` + +Get a list of all open Incidents + +### EXAMPLE 2 +``` +Update-AzSentinelIncident -WorkspaceName '' -CaseNumber 42291 -Labels "NewLabel" +``` + +Add a new Label to list of Labels for a Incident + +### EXAMPLE 3 +``` +Update-AzSentinelIncident -WorkspaceName '' -CaseNumber 42293 -Status Closed -CloseReason FalsePositive -ClosedReasonText "Your input" +``` + +Close the Incidnet using status Closed, when status closed is selected then CloseReason and ClosedReasonText prperty are required to be filled in + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -CaseNumber +Enter the case number to get specfiek details of a open case + +```yaml +Type: Int32 +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: 0 +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Severity +Enter the Severity, you can choose from Medium, High, Low and Informational + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Status +Enter the Status of the incident, you can choose from New, InProgress and Closed + +```yaml +Type: Status +Parameter Sets: (All) +Aliases: +Accepted values: New, InProgress, Closed + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Comment +Enter Comment tekst to add comment to the incident + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Labels +Add Lebels to the incident, current configured Labels will be added to the existing Labels + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -CloseReason +When Status is equil to Closed, CloseReason is required. +You can select from: TruePositive, FalsePositive + +```yaml +Type: CloseReason +Parameter Sets: (All) +Aliases: +Accepted values: TruePositive, FalsePositive + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ClosedReasonText +When Status is equil to Closed, ClosedReasonText is required to be filled in. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/AzSentinel/enums/CloseReason.ps1 b/AzSentinel/enums/CloseReason.ps1 new file mode 100644 index 0000000..2fe1939 --- /dev/null +++ b/AzSentinel/enums/CloseReason.ps1 @@ -0,0 +1,4 @@ +enum CloseReason { + TruePositive + FalsePositive +} diff --git a/AzSentinel/enums/Status.ps1 b/AzSentinel/enums/Status.ps1 new file mode 100644 index 0000000..a126c6f --- /dev/null +++ b/AzSentinel/enums/Status.ps1 @@ -0,0 +1,5 @@ +enum Status { + New + InProgress + Closed +} diff --git a/AzSentinel/tests/Unit/public/Update-AzSentinelIncident.tests.ps1 b/AzSentinel/tests/Unit/public/Update-AzSentinelIncident.tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/docs/Update-AzSentinelIncident.md b/docs/Update-AzSentinelIncident.md new file mode 100644 index 0000000..dbbe94b --- /dev/null +++ b/docs/Update-AzSentinelIncident.md @@ -0,0 +1,227 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Update-AzSentinelIncident + +## SYNOPSIS +Update Azure Sentinel Incident + +## SYNTAX + +``` +Update-AzSentinelIncident [-SubscriptionId ] -WorkspaceName -CaseNumber + [-Severity ] [-Status ] [-Comment ] [-Labels ] [-CloseReason ] + [-ClosedReasonText ] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +With this function you can update existing Azure Sentinel Incident. + +## EXAMPLES + +### EXAMPLE 1 +``` +Update-AzSentinelIncident -WorkspaceName "" +``` + +Get a list of all open Incidents + +### EXAMPLE 2 +``` +Update-AzSentinelIncident -WorkspaceName '' -CaseNumber 42291 -Labels "NewLabel" +``` + +Add a new Label to list of Labels for a Incident + +### EXAMPLE 3 +``` +Update-AzSentinelIncident -WorkspaceName '' -CaseNumber 42293 -Status Closed -CloseReason FalsePositive -ClosedReasonText "Your input" +``` + +Close the Incidnet using status Closed, when status closed is selected then CloseReason and ClosedReasonText prperty are required to be filled in + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -CaseNumber +Enter the case number to get specfiek details of a open case + +```yaml +Type: Int32 +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: 0 +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Severity +Enter the Severity, you can choose from Medium, High, Low and Informational + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Status +Enter the Status of the incident, you can choose from New, InProgress and Closed + +```yaml +Type: Status +Parameter Sets: (All) +Aliases: +Accepted values: New, InProgress, Closed + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Comment +Enter Comment tekst to add comment to the incident + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Labels +Add Lebels to the incident, current configured Labels will be added to the existing Labels + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -CloseReason +When Status is equil to Closed, CloseReason is required. +You can select from: TruePositive, FalsePositive + +```yaml +Type: CloseReason +Parameter Sets: (All) +Aliases: +Accepted values: TruePositive, FalsePositive + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ClosedReasonText +When Status is equil to Closed, ClosedReasonText is required to be filled in. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS From acc8b216ef151ca55bb0170eec879d445cd7fb9b Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Sat, 22 Feb 2020 14:40:15 +0100 Subject: [PATCH 03/53] Release Feature playbook configuration (#33) * updating get alert and hunting rule function * updated error handling * Create Get-PlayBook.ps1 * init release for playbook * cleaning up * finishing playbook * adding get alert rule action function * releasing get logic app function * release new- az sen alert action and some codue update * init release playbook function * uppdated gitignore * init release remove azsentinel action rule * fixed compare issue * Merge branch 'development' of github.com:wortell/AZSentinel into feature/playbook * updating pester test result * updating readme * updating readme * updated docs and pester test results * restoring version --- AzSentinel/AzSentinel.psd1 | 5 +- AzSentinel/Classes/AlertRule.ps1 | 5 +- AzSentinel/Private/Get-AzSentinelPlayBook.ps1 | 72 +++++++++ .../Private/Get-LogAnalyticWorkspace.ps1 | 81 +++++----- AzSentinel/Private/precheck.ps1 | 12 ++ AzSentinel/Public/Get-AzSentinelAlertRule.ps1 | 48 ++++-- .../Public/Get-AzSentinelAlertRuleAction.ps1 | 88 +++++++++++ .../Public/Get-AzSentinelHuntingRule.ps1 | 4 +- .../Public/Import-AzSentinelAlertRule.ps1 | 42 +++-- .../Public/Import-AzSentinelHuntingRule.ps1 | 2 +- AzSentinel/Public/New-AzSentinelAlertRule.ps1 | 25 ++- .../Public/New-AzSentinelAlertRuleAction.ps1 | 122 ++++++++++++++ .../Remove-AzSentinelAlertRuleAction.ps1 | 95 +++++++++++ AzSentinel/docs/Get-AzSentinelAlertRule.md | 4 +- .../docs/Get-AzSentinelAlertRuleAction.md | 103 ++++++++++++ .../docs/Get-AzSentinelHuntingRule copy.md | 14 +- AzSentinel/docs/Get-AzSentinelHuntingRule.md | 9 +- AzSentinel/docs/Import-AzSentinelAlertRule.md | 11 +- .../docs/Import-AzSentinelHuntingRule.md | 9 +- AzSentinel/docs/New-AzSentinelAlertRule.md | 26 ++- .../docs/New-AzSentinelAlertRuleAction.md | 149 ++++++++++++++++++ AzSentinel/docs/New-AzSentinelHuntingRule.md | 3 +- AzSentinel/docs/README.md | 12 +- AzSentinel/docs/Remove-AzSentinelAlertRule.md | 10 +- .../docs/Remove-AzSentinelAlertRuleAction.md | 134 ++++++++++++++++ .../docs/Remove-AzSentinelHuntingRule.md | 10 +- AzSentinel/docs/Set-AzSentinel.md | 3 +- .../private/Get-AzSentinelPlayBook.tests.ps1 | 0 .../Get-AzSentinelAlertRuleAction.tests.ps1 | 0 .../New-AzSentinelAlertRuleAction.tests.ps1 | 0 ...Remove-AzSentinelAlertRuleAction.tests.ps1 | 0 README.md | 39 +++-- changelog.md | 56 ------- docs/Get-AzSentinelAlertRule.md | 4 +- docs/Get-AzSentinelAlertRuleAction.md | 103 ++++++++++++ docs/Get-AzSentinelHuntingRule.md | 9 +- docs/Get-AzSentinelIncident.md | 9 +- docs/Import-AzSentinelAlertRule.md | 11 +- docs/Import-AzSentinelHuntingRule.md | 9 +- docs/New-AzSentinelAlertRule.md | 26 ++- docs/New-AzSentinelAlertRuleAction.md | 149 ++++++++++++++++++ docs/New-AzSentinelHuntingRule.md | 3 +- docs/README.md | 12 +- docs/Remove-AzSentinelAlertRule.md | 10 +- docs/Remove-AzSentinelAlertRuleAction.md | 134 ++++++++++++++++ docs/Remove-AzSentinelHuntingRule.md | 10 +- docs/Set-AzSentinel.md | 3 +- examples/AlertRules copy.json | 23 +++ examples/AlertRules.json | 22 +-- 49 files changed, 1459 insertions(+), 271 deletions(-) create mode 100644 AzSentinel/Private/Get-AzSentinelPlayBook.ps1 create mode 100644 AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 create mode 100644 AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 create mode 100644 AzSentinel/Public/Remove-AzSentinelAlertRuleAction.ps1 create mode 100644 AzSentinel/docs/Get-AzSentinelAlertRuleAction.md create mode 100644 AzSentinel/docs/New-AzSentinelAlertRuleAction.md create mode 100644 AzSentinel/docs/Remove-AzSentinelAlertRuleAction.md create mode 100644 AzSentinel/tests/Unit/private/Get-AzSentinelPlayBook.tests.ps1 create mode 100644 AzSentinel/tests/Unit/public/Get-AzSentinelAlertRuleAction.tests.ps1 create mode 100644 AzSentinel/tests/Unit/public/New-AzSentinelAlertRuleAction.tests.ps1 create mode 100644 AzSentinel/tests/Unit/public/Remove-AzSentinelAlertRuleAction.tests.ps1 delete mode 100644 changelog.md create mode 100644 docs/Get-AzSentinelAlertRuleAction.md create mode 100644 docs/New-AzSentinelAlertRuleAction.md create mode 100644 docs/Remove-AzSentinelAlertRuleAction.md create mode 100644 examples/AlertRules copy.json diff --git a/AzSentinel/AzSentinel.psd1 b/AzSentinel/AzSentinel.psd1 index c76e462..7d36459 100644 --- a/AzSentinel/AzSentinel.psd1 +++ b/AzSentinel/AzSentinel.psd1 @@ -90,7 +90,10 @@ 'Import-AzSentinelHuntingRule', 'Remove-AzSentinelHuntingRule', 'Get-AzSentinelIncident', - 'Update-AzSentinelIncident' + 'Update-AzSentinelIncident', + 'Get-AzSentinelAlertRuleAction', + 'New-AzSentinelAlertRuleAction', + 'Remove-AzSentinelAlertRuleAction' ) # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. diff --git a/AzSentinel/Classes/AlertRule.ps1 b/AzSentinel/Classes/AlertRule.ps1 index c9a6682..a5c3486 100644 --- a/AzSentinel/Classes/AlertRule.ps1 +++ b/AzSentinel/Classes/AlertRule.ps1 @@ -26,6 +26,8 @@ class AlertProp { [Tactics[]] $Tactics + [string] $PlaybookName + static [string] TriggerOperatorSwitch([string]$value) { switch ($value) { "gt" { $value = "GreaterThan" } @@ -37,7 +39,7 @@ class AlertProp { return $value } - AlertProp ($Name, $DisplayName, $Description, $Severity, $Enabled, $Query, $QueryFrequency, $QueryPeriod, $TriggerOperator, $TriggerThreshold, $suppressionDuration, $suppressionEnabled, $Tactics) { + AlertProp ($Name, $DisplayName, $Description, $Severity, $Enabled, $Query, $QueryFrequency, $QueryPeriod, $TriggerOperator, $TriggerThreshold, $suppressionDuration, $suppressionEnabled, $Tactics, $PlaybookName) { $this.name = $Name $this.DisplayName = $DisplayName $this.Description = $Description @@ -51,6 +53,7 @@ class AlertProp { $this.SuppressionDuration = if ((! $null -eq $suppressionDuration) -or ( $false -eq $suppressionEnabled)) { if ($suppressionDuration -like "PT*") { $suppressionDuration.ToUpper() } else { ("PT" + $suppressionDuration).ToUpper() } } else { "PT1H" } $this.SuppressionEnabled = if ($suppressionEnabled) { $suppressionEnabled } else { $false } $this.Tactics = $Tactics + $this.PlaybookName = $PlaybookName } } diff --git a/AzSentinel/Private/Get-AzSentinelPlayBook.ps1 b/AzSentinel/Private/Get-AzSentinelPlayBook.ps1 new file mode 100644 index 0000000..69dc392 --- /dev/null +++ b/AzSentinel/Private/Get-AzSentinelPlayBook.ps1 @@ -0,0 +1,72 @@ +#requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} +#requires -version 6.2 + +function Get-AzSentinelPlayBook { + <# + .SYNOPSIS + Get Logic App Playbook + .DESCRIPTION + This function is used for resolving the Logic App and testing the compability with Azure Sentinel + .PARAMETER SubscriptionId + Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + .PARAMETER Name + Enter the Logic App name + .EXAMPLE + Get-AzSentinelPlayBook -Name "pkmsentinel" + This example will get search for the Logic app within the current subscripbtio and test to see if it's compatible for Sentinel + .NOTES + NAME: Get-AzSentinelPlayBook + #> + param ( + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Name + ) + + begin { + precheck + } + + process { + if ($SubscriptionId) { + Write-Verbose "Getting LogicApp from Subscription $($subscriptionId)" + $uri = "https://management.azure.com/subscriptions/$($subscriptionId)/providers/Microsoft.Logic/workflows?api-version=2016-06-01" + } + elseif ($script:subscriptionId) { + Write-Verbose "Getting LogicApp from Subscription $($script:subscriptionId)" + $uri = "https://management.azure.com/subscriptions/$($script:subscriptionId)/providers/Microsoft.Logic/workflows?api-version=2016-06-01" + } + else { + $return = "No SubscriptionID provided" + return $return + } + + $playBook = (Invoke-RestMethod -Uri $uri -Method get -Headers $script:authHeader).value | Where-Object { $_.name -eq $Name } -ErrorAction SilentlyContinue + + if ($null -ne $playBook) { + $uri1 = "https://management.azure.com$($playBook.id)/triggers/When_a_response_to_an_Azure_Sentinel_alert_is_triggered?api-version=2016-06-01" + try { + $playbookTrigger = (Invoke-RestMethod -Uri $uri1 -Method Get -Headers $script:authHeader).properties + + if ($null -ne $playbookTrigger) { + return $playBook + } + else { + $return = "Playbook doesn't start with 'When_a_response_to_an_Azure_Sentinel_alert_is_triggered' step!" + return $return + } + } + catch { + $return = $_.Exception.Message + return $return + } + } + else { + Write-Error "Unable to find LogicApp $Name under Subscription Id: $($script:subscriptionId)" + } + } +} diff --git a/AzSentinel/Private/Get-LogAnalyticWorkspace.ps1 b/AzSentinel/Private/Get-LogAnalyticWorkspace.ps1 index 0850c59..cdb53cb 100644 --- a/AzSentinel/Private/Get-LogAnalyticWorkspace.ps1 +++ b/AzSentinel/Private/Get-LogAnalyticWorkspace.ps1 @@ -1,14 +1,14 @@ #requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} #requires -version 6.2 function Get-LogAnalyticWorkspace { - <# + <# .SYNOPSIS Get log analytic workspace .DESCRIPTION This function is used by other function for getting the workspace infiormation and seting the right values for $script:workspace and $script:baseUri .PARAMETER SubscriptionId Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used - .PARAMETER workspace + .PARAMETER WorkspaceName Enter the Workspace name .PARAMETER FullObject If you want to return the full object data @@ -24,49 +24,50 @@ function Get-LogAnalyticWorkspace { .NOTES NAME: Get-LogAnalyticWorkspace #> - param ( - [Parameter(Mandatory = $false)] - [ValidateNotNullOrEmpty()] - [string] $SubscriptionId, + param ( + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$WorkspaceName, + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$WorkspaceName, - [Parameter(Mandatory = $false)] - [ValidateNotNullOrEmpty()] - [Switch]$FullObject - ) + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [Switch]$FullObject + ) - begin { - precheck - } + begin { + precheck + } - process { - if ($SubscriptionId) { - Write-Verbose "Getting Worspace from Subscription $($subscriptionId)" - $uri = "https://management.azure.com/subscriptions/$($subscriptionId)/providers/Microsoft.OperationalInsights/workspaces?api-version=2015-11-01-preview" - } - elseif ($script:subscriptionId) { - Write-Verbose "Getting Worspace from Subscription $($script:subscriptionId)" - $uri = "https://management.azure.com/subscriptions/$($script:subscriptionId)/providers/Microsoft.OperationalInsights/workspaces?api-version=2015-11-01-preview" - } - else { - Write-Error "No SubscriptionID provided" -ErrorAction Stop - } + process { + if ($SubscriptionId) { + Write-Verbose "Getting Worspace from Subscription $($subscriptionId)" + $uri = "https://management.azure.com/subscriptions/$($subscriptionId)/providers/Microsoft.OperationalInsights/workspaces?api-version=2015-11-01-preview" + } + elseif ($script:subscriptionId) { + Write-Verbose "Getting Worspace from Subscription $($script:subscriptionId)" + $uri = "https://management.azure.com/subscriptions/$($script:subscriptionId)/providers/Microsoft.OperationalInsights/workspaces?api-version=2015-11-01-preview" + } + else { + Write-Error "No SubscriptionID provided" -ErrorAction Stop + } - $workspaces = Invoke-webrequest -Uri $uri -Method get -Headers $script:authHeader - $workspaceObject = ($workspaces.Content | ConvertFrom-Json).value | Where-Object { $_.name -eq $WorkspaceName } + $workspaces = Invoke-webrequest -Uri $uri -Method get -Headers $script:authHeader + $workspaceObject = ($workspaces.Content | ConvertFrom-Json).value | Where-Object { $_.name -eq $WorkspaceName } - if ($workspaceObject) { - $Script:workspace = ($workspaceObject.id).trim() - $script:baseUri = "https://management.azure.com$($Script:workspace)" - if ($FullObject) { return $workspaceObject } - Write-Verbose ($workspaceObject | Format-List | Format-Table | Out-String) - Write-Verbose "Found Workspace $WorkspaceName in RG $($workspaceObject.id.Split('/')[4])" - } - else { - Write-Error "Unable to find workspace $WorkspaceName under Subscription Id: $($script:subscriptionId)" -ErrorAction Stop - } + if ($workspaceObject) { + $Script:workspace = ($workspaceObject.id).trim() + Write-Verbose "Workspace is: $($Script:workspace)" + $script:baseUri = "https://management.azure.com$($Script:workspace)" + if ($FullObject) { return $workspaceObject } + Write-Verbose ($workspaceObject | Format-List | Format-Table | Out-String) + Write-Verbose "Found Workspace $WorkspaceName in RG $($workspaceObject.id.Split('/')[4])" + } + else { + Write-Error "Unable to find workspace $WorkspaceName under Subscription Id: $($script:subscriptionId)" -ErrorAction Stop } + } } diff --git a/AzSentinel/Private/precheck.ps1 b/AzSentinel/Private/precheck.ps1 index 0599156..0718601 100644 --- a/AzSentinel/Private/precheck.ps1 +++ b/AzSentinel/Private/precheck.ps1 @@ -2,6 +2,18 @@ #requires -version 6.2 function precheck { + <# + .SYNOPSIS + PreCheck + .DESCRIPTION + This function is used as a precheck step by all the functions to test all the required authentication and properties. + .EXAMPLE + precheck + Run the test + .NOTES + NAME: precheck + #> + if ($null -eq $script:accessToken) { Get-AuthToken } elseif ([datetime]::UtcNow.AddMinutes(5) -lt $script:accessToken.ExpiresOn.DateTime ) { diff --git a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 index 90477d5..0edb7ed 100644 --- a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 @@ -3,19 +3,20 @@ function Get-AzSentinelAlertRule { <# - .SYNOPSIS - Get Azure Sentinel Alert Rules - .DESCRIPTION - With this function you can get the configuration of the Azure Sentinel Alert rule from Azure Sentinel - .PARAMETER SubscriptionId - Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used - .PARAMETER WorkspaceName - Enter the Workspace name - .PARAMETER RuleName - Enter the name of the Alert rule - .EXAMPLE - Get-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" - In this example you can get configuration of multiple alert rules in once + .SYNOPSIS + Get Azure Sentinel Alert Rules + .DESCRIPTION + With this function you can get the configuration of the Azure Sentinel Alert rule from Azure Sentinel + .PARAMETER SubscriptionId + Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + .PARAMETER WorkspaceName + Enter the Workspace name + .PARAMETER RuleName + Enter the name of the Alert rule + .EXAMPLE + Get-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" + Get-AzSentinelAlertRule -WorkspaceName "pkm02" -RuleName "AlertRule01" + In this example you can get configuration of multiple alert rules in once #> [cmdletbinding(SupportsShouldProcess)] @@ -75,9 +76,20 @@ function Get-AzSentinelAlertRule { foreach ($rule in $RuleName) { [PSCustomObject]$temp = $alertRules.value | Where-Object { $_.properties.displayName -eq $rule } if ($null -ne $temp) { + + $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId ($temp.name) + + if ($playbook) { + $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] + } + else { + $playbookName = $null + } + $temp.properties | Add-Member -NotePropertyName name -NotePropertyValue $temp.name -Force $temp.properties | Add-Member -NotePropertyName etag -NotePropertyValue $temp.etag -Force $temp.properties | Add-Member -NotePropertyName id -NotePropertyValue $temp.id -Force + $temp.properties | Add-Member -NotePropertyName playbookName -NotePropertyValue $playbookName -Force $return += $temp.properties } @@ -89,7 +101,17 @@ function Get-AzSentinelAlertRule { } else { $alertRules.value | ForEach-Object { + $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId ($_.name) + + if ($playbook) { + $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] + } + else { + $playbookName = $null + } $_.properties | Add-Member -NotePropertyName name -NotePropertyValue $_.name -Force + $_.properties | Add-Member -NotePropertyName playbookName -NotePropertyValue $playbookName -Force + return $_.properties } } diff --git a/AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 new file mode 100644 index 0000000..01eaa26 --- /dev/null +++ b/AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 @@ -0,0 +1,88 @@ +#requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} +#requires -version 6.2 + +function Get-AzSentinelAlertRuleAction { + <# + .SYNOPSIS + Get Azure Sentinel Alert rule Action + .DESCRIPTION + This function can be used to see if an action is attached to the alert rule, if so then the configuration will be returned + .PARAMETER SubscriptionId + Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + .PARAMETER WorkspaceName + Enter the Workspace name + .PARAMETER RuleName + Enter the name of the Alert rule + .PARAMETER RuleId + Enter the Rule Id to skip Get-AzSentinelAlertRule step + .EXAMPLE + Get-AzSentinelAlertRuleAction -WorkspaceName "pkm02" -RuleName "testrule01" + This example will get the Workspace ands return the full data object + .NOTES + NAME: Get-AzSentinelAlertRuleAction + #> + param ( + [Parameter(Mandatory = $false, + ParameterSetName = "Sub")] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $WorkspaceName, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$RuleName, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$RuleId + ) + + begin { + precheck + } + + process { + switch ($PsCmdlet.ParameterSetName) { + Sub { + $arguments = @{ + WorkspaceName = $WorkspaceName + SubscriptionId = $SubscriptionId + } + } + default { + $arguments = @{ + WorkspaceName = $WorkspaceName + } + } + } + + if ($RuleName) { + $alertId = (Get-AzSentinelAlertRule @arguments -RuleName $RuleName).name + } + elseif ($RuleId) { + $alertId = $RuleId + } + else { + Write-Error "No Alert Name or ID is provided" + } + + if ($alertId) { + $uri = "$($Script:baseUri)/providers/Microsoft.SecurityInsights/alertRules/$($alertId)/actions?api-version=2019-01-01-preview" + try { + $return = (Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader).value + return $return + } + catch { + $return = $_.Exception.Message + return $return + } + } + else { + $return = "No Alert found with provided: $($alertId)" + return $return + } + } +} diff --git a/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 index 2f6e495..39848fa 100644 --- a/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 @@ -13,6 +13,8 @@ function Get-AzSentinelHuntingRule { Enter the Workspace name .PARAMETER RuleName Enter the name of the Hunting rule name + .PARAMETER Filter + Select which type of Hunting rules you want to see. Option: HuntingQueries, GeneralExploration, LogManagement .EXAMPLE Get-AzSentinelHuntingRule -WorkspaceName "" -RuleName "","" In this example you can get configuration of multiple Hunting rules @@ -38,7 +40,7 @@ function Get-AzSentinelHuntingRule { [string[]]$RuleName, [Parameter(Mandatory = $false)] - [validateset("Hunting Queries", "GeneralExploration", "LogManagement")] + [validateset("HuntingQueries", "GeneralExploration", "LogManagement")] [string]$Filter ) diff --git a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 index b4cc111..c340ed2 100644 --- a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 @@ -16,7 +16,7 @@ function Import-AzSentinelAlertRule { .PARAMETER SettingsFile Path to the JSON or YAML file for the AlertRules .EXAMPLE - Import-AzSentinelAlertRule -WorkspaceName "" -SettingsFile ".\examples\AlertRules.json" + Import-AzSentinelAlertRule -WorkspaceName "pkm02" -SettingsFile ".\examples\AlertRules.json" In this example all the rules configured in the JSON file will be created or updated .EXAMPLE Import-AzSentinelAlertRule -WorkspaceName "" -SettingsFile ".\examples\SuspectApplicationConsent.yaml" @@ -60,8 +60,7 @@ function Import-AzSentinelAlertRule { } } } - Get-LogAnalyticWorkspace @arguments - + #Get-LogAnalyticWorkspace @arguments if ($SettingsFile.Extension -eq '.json') { try { @@ -95,7 +94,7 @@ function Import-AzSentinelAlertRule { $content = Get-AzSentinelAlertRule @arguments -RuleName $($item.displayName) -ErrorAction SilentlyContinue if ($content) { - Write-Host -Message "Rule $($item.displayName) exists in Azure Sentinel" + Write-Output "Rule $($item.displayName) exists in Azure Sentinel" $item | Add-Member -NotePropertyName name -NotePropertyValue $content.name -Force $item | Add-Member -NotePropertyName etag -NotePropertyValue $content.etag -Force @@ -131,7 +130,8 @@ function Import-AzSentinelAlertRule { $item.triggerThreshold, $item.suppressionDuration, $item.suppressionEnabled, - $item.tactics + $item.tactics, + $item.playbookName ) $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id) } @@ -142,13 +142,23 @@ function Import-AzSentinelAlertRule { if ($content) { $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name) if ($compareResult) { - Write-Host "Found Differences for rule: $($item.displayName)" -ForegroundColor Yellow - Write-Host ($compareResult | Format-Table | Out-String) + Write-Output "Found Differences for rule: $($item.displayName)" + Write-Output ($compareResult | Format-Table | Out-String) if ($PSCmdlet.ShouldProcess("Do you want to update profile: $($body.Properties.DisplayName)")) { try { - $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -EnumsAsStrings) - Write-Host "Successfully updated rule: $($item.displayName) with status: $($result.StatusDescription)" -ForegroundColor Green + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | Select-Object * -ExcludeProperty Properties.PlaybookName | ConvertTo-Json -EnumsAsStrings) + + if (($compareResult | Where-Object PropertyName -eq "playbookName").DiffValue) { + New-AzSentinelAlertRuleAction @arguments -PlayBookName ($body.Properties.playbookName) -RuleId $($body.Name) + } + elseif (($compareResult | Where-Object PropertyName -eq "playbookName").RefValue) { + Remove-AzSentinelAlertRuleAction @arguments -RuleId $($body.Name) -Confirm:$false + } + else { + #nothing + } + Write-Output "Successfully updated rule: $($item.displayName) with status: $($result.StatusDescription)" Write-Output ($body.Properties | Format-List | Format-Table | Out-String) } catch { @@ -157,20 +167,24 @@ function Import-AzSentinelAlertRule { } } else { - Write-Host "No change have been made for rule $($item.displayName), deployment aborted" + Write-Output "No change have been made for rule $($item.displayName), deployment aborted" } } else { - Write-Host "Rule $($item.displayName) is compliance, nothing to do" - Write-Host ($body.Properties | Format-List | Format-Table | Out-String) + Write-Output "Rule $($item.displayName) is compliance, nothing to do" + Write-Output ($body.Properties | Format-List | Format-Table | Out-String) } } else { Write-Verbose "Creating new rule: $($item.displayName)" try { - $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -EnumsAsStrings) - Write-Host "Successfully created rule: $($item.displayName) with status: $($result.StatusDescription)" -ForegroundColor Green + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | Select-Object * -ExcludeProperty Properties.PlaybookName | ConvertTo-Json -EnumsAsStrings) + if ($body.Properties.playbookName) { + New-AzSentinelAlertRuleAction -PlayBookName $($body.Properties.playbookName) -RuleName $($body.Properties.DisplayName) -confirm:$false + } + + Write-Output "Successfully created rule: $($item.displayName) with status: $($result.StatusDescription)" Write-Output ($body.Properties | Format-List | Format-Table | Out-String) } catch { diff --git a/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 index be86b45..f924d3d 100644 --- a/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 @@ -87,7 +87,7 @@ function Import-AzSentinelHuntingRule { } foreach ($item in $analytics) { - Write-Host -Message "Started with Hunting rule: $($item.displayName)" + Write-Output "Started with Hunting rule: $($item.displayName)" try { Write-Verbose -Message "Get rule $($item.description)" diff --git a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 index dc5cc13..004598c 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 @@ -35,6 +35,8 @@ function New-AzSentinelAlertRule { Set $true to enable Suppression or $false to disable Suppression .PARAMETER Tactics Enter the Tactics, valid values: "InitialAccess", "Persistence", "Execution", "PrivilegeEscalation", "DefenseEvasion", "CredentialAccess", "LateralMovement", "Discovery", "Collection", "Exfiltration", "CommandAndControl", "Impact" + .PARAMETER PlaybookName + Enter the Logic App name that you want to configure as playbook trigger .EXAMPLE New-AzSentinelAlertRule -WorkspaceName "" -DisplayName "" -Description "" -Severity -Enabled $true -Query '' -QueryFrequency "" -QueryPeriod "" -TriggerOperator -TriggerThreshold -SuppressionDuration "" -SuppressionEnabled $false -Tactics @("","") In this example you create a new Alert rule by defining the rule properties from CMDLET @@ -94,7 +96,11 @@ function New-AzSentinelAlertRule { [Parameter(Mandatory)] [AllowEmptyCollection()] - [Tactics[]] $Tactics + [Tactics[]] $Tactics, + + [Parameter(Mandatory = $false)] + [AllowEmptyString()] + [string] $PlaybookName = $null ) begin { @@ -163,7 +169,8 @@ function New-AzSentinelAlertRule { $TriggerThreshold, $SuppressionDuration, $SuppressionEnabled, - $Tactics + $Tactics, + $PlaybookName ) $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id) } @@ -180,6 +187,15 @@ function New-AzSentinelAlertRule { if ($PSCmdlet.ShouldProcess("Do you want to update profile: $($body.Properties.DisplayName)")) { try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -EnumsAsStrings) + + if ($compareResult.PropertyName -contains "playbookName") { + #New-AzSentinelAlertRuleAction @arguments -PlayBookName $(($body.Properties).playbookName) -RuleId $($body.Name) -Confirm:$false + New-AzSentinelAlertRuleAction @arguments -PlayBookName 'pkmsentinel' -RuleId 'b6103d42-d2fb-4f35-bced-c76a7f31ee4e' -Confirm:$false + } + elseif ($null -ne $content.playbookName -and $null -eq $body.Properties.playbookName) { + Write-Output "Currently Playbook configured but will be removed now" + } + Write-Output "Successfully updated rule: $($DisplayName) with status: $($result.StatusDescription)" Write-Output ($body.Properties | Format-List | Format-Table | Out-String) } @@ -202,6 +218,11 @@ function New-AzSentinelAlertRule { try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -EnumsAsStrings) + + if ($body.Properties.playbookName) { + New-AzSentinelAlertRuleAction -PlayBookName $($body.Properties.playbookName) -RuleName $($body.Properties.DisplayName) -confirm:$false + } + Write-Output "Successfully created rule: $($DisplayName) with status: $($result.StatusDescription)" Write-Output ($body.Properties | Format-List | Format-Table | Out-String) } diff --git a/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 b/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 new file mode 100644 index 0000000..149fc41 --- /dev/null +++ b/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 @@ -0,0 +1,122 @@ +#requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} +#requires -version 6.2 + +function New-AzSentinelAlertRuleAction { + <# + .SYNOPSIS + Create Azure Sentinal Alert Rule Action + .DESCRIPTION + Use this function to creates Azure Sentinal Alert rule action + .PARAMETER SubscriptionId + Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + .PARAMETER WorkspaceName + Enter the Workspace name + .PARAMETER PlayBookName + Enter the Playbook name that you want to assign to the alert rule + .PARAMETER RuleName + Enter the Alert Rule name that you want to configure + .PARAMETER RuleId + Enter the Alert Rule ID that you want to configure + .EXAMPLE + New-AzSentinelAlertRuleAction -WorkspaceName pkm02 -PlayBookName "pkmsentinel" -RuleName "testrule01" + New-AzSentinelAlertRuleAction -WorkspaceName pkm02 -PlayBookName "pkmsentinel" -RuleId 'b6103d42-d2fb-4f35-bced-c76a7f31ee4e' + In this example you you assign the playbook to the Alert rule + #> + + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory = $false, + ParameterSetName = "Sub")] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $WorkspaceName, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $PlayBookName, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$RuleName, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$RuleId + ) + process { + switch ($PsCmdlet.ParameterSetName) { + Sub { + $arguments = @{ + WorkspaceName = $WorkspaceName + SubscriptionId = $SubscriptionId + } + } + default { + $arguments = @{ + WorkspaceName = $WorkspaceName + } + } + } + + if ($RuleName) { + $alertId = (Get-AzSentinelAlertRule @arguments -RuleName $RuleName -ErrorAction SilentlyContinue).name + } + elseif ($RuleId) { + $alertId = $RuleId + } + else { + Write-Error "No Alert Name or ID is provided" + } + + $action = $null + + $playBook = Get-AzSentinelPlayBook -Name $PlayBookName + $action = Get-AzSentinelAlertRuleAction @arguments -RuleId $alertId -ErrorAction SilentlyContinue + + + if ($null -eq $action) { + $guid = New-Guid + + $body = @{ + "id" = "$($Script:baseUri)/providers/Microsoft.SecurityInsights/alertRules/$($alertId)/actions/$guid" + "name" = $guid + "type" = "Microsoft.SecurityInsights/alertRules/actions" + "properties" = @{ + "ruleId" = $alertId + "triggerUri" = "$($playBook.properties.accessEndpoint)/triggers/When_a_response_to_an_Azure_Sentinel_alert_is_triggered/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2FWhen_a_response_to_an_Azure_Sentinel_alert_is_triggered%2Frun&sv=1.0&sig=NMCSM7uOK4I42L2IPWdgL2eR3-VpoKLXpbTzI9_7wvI" + "logicAppResourceId" = "$($playBook.id)" + } + } + + $uri = "$($Script:baseUri)/providers/Microsoft.SecurityInsights/alertRules/$($alertId)/actions/$($guid)?api-version=2019-01-01-preview" + try { + $return = Invoke-WebRequest -Method Put -Uri $uri -Headers $Script:authHeader -Body ($body | ConvertTo-Json -Depth 10) + if ($return.StatusCode -eq 201 -and $result.StatusDescription -eq "Created"){ + Write-Output "Successfully created Action for Rule: $($RuleName) with Playbook $($PlayBookName) Status: $($return.StatusDescription)" + return $return.StatusDescription + } + else { + Write-Error "Unable to create Action for Rule: $($RuleName) with Playbook $($PlayBookName) Status: $($return.StatusDescription)" + return $return.StatusDescription + } + } + catch { + Write-Error "Unable to create Action for Rule: $($RuleName) with Playbook $($PlayBookName) Error: $($_.Exception.Message)" + return $_.Exception.Message + Write-Verbose $_. + } + } + elseif ((($action.properties.logicAppResourceId).Split('/')[-1]) -eq $PlayBookName) { + Write-Output "Alert Rule: $($alertId) has already playbook assigned: $(($action.properties.logicAppResourceId).Split('/')[-1])" + } + elseif ((($action.properties.logicAppResourceId).Split('/')[-1]) -ne $PlayBookName) { + Write-Output "Alert rule $($RuleName) assigned to a different playbook with name $(($action.properties.logicAppResourceId).Split('/')[-1])" + } + else { + #nothing? + } + } +} diff --git a/AzSentinel/Public/Remove-AzSentinelAlertRuleAction.ps1 b/AzSentinel/Public/Remove-AzSentinelAlertRuleAction.ps1 new file mode 100644 index 0000000..a8319e8 --- /dev/null +++ b/AzSentinel/Public/Remove-AzSentinelAlertRuleAction.ps1 @@ -0,0 +1,95 @@ +#requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} +#requires -version 6.2 + +function Remove-AzSentinelAlertRuleAction { + <# + .SYNOPSIS + Remove Azure Sentinel Alert rule Action + .DESCRIPTION + This function can be used to see if an action is attached to the alert rule, if so then the configuration will be returned + .PARAMETER SubscriptionId + Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + .PARAMETER WorkspaceName + Enter the Workspace name + .PARAMETER RuleName + Enter the name of the Alert rule + .PARAMETER RuleId + Enter the Alert Rule ID that you want to configure + .EXAMPLE + Remove-AzSentinelAlertRuleAction -WorkspaceName "pkm02" -RuleName "testrule01" + This example will get the Workspace ands return the full data object + .NOTES + NAME: Remove-AzSentinelAlertRuleAction + #> + + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory = $false, + ParameterSetName = "Sub")] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $WorkspaceName, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$RuleName, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$RuleId + ) + + begin { + precheck + } + + process { + switch ($PsCmdlet.ParameterSetName) { + Sub { + $arguments = @{ + WorkspaceName = $WorkspaceName + SubscriptionId = $SubscriptionId + } + } + default { + $arguments = @{ + WorkspaceName = $WorkspaceName + } + } + } + + if ($RuleName) { + $result = Get-AzSentinelAlertRuleAction @arguments -RuleName $RuleName + } + elseif ($RuleId) { + $result = Get-AzSentinelAlertRuleAction @arguments -RuleId $RuleId + } + else { + Write-Error "No Alert Name or ID is provided" + } + + if ($result) { + $uri = "$($Script:baseUri)/providers/Microsoft.SecurityInsights/alertRules/$($result.id.split('asicustomalertsv3_')[-1])?api-version=2019-01-01-preview" + Write-Verbose $uri + + if ($PSCmdlet.ShouldProcess("Do you want to remove Alert Rule action for rule: $($RuleName)")) { + try { + $return = Invoke-WebRequest -Uri $uri -Method DELETE -Headers $script:authHeader + Write-Verbose $return + Write-Output "Rule action $($result.properties.logicAppResourceId.Split('/')[-1]) removed for rule $($RuleName) with status: $($return.StatusCode)" + return $return.StatusCode + } + catch { + Write-Verbose $_ + return $_.Exception.Message + } + } + } + else { + Write-Output "No Alert Action found for Rule: $($RuleName)" + } + } +} diff --git a/AzSentinel/docs/Get-AzSentinelAlertRule.md b/AzSentinel/docs/Get-AzSentinelAlertRule.md index bb67977..cb81a0d 100644 --- a/AzSentinel/docs/Get-AzSentinelAlertRule.md +++ b/AzSentinel/docs/Get-AzSentinelAlertRule.md @@ -25,9 +25,9 @@ With this function you can get the configuration of the Azure Sentinel Alert rul ### EXAMPLE 1 ``` Get-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" -``` - +Get-AzSentinelAlertRule -WorkspaceName "pkm02" -RuleName "AlertRule01" In this example you can get configuration of multiple alert rules in once +``` ## PARAMETERS diff --git a/AzSentinel/docs/Get-AzSentinelAlertRuleAction.md b/AzSentinel/docs/Get-AzSentinelAlertRuleAction.md new file mode 100644 index 0000000..f175bb5 --- /dev/null +++ b/AzSentinel/docs/Get-AzSentinelAlertRuleAction.md @@ -0,0 +1,103 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Get-AzSentinelAlertRuleAction + +## SYNOPSIS +Get Azure Sentinel Alert rule Action + +## SYNTAX + +``` +Get-AzSentinelAlertRuleAction [-SubscriptionId ] -WorkspaceName [-RuleName ] + [-RuleId ] [] +``` + +## DESCRIPTION +This function can be used to see if an action is attached to the alert rule, if so then the configuration will be returned + +## EXAMPLES + +### EXAMPLE 1 +``` +Get-AzSentinelAlertRuleAction -WorkspaceName "pkm02" -RuleName "testrule01" +This example will get the Workspace ands return the full data object +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleName +Enter the name of the Alert rule + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleId +Enter the Rule Id to skip Get-AzSentinelAlertRule step + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES +NAME: Get-AzSentinelAlertRuleAction + +## RELATED LINKS diff --git a/AzSentinel/docs/Get-AzSentinelHuntingRule copy.md b/AzSentinel/docs/Get-AzSentinelHuntingRule copy.md index 060c6a5..426986a 100644 --- a/AzSentinel/docs/Get-AzSentinelHuntingRule copy.md +++ b/AzSentinel/docs/Get-AzSentinelHuntingRule copy.md @@ -24,17 +24,21 @@ With this function you can get the configuration of the Azure Sentinel Hunting r ### EXAMPLE 1 ``` -Get-AzSentinelHuntingRule -WorkspaceName "" -RuleName "","" +Get-AzSentinelIncident -WorkspaceName "" +Get a list of all open Incidents ``` -In this example you can get configuration of multiple Hunting rules - ### EXAMPLE 2 ``` -Get-AzSentinelHuntingRule -WorkspaceName "" +Get-AzSentinelIncident -WorkspaceName "" -CaseNumber +Get information of a specifiek incident with providing the casenumber ``` -In this example you can get a list of all the Hunting rules in once +### EXAMPLE 3 +``` +Get-AzSentinelIncident -WorkspaceName "" -IncidentName "","" +Get information of one or more incidents with providing a incident name, this is the name of the alert rule that triggered the incident +``` ## PARAMETERS diff --git a/AzSentinel/docs/Get-AzSentinelHuntingRule.md b/AzSentinel/docs/Get-AzSentinelHuntingRule.md index 060c6a5..b4fdb96 100644 --- a/AzSentinel/docs/Get-AzSentinelHuntingRule.md +++ b/AzSentinel/docs/Get-AzSentinelHuntingRule.md @@ -25,16 +25,14 @@ With this function you can get the configuration of the Azure Sentinel Hunting r ### EXAMPLE 1 ``` Get-AzSentinelHuntingRule -WorkspaceName "" -RuleName "","" -``` - In this example you can get configuration of multiple Hunting rules +``` ### EXAMPLE 2 ``` Get-AzSentinelHuntingRule -WorkspaceName "" -``` - In this example you can get a list of all the Hunting rules in once +``` ## PARAMETERS @@ -84,7 +82,8 @@ Accept wildcard characters: False ``` ### -Filter -{{ Fill Filter Description }} +Select which type of Hunting rules you want to see. +Option: HuntingQueries, GeneralExploration, LogManagement ```yaml Type: String diff --git a/AzSentinel/docs/Import-AzSentinelAlertRule.md b/AzSentinel/docs/Import-AzSentinelAlertRule.md index 568ef4d..d771728 100644 --- a/AzSentinel/docs/Import-AzSentinelAlertRule.md +++ b/AzSentinel/docs/Import-AzSentinelAlertRule.md @@ -25,24 +25,21 @@ This way you can manage your Alert rules dynamic from JSON or multiple YAML file ### EXAMPLE 1 ``` -Import-AzSentinelAlertRule -WorkspaceName "" -SettingsFile ".\examples\AlertRules.json" -``` - +Import-AzSentinelAlertRule -WorkspaceName "pkm02" -SettingsFile ".\examples\AlertRules.json" In this example all the rules configured in the JSON file will be created or updated +``` ### EXAMPLE 2 ``` Import-AzSentinelAlertRule -WorkspaceName "" -SettingsFile ".\examples\SuspectApplicationConsent.yaml" -``` - In this example all the rules configured in the YAML file will be created or updated +``` ### EXAMPLE 3 ``` Get-Item .\examples\*.json | Import-AzSentinelAlertRule -WorkspaceName "" -``` - In this example you can select multiple JSON files and Pipeline it to the SettingsFile parameter +``` ## PARAMETERS diff --git a/AzSentinel/docs/Import-AzSentinelHuntingRule.md b/AzSentinel/docs/Import-AzSentinelHuntingRule.md index c3a08d2..80a2b70 100644 --- a/AzSentinel/docs/Import-AzSentinelHuntingRule.md +++ b/AzSentinel/docs/Import-AzSentinelHuntingRule.md @@ -26,23 +26,20 @@ This way you can manage your Hunting rules dynamic from JSON or multiple YAML fi ### EXAMPLE 1 ``` Import-AzSentinelHuntingRule -WorkspaceName "infr-weu-oms-t-7qodryzoj6agu" -SettingsFile ".\examples\HuntingRules.json" -``` - In this example all the rules configured in the JSON file will be created or updated +``` ### EXAMPLE 2 ``` Import-AzSentinelHuntingRule -WorkspaceName "" -SettingsFile ".\examples\HuntingRules.yaml" -``` - In this example all the rules configured in the YAML file will be created or updated +``` ### EXAMPLE 3 ``` Get-Item .\examples\HuntingRules*.json | Import-AzSentinelHuntingRule -WorkspaceName "" -``` - In this example you can select multiple JSON files and Pipeline it to the SettingsFile parameter +``` ## PARAMETERS diff --git a/AzSentinel/docs/New-AzSentinelAlertRule.md b/AzSentinel/docs/New-AzSentinelAlertRule.md index 3ef840e..b81acd6 100644 --- a/AzSentinel/docs/New-AzSentinelAlertRule.md +++ b/AzSentinel/docs/New-AzSentinelAlertRule.md @@ -16,8 +16,8 @@ Create Azure Sentinal Alert Rules New-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName -DisplayName -Description -Severity -Enabled -Query -QueryFrequency [-QueryPeriod ] -TriggerOperator -TriggerThreshold - -SuppressionDuration -SuppressionEnabled -Tactics [-WhatIf] [-Confirm] - [] + -SuppressionDuration -SuppressionEnabled -Tactics [-PlaybookName ] + [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -27,10 +27,9 @@ Use this function creates Azure Sentinal Alert rules from provided CMDLET ### EXAMPLE 1 ``` -New-AzSentinelAlertRule -WorkspaceName "" -DisplayName "" -Description "" -Severity "" -Enabled -Query '' -QueryFrequency "" -QueryPeriod "" -TriggerOperator "" -TriggerThreshold -SuppressionDuration "" -SuppressionEnabled $false -Tactics @("","") -``` - +New-AzSentinelAlertRule -WorkspaceName "" -DisplayName "" -Description "" -Severity -Enabled $true -Query '' -QueryFrequency "" -QueryPeriod "" -TriggerOperator -TriggerThreshold -SuppressionDuration "" -SuppressionEnabled $false -Tactics @("","") In this example you create a new Alert rule by defining the rule properties from CMDLET +``` ## PARAMETERS @@ -177,7 +176,7 @@ Select the triggert Operator, valid values are: "GreaterThan", "FewerThan", "Equ Type: TriggerOperator Parameter Sets: (All) Aliases: -Accepted values: GreaterThan, FewerThan, EqualTo, NotEqualTo +Accepted values: GreaterThan, LessThan, Equal, NotEqual, gt, lt, eq, ne Required: True Position: Named @@ -247,6 +246,21 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -PlaybookName +Enter the Logic App name that you want to configure as playbook trigger + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. diff --git a/AzSentinel/docs/New-AzSentinelAlertRuleAction.md b/AzSentinel/docs/New-AzSentinelAlertRuleAction.md new file mode 100644 index 0000000..bf1d622 --- /dev/null +++ b/AzSentinel/docs/New-AzSentinelAlertRuleAction.md @@ -0,0 +1,149 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# New-AzSentinelAlertRuleAction + +## SYNOPSIS +Create Azure Sentinal Alert Rule Action + +## SYNTAX + +``` +New-AzSentinelAlertRuleAction [-SubscriptionId ] -WorkspaceName -PlayBookName + [-RuleName ] [-RuleId ] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +Use this function to creates Azure Sentinal Alert rule action + +## EXAMPLES + +### EXAMPLE 1 +``` +New-AzSentinelAlertRuleAction -WorkspaceName pkm02 -PlayBookName "pkmsentinel" -RuleName "testrule01" +New-AzSentinelAlertRuleAction -WorkspaceName pkm02 -PlayBookName "pkmsentinel" -RuleId 'b6103d42-d2fb-4f35-bced-c76a7f31ee4e' +In this example you you assign the playbook to the Alert rule +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -PlayBookName +Enter the Playbook name that you want to assign to the alert rule + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleName +Enter the Alert Rule name that you want to configure + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleId +Enter the Alert Rule ID that you want to configure + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/AzSentinel/docs/New-AzSentinelHuntingRule.md b/AzSentinel/docs/New-AzSentinelHuntingRule.md index 4ea92ec..195e386 100644 --- a/AzSentinel/docs/New-AzSentinelHuntingRule.md +++ b/AzSentinel/docs/New-AzSentinelHuntingRule.md @@ -25,9 +25,8 @@ Use this function to creates Azure Sentinal Hunting rule ### EXAMPLE 1 ``` New-AzSentinelHuntingRule -WorkspaceName "" -DisplayName "" -Description "" -Tactics "","" -Query '' -``` - In this example you create a new hunting rule by defining the rule properties from CMDLET +``` ## PARAMETERS diff --git a/AzSentinel/docs/README.md b/AzSentinel/docs/README.md index 678f26d..95abd60 100644 --- a/AzSentinel/docs/README.md +++ b/AzSentinel/docs/README.md @@ -6,7 +6,11 @@ 4. [Import-AzSentinelAlertRule](Import-AzSentinelAlertRule.md) 5. [Remove-AzSentinelAlertRule](Remove-AzSentinelAlertRule.md) 6. [Get-AzSentinelIncident](Get-AzSentinelIncident.md) -7. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) -8. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) -9. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) -10. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) +7. [Update-AzSentinelIncident](Update-AzSentinelIncident.md) +8. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) +9. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) +10. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) +11. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) +12. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) +13. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) +14. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) diff --git a/AzSentinel/docs/Remove-AzSentinelAlertRule.md b/AzSentinel/docs/Remove-AzSentinelAlertRule.md index 186d6c6..fb0102a 100644 --- a/AzSentinel/docs/Remove-AzSentinelAlertRule.md +++ b/AzSentinel/docs/Remove-AzSentinelAlertRule.md @@ -25,25 +25,21 @@ With this function you can remove Azure Sentinal Alert rules from Powershell, if ### EXAMPLE 1 ``` Remove-AzSentinelAlertRule -WorkspaceName "" -RuleName "" -``` - In this example the defined rule will be removed from Azure Sentinel +``` ### EXAMPLE 2 ``` Remove-AzSentinelAlertRule -WorkspaceName "" -RuleName "","", "" -``` - In this example you can define multiple rules that will be removed +``` ### EXAMPLE 3 ``` Remove-AzSentinelAlertRule -WorkspaceName "" +In this example no rule is specified, all rules will be removed one by one. For each rule you need to confirm the action ``` -In this example no rule is specified, all rules will be removed one by one. -For each rule you need to confirm the action - ## PARAMETERS ### -SubscriptionId diff --git a/AzSentinel/docs/Remove-AzSentinelAlertRuleAction.md b/AzSentinel/docs/Remove-AzSentinelAlertRuleAction.md new file mode 100644 index 0000000..2630e0f --- /dev/null +++ b/AzSentinel/docs/Remove-AzSentinelAlertRuleAction.md @@ -0,0 +1,134 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Remove-AzSentinelAlertRuleAction + +## SYNOPSIS +Remove Azure Sentinel Alert rule Action + +## SYNTAX + +``` +Remove-AzSentinelAlertRuleAction [-SubscriptionId ] -WorkspaceName [-RuleName ] + [-RuleId ] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +This function can be used to see if an action is attached to the alert rule, if so then the configuration will be returned + +## EXAMPLES + +### EXAMPLE 1 +``` +Remove-AzSentinelAlertRuleAction -WorkspaceName "pkm02" -RuleName "testrule01" +This example will get the Workspace ands return the full data object +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleName +Enter the name of the Alert rule + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleId +Enter the Alert Rule ID that you want to configure + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES +NAME: Remove-AzSentinelAlertRuleAction + +## RELATED LINKS diff --git a/AzSentinel/docs/Remove-AzSentinelHuntingRule.md b/AzSentinel/docs/Remove-AzSentinelHuntingRule.md index 24ca7e6..51bd50b 100644 --- a/AzSentinel/docs/Remove-AzSentinelHuntingRule.md +++ b/AzSentinel/docs/Remove-AzSentinelHuntingRule.md @@ -25,25 +25,21 @@ With this function you can remove Azure Sentinal hunting rules from Powershell, ### EXAMPLE 1 ``` Remove-AzSentinelHuntingRule -WorkspaceName "" -RuleName "" -``` - In this example the defined hunting rule will be removed from Azure Sentinel +``` ### EXAMPLE 2 ``` Remove-AzSentinelHuntingRule -WorkspaceName "" -RuleName "","", "" -``` - In this example you can define multiple hunting rules that will be removed +``` ### EXAMPLE 3 ``` Remove-AzSentinelHuntingRule -WorkspaceName "" +In this example no hunting rule is specified, all hunting rules will be removed one by one. For each rule you need to confirm the action ``` -In this example no hunting rule is specified, all hunting rules will be removed one by one. -For each rule you need to confirm the action - ## PARAMETERS ### -SubscriptionId diff --git a/AzSentinel/docs/Set-AzSentinel.md b/AzSentinel/docs/Set-AzSentinel.md index a3e5ac0..6ccea5b 100644 --- a/AzSentinel/docs/Set-AzSentinel.md +++ b/AzSentinel/docs/Set-AzSentinel.md @@ -24,9 +24,8 @@ This function enables Azure Sentinel on a existing Workspace ### EXAMPLE 1 ``` Set-AzSentinel -WorkspaceName "" -``` - This example will enable Azure Sentinel for the provided workspace +``` ## PARAMETERS diff --git a/AzSentinel/tests/Unit/private/Get-AzSentinelPlayBook.tests.ps1 b/AzSentinel/tests/Unit/private/Get-AzSentinelPlayBook.tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/AzSentinel/tests/Unit/public/Get-AzSentinelAlertRuleAction.tests.ps1 b/AzSentinel/tests/Unit/public/Get-AzSentinelAlertRuleAction.tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/AzSentinel/tests/Unit/public/New-AzSentinelAlertRuleAction.tests.ps1 b/AzSentinel/tests/Unit/public/New-AzSentinelAlertRuleAction.tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/AzSentinel/tests/Unit/public/Remove-AzSentinelAlertRuleAction.tests.ps1 b/AzSentinel/tests/Unit/public/Remove-AzSentinelAlertRuleAction.tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md index e60fe2e..12db987 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # Azure Sentinel +| branch | status | +| ----------- | ---------------------------------------------------------------------------------------------- | +| master | ![](https://github.com/wortell/AZSentinel/workflows/Build-Module/badge.svg?branch=master) | +| development | ![](https://github.com/wortell/AZSentinel/workflows/Build-Module/badge.svg?branch=development) | + Azure Sentinel is a cloud-native SIEM that provides intelligent security analytics for your entire enterprise at cloud scale. Get limitless cloud speed and scale to help focus on what really matters. Easily collect data from all your cloud or on-premises assets, Office 365, Azure resources, and other clouds. Effectively detect threats with built-in machine learning from Microsoft’s security analytics experts. Automate threat response, using built-in orchestration and automation playbooks. [read more](https://docs.microsoft.com/en-us/azure/sentinel/overview) ## Why this PowerShell Module @@ -53,7 +58,8 @@ To create a Azure Sentinel Rule, use the following JSON format. "Persistence", "LateralMovement", "Collection" - ] + ], + "playbookName": "string" } ] } @@ -65,18 +71,19 @@ The following tables describe the values you need to set in the schema. | Name | Type | Required | Allowed Values | Example | | ------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -| displayName | string | yes | * | DisplayName | -| description | string | yes | * | Description | -| severity | string | yes | Medium, High, Low, Informational | Medium | -| enabled | bool | yes | true, false | true | -| query | string | yes | special character need to be escaped by \ | SecurityEvent \| where EventID == \"4688\" \| where CommandLine contains \\"-noni -ep bypass $\\" | -| queryFrequency | string | yes | Value must be between 5 minutes and 24 hours | 5H | -| queryPeriod | string | yes | Value must be between 5 minutes and 24 hours | 1440M | -| triggerOperator | string | yes | GreaterThan, FewerThan, EqualTo, NotEqualTo | GreaterThan | -| triggerThreshold | int | yes | The value must be between 0 and 10000 | 5 | -| suppressionDuration | string | yes | Value must be between 5 minutes and 24 hours | 11H | -| suppressionEnabled | bool | yes | true, false | true | -| tactics | array | yes | InitialAccess, Persistence,Execution,PrivilegeEscalation,DefenseEvasion,CredentialAccess,LateralMovement,Discovery,Collection,Exfiltration,CommandAndControl,Impact | true | +| displayName | string | true | * | DisplayName | +| description | string | true | * | Description | +| severity | string | true | Medium, High, Low, Informational | Medium | +| enabled | bool | true | true, false | true | +| query | string | true | special character need to be escaped by \ | SecurityEvent \| where EventID == \"4688\" \| where CommandLine contains \\"-noni -ep bypass $\\" | +| queryFrequency | string | true | Value must be between 5 minutes and 24 hours | 5H | +| queryPeriod | string | true | Value must be between 5 minutes and 24 hours | 1440M | +| triggerOperator | string | true | GreaterThan, FewerThan, EqualTo, NotEqualTo | GreaterThan | +| triggerThreshold | int | true | The value must be between 0 and 10000 | 5 | +| suppressionDuration | string | true | Value must be between 5 minutes and 24 hours | 11H | +| suppressionEnabled | bool | true | true, false | true | +| tactics | array | true | InitialAccess, Persistence,Execution,PrivilegeEscalation,DefenseEvasion,CredentialAccess,LateralMovement,Discovery,Collection,Exfiltration,CommandAndControl,Impact | true | +| playbookName | string | false | Enter the Logic App name that you want to configure as playbook trigger | LogicApp01 | ## Find us @@ -88,13 +95,13 @@ The following tables describe the values you need to set in the schema. Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. -## Thanks +## Contributors -* A big thank you goes out to [@bgelens](https://github.com/bgelens) and [@MauRiEEZZZ](https://github.com/MauRiEEZZZ) for their great contributions! +* A big thank you goes out to [@bgelens](https://github.com/bgelens) and [@MauRiEEZZZ](https://github.com/MauRiEEZZZ) for their contributions! ## Authors -* **Pouyan Khabazi** - *Initial work* - [GitHub](https://github.com/pkhabazi) / [Blog](https://pkm-technology.com) +* **Pouyan Khabazi** - *Developer and Maintainer* - [GitHub](https://github.com/pkhabazi) / [Blog](https://pkm-technology.com) See also the list of [contributors](https://github.com/wortell/AzSentinel/contributors) who participated in this project. diff --git a/changelog.md b/changelog.md deleted file mode 100644 index 0c0a14a..0000000 --- a/changelog.md +++ /dev/null @@ -1,56 +0,0 @@ -# ChangeLog - -## v0.6.1 - 14-10-2019 - -### Breaking Changes - -- nothing - -### Updates and Fixes - -- Fixed enum issue, when enum get converted to json int value was used instead of string value -- fixed Remove-AzSentinelAlertRule, wrong api was used - -### Experimental Features - -- none - -### Code Cleanup - -- updated headers -- updated enums to use standard format instead of DotNet.core - -### Tests - -- none - -### Documentation and Help Content - -- updated docs - -## v0.1.0 - 05-09-2019 - -### Breaking Changes - -- Renamed "New-AzAnalytic" to "New-AzSentinelAlertRule" - -### Updates and Fixes - -- Added support for accepting data from pipeline for "New-AzSentinelAlertRule" -SettingsFile -- updated documentation - -### Experimental Features - -- none - -### Code Cleanup - -- updated docs - -### Tests - -- none - -### Documentation and Help Content - -- updated docs diff --git a/docs/Get-AzSentinelAlertRule.md b/docs/Get-AzSentinelAlertRule.md index bb67977..cb81a0d 100644 --- a/docs/Get-AzSentinelAlertRule.md +++ b/docs/Get-AzSentinelAlertRule.md @@ -25,9 +25,9 @@ With this function you can get the configuration of the Azure Sentinel Alert rul ### EXAMPLE 1 ``` Get-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" -``` - +Get-AzSentinelAlertRule -WorkspaceName "pkm02" -RuleName "AlertRule01" In this example you can get configuration of multiple alert rules in once +``` ## PARAMETERS diff --git a/docs/Get-AzSentinelAlertRuleAction.md b/docs/Get-AzSentinelAlertRuleAction.md new file mode 100644 index 0000000..f175bb5 --- /dev/null +++ b/docs/Get-AzSentinelAlertRuleAction.md @@ -0,0 +1,103 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Get-AzSentinelAlertRuleAction + +## SYNOPSIS +Get Azure Sentinel Alert rule Action + +## SYNTAX + +``` +Get-AzSentinelAlertRuleAction [-SubscriptionId ] -WorkspaceName [-RuleName ] + [-RuleId ] [] +``` + +## DESCRIPTION +This function can be used to see if an action is attached to the alert rule, if so then the configuration will be returned + +## EXAMPLES + +### EXAMPLE 1 +``` +Get-AzSentinelAlertRuleAction -WorkspaceName "pkm02" -RuleName "testrule01" +This example will get the Workspace ands return the full data object +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleName +Enter the name of the Alert rule + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleId +Enter the Rule Id to skip Get-AzSentinelAlertRule step + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES +NAME: Get-AzSentinelAlertRuleAction + +## RELATED LINKS diff --git a/docs/Get-AzSentinelHuntingRule.md b/docs/Get-AzSentinelHuntingRule.md index 060c6a5..b4fdb96 100644 --- a/docs/Get-AzSentinelHuntingRule.md +++ b/docs/Get-AzSentinelHuntingRule.md @@ -25,16 +25,14 @@ With this function you can get the configuration of the Azure Sentinel Hunting r ### EXAMPLE 1 ``` Get-AzSentinelHuntingRule -WorkspaceName "" -RuleName "","" -``` - In this example you can get configuration of multiple Hunting rules +``` ### EXAMPLE 2 ``` Get-AzSentinelHuntingRule -WorkspaceName "" -``` - In this example you can get a list of all the Hunting rules in once +``` ## PARAMETERS @@ -84,7 +82,8 @@ Accept wildcard characters: False ``` ### -Filter -{{ Fill Filter Description }} +Select which type of Hunting rules you want to see. +Option: HuntingQueries, GeneralExploration, LogManagement ```yaml Type: String diff --git a/docs/Get-AzSentinelIncident.md b/docs/Get-AzSentinelIncident.md index ab4aa18..de1eb1f 100644 --- a/docs/Get-AzSentinelIncident.md +++ b/docs/Get-AzSentinelIncident.md @@ -26,23 +26,20 @@ You can can also filter to Incident with speciefiek case namber or Case name ### EXAMPLE 1 ``` Get-AzSentinelIncident -WorkspaceName "" -``` - Get a list of all open Incidents +``` ### EXAMPLE 2 ``` Get-AzSentinelIncident -WorkspaceName "" -CaseNumber -``` - Get information of a specifiek incident with providing the casenumber +``` ### EXAMPLE 3 ``` Get-AzSentinelIncident -WorkspaceName "" -IncidentName "","" -``` - Get information of one or more incidents with providing a incident name, this is the name of the alert rule that triggered the incident +``` ## PARAMETERS diff --git a/docs/Import-AzSentinelAlertRule.md b/docs/Import-AzSentinelAlertRule.md index 568ef4d..d771728 100644 --- a/docs/Import-AzSentinelAlertRule.md +++ b/docs/Import-AzSentinelAlertRule.md @@ -25,24 +25,21 @@ This way you can manage your Alert rules dynamic from JSON or multiple YAML file ### EXAMPLE 1 ``` -Import-AzSentinelAlertRule -WorkspaceName "" -SettingsFile ".\examples\AlertRules.json" -``` - +Import-AzSentinelAlertRule -WorkspaceName "pkm02" -SettingsFile ".\examples\AlertRules.json" In this example all the rules configured in the JSON file will be created or updated +``` ### EXAMPLE 2 ``` Import-AzSentinelAlertRule -WorkspaceName "" -SettingsFile ".\examples\SuspectApplicationConsent.yaml" -``` - In this example all the rules configured in the YAML file will be created or updated +``` ### EXAMPLE 3 ``` Get-Item .\examples\*.json | Import-AzSentinelAlertRule -WorkspaceName "" -``` - In this example you can select multiple JSON files and Pipeline it to the SettingsFile parameter +``` ## PARAMETERS diff --git a/docs/Import-AzSentinelHuntingRule.md b/docs/Import-AzSentinelHuntingRule.md index c3a08d2..80a2b70 100644 --- a/docs/Import-AzSentinelHuntingRule.md +++ b/docs/Import-AzSentinelHuntingRule.md @@ -26,23 +26,20 @@ This way you can manage your Hunting rules dynamic from JSON or multiple YAML fi ### EXAMPLE 1 ``` Import-AzSentinelHuntingRule -WorkspaceName "infr-weu-oms-t-7qodryzoj6agu" -SettingsFile ".\examples\HuntingRules.json" -``` - In this example all the rules configured in the JSON file will be created or updated +``` ### EXAMPLE 2 ``` Import-AzSentinelHuntingRule -WorkspaceName "" -SettingsFile ".\examples\HuntingRules.yaml" -``` - In this example all the rules configured in the YAML file will be created or updated +``` ### EXAMPLE 3 ``` Get-Item .\examples\HuntingRules*.json | Import-AzSentinelHuntingRule -WorkspaceName "" -``` - In this example you can select multiple JSON files and Pipeline it to the SettingsFile parameter +``` ## PARAMETERS diff --git a/docs/New-AzSentinelAlertRule.md b/docs/New-AzSentinelAlertRule.md index 3ef840e..b81acd6 100644 --- a/docs/New-AzSentinelAlertRule.md +++ b/docs/New-AzSentinelAlertRule.md @@ -16,8 +16,8 @@ Create Azure Sentinal Alert Rules New-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName -DisplayName -Description -Severity -Enabled -Query -QueryFrequency [-QueryPeriod ] -TriggerOperator -TriggerThreshold - -SuppressionDuration -SuppressionEnabled -Tactics [-WhatIf] [-Confirm] - [] + -SuppressionDuration -SuppressionEnabled -Tactics [-PlaybookName ] + [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -27,10 +27,9 @@ Use this function creates Azure Sentinal Alert rules from provided CMDLET ### EXAMPLE 1 ``` -New-AzSentinelAlertRule -WorkspaceName "" -DisplayName "" -Description "" -Severity "" -Enabled -Query '' -QueryFrequency "" -QueryPeriod "" -TriggerOperator "" -TriggerThreshold -SuppressionDuration "" -SuppressionEnabled $false -Tactics @("","") -``` - +New-AzSentinelAlertRule -WorkspaceName "" -DisplayName "" -Description "" -Severity -Enabled $true -Query '' -QueryFrequency "" -QueryPeriod "" -TriggerOperator -TriggerThreshold -SuppressionDuration "" -SuppressionEnabled $false -Tactics @("","") In this example you create a new Alert rule by defining the rule properties from CMDLET +``` ## PARAMETERS @@ -177,7 +176,7 @@ Select the triggert Operator, valid values are: "GreaterThan", "FewerThan", "Equ Type: TriggerOperator Parameter Sets: (All) Aliases: -Accepted values: GreaterThan, FewerThan, EqualTo, NotEqualTo +Accepted values: GreaterThan, LessThan, Equal, NotEqual, gt, lt, eq, ne Required: True Position: Named @@ -247,6 +246,21 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -PlaybookName +Enter the Logic App name that you want to configure as playbook trigger + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. diff --git a/docs/New-AzSentinelAlertRuleAction.md b/docs/New-AzSentinelAlertRuleAction.md new file mode 100644 index 0000000..bf1d622 --- /dev/null +++ b/docs/New-AzSentinelAlertRuleAction.md @@ -0,0 +1,149 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# New-AzSentinelAlertRuleAction + +## SYNOPSIS +Create Azure Sentinal Alert Rule Action + +## SYNTAX + +``` +New-AzSentinelAlertRuleAction [-SubscriptionId ] -WorkspaceName -PlayBookName + [-RuleName ] [-RuleId ] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +Use this function to creates Azure Sentinal Alert rule action + +## EXAMPLES + +### EXAMPLE 1 +``` +New-AzSentinelAlertRuleAction -WorkspaceName pkm02 -PlayBookName "pkmsentinel" -RuleName "testrule01" +New-AzSentinelAlertRuleAction -WorkspaceName pkm02 -PlayBookName "pkmsentinel" -RuleId 'b6103d42-d2fb-4f35-bced-c76a7f31ee4e' +In this example you you assign the playbook to the Alert rule +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -PlayBookName +Enter the Playbook name that you want to assign to the alert rule + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleName +Enter the Alert Rule name that you want to configure + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleId +Enter the Alert Rule ID that you want to configure + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/docs/New-AzSentinelHuntingRule.md b/docs/New-AzSentinelHuntingRule.md index 4ea92ec..195e386 100644 --- a/docs/New-AzSentinelHuntingRule.md +++ b/docs/New-AzSentinelHuntingRule.md @@ -25,9 +25,8 @@ Use this function to creates Azure Sentinal Hunting rule ### EXAMPLE 1 ``` New-AzSentinelHuntingRule -WorkspaceName "" -DisplayName "" -Description "" -Tactics "","" -Query '' -``` - In this example you create a new hunting rule by defining the rule properties from CMDLET +``` ## PARAMETERS diff --git a/docs/README.md b/docs/README.md index 678f26d..95abd60 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,7 +6,11 @@ 4. [Import-AzSentinelAlertRule](Import-AzSentinelAlertRule.md) 5. [Remove-AzSentinelAlertRule](Remove-AzSentinelAlertRule.md) 6. [Get-AzSentinelIncident](Get-AzSentinelIncident.md) -7. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) -8. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) -9. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) -10. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) +7. [Update-AzSentinelIncident](Update-AzSentinelIncident.md) +8. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) +9. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) +10. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) +11. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) +12. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) +13. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) +14. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) diff --git a/docs/Remove-AzSentinelAlertRule.md b/docs/Remove-AzSentinelAlertRule.md index 186d6c6..fb0102a 100644 --- a/docs/Remove-AzSentinelAlertRule.md +++ b/docs/Remove-AzSentinelAlertRule.md @@ -25,25 +25,21 @@ With this function you can remove Azure Sentinal Alert rules from Powershell, if ### EXAMPLE 1 ``` Remove-AzSentinelAlertRule -WorkspaceName "" -RuleName "" -``` - In this example the defined rule will be removed from Azure Sentinel +``` ### EXAMPLE 2 ``` Remove-AzSentinelAlertRule -WorkspaceName "" -RuleName "","", "" -``` - In this example you can define multiple rules that will be removed +``` ### EXAMPLE 3 ``` Remove-AzSentinelAlertRule -WorkspaceName "" +In this example no rule is specified, all rules will be removed one by one. For each rule you need to confirm the action ``` -In this example no rule is specified, all rules will be removed one by one. -For each rule you need to confirm the action - ## PARAMETERS ### -SubscriptionId diff --git a/docs/Remove-AzSentinelAlertRuleAction.md b/docs/Remove-AzSentinelAlertRuleAction.md new file mode 100644 index 0000000..2630e0f --- /dev/null +++ b/docs/Remove-AzSentinelAlertRuleAction.md @@ -0,0 +1,134 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Remove-AzSentinelAlertRuleAction + +## SYNOPSIS +Remove Azure Sentinel Alert rule Action + +## SYNTAX + +``` +Remove-AzSentinelAlertRuleAction [-SubscriptionId ] -WorkspaceName [-RuleName ] + [-RuleId ] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +This function can be used to see if an action is attached to the alert rule, if so then the configuration will be returned + +## EXAMPLES + +### EXAMPLE 1 +``` +Remove-AzSentinelAlertRuleAction -WorkspaceName "pkm02" -RuleName "testrule01" +This example will get the Workspace ands return the full data object +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleName +Enter the name of the Alert rule + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleId +Enter the Alert Rule ID that you want to configure + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES +NAME: Remove-AzSentinelAlertRuleAction + +## RELATED LINKS diff --git a/docs/Remove-AzSentinelHuntingRule.md b/docs/Remove-AzSentinelHuntingRule.md index 24ca7e6..51bd50b 100644 --- a/docs/Remove-AzSentinelHuntingRule.md +++ b/docs/Remove-AzSentinelHuntingRule.md @@ -25,25 +25,21 @@ With this function you can remove Azure Sentinal hunting rules from Powershell, ### EXAMPLE 1 ``` Remove-AzSentinelHuntingRule -WorkspaceName "" -RuleName "" -``` - In this example the defined hunting rule will be removed from Azure Sentinel +``` ### EXAMPLE 2 ``` Remove-AzSentinelHuntingRule -WorkspaceName "" -RuleName "","", "" -``` - In this example you can define multiple hunting rules that will be removed +``` ### EXAMPLE 3 ``` Remove-AzSentinelHuntingRule -WorkspaceName "" +In this example no hunting rule is specified, all hunting rules will be removed one by one. For each rule you need to confirm the action ``` -In this example no hunting rule is specified, all hunting rules will be removed one by one. -For each rule you need to confirm the action - ## PARAMETERS ### -SubscriptionId diff --git a/docs/Set-AzSentinel.md b/docs/Set-AzSentinel.md index a3e5ac0..6ccea5b 100644 --- a/docs/Set-AzSentinel.md +++ b/docs/Set-AzSentinel.md @@ -24,9 +24,8 @@ This function enables Azure Sentinel on a existing Workspace ### EXAMPLE 1 ``` Set-AzSentinel -WorkspaceName "" -``` - This example will enable Azure Sentinel for the provided workspace +``` ## PARAMETERS diff --git a/examples/AlertRules copy.json b/examples/AlertRules copy.json new file mode 100644 index 0000000..4d897dc --- /dev/null +++ b/examples/AlertRules copy.json @@ -0,0 +1,23 @@ +{ + "analytics": [ + { + "displayName": "AlertRule01", + "description": "test", + "severity": "Medium", + "enabled": true, + "query": "SecurityEvent | where EventID == \"4688\" | where CommandLine contains \"-noni -ep bypass $\"", + "queryFrequency": "5H", + "queryPeriod": "6H", + "triggerOperator": "GreaterThan", + "triggerThreshold": 5, + "suppressionDuration": "6H", + "suppressionEnabled": false, + "tactics": [ + "Persistence", + "LateralMovement", + "Collection" + ], + "playbookName": "aaaa" + } + ] +} diff --git a/examples/AlertRules.json b/examples/AlertRules.json index 6f0f626..904cbc8 100644 --- a/examples/AlertRules.json +++ b/examples/AlertRules.json @@ -2,7 +2,7 @@ "analytics": [ { "displayName": "AlertRule01", - "description": "test", + "description": "", "severity": "Medium", "enabled": true, "query": "SecurityEvent | where EventID == \"4688\" | where CommandLine contains \"-noni -ep bypass $\"", @@ -16,24 +16,8 @@ "Persistence", "LateralMovement", "Collection" - ] - }, - { - "displayName": "AlertRule02", - "description": "test", - "severity": "High", - "enabled": true, - "query": "SecurityEvent | where EventID == \"4688\" | where CommandLine contains \"-noni -ep bypass $\"", - "queryFrequency": "5H", - "queryPeriod": "6H", - "triggerOperator": "GreaterThan", - "triggerThreshold": 5, - "suppressionDuration": "6H", - "suppressionEnabled": false, - "tactics": [ - "Persistence", - "LateralMovement" - ] + ], + "playbookName": "pkmsentinel" } ] } From a9e559bf900322bdfa8e9bca7b577fb772ef6f9a Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Mon, 24 Feb 2020 14:41:24 +0100 Subject: [PATCH 04/53] Fix/smallconflicts (#40) * updating docs * updating examples * updating pipeline --- .github/workflows/main.yml | 2 +- AzSentinel/Public/Get-AzSentinelAlertRule.ps1 | 5 ++- .../Public/Get-AzSentinelAlertRuleAction.ps1 | 2 +- .../Public/Import-AzSentinelAlertRule.ps1 | 36 ++++++++++++++++--- AzSentinel/Public/New-AzSentinelAlertRule.ps1 | 26 ++++++++------ .../Public/New-AzSentinelAlertRuleAction.ps1 | 14 +++----- .../Remove-AzSentinelAlertRuleAction.ps1 | 2 +- AzSentinel/docs/Get-AzSentinelAlertRule.md | 1 - .../docs/Get-AzSentinelAlertRuleAction.md | 2 +- ...Rule copy.md => Get-AzSentinelIncident.md} | 23 ++++++------ AzSentinel/docs/Import-AzSentinelAlertRule.md | 23 +++++++++++- AzSentinel/docs/New-AzSentinelAlertRule.md | 2 +- .../docs/New-AzSentinelAlertRuleAction.md | 4 +-- .../docs/Remove-AzSentinelAlertRuleAction.md | 2 +- AzSentinel/docs/Update-AzSentinelIncident.md | 9 ++--- docs/Get-AzSentinelAlertRule.md | 1 - docs/Get-AzSentinelAlertRuleAction.md | 2 +- docs/Import-AzSentinelAlertRule.md | 23 +++++++++++- docs/New-AzSentinelAlertRule.md | 2 +- docs/New-AzSentinelAlertRuleAction.md | 4 +-- docs/Remove-AzSentinelAlertRuleAction.md | 2 +- docs/Update-AzSentinelIncident.md | 9 ++--- examples/AlertRules copy.json | 23 ------------ examples/AlertRules.json | 2 +- examples/SuspectApplicationConsent.yaml | 1 + 25 files changed, 131 insertions(+), 91 deletions(-) rename AzSentinel/docs/{Get-AzSentinelHuntingRule copy.md => Get-AzSentinelIncident.md} (79%) delete mode 100644 examples/AlertRules copy.json diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 948a999..70d8519 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,7 +4,7 @@ on: pull_request: # Sequence of patterns matched against refs/heads branches: - - master + - development push: # Sequence of patterns matched against refs/heads branches: diff --git a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 index 0edb7ed..6714589 100644 --- a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 @@ -15,7 +15,6 @@ function Get-AzSentinelAlertRule { Enter the name of the Alert rule .EXAMPLE Get-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" - Get-AzSentinelAlertRule -WorkspaceName "pkm02" -RuleName "AlertRule01" In this example you can get configuration of multiple alert rules in once #> @@ -83,7 +82,7 @@ function Get-AzSentinelAlertRule { $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] } else { - $playbookName = $null + $playbookName = "" } $temp.properties | Add-Member -NotePropertyName name -NotePropertyValue $temp.name -Force @@ -107,7 +106,7 @@ function Get-AzSentinelAlertRule { $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] } else { - $playbookName = $null + $playbookName = "" } $_.properties | Add-Member -NotePropertyName name -NotePropertyValue $_.name -Force $_.properties | Add-Member -NotePropertyName playbookName -NotePropertyValue $playbookName -Force diff --git a/AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 index 01eaa26..3e298ac 100644 --- a/AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 @@ -16,7 +16,7 @@ function Get-AzSentinelAlertRuleAction { .PARAMETER RuleId Enter the Rule Id to skip Get-AzSentinelAlertRule step .EXAMPLE - Get-AzSentinelAlertRuleAction -WorkspaceName "pkm02" -RuleName "testrule01" + Get-AzSentinelAlertRuleAction -WorkspaceName "" -RuleName "testrule01" This example will get the Workspace ands return the full data object .NOTES NAME: Get-AzSentinelAlertRuleAction diff --git a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 index c340ed2..797bd7f 100644 --- a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 @@ -16,8 +16,30 @@ function Import-AzSentinelAlertRule { .PARAMETER SettingsFile Path to the JSON or YAML file for the AlertRules .EXAMPLE - Import-AzSentinelAlertRule -WorkspaceName "pkm02" -SettingsFile ".\examples\AlertRules.json" + Import-AzSentinelAlertRule -WorkspaceName "" -SettingsFile ".\examples\AlertRules.json" In this example all the rules configured in the JSON file will be created or updated + + Performing the operation "Import-AzSentinelAlertRule" on target "Do you want to update profile: AlertRule01". + [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Yes"): + Successfully created Action for Rule: with Playbook pkmsentinel Status: Created + Created + Successfully updated rule: AlertRule01 with status: OK + + Name : b6103d42-xxx-4f35-xxx-c76a7f31ee4e + DisplayName : AlertRule01 + Description : + Severity : Medium + Enabled : True + Query : SecurityEvent | where EventID == "4688" | where CommandLine contains "-noni -ep bypass $" + QueryFrequency : PT5H + QueryPeriod : PT6H + TriggerOperator : GreaterThan + TriggerThreshold : 5 + SuppressionDuration : PT6H + SuppressionEnabled : False + Tactics : {Persistence, LateralMovement, Collection} + PlaybookName : Playbook01 + .EXAMPLE Import-AzSentinelAlertRule -WorkspaceName "" -SettingsFile ".\examples\SuspectApplicationConsent.yaml" In this example all the rules configured in the YAML file will be created or updated @@ -130,17 +152,23 @@ function Import-AzSentinelAlertRule { $item.triggerThreshold, $item.suppressionDuration, $item.suppressionEnabled, - $item.tactics, + $item.Tactics, $item.playbookName ) $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id) + } catch { Write-Error "Unable to initiate class with error: $($_.Exception.Message)" -ErrorAction Continue } if ($content) { - $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name) + if ($item.playbookName) { + $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name) + } + else { + $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id, PlaybookName) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name, PlaybookName) + } if ($compareResult) { Write-Output "Found Differences for rule: $($item.displayName)" Write-Output ($compareResult | Format-Table | Out-String) @@ -181,7 +209,7 @@ function Import-AzSentinelAlertRule { try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | Select-Object * -ExcludeProperty Properties.PlaybookName | ConvertTo-Json -EnumsAsStrings) if ($body.Properties.playbookName) { - New-AzSentinelAlertRuleAction -PlayBookName $($body.Properties.playbookName) -RuleName $($body.Properties.DisplayName) -confirm:$false + New-AzSentinelAlertRuleAction @arguments -PlayBookName $($body.Properties.playbookName) -RuleId $($body.Properties.Name) -confirm:$false } Write-Output "Successfully created rule: $($item.displayName) with status: $($result.StatusDescription)" diff --git a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 index 004598c..e931eab 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 @@ -38,7 +38,7 @@ function New-AzSentinelAlertRule { .PARAMETER PlaybookName Enter the Logic App name that you want to configure as playbook trigger .EXAMPLE - New-AzSentinelAlertRule -WorkspaceName "" -DisplayName "" -Description "" -Severity -Enabled $true -Query '' -QueryFrequency "" -QueryPeriod "" -TriggerOperator -TriggerThreshold -SuppressionDuration "" -SuppressionEnabled $false -Tactics @("","") + New-AzSentinelAlertRule -WorkspaceName "" -DisplayName "" -Description "" -Severity -Enabled $true -Query '' -QueryFrequency "" -QueryPeriod "" -TriggerOperator -TriggerThreshold -SuppressionDuration "" -SuppressionEnabled $false -Tactics @("","") -PlaybookName "" In this example you create a new Alert rule by defining the rule properties from CMDLET #> @@ -179,8 +179,12 @@ function New-AzSentinelAlertRule { } if ($content) { - $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name) - if ($compareResult) { + if ($PlaybookName) { + $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name) + } + else { + $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id, PlaybookName) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name, PlaybookName) + } if ($compareResult) { Write-Output "Found Differences for rule: $($DisplayName)" Write-Output ($compareResult | Format-Table | Out-String) @@ -188,12 +192,14 @@ function New-AzSentinelAlertRule { try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -EnumsAsStrings) - if ($compareResult.PropertyName -contains "playbookName") { - #New-AzSentinelAlertRuleAction @arguments -PlayBookName $(($body.Properties).playbookName) -RuleId $($body.Name) -Confirm:$false - New-AzSentinelAlertRuleAction @arguments -PlayBookName 'pkmsentinel' -RuleId 'b6103d42-d2fb-4f35-bced-c76a7f31ee4e' -Confirm:$false + if (($compareResult | Where-Object PropertyName -eq "playbookName").DiffValue) { + New-AzSentinelAlertRuleAction @arguments -PlayBookName ($body.Properties.playbookName) -RuleId $($body.Name) + } + elseif (($compareResult | Where-Object PropertyName -eq "playbookName").RefValue) { + Remove-AzSentinelAlertRuleAction @arguments -RuleId $($body.Name) -Confirm:$false } - elseif ($null -ne $content.playbookName -and $null -eq $body.Properties.playbookName) { - Write-Output "Currently Playbook configured but will be removed now" + else { + #nothing } Write-Output "Successfully updated rule: $($DisplayName) with status: $($result.StatusDescription)" @@ -219,8 +225,8 @@ function New-AzSentinelAlertRule { try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -EnumsAsStrings) - if ($body.Properties.playbookName) { - New-AzSentinelAlertRuleAction -PlayBookName $($body.Properties.playbookName) -RuleName $($body.Properties.DisplayName) -confirm:$false + if ($null -ne $body.Properties.playbookName) { + New-AzSentinelAlertRuleAction @arguments -PlayBookName ($body.Properties.playbookName) -RuleId $($body.Properties.Name) -confirm:$false } Write-Output "Successfully created rule: $($DisplayName) with status: $($result.StatusDescription)" diff --git a/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 b/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 index 149fc41..b129f3f 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 @@ -18,8 +18,8 @@ function New-AzSentinelAlertRuleAction { .PARAMETER RuleId Enter the Alert Rule ID that you want to configure .EXAMPLE - New-AzSentinelAlertRuleAction -WorkspaceName pkm02 -PlayBookName "pkmsentinel" -RuleName "testrule01" - New-AzSentinelAlertRuleAction -WorkspaceName pkm02 -PlayBookName "pkmsentinel" -RuleId 'b6103d42-d2fb-4f35-bced-c76a7f31ee4e' + New-AzSentinelAlertRuleAction -WorkspaceName "" -PlayBookName "Playbook01" -RuleName "AlertRule01" + New-AzSentinelAlertRuleAction -WorkspaceName "" -PlayBookName "Playbook01" -RuleId 'b6103d42-d2fb-4f35-xxx-c76a7f31ee4e' In this example you you assign the playbook to the Alert rule #> @@ -94,14 +94,8 @@ function New-AzSentinelAlertRuleAction { $uri = "$($Script:baseUri)/providers/Microsoft.SecurityInsights/alertRules/$($alertId)/actions/$($guid)?api-version=2019-01-01-preview" try { $return = Invoke-WebRequest -Method Put -Uri $uri -Headers $Script:authHeader -Body ($body | ConvertTo-Json -Depth 10) - if ($return.StatusCode -eq 201 -and $result.StatusDescription -eq "Created"){ - Write-Output "Successfully created Action for Rule: $($RuleName) with Playbook $($PlayBookName) Status: $($return.StatusDescription)" - return $return.StatusDescription - } - else { - Write-Error "Unable to create Action for Rule: $($RuleName) with Playbook $($PlayBookName) Status: $($return.StatusDescription)" - return $return.StatusDescription - } + Write-Output "Successfully created Action for Rule: $($RuleName) with Playbook $($PlayBookName) Status: $($return.StatusDescription)" + return $return.StatusDescription } catch { Write-Error "Unable to create Action for Rule: $($RuleName) with Playbook $($PlayBookName) Error: $($_.Exception.Message)" diff --git a/AzSentinel/Public/Remove-AzSentinelAlertRuleAction.ps1 b/AzSentinel/Public/Remove-AzSentinelAlertRuleAction.ps1 index a8319e8..e72e841 100644 --- a/AzSentinel/Public/Remove-AzSentinelAlertRuleAction.ps1 +++ b/AzSentinel/Public/Remove-AzSentinelAlertRuleAction.ps1 @@ -16,7 +16,7 @@ function Remove-AzSentinelAlertRuleAction { .PARAMETER RuleId Enter the Alert Rule ID that you want to configure .EXAMPLE - Remove-AzSentinelAlertRuleAction -WorkspaceName "pkm02" -RuleName "testrule01" + Remove-AzSentinelAlertRuleAction -WorkspaceName "" -RuleName "AlertRule01" This example will get the Workspace ands return the full data object .NOTES NAME: Remove-AzSentinelAlertRuleAction diff --git a/AzSentinel/docs/Get-AzSentinelAlertRule.md b/AzSentinel/docs/Get-AzSentinelAlertRule.md index cb81a0d..23e92c1 100644 --- a/AzSentinel/docs/Get-AzSentinelAlertRule.md +++ b/AzSentinel/docs/Get-AzSentinelAlertRule.md @@ -25,7 +25,6 @@ With this function you can get the configuration of the Azure Sentinel Alert rul ### EXAMPLE 1 ``` Get-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" -Get-AzSentinelAlertRule -WorkspaceName "pkm02" -RuleName "AlertRule01" In this example you can get configuration of multiple alert rules in once ``` diff --git a/AzSentinel/docs/Get-AzSentinelAlertRuleAction.md b/AzSentinel/docs/Get-AzSentinelAlertRuleAction.md index f175bb5..f0a178e 100644 --- a/AzSentinel/docs/Get-AzSentinelAlertRuleAction.md +++ b/AzSentinel/docs/Get-AzSentinelAlertRuleAction.md @@ -24,7 +24,7 @@ This function can be used to see if an action is attached to the alert rule, if ### EXAMPLE 1 ``` -Get-AzSentinelAlertRuleAction -WorkspaceName "pkm02" -RuleName "testrule01" +Get-AzSentinelAlertRuleAction -WorkspaceName "" -RuleName "testrule01" This example will get the Workspace ands return the full data object ``` diff --git a/AzSentinel/docs/Get-AzSentinelHuntingRule copy.md b/AzSentinel/docs/Get-AzSentinelIncident.md similarity index 79% rename from AzSentinel/docs/Get-AzSentinelHuntingRule copy.md rename to AzSentinel/docs/Get-AzSentinelIncident.md index 426986a..de1eb1f 100644 --- a/AzSentinel/docs/Get-AzSentinelHuntingRule copy.md +++ b/AzSentinel/docs/Get-AzSentinelIncident.md @@ -5,20 +5,21 @@ online version: schema: 2.0.0 --- -# Get-AzSentinelHuntingRule +# Get-AzSentinelIncident ## SYNOPSIS -Get Azure Sentinel Hunting rule +Get Azure Sentinel Incident ## SYNTAX ``` -Get-AzSentinelHuntingRule [-SubscriptionId ] -WorkspaceName [-RuleName ] - [-Filter ] [-WhatIf] [-Confirm] [] +Get-AzSentinelIncident [-SubscriptionId ] -WorkspaceName [-IncidentName ] + [-CaseNumber ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION -With this function you can get the configuration of the Azure Sentinel Hunting rule from Azure Sentinel +With this function you can get a list of open incidents from Azure Sentinel. +You can can also filter to Incident with speciefiek case namber or Case name ## EXAMPLES @@ -72,8 +73,8 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -RuleName -Enter the name of the Hunting rule name +### -IncidentName +Enter incident name, this is the same name as the alert rule that triggered the incident ```yaml Type: String[] @@ -87,18 +88,18 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` -### -Filter -{{ Fill Filter Description }} +### -CaseNumber +Enter the case number to get specfiek details of a open case ```yaml -Type: String +Type: Int32[] Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None -Accept pipeline input: False +Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` diff --git a/AzSentinel/docs/Import-AzSentinelAlertRule.md b/AzSentinel/docs/Import-AzSentinelAlertRule.md index d771728..c24c13d 100644 --- a/AzSentinel/docs/Import-AzSentinelAlertRule.md +++ b/AzSentinel/docs/Import-AzSentinelAlertRule.md @@ -25,10 +25,31 @@ This way you can manage your Alert rules dynamic from JSON or multiple YAML file ### EXAMPLE 1 ``` -Import-AzSentinelAlertRule -WorkspaceName "pkm02" -SettingsFile ".\examples\AlertRules.json" +Import-AzSentinelAlertRule -WorkspaceName "" -SettingsFile ".\examples\AlertRules.json" In this example all the rules configured in the JSON file will be created or updated ``` +Performing the operation "Import-AzSentinelAlertRule" on target "Do you want to update profile: AlertRule01". +\[Y\] Yes \[A\] Yes to All \[N\] No \[L\] No to All \[S\] Suspend \[?\] Help (default is "Yes"): +Successfully created Action for Rule: with Playbook pkmsentinel Status: Created +Created +Successfully updated rule: AlertRule01 with status: OK + +Name : b6103d42-xxx-4f35-xxx-c76a7f31ee4e +DisplayName : AlertRule01 +Description : +Severity : Medium +Enabled : True +Query : SecurityEvent | where EventID == "4688" | where CommandLine contains "-noni -ep bypass $" +QueryFrequency : PT5H +QueryPeriod : PT6H +TriggerOperator : GreaterThan +TriggerThreshold : 5 +SuppressionDuration : PT6H +SuppressionEnabled : False +Tactics : {Persistence, LateralMovement, Collection} +PlaybookName : Playbook01 + ### EXAMPLE 2 ``` Import-AzSentinelAlertRule -WorkspaceName "" -SettingsFile ".\examples\SuspectApplicationConsent.yaml" diff --git a/AzSentinel/docs/New-AzSentinelAlertRule.md b/AzSentinel/docs/New-AzSentinelAlertRule.md index b81acd6..60ea684 100644 --- a/AzSentinel/docs/New-AzSentinelAlertRule.md +++ b/AzSentinel/docs/New-AzSentinelAlertRule.md @@ -27,7 +27,7 @@ Use this function creates Azure Sentinal Alert rules from provided CMDLET ### EXAMPLE 1 ``` -New-AzSentinelAlertRule -WorkspaceName "" -DisplayName "" -Description "" -Severity -Enabled $true -Query '' -QueryFrequency "" -QueryPeriod "" -TriggerOperator -TriggerThreshold -SuppressionDuration "" -SuppressionEnabled $false -Tactics @("","") +New-AzSentinelAlertRule -WorkspaceName "" -DisplayName "" -Description "" -Severity -Enabled $true -Query '' -QueryFrequency "" -QueryPeriod "" -TriggerOperator -TriggerThreshold -SuppressionDuration "" -SuppressionEnabled $false -Tactics @("","") -PlaybookName "" In this example you create a new Alert rule by defining the rule properties from CMDLET ``` diff --git a/AzSentinel/docs/New-AzSentinelAlertRuleAction.md b/AzSentinel/docs/New-AzSentinelAlertRuleAction.md index bf1d622..ca1b9a1 100644 --- a/AzSentinel/docs/New-AzSentinelAlertRuleAction.md +++ b/AzSentinel/docs/New-AzSentinelAlertRuleAction.md @@ -24,8 +24,8 @@ Use this function to creates Azure Sentinal Alert rule action ### EXAMPLE 1 ``` -New-AzSentinelAlertRuleAction -WorkspaceName pkm02 -PlayBookName "pkmsentinel" -RuleName "testrule01" -New-AzSentinelAlertRuleAction -WorkspaceName pkm02 -PlayBookName "pkmsentinel" -RuleId 'b6103d42-d2fb-4f35-bced-c76a7f31ee4e' +New-AzSentinelAlertRuleAction -WorkspaceName "" -PlayBookName "Playbook01" -RuleName "AlertRule01" +New-AzSentinelAlertRuleAction -WorkspaceName "" -PlayBookName "Playbook01" -RuleId 'b6103d42-d2fb-4f35-xxx-c76a7f31ee4e' In this example you you assign the playbook to the Alert rule ``` diff --git a/AzSentinel/docs/Remove-AzSentinelAlertRuleAction.md b/AzSentinel/docs/Remove-AzSentinelAlertRuleAction.md index 2630e0f..4048f1e 100644 --- a/AzSentinel/docs/Remove-AzSentinelAlertRuleAction.md +++ b/AzSentinel/docs/Remove-AzSentinelAlertRuleAction.md @@ -24,7 +24,7 @@ This function can be used to see if an action is attached to the alert rule, if ### EXAMPLE 1 ``` -Remove-AzSentinelAlertRuleAction -WorkspaceName "pkm02" -RuleName "testrule01" +Remove-AzSentinelAlertRuleAction -WorkspaceName "" -RuleName "AlertRule01" This example will get the Workspace ands return the full data object ``` diff --git a/AzSentinel/docs/Update-AzSentinelIncident.md b/AzSentinel/docs/Update-AzSentinelIncident.md index dbbe94b..8b4709a 100644 --- a/AzSentinel/docs/Update-AzSentinelIncident.md +++ b/AzSentinel/docs/Update-AzSentinelIncident.md @@ -26,23 +26,20 @@ With this function you can update existing Azure Sentinel Incident. ### EXAMPLE 1 ``` Update-AzSentinelIncident -WorkspaceName "" -``` - Get a list of all open Incidents +``` ### EXAMPLE 2 ``` Update-AzSentinelIncident -WorkspaceName '' -CaseNumber 42291 -Labels "NewLabel" -``` - Add a new Label to list of Labels for a Incident +``` ### EXAMPLE 3 ``` Update-AzSentinelIncident -WorkspaceName '' -CaseNumber 42293 -Status Closed -CloseReason FalsePositive -ClosedReasonText "Your input" -``` - Close the Incidnet using status Closed, when status closed is selected then CloseReason and ClosedReasonText prperty are required to be filled in +``` ## PARAMETERS diff --git a/docs/Get-AzSentinelAlertRule.md b/docs/Get-AzSentinelAlertRule.md index cb81a0d..23e92c1 100644 --- a/docs/Get-AzSentinelAlertRule.md +++ b/docs/Get-AzSentinelAlertRule.md @@ -25,7 +25,6 @@ With this function you can get the configuration of the Azure Sentinel Alert rul ### EXAMPLE 1 ``` Get-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" -Get-AzSentinelAlertRule -WorkspaceName "pkm02" -RuleName "AlertRule01" In this example you can get configuration of multiple alert rules in once ``` diff --git a/docs/Get-AzSentinelAlertRuleAction.md b/docs/Get-AzSentinelAlertRuleAction.md index f175bb5..f0a178e 100644 --- a/docs/Get-AzSentinelAlertRuleAction.md +++ b/docs/Get-AzSentinelAlertRuleAction.md @@ -24,7 +24,7 @@ This function can be used to see if an action is attached to the alert rule, if ### EXAMPLE 1 ``` -Get-AzSentinelAlertRuleAction -WorkspaceName "pkm02" -RuleName "testrule01" +Get-AzSentinelAlertRuleAction -WorkspaceName "" -RuleName "testrule01" This example will get the Workspace ands return the full data object ``` diff --git a/docs/Import-AzSentinelAlertRule.md b/docs/Import-AzSentinelAlertRule.md index d771728..c24c13d 100644 --- a/docs/Import-AzSentinelAlertRule.md +++ b/docs/Import-AzSentinelAlertRule.md @@ -25,10 +25,31 @@ This way you can manage your Alert rules dynamic from JSON or multiple YAML file ### EXAMPLE 1 ``` -Import-AzSentinelAlertRule -WorkspaceName "pkm02" -SettingsFile ".\examples\AlertRules.json" +Import-AzSentinelAlertRule -WorkspaceName "" -SettingsFile ".\examples\AlertRules.json" In this example all the rules configured in the JSON file will be created or updated ``` +Performing the operation "Import-AzSentinelAlertRule" on target "Do you want to update profile: AlertRule01". +\[Y\] Yes \[A\] Yes to All \[N\] No \[L\] No to All \[S\] Suspend \[?\] Help (default is "Yes"): +Successfully created Action for Rule: with Playbook pkmsentinel Status: Created +Created +Successfully updated rule: AlertRule01 with status: OK + +Name : b6103d42-xxx-4f35-xxx-c76a7f31ee4e +DisplayName : AlertRule01 +Description : +Severity : Medium +Enabled : True +Query : SecurityEvent | where EventID == "4688" | where CommandLine contains "-noni -ep bypass $" +QueryFrequency : PT5H +QueryPeriod : PT6H +TriggerOperator : GreaterThan +TriggerThreshold : 5 +SuppressionDuration : PT6H +SuppressionEnabled : False +Tactics : {Persistence, LateralMovement, Collection} +PlaybookName : Playbook01 + ### EXAMPLE 2 ``` Import-AzSentinelAlertRule -WorkspaceName "" -SettingsFile ".\examples\SuspectApplicationConsent.yaml" diff --git a/docs/New-AzSentinelAlertRule.md b/docs/New-AzSentinelAlertRule.md index b81acd6..60ea684 100644 --- a/docs/New-AzSentinelAlertRule.md +++ b/docs/New-AzSentinelAlertRule.md @@ -27,7 +27,7 @@ Use this function creates Azure Sentinal Alert rules from provided CMDLET ### EXAMPLE 1 ``` -New-AzSentinelAlertRule -WorkspaceName "" -DisplayName "" -Description "" -Severity -Enabled $true -Query '' -QueryFrequency "" -QueryPeriod "" -TriggerOperator -TriggerThreshold -SuppressionDuration "" -SuppressionEnabled $false -Tactics @("","") +New-AzSentinelAlertRule -WorkspaceName "" -DisplayName "" -Description "" -Severity -Enabled $true -Query '' -QueryFrequency "" -QueryPeriod "" -TriggerOperator -TriggerThreshold -SuppressionDuration "" -SuppressionEnabled $false -Tactics @("","") -PlaybookName "" In this example you create a new Alert rule by defining the rule properties from CMDLET ``` diff --git a/docs/New-AzSentinelAlertRuleAction.md b/docs/New-AzSentinelAlertRuleAction.md index bf1d622..ca1b9a1 100644 --- a/docs/New-AzSentinelAlertRuleAction.md +++ b/docs/New-AzSentinelAlertRuleAction.md @@ -24,8 +24,8 @@ Use this function to creates Azure Sentinal Alert rule action ### EXAMPLE 1 ``` -New-AzSentinelAlertRuleAction -WorkspaceName pkm02 -PlayBookName "pkmsentinel" -RuleName "testrule01" -New-AzSentinelAlertRuleAction -WorkspaceName pkm02 -PlayBookName "pkmsentinel" -RuleId 'b6103d42-d2fb-4f35-bced-c76a7f31ee4e' +New-AzSentinelAlertRuleAction -WorkspaceName "" -PlayBookName "Playbook01" -RuleName "AlertRule01" +New-AzSentinelAlertRuleAction -WorkspaceName "" -PlayBookName "Playbook01" -RuleId 'b6103d42-d2fb-4f35-xxx-c76a7f31ee4e' In this example you you assign the playbook to the Alert rule ``` diff --git a/docs/Remove-AzSentinelAlertRuleAction.md b/docs/Remove-AzSentinelAlertRuleAction.md index 2630e0f..4048f1e 100644 --- a/docs/Remove-AzSentinelAlertRuleAction.md +++ b/docs/Remove-AzSentinelAlertRuleAction.md @@ -24,7 +24,7 @@ This function can be used to see if an action is attached to the alert rule, if ### EXAMPLE 1 ``` -Remove-AzSentinelAlertRuleAction -WorkspaceName "pkm02" -RuleName "testrule01" +Remove-AzSentinelAlertRuleAction -WorkspaceName "" -RuleName "AlertRule01" This example will get the Workspace ands return the full data object ``` diff --git a/docs/Update-AzSentinelIncident.md b/docs/Update-AzSentinelIncident.md index dbbe94b..8b4709a 100644 --- a/docs/Update-AzSentinelIncident.md +++ b/docs/Update-AzSentinelIncident.md @@ -26,23 +26,20 @@ With this function you can update existing Azure Sentinel Incident. ### EXAMPLE 1 ``` Update-AzSentinelIncident -WorkspaceName "" -``` - Get a list of all open Incidents +``` ### EXAMPLE 2 ``` Update-AzSentinelIncident -WorkspaceName '' -CaseNumber 42291 -Labels "NewLabel" -``` - Add a new Label to list of Labels for a Incident +``` ### EXAMPLE 3 ``` Update-AzSentinelIncident -WorkspaceName '' -CaseNumber 42293 -Status Closed -CloseReason FalsePositive -ClosedReasonText "Your input" -``` - Close the Incidnet using status Closed, when status closed is selected then CloseReason and ClosedReasonText prperty are required to be filled in +``` ## PARAMETERS diff --git a/examples/AlertRules copy.json b/examples/AlertRules copy.json deleted file mode 100644 index 4d897dc..0000000 --- a/examples/AlertRules copy.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "analytics": [ - { - "displayName": "AlertRule01", - "description": "test", - "severity": "Medium", - "enabled": true, - "query": "SecurityEvent | where EventID == \"4688\" | where CommandLine contains \"-noni -ep bypass $\"", - "queryFrequency": "5H", - "queryPeriod": "6H", - "triggerOperator": "GreaterThan", - "triggerThreshold": 5, - "suppressionDuration": "6H", - "suppressionEnabled": false, - "tactics": [ - "Persistence", - "LateralMovement", - "Collection" - ], - "playbookName": "aaaa" - } - ] -} diff --git a/examples/AlertRules.json b/examples/AlertRules.json index 904cbc8..ddf76fa 100644 --- a/examples/AlertRules.json +++ b/examples/AlertRules.json @@ -17,7 +17,7 @@ "LateralMovement", "Collection" ], - "playbookName": "pkmsentinel" + "playbookName": "Playbook01" } ] } diff --git a/examples/SuspectApplicationConsent.yaml b/examples/SuspectApplicationConsent.yaml index b99543f..a7ebbad 100644 --- a/examples/SuspectApplicationConsent.yaml +++ b/examples/SuspectApplicationConsent.yaml @@ -19,6 +19,7 @@ tactics: - Persistence - LateralMovement - Collection +playbookName: Playbook01 query: | AzureActivity From 2c138a5e088de0d89eed1082dbb05785bffc15e3 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Wed, 26 Feb 2020 09:54:41 +0100 Subject: [PATCH 05/53] fixing Subscribtion parameter for playbook (#43) --- AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 b/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 index b129f3f..5dea58d 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 @@ -73,7 +73,13 @@ function New-AzSentinelAlertRuleAction { $action = $null - $playBook = Get-AzSentinelPlayBook -Name $PlayBookName + if ($SubscriptionId) { + $playBook = Get-AzSentinelPlayBook -SubscriptionId $SubscriptionId -Name $PlayBookName + } + else { + $playBook = Get-AzSentinelPlayBook -Name $PlayBookName + } + $action = Get-AzSentinelAlertRuleAction @arguments -RuleId $alertId -ErrorAction SilentlyContinue From bf078606acbbea0838766a82a65d2c9f8030711f Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Wed, 26 Feb 2020 10:19:02 +0100 Subject: [PATCH 06/53] fixing Subscribtion parameter for playbook (#45) --- AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 b/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 index b129f3f..5dea58d 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 @@ -73,7 +73,13 @@ function New-AzSentinelAlertRuleAction { $action = $null - $playBook = Get-AzSentinelPlayBook -Name $PlayBookName + if ($SubscriptionId) { + $playBook = Get-AzSentinelPlayBook -SubscriptionId $SubscriptionId -Name $PlayBookName + } + else { + $playBook = Get-AzSentinelPlayBook -Name $PlayBookName + } + $action = Get-AzSentinelAlertRuleAction @arguments -RuleId $alertId -ErrorAction SilentlyContinue From 19395a7154ec40f0516a38d86778737e0b706a9d Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Sun, 15 Mar 2020 14:19:06 +0100 Subject: [PATCH 07/53] Fix- get-Azsentinalhuntingrule - Cannot validate argument on parameter "Property" (#50) * fix huntng rule * fixing hunting rule issue --- AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 index 39848fa..8305000 100644 --- a/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 @@ -39,8 +39,9 @@ function Get-AzSentinelHuntingRule { [ValidateNotNullOrEmpty()] [string[]]$RuleName, - [Parameter(Mandatory = $false)] - [validateset("HuntingQueries", "GeneralExploration", "LogManagement")] + [Parameter(Mandatory = $false, + ValueFromPipeline)] + [ValidateNotNullOrEmpty()] [string]$Filter ) @@ -67,8 +68,16 @@ function Get-AzSentinelHuntingRule { $uri = "$script:baseUri/savedSearches?api-version=2017-04-26-preview" Write-Verbose -Message "Using URI: $($uri)" + + try { - $huntingRules = (Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader | Where-Object $_.Category -eq $Filter) + if ($Filter) { + $huntingRules = (Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader) | Where-Object $_.Category -eq $Filter + } + else { + $huntingRules = (Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader) + + } } catch { Write-Verbose $_ @@ -81,7 +90,7 @@ function Get-AzSentinelHuntingRule { Write-Verbose "Found $($huntingRules.value.count) hunting rules" if ($RuleName.Count -ge 1) { foreach ($rule in $RuleName) { - [PSCustomObject]$temp = $huntingRules.value | Where-Object { $_.properties.displayName -eq $rule } + [PSCustomObject]$temp = $huntingRules.value | Where-Object { $_.displayName -eq $rule } if ($null -ne $temp) { $temp.properties | Add-Member -NotePropertyName name -NotePropertyValue $temp.name -Force $temp.properties | Add-Member -NotePropertyName id -NotePropertyValue $temp.id -Force From 6406bdec7fb3d7e3f59c0ef6c99d4b621e4ef348 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Sun, 15 Mar 2020 15:49:04 +0100 Subject: [PATCH 08/53] Fix - new-azsentinelalertrule playbook property (#49) * fixing the if statement * fixing the if statement --- AzSentinel/Public/New-AzSentinelAlertRule.ps1 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 index e931eab..96baf2a 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 @@ -224,9 +224,8 @@ function New-AzSentinelAlertRule { try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -EnumsAsStrings) - - if ($null -ne $body.Properties.playbookName) { - New-AzSentinelAlertRuleAction @arguments -PlayBookName ($body.Properties.playbookName) -RuleId $($body.Properties.Name) -confirm:$false + if (($body.Properties.PlaybookName)) { + New-AzSentinelAlertRuleAction @arguments -PlayBookName ($body.Properties.PlaybookName) -RuleId $($body.Properties.Name) -confirm:$false } Write-Output "Successfully created rule: $($DisplayName) with status: $($result.StatusDescription)" From 9007362164b29c403f5ffa804bc104283cdae442 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Thu, 26 Mar 2020 21:03:45 +0100 Subject: [PATCH 09/53] Feature - get all incidents (#51) * updating get incident * updating get incident function and docs * updating powershell-yaml * updating importmodule error * workaround * removing powershell-yaml depending --- .build/PSDeploy/DeployAll.PSDeploy.build.ps1 | 2 +- AzSentinel/AzSentinel.psd1 | 4 -- AzSentinel/Public/Get-AzSentinelIncident.ps1 | 38 ++++++++++++++----- .../Public/Import-AzSentinelAlertRule.ps1 | 1 - .../Public/Import-AzSentinelHuntingRule.ps1 | 1 - AzSentinel/docs/Get-AzSentinelIncident.md | 29 ++++++++++++-- PSDepend.build.psd1 | 1 - docs/Get-AzSentinelIncident.md | 29 ++++++++++++-- 8 files changed, 80 insertions(+), 25 deletions(-) diff --git a/.build/PSDeploy/DeployAll.PSDeploy.build.ps1 b/.build/PSDeploy/DeployAll.PSDeploy.build.ps1 index cdd8b57..2491096 100644 --- a/.build/PSDeploy/DeployAll.PSDeploy.build.ps1 +++ b/.build/PSDeploy/DeployAll.PSDeploy.build.ps1 @@ -32,6 +32,6 @@ task Deploy_with_PSDeploy { $null = $InvokePSDeployArgs.Add('Tags',$DeploymentTags) } - Import-Module PSDeploy + Import-Module PSDeploy -Force Invoke-PSDeploy @InvokePSDeployArgs } diff --git a/AzSentinel/AzSentinel.psd1 b/AzSentinel/AzSentinel.psd1 index 7d36459..dca494f 100644 --- a/AzSentinel/AzSentinel.psd1 +++ b/AzSentinel/AzSentinel.psd1 @@ -56,10 +56,6 @@ ModuleName = 'Az.Accounts' ModuleVersion = '1.5.2' } - @{ - ModuleName = 'powershell-yaml' - ModuleVersion = '0.4.0' - } ) # Assemblies that must be loaded prior to importing this module diff --git a/AzSentinel/Public/Get-AzSentinelIncident.ps1 b/AzSentinel/Public/Get-AzSentinelIncident.ps1 index 71ebbb7..bd5ef30 100644 --- a/AzSentinel/Public/Get-AzSentinelIncident.ps1 +++ b/AzSentinel/Public/Get-AzSentinelIncident.ps1 @@ -16,14 +16,19 @@ function Get-AzSentinelIncident { Enter incident name, this is the same name as the alert rule that triggered the incident .PARAMETER CaseNumber Enter the case number to get specfiek details of a open case + .PARAMETER All + Use -All switch to get a list of all the incidents .EXAMPLE Get-AzSentinelIncident -WorkspaceName "" - Get a list of all open Incidents + Get a list of the last 200 Incidents + .EXAMPLE + Get-AzSentinelIncident -WorkspaceName "" -All + Get a list of all Incidents .EXAMPLE Get-AzSentinelIncident -WorkspaceName "" -CaseNumber Get information of a specifiek incident with providing the casenumber .EXAMPLE - Get-AzSentinelIncident -WorkspaceName "" -IncidentName "","" + Get-AzSentinelIncident -WorkspaceName "" -IncidentName "", "" Get information of one or more incidents with providing a incident name, this is the name of the alert rule that triggered the incident #> @@ -46,7 +51,11 @@ function Get-AzSentinelIncident { [Parameter(Mandatory = $false, ValueFromPipeline)] [ValidateNotNullOrEmpty()] - [int[]]$CaseNumber + [int[]]$CaseNumber, + + [Parameter(Mandatory = $false, + ValueFromPipeline)] + [Switch]$All ) begin { @@ -73,7 +82,15 @@ function Get-AzSentinelIncident { Write-Verbose -Message "Using URI: $($uri)" try { - $incident = Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader + $incidentRaw = (Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader) + $incident += $incidentRaw.value + + if ($All){ + while ($incidentRaw.nextLink) { + $incidentRaw = (Invoke-RestMethod -Uri $($incidentRaw.nextLink) -Headers $script:authHeader -Method Get) + $incident += $incidentRaw.value + } + } } catch { Write-Verbose $_ @@ -83,10 +100,12 @@ function Get-AzSentinelIncident { $return = @() if ($incident) { - Write-Verbose "Found $($incident.value.count) incidents" + Write-Verbose "Found $($incident.count) incidents" + if ($IncidentName.Count -ge 1) { foreach ($rule in $IncidentName) { - [PSCustomObject]$temp = $incident.value | Where-Object { $_.properties.title -eq $rule } + [PSCustomObject]$temp = $incident | Where-Object { $_.properties.title -eq $rule } + if ($null -ne $temp) { $temp.properties | Add-Member -NotePropertyName etag -NotePropertyValue $temp.etag -Force $temp.properties | Add-Member -NotePropertyName name -NotePropertyValue $temp.name -Force @@ -100,7 +119,8 @@ function Get-AzSentinelIncident { } elseif ($CaseNumber.Count -ge 1) { foreach ($rule in $CaseNumber) { - [PSCustomObject]$temp = $incident.value | Where-Object { $_.properties.caseNumber -eq $rule } + [PSCustomObject]$temp = $incident | Where-Object { $_.properties.caseNumber -eq $rule } + if ($null -ne $temp) { $temp.properties | Add-Member -NotePropertyName etag -NotePropertyValue $temp.etag -Force $temp.properties | Add-Member -NotePropertyName name -NotePropertyValue $temp.name -Force @@ -113,11 +133,11 @@ function Get-AzSentinelIncident { return $return } else { - ($incident.Content | ConvertFrom-Json).value | ForEach-Object { + $incident | ForEach-Object { $_.properties | Add-Member -NotePropertyName etag -NotePropertyValue $_.etag -Force $_.properties | Add-Member -NotePropertyName name -NotePropertyValue $_.name -Force - return $_.properties } + return $incident.properties } } else { diff --git a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 index 797bd7f..17e07aa 100644 --- a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 @@ -1,5 +1,4 @@ #requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} -#requires -module @{ModuleNAme = 'powershell-yaml'; ModuleVersion = '0.4.0'} #requires -version 6.2 function Import-AzSentinelAlertRule { diff --git a/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 index f924d3d..be51e6d 100644 --- a/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 @@ -1,5 +1,4 @@ #requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} -#requires -module @{ModuleNAme = 'powershell-yaml'; ModuleVersion = '0.4.0'} #requires -version 6.2 function Import-AzSentinelHuntingRule { diff --git a/AzSentinel/docs/Get-AzSentinelIncident.md b/AzSentinel/docs/Get-AzSentinelIncident.md index de1eb1f..125865c 100644 --- a/AzSentinel/docs/Get-AzSentinelIncident.md +++ b/AzSentinel/docs/Get-AzSentinelIncident.md @@ -14,7 +14,7 @@ Get Azure Sentinel Incident ``` Get-AzSentinelIncident [-SubscriptionId ] -WorkspaceName [-IncidentName ] - [-CaseNumber ] [-WhatIf] [-Confirm] [] + [-CaseNumber ] [-All] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -26,18 +26,24 @@ You can can also filter to Incident with speciefiek case namber or Case name ### EXAMPLE 1 ``` Get-AzSentinelIncident -WorkspaceName "" -Get a list of all open Incidents +Get a list of the last 200 Incidents ``` ### EXAMPLE 2 ``` +Get-AzSentinelIncident -WorkspaceName "" -All +Get a list of all Incidents +``` + +### EXAMPLE 3 +``` Get-AzSentinelIncident -WorkspaceName "" -CaseNumber Get information of a specifiek incident with providing the casenumber ``` -### EXAMPLE 3 +### EXAMPLE 4 ``` -Get-AzSentinelIncident -WorkspaceName "" -IncidentName "","" +Get-AzSentinelIncident -WorkspaceName "" -IncidentName "", "" Get information of one or more incidents with providing a incident name, this is the name of the alert rule that triggered the incident ``` @@ -103,6 +109,21 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -All +Use -All switch to get a list of all the incidents + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. diff --git a/PSDepend.build.psd1 b/PSDepend.build.psd1 index 876e141..11aa62e 100644 --- a/PSDepend.build.psd1 +++ b/PSDepend.build.psd1 @@ -13,6 +13,5 @@ PSScriptAnalyzer = 'latest' platyPS = 'latest' PSDeploy = 'latest' - 'powershell-yaml' = '0.4.0' 'Az.Accounts' = '1.6.4' } diff --git a/docs/Get-AzSentinelIncident.md b/docs/Get-AzSentinelIncident.md index de1eb1f..125865c 100644 --- a/docs/Get-AzSentinelIncident.md +++ b/docs/Get-AzSentinelIncident.md @@ -14,7 +14,7 @@ Get Azure Sentinel Incident ``` Get-AzSentinelIncident [-SubscriptionId ] -WorkspaceName [-IncidentName ] - [-CaseNumber ] [-WhatIf] [-Confirm] [] + [-CaseNumber ] [-All] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -26,18 +26,24 @@ You can can also filter to Incident with speciefiek case namber or Case name ### EXAMPLE 1 ``` Get-AzSentinelIncident -WorkspaceName "" -Get a list of all open Incidents +Get a list of the last 200 Incidents ``` ### EXAMPLE 2 ``` +Get-AzSentinelIncident -WorkspaceName "" -All +Get a list of all Incidents +``` + +### EXAMPLE 3 +``` Get-AzSentinelIncident -WorkspaceName "" -CaseNumber Get information of a specifiek incident with providing the casenumber ``` -### EXAMPLE 3 +### EXAMPLE 4 ``` -Get-AzSentinelIncident -WorkspaceName "" -IncidentName "","" +Get-AzSentinelIncident -WorkspaceName "" -IncidentName "", "" Get information of one or more incidents with providing a incident name, this is the name of the alert rule that triggered the incident ``` @@ -103,6 +109,21 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -All +Use -All switch to get a list of all the incidents + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. From 4d423a13e878742324ea2e6201ccbae4bb77208d Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Thu, 26 Mar 2020 21:09:41 +0100 Subject: [PATCH 10/53] fixing logicapp sas token (#52) --- AzSentinel/Private/Get-AzSentinelPlayBook.ps1 | 36 ++++++++++--------- .../Public/New-AzSentinelAlertRuleAction.ps1 | 4 +-- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/AzSentinel/Private/Get-AzSentinelPlayBook.ps1 b/AzSentinel/Private/Get-AzSentinelPlayBook.ps1 index 69dc392..395d185 100644 --- a/AzSentinel/Private/Get-AzSentinelPlayBook.ps1 +++ b/AzSentinel/Private/Get-AzSentinelPlayBook.ps1 @@ -12,7 +12,7 @@ function Get-AzSentinelPlayBook { .PARAMETER Name Enter the Logic App name .EXAMPLE - Get-AzSentinelPlayBook -Name "pkmsentinel" + Get-AzSentinelPlayBook -Name "" This example will get search for the Logic app within the current subscripbtio and test to see if it's compatible for Sentinel .NOTES NAME: Get-AzSentinelPlayBook @@ -32,6 +32,9 @@ function Get-AzSentinelPlayBook { } process { + + $triggerName = 'When_a_response_to_an_Azure_Sentinel_alert_is_triggered' + if ($SubscriptionId) { Write-Verbose "Getting LogicApp from Subscription $($subscriptionId)" $uri = "https://management.azure.com/subscriptions/$($subscriptionId)/providers/Microsoft.Logic/workflows?api-version=2016-06-01" @@ -45,28 +48,29 @@ function Get-AzSentinelPlayBook { return $return } - $playBook = (Invoke-RestMethod -Uri $uri -Method get -Headers $script:authHeader).value | Where-Object { $_.name -eq $Name } -ErrorAction SilentlyContinue + try { + $playBook = (Invoke-RestMethod -Uri $uri -Method get -Headers $script:authHeader).value | Where-Object { $_.name -eq $Name } - if ($null -ne $playBook) { - $uri1 = "https://management.azure.com$($playBook.id)/triggers/When_a_response_to_an_Azure_Sentinel_alert_is_triggered?api-version=2016-06-01" - try { - $playbookTrigger = (Invoke-RestMethod -Uri $uri1 -Method Get -Headers $script:authHeader).properties + if ($playBook){ + $uri1 = "https://management.azure.com$($playBook.id)/triggers/$($triggerName)/listCallbackUrl?api-version=2016-06-01" + try { + $playbookTrigger = (Invoke-RestMethod -Uri $uri1 -Method Post -Headers $script:authHeader) + $playbookTrigger | Add-Member -NotePropertyName ResourceId -NotePropertyValue $playBook.id -Force - if ($null -ne $playbookTrigger) { - return $playBook + return $playbookTrigger } - else { - $return = "Playbook doesn't start with 'When_a_response_to_an_Azure_Sentinel_alert_is_triggered' step!" - return $return + catch { + $return = "Playbook $($Name) doesn't start with 'When_a_response_to_an_Azure_Sentinel_alert_is_triggered' step! Error message: $($_.Exception.Message)" + Write-Error $return } } - catch { - $return = $_.Exception.Message - return $return + else { + Write-Warning "Unable to find LogicApp $Name under Subscription Id: $($script:subscriptionId)" } } - else { - Write-Error "Unable to find LogicApp $Name under Subscription Id: $($script:subscriptionId)" + catch { + $return = $_.Exception.Message + Write-Error $return } } } diff --git a/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 b/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 index 5dea58d..7bb8190 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 @@ -92,8 +92,8 @@ function New-AzSentinelAlertRuleAction { "type" = "Microsoft.SecurityInsights/alertRules/actions" "properties" = @{ "ruleId" = $alertId - "triggerUri" = "$($playBook.properties.accessEndpoint)/triggers/When_a_response_to_an_Azure_Sentinel_alert_is_triggered/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2FWhen_a_response_to_an_Azure_Sentinel_alert_is_triggered%2Frun&sv=1.0&sig=NMCSM7uOK4I42L2IPWdgL2eR3-VpoKLXpbTzI9_7wvI" - "logicAppResourceId" = "$($playBook.id)" + "triggerUri" = $playBook.value + "logicAppResourceId" = $playBook.ResourceId } } From 10331af5db1c456166c4cb9c766cc1f9baeb9189 Mon Sep 17 00:00:00 2001 From: pemontto <939704+pemontto@users.noreply.github.com> Date: Thu, 16 Apr 2020 08:14:34 +0100 Subject: [PATCH 11/53] Add support for day time periods (#61) --- AzSentinel/Classes/AlertRule.ps1 | 24 ++++++++++++++++--- .../Public/Import-AzSentinelAlertRule.ps1 | 2 +- AzSentinel/Public/New-AzSentinelAlertRule.ps1 | 6 ++--- AzSentinel/docs/New-AzSentinelAlertRule.md | 6 ++--- README.md | 6 ++--- docs/New-AzSentinelAlertRule.md | 6 ++--- 6 files changed, 34 insertions(+), 16 deletions(-) diff --git a/AzSentinel/Classes/AlertRule.ps1 b/AzSentinel/Classes/AlertRule.ps1 index a5c3486..67c8153 100644 --- a/AzSentinel/Classes/AlertRule.ps1 +++ b/AzSentinel/Classes/AlertRule.ps1 @@ -39,6 +39,24 @@ class AlertProp { return $value } + # Convert string to ISO_8601 format PdDThHmMsS + static [string] TimeString([string]$value) { + $value = $value.ToUpper() + # Return values already in ISO 8601 format + if ($value -match "PT.*|P.*D") { + return $value + } + # Format day time periods + if ($value -like "*D") { + return "P$value" + } + # Format hour and minute time periods + if ($value -match ".*[HM]") { + return "PT$value" + } + return $value + } + AlertProp ($Name, $DisplayName, $Description, $Severity, $Enabled, $Query, $QueryFrequency, $QueryPeriod, $TriggerOperator, $TriggerThreshold, $suppressionDuration, $suppressionEnabled, $Tactics, $PlaybookName) { $this.name = $Name $this.DisplayName = $DisplayName @@ -46,11 +64,11 @@ class AlertProp { $this.Severity = $Severity $this.Enabled = $Enabled $this.Query = $Query - $this.QueryFrequency = if ($QueryFrequency -like "PT*") { $QueryFrequency.ToUpper() } else { ("PT" + $QueryFrequency).ToUpper() } - $this.QueryPeriod = if ($QueryPeriod -like "PT*") { $QueryPeriod.ToUpper() } else { ("PT" + $QueryPeriod).ToUpper() } + $this.QueryFrequency = [AlertProp]::TimeString($QueryFrequency) + $this.QueryPeriod = [AlertProp]::TimeString($QueryPeriod) $this.TriggerOperator = [AlertProp]::TriggerOperatorSwitch($TriggerOperator) $this.TriggerThreshold = $TriggerThreshold - $this.SuppressionDuration = if ((! $null -eq $suppressionDuration) -or ( $false -eq $suppressionEnabled)) { if ($suppressionDuration -like "PT*") { $suppressionDuration.ToUpper() } else { ("PT" + $suppressionDuration).ToUpper() } } else { "PT1H" } + $this.SuppressionDuration = if ((! $null -eq $suppressionDuration) -or ( $false -eq $suppressionEnabled)) { [AlertProp]::TimeString($suppressionDuration) } else { "PT1H" } $this.SuppressionEnabled = if ($suppressionEnabled) { $suppressionEnabled } else { $false } $this.Tactics = $Tactics $this.PlaybookName = $PlaybookName diff --git a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 index 17e07aa..aecf5a7 100644 --- a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 @@ -124,7 +124,7 @@ function Import-AzSentinelAlertRule { $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($content.name)?api-version=2019-01-01-preview" } else { - Write-Verbose -Message "Rule $($item.displayName) doesn't exists in Azure Sentinel" + Write-Verbose -Message "Rule $($item.displayName) doesn't exist in Azure Sentinel" $item | Add-Member -NotePropertyName name -NotePropertyValue $guid -Force $item | Add-Member -NotePropertyName etag -NotePropertyValue $null -Force diff --git a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 index 96baf2a..947ef05 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 @@ -22,15 +22,15 @@ function New-AzSentinelAlertRule { .PARAMETER Query Enter the Query that you want to use .PARAMETER QueryFrequency - Enter the query frequency, example: 5H or 5M (H stands for Hour and M stands for Minute) + Enter the query frequency, example: 5H, 5M, 5D (H stands for Hour, M stands for Minute and D stands for Day) .PARAMETER QueryPeriod - Enter the quury period, exmaple: 5H or 5M (H stands for Hour and M stands for Minute) + Enter the query period, exmaple: 5H, 5M, 5D (H stands for Hour, M stands for Minute and D stands for Day) .PARAMETER TriggerOperator Select the triggert Operator, valid values are: "GreaterThan", "FewerThan", "EqualTo", "NotEqualTo" .PARAMETER TriggerThreshold Enter the trigger treshold .PARAMETER SuppressionDuration - Enter the suppression duration, example: 5H or 5M (H stands for Hour and M stands for Minute) + Enter the suppression duration, example: 5H, 5M, 5D (H stands for Hour, M stands for Minute and D stands for Day) .PARAMETER SuppressionEnabled Set $true to enable Suppression or $false to disable Suppression .PARAMETER Tactics diff --git a/AzSentinel/docs/New-AzSentinelAlertRule.md b/AzSentinel/docs/New-AzSentinelAlertRule.md index 60ea684..fcaf08c 100644 --- a/AzSentinel/docs/New-AzSentinelAlertRule.md +++ b/AzSentinel/docs/New-AzSentinelAlertRule.md @@ -140,7 +140,7 @@ Accept wildcard characters: False ``` ### -QueryFrequency -Enter the query frequency, example: 5H or 5M (H stands for Hour and M stands for Minute) +Enter the query frequency, example: 5H, 5M, 5D (H stands for Hour, M stands for Minute and D stands for Day) ```yaml Type: String @@ -155,7 +155,7 @@ Accept wildcard characters: False ``` ### -QueryPeriod -Enter the quury period, exmaple: 5H or 5M (H stands for Hour and M stands for Minute) +Enter the query period, exmaple: 5H, 5M, 5D (H stands for Hour, M stands for Minute and D stands for Day) ```yaml Type: String @@ -201,7 +201,7 @@ Accept wildcard characters: False ``` ### -SuppressionDuration -Enter the suppression duration, example: 5H or 5M (H stands for Hour and M stands for Minute) +Enter the suppression duration, example: 5H, 5M, 5D (H stands for Hour, M stands for Minute and D stands for Day) ```yaml Type: String diff --git a/README.md b/README.md index 12db987..a38f29c 100644 --- a/README.md +++ b/README.md @@ -76,11 +76,11 @@ The following tables describe the values you need to set in the schema. | severity | string | true | Medium, High, Low, Informational | Medium | | enabled | bool | true | true, false | true | | query | string | true | special character need to be escaped by \ | SecurityEvent \| where EventID == \"4688\" \| where CommandLine contains \\"-noni -ep bypass $\\" | -| queryFrequency | string | true | Value must be between 5 minutes and 24 hours | 5H | -| queryPeriod | string | true | Value must be between 5 minutes and 24 hours | 1440M | +| queryFrequency | string | true | Value must be greater than 5 minutes | 30M | +| queryPeriod | string | true | Value must be greater than 5 minutes | 6H | | triggerOperator | string | true | GreaterThan, FewerThan, EqualTo, NotEqualTo | GreaterThan | | triggerThreshold | int | true | The value must be between 0 and 10000 | 5 | -| suppressionDuration | string | true | Value must be between 5 minutes and 24 hours | 11H | +| suppressionDuration | string | true | Value must be greater than 5 minutes | 1D | | suppressionEnabled | bool | true | true, false | true | | tactics | array | true | InitialAccess, Persistence,Execution,PrivilegeEscalation,DefenseEvasion,CredentialAccess,LateralMovement,Discovery,Collection,Exfiltration,CommandAndControl,Impact | true | | playbookName | string | false | Enter the Logic App name that you want to configure as playbook trigger | LogicApp01 | diff --git a/docs/New-AzSentinelAlertRule.md b/docs/New-AzSentinelAlertRule.md index 60ea684..fe704f3 100644 --- a/docs/New-AzSentinelAlertRule.md +++ b/docs/New-AzSentinelAlertRule.md @@ -140,7 +140,7 @@ Accept wildcard characters: False ``` ### -QueryFrequency -Enter the query frequency, example: 5H or 5M (H stands for Hour and M stands for Minute) +Enter the query period, exmaple: 5H, 5M, 5D (H stands for Hour, M stands for Minute and D stands for Day) ```yaml Type: String @@ -155,7 +155,7 @@ Accept wildcard characters: False ``` ### -QueryPeriod -Enter the quury period, exmaple: 5H or 5M (H stands for Hour and M stands for Minute) +Enter the query period, exmaple: 5H, 5M, 5D (H stands for Hour, M stands for Minute and D stands for Day) ```yaml Type: String @@ -201,7 +201,7 @@ Accept wildcard characters: False ``` ### -SuppressionDuration -Enter the suppression duration, example: 5H or 5M (H stands for Hour and M stands for Minute) +Enter the query period, exmaple: 5H, 5M, 5D (H stands for Hour, M stands for Minute and D stands for Day) ```yaml Type: String From 969cf290dc284872aea9da90b7853fd02c69cb94 Mon Sep 17 00:00:00 2001 From: NVolcz Date: Thu, 16 Apr 2020 09:15:02 +0200 Subject: [PATCH 12/53] Add missing dot to yml file extension (#59) The Import-AZSentinelAlertRule function is not able to import yml files due to missing dot in the file extension. --- AzSentinel/Public/Import-AzSentinelAlertRule.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 index aecf5a7..5a4d2ca 100644 --- a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 @@ -93,7 +93,7 @@ function Import-AzSentinelAlertRule { Write-Error -Message 'Unable to convert JSON file' -ErrorAction Stop } } - elseif ($SettingsFile.Extension -in '.yaml', 'yml') { + elseif ($SettingsFile.Extension -in '.yaml', '.yml') { try { $analytics = [pscustomobject](Get-Content $SettingsFile -Raw | ConvertFrom-Yaml -ErrorAction Stop) $analytics | Add-Member -MemberType NoteProperty -Name DisplayName -Value $analytics.name @@ -103,6 +103,8 @@ function Import-AzSentinelAlertRule { Write-Verbose $_ Write-Error -Message 'Unable to convert yaml file' -ErrorAction Stop } + } else { + Write-Error -Message 'Unsupported extension for SettingsFile' -ErrorAction Stop } foreach ($item in $analytics) { From d79f8d5f0ee5453efed5a7341fda2e1341e15a7e Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Thu, 7 May 2020 21:29:15 +0200 Subject: [PATCH 13/53] adding support for resource provider in set-azsentinel (#69) --- .../Get-AzSentinelResourceProvider.ps1 | 30 +++++++++++++++ .../Set-AzSentinelResourceProvider.ps1 | 35 +++++++++++++++++ AzSentinel/Public/Set-AzSentinel.ps1 | 38 +++++++++++++++++-- .../Get-AzSentinelResourceProvider.ps1 | 0 .../Set-AzSentinelResourceProvider.ps1 | 0 5 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 AzSentinel/Private/Get-AzSentinelResourceProvider.ps1 create mode 100644 AzSentinel/Private/Set-AzSentinelResourceProvider.ps1 create mode 100644 AzSentinel/tests/Unit/private/Get-AzSentinelResourceProvider.ps1 create mode 100644 AzSentinel/tests/Unit/private/Set-AzSentinelResourceProvider.ps1 diff --git a/AzSentinel/Private/Get-AzSentinelResourceProvider.ps1 b/AzSentinel/Private/Get-AzSentinelResourceProvider.ps1 new file mode 100644 index 0000000..330043e --- /dev/null +++ b/AzSentinel/Private/Get-AzSentinelResourceProvider.ps1 @@ -0,0 +1,30 @@ +#requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} +#requires -version 6.2 + +function Get-AzSentinelResourceProvider { + <# + .SYNOPSIS + Get AzSentinelResourceProvider + .DESCRIPTION + This function is used to get status of the required resource providers + .PARAMETER NameSpace + Enter the name of the namespace without 'Microsoft.' + .EXAMPLE + Get-AzSentinelResourceProvider -NameSpace 'OperationsManagement' + #> + param ( + [string]$NameSpace + ) + + $uri = "https://management.azure.com/subscriptions/$($script:subscriptionId)/providers/Microsoft.$($NameSpace)?api-version=2019-10-01" + + try { + $invokeReturn = Invoke-RestMethod -Method Get -Uri $uri -Headers $script:authHeader + return $invokeReturn + } + catch { + $return = $_.Exception.Message + Write-Error $return + return $return + } +} diff --git a/AzSentinel/Private/Set-AzSentinelResourceProvider.ps1 b/AzSentinel/Private/Set-AzSentinelResourceProvider.ps1 new file mode 100644 index 0000000..78cd8ff --- /dev/null +++ b/AzSentinel/Private/Set-AzSentinelResourceProvider.ps1 @@ -0,0 +1,35 @@ +#requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} +#requires -version 6.2 + +function Set-AzSentinelResourceProvider { + <# + .SYNOPSIS + Set AzSentinelResourceProvider + .DESCRIPTION + This function is enables the required Resource providers + .PARAMETER NameSpace + Enter the name of the namespace without 'Microsoft.' + .EXAMPLE + Set-AzSentinelResourceProvider -NameSpace 'OperationsManagementOperationsManagement' + #> + param ( + [string]$NameSpace + ) + + $uri = "https://management.azure.com/subscriptions/$($script:subscriptionId)/providers/Microsoft.$($NameSpace)/register?api-version=2019-10-01" + + try { + $invokeReturn = Invoke-RestMethod -Method Post -Uri $uri -Headers $script:authHeader + do { + $resourceProviderStatus = Get-AzSentinelResourceProvider -NameSpace $NameSpace + } + until ($resourceProviderStatus.registrationState -eq 'Registered') + $return = "Successfully enabled Microsoft.$($NameSpace) on subscription $($script:subscriptionId). Status:$($resourceProviderStatus.registrationState)" + return $return + } + catch { + $return = $_.Exception.Message + Write-Error $return + return $return + } +} diff --git a/AzSentinel/Public/Set-AzSentinel.ps1 b/AzSentinel/Public/Set-AzSentinel.ps1 index 0b98c1f..97c7ecf 100644 --- a/AzSentinel/Public/Set-AzSentinel.ps1 +++ b/AzSentinel/Public/Set-AzSentinel.ps1 @@ -52,6 +52,39 @@ function Set-AzSentinel { $errorResult = '' if ($workspaceResult.properties.provisioningState -eq 'Succeeded') { + + <# + Testing to see if OperationsManagement resource provider is enabled on subscription + #> + $operationsManagementProvider = Get-AzSentinelResourceProvider -NameSpace "OperationsManagement" + if ($operationsManagementProvider.registrationState -ne 'Registered') { + Write-Warning "Resource provider 'Microsoft.OperationsManagement' is not registered" + + if ($PSCmdlet.ShouldProcess("Do you want to enable 'Microsoft.OperationsManagement' on subscription $($script:subscriptionId)")) { + Set-AzSentinelResourceProvider -NameSpace 'OperationsManagement' + } + else { + Write-Output "No change have been." + break + } + } + + <# + Testing to see if SecurityInsights resource provider is enabled on subscription + #> + $securityInsightsProvider = Get-AzSentinelResourceProvider -NameSpace 'SecurityInsights' + if ($securityInsightsProvider.registrationState -ne 'Registered') { + Write-Warning "Resource provider 'Microsoft.SecurityInsights' is not registered" + + if ($PSCmdlet.ShouldProcess("Do you want to enable 'Microsoft.SecurityInsights' on subscription $($script:subscriptionId)")) { + Set-AzSentinelResourceProvider -NameSpace 'SecurityInsights' + } + else { + Write-Output "No change have been." + break + } + } + $body = @{ 'id' = '' 'etag' = '' @@ -85,21 +118,20 @@ function Set-AzSentinel { Write-Output "Successfully enabled Sentinel on workspae: $WorkspaceName with result code $($result.StatusDescription)" } else { - Write-Output "No change have been made for rule $WorkspaceName, deployment aborted" + Write-Output "No change have been made for $WorkspaceName, deployment aborted" + break } } catch { Write-Verbose $_ Write-Error "Unable to enable Sentinel on $WorkspaceName with error message: $($_.Exception.Message)" } - } else { Write-Verbose $_ Write-Error "Unable to Azure Sentinel with error message: $($_.Exception.Message)" -ErrorAction Stop } } - } else { Write-Error "Workspace $WorkspaceName is currently in $($workspaceResult.properties.provisioningState) status, setup canceled" diff --git a/AzSentinel/tests/Unit/private/Get-AzSentinelResourceProvider.ps1 b/AzSentinel/tests/Unit/private/Get-AzSentinelResourceProvider.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/AzSentinel/tests/Unit/private/Set-AzSentinelResourceProvider.ps1 b/AzSentinel/tests/Unit/private/Set-AzSentinelResourceProvider.ps1 new file mode 100644 index 0000000..e69de29 From 1f38c8c3c01705c174083ac5ebb1ff34441bac76 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Thu, 7 May 2020 21:30:51 +0200 Subject: [PATCH 14/53] New function for enabling and disabling Alert rules (#71) * init release enable and disable function * adding empty test files * updating return message --- AzSentinel/AzSentinel.psd1 | 2 + AzSentinel/Classes/AlertRule.ps1 | 29 ++++- .../Public/Disable-AzSentinelAlertRule.ps1 | 83 ++++++++++++ .../Public/Enable-AzSentinelAlertRule.ps1 | 82 ++++++++++++ .../docs/Disable-AzSentinelAlertRule.md | 118 ++++++++++++++++++ AzSentinel/docs/Enable-AzSentinelAlertRule.md | 118 ++++++++++++++++++ AzSentinel/docs/README.md | 20 +-- .../Disable-AzSentinelAlertRule.tests.ps1 | 0 .../Enable-AzSentinelAlertRule.tests.ps1 | 0 docs/Disable-AzSentinelAlertRule.md | 118 ++++++++++++++++++ docs/Enable-AzSentinelAlertRule.md | 118 ++++++++++++++++++ docs/README.md | 20 +-- 12 files changed, 689 insertions(+), 19 deletions(-) create mode 100644 AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 create mode 100644 AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 create mode 100644 AzSentinel/docs/Disable-AzSentinelAlertRule.md create mode 100644 AzSentinel/docs/Enable-AzSentinelAlertRule.md create mode 100644 AzSentinel/tests/Unit/public/Disable-AzSentinelAlertRule.tests.ps1 create mode 100644 AzSentinel/tests/Unit/public/Enable-AzSentinelAlertRule.tests.ps1 create mode 100644 docs/Disable-AzSentinelAlertRule.md create mode 100644 docs/Enable-AzSentinelAlertRule.md diff --git a/AzSentinel/AzSentinel.psd1 b/AzSentinel/AzSentinel.psd1 index dca494f..a50a7b0 100644 --- a/AzSentinel/AzSentinel.psd1 +++ b/AzSentinel/AzSentinel.psd1 @@ -78,6 +78,8 @@ 'Set-AzSentinel', 'New-AzSentinelAlertRule', 'Get-AzSentinelAlertRule', + 'Enable-AzSentinelAlertRule', + 'Disable-AzSentinelAlertRule', 'Import-AzSentinelAlertRule', 'Remove-AzSentinelAlertRule', 'Import-AzSentinelAlertRule', diff --git a/AzSentinel/Classes/AlertRule.ps1 b/AzSentinel/Classes/AlertRule.ps1 index 67c8153..04b4065 100644 --- a/AzSentinel/Classes/AlertRule.ps1 +++ b/AzSentinel/Classes/AlertRule.ps1 @@ -28,6 +28,8 @@ class AlertProp { [string] $PlaybookName + hidden $properties + static [string] TriggerOperatorSwitch([string]$value) { switch ($value) { "gt" { $value = "GreaterThan" } @@ -56,6 +58,22 @@ class AlertProp { } return $value } + AlertProp ($properties) { + $this.name = $properties.Name + $this.DisplayName = $properties.DisplayName + $this.Description = $properties.Description + $this.Severity = $properties.Severity + $this.Enabled = $properties.Enabled + $this.Query = $properties.Query + $this.QueryFrequency = $properties.QueryFrequency + $this.QueryPeriod = $properties.QueryPeriod + $this.TriggerOperator = $properties.TriggerOperator + $this.TriggerThreshold = $properties.TriggerThreshold + $this.SuppressionDuration = $properties.SuppressionDuration + $this.SuppressionEnabled = $properties.SuppressionEnabled + $this.Tactics = $properties.Tactics + $this.PlaybookName = $properties.PlaybookName + } AlertProp ($Name, $DisplayName, $Description, $Severity, $Enabled, $Query, $QueryFrequency, $QueryPeriod, $TriggerOperator, $TriggerThreshold, $suppressionDuration, $suppressionEnabled, $Tactics, $PlaybookName) { $this.name = $Name @@ -84,9 +102,18 @@ class AlertRule { [AlertProp]$Properties - [Parameter(Mandatory)] [string]$Id + $header + + AlertRule($header, $properties) { + $this.id = $header.Id + $this.type = 'Microsoft.SecurityInsights/alertRules' + $this.Name = $header.Name + $this.Etag = $header.Etag + $this.Properties = $properties + } + AlertRule ([guid]$Name, [string]$Etag, [AlertProp]$Properties, $Id) { $this.id = $Id diff --git a/AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 new file mode 100644 index 0000000..ee13f48 --- /dev/null +++ b/AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 @@ -0,0 +1,83 @@ +function Disable-AzSentinelAlertRule { + <# + .SYNOPSIS + Disable Azure Sentinel Alert Rules + .DESCRIPTION + With this function you can disbale Azure Sentinel Alert rule + .PARAMETER SubscriptionId + Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + .PARAMETER WorkspaceName + Enter the Workspace name + .PARAMETER RuleName + Enter the name of the Alert rule + .EXAMPLE + Disable-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" + In this example you can get configuration of multiple alert rules in once + #> + [cmdletbinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory = $false, + ParameterSetName = "Sub")] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$WorkspaceName, + + [Parameter(Mandatory = $false, + ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]]$RuleName + ) + + begin { + precheck + } + + process { + switch ($PsCmdlet.ParameterSetName) { + Sub { + $arguments = @{ + WorkspaceName = $WorkspaceName + SubscriptionId = $SubscriptionId + } + } + default { + $arguments = @{ + WorkspaceName = $WorkspaceName + } + } + } + + $rules = Get-AzSentinelAlertRule @arguments -RuleName $RuleName + + foreach ($rule in $rules) { + if ($rule.enabled -eq $false) { + Write-Host "'$($rule.DisplayName)' already has status '$($rule.enabled)'" + } + else { + $rule.enabled = $false + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($rule.name)?api-version=2019-01-01-preview" + + $bodyAlertProp = [AlertProp]::new( + ($rule | Select-Object * -ExcludeProperty lastModifiedUtc, etag, id) + ) + + $body = [AlertRule]::new( + ($rule | Select-Object lastModifiedUtc, etag, id, name), + $bodyAlertProp + ) + + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) + Write-Host "Status of '$($rule.DisplayName)' changed to '$($rule.enabled)'" + + } + catch { + Write-Error $_.Exception.Message + } + } + } + } +} diff --git a/AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 new file mode 100644 index 0000000..459a01a --- /dev/null +++ b/AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 @@ -0,0 +1,82 @@ +function Enable-AzSentinelAlertRule { + <# + .SYNOPSIS + Enable Azure Sentinel Alert Rules + .DESCRIPTION + With this function you can enable Azure Sentinel Alert rule + .PARAMETER SubscriptionId + Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + .PARAMETER WorkspaceName + Enter the Workspace name + .PARAMETER RuleName + Enter the name of the Alert rule + .EXAMPLE + Enable-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" + In this example you can get configuration of multiple alert rules in once + #> + [cmdletbinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory = $false, + ParameterSetName = "Sub")] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$WorkspaceName, + + [Parameter(Mandatory = $false, + ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]]$RuleName + ) + + begin { + precheck + } + + process { + switch ($PsCmdlet.ParameterSetName) { + Sub { + $arguments = @{ + WorkspaceName = $WorkspaceName + SubscriptionId = $SubscriptionId + } + } + default { + $arguments = @{ + WorkspaceName = $WorkspaceName + } + } + } + + $rules = Get-AzSentinelAlertRule @arguments -RuleName $RuleName -ErrorAction Stop + + foreach ($rule in $rules) { + if ($rule.enabled -eq $true) { + Write-Host "'$($rule.DisplayName)' already has status '$($rule.enabled)'" + } + else { + $rule.enabled = $true + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($rule.name)?api-version=2019-01-01-preview" + + $bodyAlertProp = [AlertProp]::new( + ($rule | Select-Object * -ExcludeProperty lastModifiedUtc, etag, id) + ) + + $body = [AlertRule]::new( + ($rule | Select-Object lastModifiedUtc, etag, id, name), + $bodyAlertProp + ) + + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) + Write-Host "Status of '$($rule.DisplayName)' changed to '$($rule.enabled)'" + } + catch { + Write-Error $_.Exception.Message + } + } + } + } +} diff --git a/AzSentinel/docs/Disable-AzSentinelAlertRule.md b/AzSentinel/docs/Disable-AzSentinelAlertRule.md new file mode 100644 index 0000000..e2e682b --- /dev/null +++ b/AzSentinel/docs/Disable-AzSentinelAlertRule.md @@ -0,0 +1,118 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Disable-AzSentinelAlertRule + +## SYNOPSIS +Disable Azure Sentinel Alert Rules + +## SYNTAX + +``` +Disable-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-RuleName ] [-WhatIf] + [-Confirm] [] +``` + +## DESCRIPTION +With this function you can disbale Azure Sentinel Alert rule + +## EXAMPLES + +### EXAMPLE 1 +``` +Disable-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" +In this example you can get configuration of multiple alert rules in once +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleName +Enter the name of the Alert rule + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/AzSentinel/docs/Enable-AzSentinelAlertRule.md b/AzSentinel/docs/Enable-AzSentinelAlertRule.md new file mode 100644 index 0000000..6686195 --- /dev/null +++ b/AzSentinel/docs/Enable-AzSentinelAlertRule.md @@ -0,0 +1,118 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Enable-AzSentinelAlertRule + +## SYNOPSIS +Enable Azure Sentinel Alert Rules + +## SYNTAX + +``` +Enable-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-RuleName ] [-WhatIf] + [-Confirm] [] +``` + +## DESCRIPTION +With this function you can enable Azure Sentinel Alert rule + +## EXAMPLES + +### EXAMPLE 1 +``` +Enable-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" +In this example you can get configuration of multiple alert rules in once +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleName +Enter the name of the Alert rule + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/AzSentinel/docs/README.md b/AzSentinel/docs/README.md index 95abd60..4f2eb17 100644 --- a/AzSentinel/docs/README.md +++ b/AzSentinel/docs/README.md @@ -5,12 +5,14 @@ 3. [Get-AzSentinelAlertRule](Get-AzSentinelAlertRule.md) 4. [Import-AzSentinelAlertRule](Import-AzSentinelAlertRule.md) 5. [Remove-AzSentinelAlertRule](Remove-AzSentinelAlertRule.md) -6. [Get-AzSentinelIncident](Get-AzSentinelIncident.md) -7. [Update-AzSentinelIncident](Update-AzSentinelIncident.md) -8. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) -9. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) -10. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) -11. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) -12. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) -13. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) -14. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) +6. [Enable-AzSentinelAlertRule](Enable-AzSentinelAlertRule.md) +7. [Disable-AzSentinelAlertRule](Disable-AzSentinelAlertRule.md) +8. [Get-AzSentinelIncident](Get-AzSentinelIncident.md) +9. [Update-AzSentinelIncident](Update-AzSentinelIncident.md) +10. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) +11. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) +12. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) +13. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) +14. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) +15. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) +16. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) diff --git a/AzSentinel/tests/Unit/public/Disable-AzSentinelAlertRule.tests.ps1 b/AzSentinel/tests/Unit/public/Disable-AzSentinelAlertRule.tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/AzSentinel/tests/Unit/public/Enable-AzSentinelAlertRule.tests.ps1 b/AzSentinel/tests/Unit/public/Enable-AzSentinelAlertRule.tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/docs/Disable-AzSentinelAlertRule.md b/docs/Disable-AzSentinelAlertRule.md new file mode 100644 index 0000000..e2e682b --- /dev/null +++ b/docs/Disable-AzSentinelAlertRule.md @@ -0,0 +1,118 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Disable-AzSentinelAlertRule + +## SYNOPSIS +Disable Azure Sentinel Alert Rules + +## SYNTAX + +``` +Disable-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-RuleName ] [-WhatIf] + [-Confirm] [] +``` + +## DESCRIPTION +With this function you can disbale Azure Sentinel Alert rule + +## EXAMPLES + +### EXAMPLE 1 +``` +Disable-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" +In this example you can get configuration of multiple alert rules in once +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleName +Enter the name of the Alert rule + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/docs/Enable-AzSentinelAlertRule.md b/docs/Enable-AzSentinelAlertRule.md new file mode 100644 index 0000000..6686195 --- /dev/null +++ b/docs/Enable-AzSentinelAlertRule.md @@ -0,0 +1,118 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Enable-AzSentinelAlertRule + +## SYNOPSIS +Enable Azure Sentinel Alert Rules + +## SYNTAX + +``` +Enable-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-RuleName ] [-WhatIf] + [-Confirm] [] +``` + +## DESCRIPTION +With this function you can enable Azure Sentinel Alert rule + +## EXAMPLES + +### EXAMPLE 1 +``` +Enable-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" +In this example you can get configuration of multiple alert rules in once +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RuleName +Enter the name of the Alert rule + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/docs/README.md b/docs/README.md index 95abd60..4f2eb17 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,12 +5,14 @@ 3. [Get-AzSentinelAlertRule](Get-AzSentinelAlertRule.md) 4. [Import-AzSentinelAlertRule](Import-AzSentinelAlertRule.md) 5. [Remove-AzSentinelAlertRule](Remove-AzSentinelAlertRule.md) -6. [Get-AzSentinelIncident](Get-AzSentinelIncident.md) -7. [Update-AzSentinelIncident](Update-AzSentinelIncident.md) -8. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) -9. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) -10. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) -11. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) -12. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) -13. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) -14. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) +6. [Enable-AzSentinelAlertRule](Enable-AzSentinelAlertRule.md) +7. [Disable-AzSentinelAlertRule](Disable-AzSentinelAlertRule.md) +8. [Get-AzSentinelIncident](Get-AzSentinelIncident.md) +9. [Update-AzSentinelIncident](Update-AzSentinelIncident.md) +10. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) +11. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) +12. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) +13. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) +14. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) +15. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) +16. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) From 19a63bda956c19db55be1c4e335529917fb71856 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Wed, 10 Jun 2020 10:39:35 +0200 Subject: [PATCH 15/53] New feature change the displayName of an alert (#68) * Release Rename Alert rule function * updating rename function --- AzSentinel/AzSentinel.psd1 | 1 + .../Public/Rename-AzSentinelAlertRule.ps1 | 95 +++++++++++++ AzSentinel/docs/README.md | 19 +-- AzSentinel/docs/Rename-AzSentinelAlertRule.md | 133 ++++++++++++++++++ docs/README.md | 19 +-- docs/Rename-AzSentinelAlertRule.md | 133 ++++++++++++++++++ 6 files changed, 382 insertions(+), 18 deletions(-) create mode 100644 AzSentinel/Public/Rename-AzSentinelAlertRule.ps1 create mode 100644 AzSentinel/docs/Rename-AzSentinelAlertRule.md create mode 100644 docs/Rename-AzSentinelAlertRule.md diff --git a/AzSentinel/AzSentinel.psd1 b/AzSentinel/AzSentinel.psd1 index a50a7b0..ae052e3 100644 --- a/AzSentinel/AzSentinel.psd1 +++ b/AzSentinel/AzSentinel.psd1 @@ -78,6 +78,7 @@ 'Set-AzSentinel', 'New-AzSentinelAlertRule', 'Get-AzSentinelAlertRule', + 'Rename-AzSentinelAlertRule', 'Enable-AzSentinelAlertRule', 'Disable-AzSentinelAlertRule', 'Import-AzSentinelAlertRule', diff --git a/AzSentinel/Public/Rename-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Rename-AzSentinelAlertRule.ps1 new file mode 100644 index 0000000..b2f15e6 --- /dev/null +++ b/AzSentinel/Public/Rename-AzSentinelAlertRule.ps1 @@ -0,0 +1,95 @@ +#requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} +#requires -version 6.2 + +function Rename-AzSentinelAlertRule { + <# + .SYNOPSIS + Rename Azure Sentinel Alert Rule + .DESCRIPTION + With this function you can rename Azure Sentinel Alert rule + .PARAMETER SubscriptionId + Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + .PARAMETER WorkspaceName + Enter the Workspace name + .PARAMETER CurrentRuleName + Enter the current name of the Alert rule + .PARAMETER NewRuleName + Enter the new name of the Alert rule + .EXAMPLE + Rename-AzSentinelAlertRule -WorkspaceName "" -CurrentRuleName "" -NewRuleName "" + In this example you can rename the alert rule + #> + + [cmdletbinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory = $false, + ParameterSetName = "Sub")] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$WorkspaceName, + + [Parameter(Mandatory = $true, + ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string]$CurrentRuleName, + + [Parameter(Mandatory = $true, + ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string]$NewRuleName + ) + + begin { + precheck + } + + process { + switch ($PsCmdlet.ParameterSetName) { + Sub { + $arguments = @{ + WorkspaceName = $WorkspaceName + SubscriptionId = $SubscriptionId + } + } + default { + $arguments = @{ + WorkspaceName = $WorkspaceName + } + } + } + + try { + $rule = Get-AzSentinelAlertRule @arguments -RuleName $CurrentRuleName -ErrorAction Stop + + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($currentRule.name)?api-version=2019-01-01-preview" + + $rule.displayName = $NewRuleName + + $bodyAlertProp = [AlertProp]::new( + ($rule | Select-Object * -ExcludeProperty lastModifiedUtc, etag, id) + ) + + $body = [AlertRule]::new( + ($rule | Select-Object lastModifiedUtc, etag, id, name), + $bodyAlertProp + ) + + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -EnumsAsStrings) + $return = "Successfully renamed rule $($CurrentRuleName) to $($NewRuleName) with status: $($result.StatusDescription)" + return $return + } + catch { + $return = $_.Exception.Message + Write-Error "Rename failed with error $return" + } + } + catch { + $return = $_.Exception.Message + Write-Error $return + } + } +} diff --git a/AzSentinel/docs/README.md b/AzSentinel/docs/README.md index 4f2eb17..e766892 100644 --- a/AzSentinel/docs/README.md +++ b/AzSentinel/docs/README.md @@ -7,12 +7,13 @@ 5. [Remove-AzSentinelAlertRule](Remove-AzSentinelAlertRule.md) 6. [Enable-AzSentinelAlertRule](Enable-AzSentinelAlertRule.md) 7. [Disable-AzSentinelAlertRule](Disable-AzSentinelAlertRule.md) -8. [Get-AzSentinelIncident](Get-AzSentinelIncident.md) -9. [Update-AzSentinelIncident](Update-AzSentinelIncident.md) -10. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) -11. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) -12. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) -13. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) -14. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) -15. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) -16. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) +8. [Rename-AzSentinelAlertRule](Rename-AzSentinelAlertRule.md) +9. [Get-AzSentinelIncident](Get-AzSentinelIncident.md) +10. [Update-AzSentinelIncident](Update-AzSentinelIncident.md) +11. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) +12. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) +13. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) +14. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) +15. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) +16. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) +17. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) diff --git a/AzSentinel/docs/Rename-AzSentinelAlertRule.md b/AzSentinel/docs/Rename-AzSentinelAlertRule.md new file mode 100644 index 0000000..bfcbcf9 --- /dev/null +++ b/AzSentinel/docs/Rename-AzSentinelAlertRule.md @@ -0,0 +1,133 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Rename-AzSentinelAlertRule + +## SYNOPSIS +Rename Azure Sentinel Alert Rule + +## SYNTAX + +``` +Rename-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName -CurrentRuleName + -NewRuleName [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +With this function you can rename Azure Sentinel Alert rule + +## EXAMPLES + +### EXAMPLE 1 +``` +Rename-AzSentinelAlertRule -WorkspaceName "" -CurrentRuleName "" -NewRuleName "" +In this example you can rename the alert rule +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -CurrentRuleName +Enter the current name of the Alert rule + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -NewRuleName +Enter the new name of the Alert rule + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/docs/README.md b/docs/README.md index 4f2eb17..e766892 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,12 +7,13 @@ 5. [Remove-AzSentinelAlertRule](Remove-AzSentinelAlertRule.md) 6. [Enable-AzSentinelAlertRule](Enable-AzSentinelAlertRule.md) 7. [Disable-AzSentinelAlertRule](Disable-AzSentinelAlertRule.md) -8. [Get-AzSentinelIncident](Get-AzSentinelIncident.md) -9. [Update-AzSentinelIncident](Update-AzSentinelIncident.md) -10. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) -11. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) -12. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) -13. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) -14. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) -15. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) -16. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) +8. [Rename-AzSentinelAlertRule](Rename-AzSentinelAlertRule.md) +9. [Get-AzSentinelIncident](Get-AzSentinelIncident.md) +10. [Update-AzSentinelIncident](Update-AzSentinelIncident.md) +11. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) +12. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) +13. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) +14. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) +15. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) +16. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) +17. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) diff --git a/docs/Rename-AzSentinelAlertRule.md b/docs/Rename-AzSentinelAlertRule.md new file mode 100644 index 0000000..bfcbcf9 --- /dev/null +++ b/docs/Rename-AzSentinelAlertRule.md @@ -0,0 +1,133 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Rename-AzSentinelAlertRule + +## SYNOPSIS +Rename Azure Sentinel Alert Rule + +## SYNTAX + +``` +Rename-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName -CurrentRuleName + -NewRuleName [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +With this function you can rename Azure Sentinel Alert rule + +## EXAMPLES + +### EXAMPLE 1 +``` +Rename-AzSentinelAlertRule -WorkspaceName "" -CurrentRuleName "" -NewRuleName "" +In this example you can rename the alert rule +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -CurrentRuleName +Enter the current name of the Alert rule + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -NewRuleName +Enter the new name of the Alert rule + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS From 472e0646dd0e90a39adb9ea83393cfa874c3bb8d Mon Sep 17 00:00:00 2001 From: stehod <34159548+stehod@users.noreply.github.com> Date: Fri, 26 Jun 2020 14:10:24 +0100 Subject: [PATCH 16/53] Handle nextLink for Playbooks (#78) When retrieving playbooks not all are being returned. Code copied from Issue #35 Retrieving all incidents. --- AzSentinel/Private/Get-AzSentinelPlayBook.ps1 | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/AzSentinel/Private/Get-AzSentinelPlayBook.ps1 b/AzSentinel/Private/Get-AzSentinelPlayBook.ps1 index 395d185..2e2cbb2 100644 --- a/AzSentinel/Private/Get-AzSentinelPlayBook.ps1 +++ b/AzSentinel/Private/Get-AzSentinelPlayBook.ps1 @@ -49,8 +49,13 @@ function Get-AzSentinelPlayBook { } try { - $playBook = (Invoke-RestMethod -Uri $uri -Method get -Headers $script:authHeader).value | Where-Object { $_.name -eq $Name } - + $logicappRaw = (Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader) + $logicapp = $logicappRaw.value + while ($logicappRaw.nextLink) { + $logicappRaw = (Invoke-RestMethod -Uri $($logicappRaw.nextLink) -Headers $script:authHeader -Method Get) + $logicapp += $logicappRaw.value + } + $playBook = $logicapp | Where-Object { $_.name -eq $Name } if ($playBook){ $uri1 = "https://management.azure.com$($playBook.id)/triggers/$($triggerName)/listCallbackUrl?api-version=2016-06-01" try { From 3272c3c50fa9ecdc3f7c54334140298aa70c8881 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Fri, 26 Jun 2020 16:44:01 +0200 Subject: [PATCH 17/53] adding support for alert aggregation (#65) * adding support for alert aggregation, classes created * updaing classes * updated the class and created first rule wih no error * update class and made import function backwards compatible * small changes * tested with import method * updating new function * checking working code, starting cleanup * updating documentation * updating docs and cleaning up * updating build errors * change pester version * updating pester version --- AzSentinel/Classes/AlertRule.ps1 | 112 +------------- AzSentinel/Classes/IncidentConfiguration.ps1 | 10 ++ AzSentinel/Classes/ScheduledAlertProp.ps1 | 101 +++++++++++++ AzSentinel/Classes/classes.psd1 | 3 + AzSentinel/Classes/groupingConfiguration.ps1 | 52 +++++++ .../Set-AzSentinelResourceProvider.ps1 | 4 + .../Public/Disable-AzSentinelAlertRule.ps1 | 5 +- .../Public/Enable-AzSentinelAlertRule.ps1 | 5 +- .../Public/Import-AzSentinelAlertRule.ps1 | 96 ++++++------ AzSentinel/Public/New-AzSentinelAlertRule.ps1 | 139 ++++++++++++------ .../Public/Rename-AzSentinelAlertRule.ps1 | 1 + AzSentinel/docs/Get-AzSentinelHuntingRule.md | 2 +- AzSentinel/docs/New-AzSentinelAlertRule.md | 115 ++++++++++++++- AzSentinel/enums/GroupByEntities.ps1 | 6 + AzSentinel/enums/MatchingMethod.ps1 | 5 + AzSentinel/enums/aggregationKind.ps1 | 4 + AzSentinel/tests/QA/module.tests.ps1 | 2 +- .../tests/Unit/classes/AlertRule.tests.ps1 | 58 ++++++++ ... Get-AzSentinelResourceProvider.tests.ps1} | 0 ... Set-AzSentinelResourceProvider.tests.ps1} | 0 .../Rename-AzSentinelAlertRule.tests.ps1 | 0 PSDepend.build.psd1 | 2 +- README.md | 53 +++++-- docs/Get-AzSentinelHuntingRule.md | 2 +- docs/New-AzSentinelAlertRule.md | 119 ++++++++++++++- examples/AlertRules.json | 33 +++++ 26 files changed, 703 insertions(+), 226 deletions(-) create mode 100644 AzSentinel/Classes/IncidentConfiguration.ps1 create mode 100644 AzSentinel/Classes/ScheduledAlertProp.ps1 create mode 100644 AzSentinel/Classes/groupingConfiguration.ps1 create mode 100644 AzSentinel/enums/GroupByEntities.ps1 create mode 100644 AzSentinel/enums/MatchingMethod.ps1 create mode 100644 AzSentinel/enums/aggregationKind.ps1 rename AzSentinel/tests/Unit/private/{Get-AzSentinelResourceProvider.ps1 => Get-AzSentinelResourceProvider.tests.ps1} (100%) rename AzSentinel/tests/Unit/private/{Set-AzSentinelResourceProvider.ps1 => Set-AzSentinelResourceProvider.tests.ps1} (100%) create mode 100644 AzSentinel/tests/Unit/public/Rename-AzSentinelAlertRule.tests.ps1 diff --git a/AzSentinel/Classes/AlertRule.ps1 b/AzSentinel/Classes/AlertRule.ps1 index 04b4065..6e2de2e 100644 --- a/AzSentinel/Classes/AlertRule.ps1 +++ b/AzSentinel/Classes/AlertRule.ps1 @@ -1,98 +1,3 @@ -class AlertProp { - - [guid] $Name - - [string] $DisplayName - - [string] $Description - - [Severity] $Severity - - [bool] $Enabled - - [string] $Query - - [string] $QueryFrequency - - [string] $QueryPeriod - - [TriggerOperator]$TriggerOperator - - [Int] $TriggerThreshold - - [string] $SuppressionDuration - - [bool] $SuppressionEnabled - - [Tactics[]] $Tactics - - [string] $PlaybookName - - hidden $properties - - static [string] TriggerOperatorSwitch([string]$value) { - switch ($value) { - "gt" { $value = "GreaterThan" } - "lt" { $value = "LessThan" } - "eq" { $value = "Equal" } - "ne" { $value = "NotEqual" } - default { $value } - } - return $value - } - - # Convert string to ISO_8601 format PdDThHmMsS - static [string] TimeString([string]$value) { - $value = $value.ToUpper() - # Return values already in ISO 8601 format - if ($value -match "PT.*|P.*D") { - return $value - } - # Format day time periods - if ($value -like "*D") { - return "P$value" - } - # Format hour and minute time periods - if ($value -match ".*[HM]") { - return "PT$value" - } - return $value - } - AlertProp ($properties) { - $this.name = $properties.Name - $this.DisplayName = $properties.DisplayName - $this.Description = $properties.Description - $this.Severity = $properties.Severity - $this.Enabled = $properties.Enabled - $this.Query = $properties.Query - $this.QueryFrequency = $properties.QueryFrequency - $this.QueryPeriod = $properties.QueryPeriod - $this.TriggerOperator = $properties.TriggerOperator - $this.TriggerThreshold = $properties.TriggerThreshold - $this.SuppressionDuration = $properties.SuppressionDuration - $this.SuppressionEnabled = $properties.SuppressionEnabled - $this.Tactics = $properties.Tactics - $this.PlaybookName = $properties.PlaybookName - } - - AlertProp ($Name, $DisplayName, $Description, $Severity, $Enabled, $Query, $QueryFrequency, $QueryPeriod, $TriggerOperator, $TriggerThreshold, $suppressionDuration, $suppressionEnabled, $Tactics, $PlaybookName) { - $this.name = $Name - $this.DisplayName = $DisplayName - $this.Description = $Description - $this.Severity = $Severity - $this.Enabled = $Enabled - $this.Query = $Query - $this.QueryFrequency = [AlertProp]::TimeString($QueryFrequency) - $this.QueryPeriod = [AlertProp]::TimeString($QueryPeriod) - $this.TriggerOperator = [AlertProp]::TriggerOperatorSwitch($TriggerOperator) - $this.TriggerThreshold = $TriggerThreshold - $this.SuppressionDuration = if ((! $null -eq $suppressionDuration) -or ( $false -eq $suppressionEnabled)) { [AlertProp]::TimeString($suppressionDuration) } else { "PT1H" } - $this.SuppressionEnabled = if ($suppressionEnabled) { $suppressionEnabled } else { $false } - $this.Tactics = $Tactics - $this.PlaybookName = $PlaybookName - } -} - class AlertRule { [guid] $Name @@ -100,24 +5,17 @@ class AlertRule { [string]$type - [AlertProp]$Properties - - [string]$Id + [string]$kind - $header + [pscustomobject]$Properties - AlertRule($header, $properties) { - $this.id = $header.Id - $this.type = 'Microsoft.SecurityInsights/alertRules' - $this.Name = $header.Name - $this.Etag = $header.Etag - $this.Properties = $properties - } + [string]$Id - AlertRule ([guid]$Name, [string]$Etag, [AlertProp]$Properties, $Id) { + AlertRule ($Name, $Etag, $Properties, $Id) { $this.id = $Id $this.type = 'Microsoft.SecurityInsights/alertRules' + $this.kind = 'Scheduled' $this.Name = $Name $this.Etag = $Etag $this.Properties = $Properties diff --git a/AzSentinel/Classes/IncidentConfiguration.ps1 b/AzSentinel/Classes/IncidentConfiguration.ps1 new file mode 100644 index 0000000..b44e8f6 --- /dev/null +++ b/AzSentinel/Classes/IncidentConfiguration.ps1 @@ -0,0 +1,10 @@ +class IncidentConfiguration { + [bool] $CreateIncident + + [GroupingConfiguration]$GroupingConfiguration + + IncidentConfiguration ($CreateIncident, $GroupingConfiguration) { + $this.createIncident = if ($null -ne $createIncident) { $createIncident } else { $true } + $this.groupingConfiguration = $GroupingConfiguration + } +} diff --git a/AzSentinel/Classes/ScheduledAlertProp.ps1 b/AzSentinel/Classes/ScheduledAlertProp.ps1 new file mode 100644 index 0000000..672154e --- /dev/null +++ b/AzSentinel/Classes/ScheduledAlertProp.ps1 @@ -0,0 +1,101 @@ +class ScheduledAlertProp { + + [guid] $Name + + [string] $DisplayName + + [string] $Description + + [Severity] $Severity + + [bool] $Enabled + + [string] $Query + + [string] $QueryFrequency + + [string] $QueryPeriod + + [TriggerOperator]$TriggerOperator + + [Int] $TriggerThreshold + + [string] $SuppressionDuration + + [bool] $SuppressionEnabled + + [Tactics[]] $Tactics + + [string] $PlaybookName + + [IncidentConfiguration]$IncidentConfiguration + + $queryResultsAggregationSettings + + hidden [AggregationKind]$aggregationKind + + static [string] TriggerOperatorSwitch([string]$value) { + switch ($value) { + "gt" { $value = "GreaterThan" } + "lt" { $value = "LessThan" } + "eq" { $value = "Equal" } + "ne" { $value = "NotEqual" } + default { $value } + } + return $value + } + + # Convert string to ISO_8601 format PdDThHmMsS + static [string] TimeString([string]$value) { + $value = $value.ToUpper() + # Return values already in ISO 8601 format + if ($value -match "PT.*|P.*D") { + return $value + } + # Format day time periods + if ($value -like "*D") { + return "P$value" + } + # Format hour and minute time periods + if ($value -match ".*[HM]") { + return "PT$value" + } + return $value + } + ScheduledAlertProp (){ + + } + + ScheduledAlertProp ($Name, $DisplayName, $Description, $Severity, $Enabled, $Query, $QueryFrequency, ` + $QueryPeriod, $TriggerOperator, $TriggerThreshold, $suppressionDuration, ` + $suppressionEnabled, $Tactics, $PlaybookName, $IncidentConfiguration, $aggregationKind) { + $this.name = $Name + $this.DisplayName = $DisplayName + $this.Description = $Description + $this.Severity = $Severity + $this.Enabled = $Enabled + $this.Query = $Query + $this.QueryFrequency = [ScheduledAlertProp]::TimeString($QueryFrequency) + $this.QueryPeriod = [ScheduledAlertProp]::TimeString($QueryPeriod) + $this.TriggerOperator = [ScheduledAlertProp]::TriggerOperatorSwitch($TriggerOperator) + $this.TriggerThreshold = $TriggerThreshold + $this.SuppressionDuration = if (($null -eq $suppressionDuration) -or ( $false -eq $suppressionEnabled)) { + "PT1H" + } + else { + if ( [ScheduledAlertProp]::TimeString($suppressionDuration) -ge [ScheduledAlertProp]::TimeString($QueryFrequency) ) { + [ScheduledAlertProp]::TimeString($suppressionDuration) + } + else { + Write-Error "Invalid Properties for Scheduled alert rule: 'suppressionDuration' should be greater than or equal to 'queryFrequency'" -ErrorAction Stop + } + } + $this.SuppressionEnabled = if ($suppressionEnabled) { $suppressionEnabled } else { $false } + $this.Tactics = $Tactics + $this.PlaybookName = $PlaybookName + $this.IncidentConfiguration = $IncidentConfiguration + $this.queryResultsAggregationSettings = @{ + aggregationKind = if ($aggregationKind) { $aggregationKind } else { "SingleAlert" } + } + } +} diff --git a/AzSentinel/Classes/classes.psd1 b/AzSentinel/Classes/classes.psd1 index cccf92d..980c7a3 100644 --- a/AzSentinel/Classes/classes.psd1 +++ b/AzSentinel/Classes/classes.psd1 @@ -1,5 +1,8 @@ @{ order = @( + ,'groupingConfiguration' + ,'IncidentConfiguration' + ,'ScheduledAlertProp' ,'AlertRule' ) } diff --git a/AzSentinel/Classes/groupingConfiguration.ps1 b/AzSentinel/Classes/groupingConfiguration.ps1 new file mode 100644 index 0000000..d842bd1 --- /dev/null +++ b/AzSentinel/Classes/groupingConfiguration.ps1 @@ -0,0 +1,52 @@ +class GroupingConfiguration { + [bool]$Enabled + + [bool]$reopenClosedIncident + + [string]$lookbackDuration + + [MatchingMethod]$entitiesMatchingMethod + + [GroupByEntities[]]$groupByEntities + + # Convert string to ISO_8601 format PdDThHmMsS + static [string] TimeString([string]$value) { + $value = $value.ToUpper() + # Return values already in ISO 8601 format + if ($value -match "PT.*|P.*D") { + return $value + } + # Format day time periods + if ($value -like "*D") { + return "P$value" + } + # Format hour and minute time periods + if ($value -match ".*[HM]") { + return "PT$value" + } + return $value + } + + groupingConfiguration ($properties) { + $this.enabled = $properties.enabled + $this.reopenClosedIncident = $properties.reopenClosedIncident + $this.lookbackDuration = $properties.lookbackDuration + $this.entitiesMatchingMethod = $properties.entitiesMatchingMethod + $this.groupByEntities = $properties.groupByEntities + } + + groupingConfiguration ($Enabled, $reopenClosedIncident, $lookbackDuration, $entitiesMatchingMethod, $groupByEntities) { + $this.enabled = if ($Enabled) { $null -ne $Enabled } else { $true } + $this.reopenClosedIncident = if ($null -ne $reopenClosedIncident) { $reopenClosedIncident } else { $false } + $this.lookbackDuration = if ($lookbackDuration) { [groupingConfiguration]::TimeString($lookbackDuration) } else { "PT5H" } + $this.entitiesMatchingMethod = if ($entitiesMatchingMethod) { $entitiesMatchingMethod } else { "All" } + $this.groupByEntities = if ($groupByEntities) { $groupByEntities } else { + @( + "Account", + "Ip", + "Host", + "Url" + ) + } + } +} diff --git a/AzSentinel/Private/Set-AzSentinelResourceProvider.ps1 b/AzSentinel/Private/Set-AzSentinelResourceProvider.ps1 index 78cd8ff..8c86d4b 100644 --- a/AzSentinel/Private/Set-AzSentinelResourceProvider.ps1 +++ b/AzSentinel/Private/Set-AzSentinelResourceProvider.ps1 @@ -1,6 +1,7 @@ #requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} #requires -version 6.2 + function Set-AzSentinelResourceProvider { <# .SYNOPSIS @@ -12,6 +13,8 @@ function Set-AzSentinelResourceProvider { .EXAMPLE Set-AzSentinelResourceProvider -NameSpace 'OperationsManagementOperationsManagement' #> + + [OutputType([String])] param ( [string]$NameSpace ) @@ -20,6 +23,7 @@ function Set-AzSentinelResourceProvider { try { $invokeReturn = Invoke-RestMethod -Method Post -Uri $uri -Headers $script:authHeader + Write-Verbose $invokeReturn do { $resourceProviderStatus = Get-AzSentinelResourceProvider -NameSpace $NameSpace } diff --git a/AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 index ee13f48..2f5fb45 100644 --- a/AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 @@ -54,7 +54,7 @@ function Disable-AzSentinelAlertRule { foreach ($rule in $rules) { if ($rule.enabled -eq $false) { - Write-Host "'$($rule.DisplayName)' already has status '$($rule.enabled)'" + Write-Output "'$($rule.DisplayName)' already has status '$($rule.enabled)'" } else { $rule.enabled = $false @@ -71,7 +71,8 @@ function Disable-AzSentinelAlertRule { try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) - Write-Host "Status of '$($rule.DisplayName)' changed to '$($rule.enabled)'" + Write-Verbose $result + Write-Output "Status of '$($rule.DisplayName)' changed to '$($rule.enabled)'" } catch { diff --git a/AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 index 459a01a..43826bd 100644 --- a/AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 @@ -54,7 +54,7 @@ function Enable-AzSentinelAlertRule { foreach ($rule in $rules) { if ($rule.enabled -eq $true) { - Write-Host "'$($rule.DisplayName)' already has status '$($rule.enabled)'" + Write-Output "'$($rule.DisplayName)' already has status '$($rule.enabled)'" } else { $rule.enabled = $true @@ -71,7 +71,8 @@ function Enable-AzSentinelAlertRule { try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) - Write-Host "Status of '$($rule.DisplayName)' changed to '$($rule.enabled)'" + Write-Verbose $result + Write-Output "Status of '$($rule.DisplayName)' changed to '$($rule.enabled)'" } catch { Write-Error $_.Exception.Message diff --git a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 index 5a4d2ca..8e56701 100644 --- a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 @@ -81,7 +81,6 @@ function Import-AzSentinelAlertRule { } } } - #Get-LogAnalyticWorkspace @arguments if ($SettingsFile.Extension -eq '.json') { try { @@ -103,10 +102,13 @@ function Import-AzSentinelAlertRule { Write-Verbose $_ Write-Error -Message 'Unable to convert yaml file' -ErrorAction Stop } - } else { + } + else { Write-Error -Message 'Unsupported extension for SettingsFile' -ErrorAction Stop } + $return = @() + foreach ($item in $analytics) { Write-Verbose -Message "Started with rule: $($item.displayName)" @@ -117,7 +119,7 @@ function Import-AzSentinelAlertRule { $content = Get-AzSentinelAlertRule @arguments -RuleName $($item.displayName) -ErrorAction SilentlyContinue if ($content) { - Write-Output "Rule $($item.displayName) exists in Azure Sentinel" + Write-Verbose "Rule $($item.displayName) exists in Azure Sentinel" $item | Add-Member -NotePropertyName name -NotePropertyValue $content.name -Force $item | Add-Member -NotePropertyName etag -NotePropertyValue $content.etag -Force @@ -138,9 +140,20 @@ function Import-AzSentinelAlertRule { Write-Verbose $_ Write-Error "Unable to connect to APi to get Analytic rules with message: $($_.Exception.Message)" -ErrorAction Stop } - try { - $bodyAlertProp = [AlertProp]::new( + + $groupingConfiguration = [GroupingConfiguration]::new( + $item.groupingConfiguration.enabled, + $item.groupingConfiguration.reopenClosedIncident, + $item.groupingConfiguration.lookbackDuration, + $item.groupingConfiguration.entitiesMatchingMethod, + $item.groupingConfiguration.groupByEntities + ) + $IncidentConfiguration = [IncidentConfiguration]::new( + $item.createIncident, + $groupingConfiguration + ) + $bodyAlertProp = [ScheduledAlertProp]::new( $item.name, $item.displayName, $item.description, @@ -154,73 +167,68 @@ function Import-AzSentinelAlertRule { $item.suppressionDuration, $item.suppressionEnabled, $item.Tactics, - $item.playbookName + $item.playbookName, + $IncidentConfiguration, + $item.aggregationKind ) $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id) - } catch { - Write-Error "Unable to initiate class with error: $($_.Exception.Message)" -ErrorAction Continue + Write-Error "Unable to initiate class with error: $($_.Exception.Message)" -ErrorAction Stop } + if ($content) { - if ($item.playbookName) { - $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name) + + if ($item.playbookName -or $content.playbookName) { + $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id,incidentConfiguration, queryResultsAggregationSettings) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id,incidentConfiguration, queryResultsAggregationSettings) } else { - $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id, PlaybookName) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name, PlaybookName) + $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id, PlaybookName, incidentConfiguration, queryResultsAggregationSettings) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name, PlaybookName, incidentConfiguration, queryResultsAggregationSettings) } - if ($compareResult) { - Write-Output "Found Differences for rule: $($item.displayName)" - Write-Output ($compareResult | Format-Table | Out-String) - - if ($PSCmdlet.ShouldProcess("Do you want to update profile: $($body.Properties.DisplayName)")) { - try { - $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | Select-Object * -ExcludeProperty Properties.PlaybookName | ConvertTo-Json -EnumsAsStrings) - - if (($compareResult | Where-Object PropertyName -eq "playbookName").DiffValue) { - New-AzSentinelAlertRuleAction @arguments -PlayBookName ($body.Properties.playbookName) -RuleId $($body.Name) - } - elseif (($compareResult | Where-Object PropertyName -eq "playbookName").RefValue) { - Remove-AzSentinelAlertRuleAction @arguments -RuleId $($body.Name) -Confirm:$false - } - else { - #nothing - } - Write-Output "Successfully updated rule: $($item.displayName) with status: $($result.StatusDescription)" - Write-Output ($body.Properties | Format-List | Format-Table | Out-String) - } - catch { - Write-Verbose $_ - Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Continue - } + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | Select-Object * -ExcludeProperty Properties.PlaybookName | ConvertTo-Json -Depth 10 -EnumsAsStrings) + + if (($compareResult | Where-Object PropertyName -eq "playbookName").DiffValue) { + New-AzSentinelAlertRuleAction @arguments -PlayBookName ($body.Properties.playbookName) -RuleId $($body.Name) + } + elseif (($compareResult | Where-Object PropertyName -eq "playbookName").RefValue) { + Remove-AzSentinelAlertRuleAction @arguments -RuleId $($body.Name) -Confirm:$false } else { - Write-Output "No change have been made for rule $($item.displayName), deployment aborted" + #nothing } + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + $return += $body.Properties } - else { - Write-Output "Rule $($item.displayName) is compliance, nothing to do" - Write-Output ($body.Properties | Format-List | Format-Table | Out-String) + catch { + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue "failed" -Force + $return += $body.Properties + + Write-Verbose $_ + Write-Error "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue } } else { Write-Verbose "Creating new rule: $($item.displayName)" try { - $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | Select-Object * -ExcludeProperty Properties.PlaybookName | ConvertTo-Json -EnumsAsStrings) + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | Select-Object * -ExcludeProperty Properties.PlaybookName | ConvertTo-Json -Depth 10 -EnumsAsStrings) if ($body.Properties.playbookName) { New-AzSentinelAlertRuleAction @arguments -PlayBookName $($body.Properties.playbookName) -RuleId $($body.Properties.Name) -confirm:$false } - - Write-Output "Successfully created rule: $($item.displayName) with status: $($result.StatusDescription)" - Write-Output ($body.Properties | Format-List | Format-Table | Out-String) + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + $return += $body.Properties } catch { + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue "failed" -Force + $return += $body.Properties + Write-Verbose $_ - Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Continue + Write-Error "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue } } } + return $return } } diff --git a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 index 947ef05..33b23e1 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 @@ -37,6 +37,20 @@ function New-AzSentinelAlertRule { Enter the Tactics, valid values: "InitialAccess", "Persistence", "Execution", "PrivilegeEscalation", "DefenseEvasion", "CredentialAccess", "LateralMovement", "Discovery", "Collection", "Exfiltration", "CommandAndControl", "Impact" .PARAMETER PlaybookName Enter the Logic App name that you want to configure as playbook trigger + .PARAMETER CreateIncident + Create incidents from alerts triggered by this analytics rule + .PARAMETER GroupingConfigurationEnabled + Group related alerts, triggered by this analytics rule, into incidents + .PARAMETER ReopenClosedIncident + Re-open closed matching incidents + .PARAMETER LookbackDuration + Limit the group to alerts created within the selected time frame + .PARAMETER EntitiesMatchingMethod + Group alerts triggered by this analytics rule into a single incident by + .PARAMETER GroupByEntities + Grouping alerts into a single incident if the selected entities match: + .PARAMETER AggregationKind + Configure how rule query results are grouped into alerts .EXAMPLE New-AzSentinelAlertRule -WorkspaceName "" -DisplayName "" -Description "" -Severity -Enabled $true -Query '' -QueryFrequency "" -QueryPeriod "" -TriggerOperator -TriggerThreshold -SuppressionDuration "" -SuppressionEnabled $false -Tactics @("","") -PlaybookName "" In this example you create a new Alert rule by defining the rule properties from CMDLET @@ -44,8 +58,7 @@ function New-AzSentinelAlertRule { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param ( - [Parameter(Mandatory = $false, - ParameterSetName = "Sub")] + [Parameter(Mandatory = $false, ParameterSetName = "Sub")] [ValidateNotNullOrEmpty()] [string] $SubscriptionId, @@ -96,16 +109,47 @@ function New-AzSentinelAlertRule { [Parameter(Mandatory)] [AllowEmptyCollection()] - [Tactics[]] $Tactics, + #[Tactics[]] $Tactics, + [string[]] $Tactics, [Parameter(Mandatory = $false)] [AllowEmptyString()] - [string] $PlaybookName = $null + [string] $PlaybookName = $null, + + [Parameter(Mandatory = $false)] + [AllowEmptyString()] + [bool]$CreateIncident, + + [Parameter(Mandatory = $false)] + [AllowEmptyString()] + [bool]$GroupingConfigurationEnabled, + + [Parameter(Mandatory = $false)] + [AllowEmptyString()] + [bool]$ReopenClosedIncident, + + [Parameter(Mandatory = $false)] + [AllowEmptyString()] + [string]$LookbackDuration, + + [Parameter(Mandatory = $false)] + [AllowEmptyString()] + [MatchingMethod]$EntitiesMatchingMethod, + + [Parameter(Mandatory = $false)] + [AllowEmptyString()] + #[groupByEntities[]]$GroupByEntities, + [string[]]$GroupByEntities, + + [Parameter(Mandatory = $false)] + [AllowEmptyString()] + [string]$AggregationKind ) begin { precheck } + process { switch ($PsCmdlet.ParameterSetName) { Sub { @@ -120,7 +164,6 @@ function New-AzSentinelAlertRule { } } } - Get-LogAnalyticWorkspace @arguments $item = @{ } @@ -156,7 +199,20 @@ function New-AzSentinelAlertRule { } try { - $bodyAlertProp = [AlertProp]::new( + $groupingConfiguration = [GroupingConfiguration]::new( + $GroupingConfigurationEnabled, + $ReopenClosedIncident, + $LookbackDuration, + $EntitiesMatchingMethod, + $GroupByEntities + ) + + $incidentConfiguration = [IncidentConfiguration]::new( + $CreateIncident, + $groupingConfiguration + ) + + $bodyAlertProp = [ScheduledAlertProp]::new( $item.name, $DisplayName, $Description, @@ -170,8 +226,11 @@ function New-AzSentinelAlertRule { $SuppressionDuration, $SuppressionEnabled, $Tactics, - $PlaybookName + $PlaybookName, + $incidentConfiguration, + $AggregationKind ) + $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id) } catch { @@ -179,61 +238,59 @@ function New-AzSentinelAlertRule { } if ($content) { - if ($PlaybookName) { + if ($PlaybookName -or $content.playbookName) { $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name) } else { $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id, PlaybookName) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name, PlaybookName) - } if ($compareResult) { - Write-Output "Found Differences for rule: $($DisplayName)" - Write-Output ($compareResult | Format-Table | Out-String) - - if ($PSCmdlet.ShouldProcess("Do you want to update profile: $($body.Properties.DisplayName)")) { - try { - $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -EnumsAsStrings) - - if (($compareResult | Where-Object PropertyName -eq "playbookName").DiffValue) { - New-AzSentinelAlertRuleAction @arguments -PlayBookName ($body.Properties.playbookName) -RuleId $($body.Name) - } - elseif (($compareResult | Where-Object PropertyName -eq "playbookName").RefValue) { - Remove-AzSentinelAlertRuleAction @arguments -RuleId $($body.Name) -Confirm:$false - } - else { - #nothing - } - - Write-Output "Successfully updated rule: $($DisplayName) with status: $($result.StatusDescription)" - Write-Output ($body.Properties | Format-List | Format-Table | Out-String) - } - catch { - Write-Verbose $_ - Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Stop - } + } + + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) + + if (($compareResult | Where-Object PropertyName -eq "playbookName").DiffValue) { + New-AzSentinelAlertRuleAction @arguments -PlayBookName ($body.Properties.playbookName) -RuleId $($body.Name) + } + elseif (($compareResult | Where-Object PropertyName -eq "playbookName").RefValue) { + Remove-AzSentinelAlertRuleAction @arguments -RuleId $($body.Name) -Confirm:$false } else { - Write-Output "No change have been made for rule $($DisplayName), deployment aborted" + #nothing } + + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + $return += $body.Properties + return $return } - else { - Write-Output "Rule $($DisplayName) is compliance, nothing to do" - Write-Output ($body.Properties | Format-List | Format-Table | Out-String) + catch { + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue "failed" -Force + $return += $body.Properties + return $return + + Write-Verbose $_ + Write-Error "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue } } else { Write-Verbose "Creating new rule: $($DisplayName)" try { - $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -EnumsAsStrings) + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) if (($body.Properties.PlaybookName)) { New-AzSentinelAlertRuleAction @arguments -PlayBookName ($body.Properties.PlaybookName) -RuleId $($body.Properties.Name) -confirm:$false } - Write-Output "Successfully created rule: $($DisplayName) with status: $($result.StatusDescription)" - Write-Output ($body.Properties | Format-List | Format-Table | Out-String) + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + $return += $body.Properties + return $return } catch { + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue "failed" -Force + $return += $body.Properties + return $return + Write-Verbose $_ - Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Stop + Write-Error "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue } } } diff --git a/AzSentinel/Public/Rename-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Rename-AzSentinelAlertRule.ps1 index b2f15e6..80b6207 100644 --- a/AzSentinel/Public/Rename-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Rename-AzSentinelAlertRule.ps1 @@ -21,6 +21,7 @@ function Rename-AzSentinelAlertRule { #> [cmdletbinding(SupportsShouldProcess)] + [OutputType([String])] param ( [Parameter(Mandatory = $false, ParameterSetName = "Sub")] diff --git a/AzSentinel/docs/Get-AzSentinelHuntingRule.md b/AzSentinel/docs/Get-AzSentinelHuntingRule.md index b4fdb96..2ec6bd8 100644 --- a/AzSentinel/docs/Get-AzSentinelHuntingRule.md +++ b/AzSentinel/docs/Get-AzSentinelHuntingRule.md @@ -93,7 +93,7 @@ Aliases: Required: False Position: Named Default value: None -Accept pipeline input: False +Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` diff --git a/AzSentinel/docs/New-AzSentinelAlertRule.md b/AzSentinel/docs/New-AzSentinelAlertRule.md index fcaf08c..778b1b8 100644 --- a/AzSentinel/docs/New-AzSentinelAlertRule.md +++ b/AzSentinel/docs/New-AzSentinelAlertRule.md @@ -16,8 +16,10 @@ Create Azure Sentinal Alert Rules New-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName -DisplayName -Description -Severity -Enabled -Query -QueryFrequency [-QueryPeriod ] -TriggerOperator -TriggerThreshold - -SuppressionDuration -SuppressionEnabled -Tactics [-PlaybookName ] - [-WhatIf] [-Confirm] [] + -SuppressionDuration -SuppressionEnabled -Tactics [-PlaybookName ] + [-CreateIncident ] [-GroupingConfigurationEnabled ] [-ReopenClosedIncident ] + [-LookbackDuration ] [-EntitiesMatchingMethod ] [-GroupByEntities ] + [-AggregationKind ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -234,10 +236,9 @@ Accept wildcard characters: False Enter the Tactics, valid values: "InitialAccess", "Persistence", "Execution", "PrivilegeEscalation", "DefenseEvasion", "CredentialAccess", "LateralMovement", "Discovery", "Collection", "Exfiltration", "CommandAndControl", "Impact" ```yaml -Type: Tactics[] +Type: String[] Parameter Sets: (All) Aliases: -Accepted values: InitialAccess, Persistence, Execution, PrivilegeEscalation, DefenseEvasion, CredentialAccess, LateralMovement, Discovery, Collection, Exfiltration, CommandAndControl, Impact Required: True Position: Named @@ -261,6 +262,112 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -CreateIncident +Create incidents from alerts triggered by this analytics rule + +```yaml +Type: Boolean +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -GroupingConfigurationEnabled +Group related alerts, triggered by this analytics rule, into incidents + +```yaml +Type: Boolean +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ReopenClosedIncident +Re-open closed matching incidents + +```yaml +Type: Boolean +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LookbackDuration +Limit the group to alerts created within the selected time frame + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -EntitiesMatchingMethod +Group alerts triggered by this analytics rule into a single incident by + +```yaml +Type: MatchingMethod +Parameter Sets: (All) +Aliases: +Accepted values: All, None, Custom + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -GroupByEntities +Grouping alerts into a single incident if the selected entities match: + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -AggregationKind +Configure how rule query results are grouped into alerts + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. diff --git a/AzSentinel/enums/GroupByEntities.ps1 b/AzSentinel/enums/GroupByEntities.ps1 new file mode 100644 index 0000000..05a6441 --- /dev/null +++ b/AzSentinel/enums/GroupByEntities.ps1 @@ -0,0 +1,6 @@ +enum GroupByEntities { + Account + Ip + Host + Url +} diff --git a/AzSentinel/enums/MatchingMethod.ps1 b/AzSentinel/enums/MatchingMethod.ps1 new file mode 100644 index 0000000..40ce4a8 --- /dev/null +++ b/AzSentinel/enums/MatchingMethod.ps1 @@ -0,0 +1,5 @@ +enum MatchingMethod { + All + None + Custom +} diff --git a/AzSentinel/enums/aggregationKind.ps1 b/AzSentinel/enums/aggregationKind.ps1 new file mode 100644 index 0000000..950314d --- /dev/null +++ b/AzSentinel/enums/aggregationKind.ps1 @@ -0,0 +1,4 @@ +enum AggregationKind { + SingleAlert + AlertPerRow +} diff --git a/AzSentinel/tests/QA/module.tests.ps1 b/AzSentinel/tests/QA/module.tests.ps1 index 6e47bf1..2fd8376 100644 --- a/AzSentinel/tests/QA/module.tests.ps1 +++ b/AzSentinel/tests/QA/module.tests.ps1 @@ -46,7 +46,7 @@ foreach ($function in $allModuleFunctions) { It "Script Analyzer for $($function.BaseName)" { forEach ($scriptAnalyzerRule in $scriptAnalyzerRules) { $PSSAResult = (Invoke-ScriptAnalyzer -Path $function.FullName -IncludeRule $scriptAnalyzerRule) - ($PSSAResult | Select-Object Message,Line | Out-String) | Should -BeNullOrEmpty + ($PSSAResult | Select-Object Message,Line | Out-String) | Should BeNullOrEmpty } } } diff --git a/AzSentinel/tests/Unit/classes/AlertRule.tests.ps1 b/AzSentinel/tests/Unit/classes/AlertRule.tests.ps1 index e69de29..4b9e1e3 100644 --- a/AzSentinel/tests/Unit/classes/AlertRule.tests.ps1 +++ b/AzSentinel/tests/Unit/classes/AlertRule.tests.ps1 @@ -0,0 +1,58 @@ +Describe "Testing mocking" { + it "Mock test" { + class Mock : ChocoClass { + [string] FunctionToMock() { return "mystring" } + } + $package = New-Object Mock + $expected = $package.OutputToOverwrite() + $expected | should BeExactly "mystring" + } +} + + + +Describe ScheduledAlertProp { + Context 'Constructors' { + It 'Class groupingConfiguration has a constructor' { + + $groupingConfiguration = [groupingConfiguration]::new( + $true, + $false, + "PT5H", + "All", + @( + "Account", + "Ip", + "Host", + "Url" + ) + ) + $groupingConfiguration | Should -Not -BeNullOrEmpty + } + } + Context 'Constructors' { + It 'Class IncidentConfiguration has a constructor' { + + $groupingConfiguration = [groupingConfiguration]::new( + $true, + $false, + "PT5H", + "All", + @( + "Account", + "Ip", + "Host", + "Url" + ) + ) + $groupingConfiguration | Should -Not -BeNullOrEmpty + + + $IncidentConfiguration = [IncidentConfiguration]::new( + $true, + $groupingConfiguration + ) + $IncidentConfiguration | Should -Not -BeNullOrEmpty + } + } +} diff --git a/AzSentinel/tests/Unit/private/Get-AzSentinelResourceProvider.ps1 b/AzSentinel/tests/Unit/private/Get-AzSentinelResourceProvider.tests.ps1 similarity index 100% rename from AzSentinel/tests/Unit/private/Get-AzSentinelResourceProvider.ps1 rename to AzSentinel/tests/Unit/private/Get-AzSentinelResourceProvider.tests.ps1 diff --git a/AzSentinel/tests/Unit/private/Set-AzSentinelResourceProvider.ps1 b/AzSentinel/tests/Unit/private/Set-AzSentinelResourceProvider.tests.ps1 similarity index 100% rename from AzSentinel/tests/Unit/private/Set-AzSentinelResourceProvider.ps1 rename to AzSentinel/tests/Unit/private/Set-AzSentinelResourceProvider.tests.ps1 diff --git a/AzSentinel/tests/Unit/public/Rename-AzSentinelAlertRule.tests.ps1 b/AzSentinel/tests/Unit/public/Rename-AzSentinelAlertRule.tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/PSDepend.build.psd1 b/PSDepend.build.psd1 index 11aa62e..5a82145 100644 --- a/PSDepend.build.psd1 +++ b/PSDepend.build.psd1 @@ -9,7 +9,7 @@ BuildHelpers = 'latest' InvokeBuild = 'latest' - Pester = 'latest' + Pester = '4.10.1' PSScriptAnalyzer = 'latest' platyPS = 'latest' PSDeploy = 'latest' diff --git a/README.md b/README.md index a38f29c..da3b0a0 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,21 @@ To create a Azure Sentinel Rule, use the following JSON format. "LateralMovement", "Collection" ], - "playbookName": "string" + "playbookName": "string", + "aggregationKind": "string", + "createIncident": true, + "groupingConfiguration": { + "GroupingConfigurationEnabled": true, + "reopenClosedIncident": true, + "lookbackDuration": "PT6H", + "entitiesMatchingMethod": "string", + "groupByEntities": [ + "Account", + "Ip", + "Host", + "Url" + ] + } } ] } @@ -69,21 +83,28 @@ To create a Azure Sentinel Rule, use the following JSON format. The following tables describe the values you need to set in the schema. -| Name | Type | Required | Allowed Values | Example | -| ------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -| displayName | string | true | * | DisplayName | -| description | string | true | * | Description | -| severity | string | true | Medium, High, Low, Informational | Medium | -| enabled | bool | true | true, false | true | -| query | string | true | special character need to be escaped by \ | SecurityEvent \| where EventID == \"4688\" \| where CommandLine contains \\"-noni -ep bypass $\\" | -| queryFrequency | string | true | Value must be greater than 5 minutes | 30M | -| queryPeriod | string | true | Value must be greater than 5 minutes | 6H | -| triggerOperator | string | true | GreaterThan, FewerThan, EqualTo, NotEqualTo | GreaterThan | -| triggerThreshold | int | true | The value must be between 0 and 10000 | 5 | -| suppressionDuration | string | true | Value must be greater than 5 minutes | 1D | -| suppressionEnabled | bool | true | true, false | true | -| tactics | array | true | InitialAccess, Persistence,Execution,PrivilegeEscalation,DefenseEvasion,CredentialAccess,LateralMovement,Discovery,Collection,Exfiltration,CommandAndControl,Impact | true | -| playbookName | string | false | Enter the Logic App name that you want to configure as playbook trigger | LogicApp01 | +| Name | Type | Required | Allowed Values | Example | +| ---------------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | +| displayName | string | true | * | DisplayName | +| description | string | true | * | Description | +| severity | string | true | Medium, High, Low, Informational | Medium | +| enabled | bool | true | true, false | true | +| query | string | true | special character need to be escaped by \ | SecurityEvent \| where EventID == \"4688\" \| where CommandLine contains \\"-noni -ep bypass $\\" | +| queryFrequency | string | true | Value must be between 5 minutes and 24 hours | 30M | +| queryPeriod | string | true | Value must be between 5 minutes and 14 days | 6H | +| triggerOperator | string | true | GreaterThan, FewerThan, EqualTo, NotEqualTo | GreaterThan | +| triggerThreshold | int | true | The value must be between 0 and 10000 | 5 | +| suppressionDuration | string | true | Value must be greater than 5 minutes | 1D | +| suppressionEnabled | bool | true | true, false | true | +| tactics | array | true | InitialAccess, Persistence,Execution,PrivilegeEscalation,DefenseEvasion,CredentialAccess,LateralMovement,Discovery,Collection,Exfiltration,CommandAndControl,Impact | true | +| playbookName | string | false | Enter the Logic App name that you want to configure as playbook trigger | LogicApp01 | +| aggregationKind | string | false | SingleAlert, AlertPerRow | SingleAlert | +| createIncident | bool | false | true, false | true | +| GroupingConfigurationEnabled | bool | false | true, false | true | +| reopenClosedIncident | bool | false | true, false | true | +| lookbackDuration | string | false | Value must be between 5 minutes and 24 hours. | PT6H | +| entitiesMatchingMethod | string | false | All, None, Custom | All | +| groupByEntities | string | false | Account, Ip, Host, Url | Account | ## Find us diff --git a/docs/Get-AzSentinelHuntingRule.md b/docs/Get-AzSentinelHuntingRule.md index b4fdb96..2ec6bd8 100644 --- a/docs/Get-AzSentinelHuntingRule.md +++ b/docs/Get-AzSentinelHuntingRule.md @@ -93,7 +93,7 @@ Aliases: Required: False Position: Named Default value: None -Accept pipeline input: False +Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` diff --git a/docs/New-AzSentinelAlertRule.md b/docs/New-AzSentinelAlertRule.md index fe704f3..778b1b8 100644 --- a/docs/New-AzSentinelAlertRule.md +++ b/docs/New-AzSentinelAlertRule.md @@ -16,8 +16,10 @@ Create Azure Sentinal Alert Rules New-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName -DisplayName -Description -Severity -Enabled -Query -QueryFrequency [-QueryPeriod ] -TriggerOperator -TriggerThreshold - -SuppressionDuration -SuppressionEnabled -Tactics [-PlaybookName ] - [-WhatIf] [-Confirm] [] + -SuppressionDuration -SuppressionEnabled -Tactics [-PlaybookName ] + [-CreateIncident ] [-GroupingConfigurationEnabled ] [-ReopenClosedIncident ] + [-LookbackDuration ] [-EntitiesMatchingMethod ] [-GroupByEntities ] + [-AggregationKind ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -140,7 +142,7 @@ Accept wildcard characters: False ``` ### -QueryFrequency -Enter the query period, exmaple: 5H, 5M, 5D (H stands for Hour, M stands for Minute and D stands for Day) +Enter the query frequency, example: 5H, 5M, 5D (H stands for Hour, M stands for Minute and D stands for Day) ```yaml Type: String @@ -201,7 +203,7 @@ Accept wildcard characters: False ``` ### -SuppressionDuration -Enter the query period, exmaple: 5H, 5M, 5D (H stands for Hour, M stands for Minute and D stands for Day) +Enter the suppression duration, example: 5H, 5M, 5D (H stands for Hour, M stands for Minute and D stands for Day) ```yaml Type: String @@ -234,10 +236,9 @@ Accept wildcard characters: False Enter the Tactics, valid values: "InitialAccess", "Persistence", "Execution", "PrivilegeEscalation", "DefenseEvasion", "CredentialAccess", "LateralMovement", "Discovery", "Collection", "Exfiltration", "CommandAndControl", "Impact" ```yaml -Type: Tactics[] +Type: String[] Parameter Sets: (All) Aliases: -Accepted values: InitialAccess, Persistence, Execution, PrivilegeEscalation, DefenseEvasion, CredentialAccess, LateralMovement, Discovery, Collection, Exfiltration, CommandAndControl, Impact Required: True Position: Named @@ -261,6 +262,112 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -CreateIncident +Create incidents from alerts triggered by this analytics rule + +```yaml +Type: Boolean +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -GroupingConfigurationEnabled +Group related alerts, triggered by this analytics rule, into incidents + +```yaml +Type: Boolean +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ReopenClosedIncident +Re-open closed matching incidents + +```yaml +Type: Boolean +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LookbackDuration +Limit the group to alerts created within the selected time frame + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -EntitiesMatchingMethod +Group alerts triggered by this analytics rule into a single incident by + +```yaml +Type: MatchingMethod +Parameter Sets: (All) +Aliases: +Accepted values: All, None, Custom + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -GroupByEntities +Grouping alerts into a single incident if the selected entities match: + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -AggregationKind +Configure how rule query results are grouped into alerts + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. diff --git a/examples/AlertRules.json b/examples/AlertRules.json index ddf76fa..15ed4db 100644 --- a/examples/AlertRules.json +++ b/examples/AlertRules.json @@ -17,6 +17,39 @@ "LateralMovement", "Collection" ], + "playbookName": "Playbook01", + "aggregationKind": "SingleAlert", + "createIncident": true, + "groupingConfiguration": { + "enabled": false, + "reopenClosedIncident": false, + "lookbackDuration": "PT5H", + "entitiesMatchingMethod": "All", + "groupByEntities": [ + "Account", + "Ip", + "Host", + "Url" + ] + } + }, + { + "displayName": "AlertRule02", + "description": "", + "severity": "Medium", + "enabled": true, + "query": "SecurityEvent | where EventID == \"4688\" | where CommandLine contains \"-noni -ep bypass $\"", + "queryFrequency": "5H", + "queryPeriod": "6H", + "triggerOperator": "GreaterThan", + "triggerThreshold": 5, + "suppressionDuration": "6H", + "suppressionEnabled": false, + "tactics": [ + "Persistence", + "LateralMovement", + "Collection" + ], "playbookName": "Playbook01" } ] From eb36838300da6eefb3261ad75893b1e1135eae15 Mon Sep 17 00:00:00 2001 From: ThijsLecomte <42153270+ThijsLecomte@users.noreply.github.com> Date: Thu, 27 Aug 2020 11:26:43 +0200 Subject: [PATCH 18/53] Update groupingConfiguration.ps1 (#87) --- AzSentinel/Classes/groupingConfiguration.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AzSentinel/Classes/groupingConfiguration.ps1 b/AzSentinel/Classes/groupingConfiguration.ps1 index d842bd1..5be2398 100644 --- a/AzSentinel/Classes/groupingConfiguration.ps1 +++ b/AzSentinel/Classes/groupingConfiguration.ps1 @@ -36,7 +36,7 @@ class GroupingConfiguration { } groupingConfiguration ($Enabled, $reopenClosedIncident, $lookbackDuration, $entitiesMatchingMethod, $groupByEntities) { - $this.enabled = if ($Enabled) { $null -ne $Enabled } else { $true } + $this.enabled = if ($null -ne $Enabled ) { $Enabled } else { $true } $this.reopenClosedIncident = if ($null -ne $reopenClosedIncident) { $reopenClosedIncident } else { $false } $this.lookbackDuration = if ($lookbackDuration) { [groupingConfiguration]::TimeString($lookbackDuration) } else { "PT5H" } $this.entitiesMatchingMethod = if ($entitiesMatchingMethod) { $entitiesMatchingMethod } else { "All" } From 4d9376a8c269afb7fb7876aea9849c0921d45f1c Mon Sep 17 00:00:00 2001 From: Jonathan Holtmann Date: Mon, 7 Sep 2020 00:30:46 -0700 Subject: [PATCH 19/53] Fix bug that causes loss of certain incident properties, add option to set incident description (#91) --- .../Public/Update-AzSentinelIncident.ps1 | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/AzSentinel/Public/Update-AzSentinelIncident.ps1 b/AzSentinel/Public/Update-AzSentinelIncident.ps1 index e4029ee..6449343 100644 --- a/AzSentinel/Public/Update-AzSentinelIncident.ps1 +++ b/AzSentinel/Public/Update-AzSentinelIncident.ps1 @@ -74,7 +74,11 @@ function Update-AzSentinelIncident { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] - [string]$ClosedReasonText + [string]$ClosedReasonText, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$Description ) begin { @@ -119,26 +123,24 @@ function Update-AzSentinelIncident { "endTimeUtc" = $($incident.endTimeUtc) "lastUpdatedTimeUtc" = $($incident.lastUpdatedTimeUtc) "lastComment" = "" - "totalComments" = 0 - "metrics" = @{ } - [pscustomobject]"relatedAlertIds" = @( - ) - [pscustomobject]"relatedAlertProductNames" = @( - ) + "totalComments" = $incident.TotalComments + "metrics" = $incident.Metrics + "relatedAlertIds" = $incident.RelatedAlertIds + "relatedAlertProductNames" = $incident.RelatedAlertProductNames "severity" = if ($Severity) { $Severity } else { $incident.severity } "startTimeUtc" = $($incident.startTimeUtc) "status" = if ($Status) { $Status } else { $incident.status } - "closeReason" = if ($Status -eq 'Closed') { if ($null -ne [CloseReason]$CloseReason) { $CloseReason } else { Write-Error "No Close Reasen provided" -ErrorAction Stop } } else { $null } + "closeReason" = if ($Status -eq 'Closed') { if ($null -ne [CloseReason]$CloseReason) { $CloseReason } else { Write-Error "No close reason provided" -ErrorAction Stop } } else { $null } "closedReasonText" = if ($Status -eq 'Closed') { if ($ClosedReasonText) { $ClosedReasonText } else { Write-Error 'No closed comment provided' } } else { $null } [pscustomobject]"labels" = @( $LabelsUnique) "title" = $($incident.title) - "description" = "" - "firstAlertTimeGenerated" = "" - "lastAlertTimeGenerated" = "" + "description" = if ($Description) { $Description } else { $incident.Description } + "firstAlertTimeGenerated" = $incident.FirstAlertTimeGenerated + "lastAlertTimeGenerated" = $incident.LastAlertTimeGenerated "owner" = @{ - "name" = $null - "email" = $null - "objectId" = $null + "name" = $incident.Owner.Name + "email" = $incident.Owner.Email + "objectId" = $incident.Owner.ObjectId } } } From f86f8d33f8eda1a6fd32682707b254900738c4ae Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Tue, 15 Sep 2020 17:18:10 +0200 Subject: [PATCH 20/53] Feature - Adding support for all alert rule types (#90) * init release * updating docs Co-authored-by: Khabazi --- AzSentinel/Classes/AlertRule.ps1 | 6 +- AzSentinel/Classes/Fusion.ps1 | 9 + AzSentinel/Classes/MLBehaviorAnalytics.ps1 | 9 + .../MicrosoftSecurityIncidentCreation.ps1 | 17 + AzSentinel/Classes/classes.psd1 | 3 + AzSentinel/Public/Get-AzSentinelAlertRule.ps1 | 55 ++- .../Public/Import-AzSentinelAlertRule.ps1 | 253 ++++++++++-- AzSentinel/Public/New-AzSentinelAlertRule.ps1 | 375 ++++++++++++------ .../Public/New-AzSentinelAlertRuleAction.ps1 | 2 +- .../Remove-AzSentinelAlertRuleAction.ps1 | 2 +- AzSentinel/docs/Get-AzSentinelAlertRule.md | 20 +- AzSentinel/docs/New-AzSentinelAlertRule.md | 144 +++++-- AzSentinel/docs/Rename-AzSentinelAlertRule.md | 1 + AzSentinel/enums/Kind.ps1 | 6 + README.md | 163 ++++++-- docs/Get-AzSentinelAlertRule.md | 20 +- docs/New-AzSentinelAlertRule.md | 144 +++++-- docs/Rename-AzSentinelAlertRule.md | 1 + examples/AlertRules.json | 30 +- 19 files changed, 999 insertions(+), 261 deletions(-) create mode 100644 AzSentinel/Classes/Fusion.ps1 create mode 100644 AzSentinel/Classes/MLBehaviorAnalytics.ps1 create mode 100644 AzSentinel/Classes/MicrosoftSecurityIncidentCreation.ps1 create mode 100644 AzSentinel/enums/Kind.ps1 diff --git a/AzSentinel/Classes/AlertRule.ps1 b/AzSentinel/Classes/AlertRule.ps1 index 6e2de2e..1677ea7 100644 --- a/AzSentinel/Classes/AlertRule.ps1 +++ b/AzSentinel/Classes/AlertRule.ps1 @@ -5,17 +5,17 @@ class AlertRule { [string]$type - [string]$kind + [Kind]$kind = 'Scheduled' [pscustomobject]$Properties [string]$Id - AlertRule ($Name, $Etag, $Properties, $Id) { + AlertRule ($Name, $Etag, $Properties, $Id, $kind) { $this.id = $Id $this.type = 'Microsoft.SecurityInsights/alertRules' - $this.kind = 'Scheduled' + $this.kind = $kind $this.Name = $Name $this.Etag = $Etag $this.Properties = $Properties diff --git a/AzSentinel/Classes/Fusion.ps1 b/AzSentinel/Classes/Fusion.ps1 new file mode 100644 index 0000000..82108ba --- /dev/null +++ b/AzSentinel/Classes/Fusion.ps1 @@ -0,0 +1,9 @@ +class Fusion { + [bool]$Enabled + [string]$AlertRuleTemplateName + + Fusion ($Enabled, $AlertRuleTemplateName) { + $this.enabled = $Enabled + $this.AlertRuleTemplateName = $AlertRuleTemplateName + } +} diff --git a/AzSentinel/Classes/MLBehaviorAnalytics.ps1 b/AzSentinel/Classes/MLBehaviorAnalytics.ps1 new file mode 100644 index 0000000..8a8c5c0 --- /dev/null +++ b/AzSentinel/Classes/MLBehaviorAnalytics.ps1 @@ -0,0 +1,9 @@ +class MLBehaviorAnalytics { + [bool]$Enabled + [string]$AlertRuleTemplateName + + MLBehaviorAnalytics ($Enabled, $AlertRuleTemplateName) { + $this.enabled = $Enabled + $this.AlertRuleTemplateName = $AlertRuleTemplateName + } +} diff --git a/AzSentinel/Classes/MicrosoftSecurityIncidentCreation.ps1 b/AzSentinel/Classes/MicrosoftSecurityIncidentCreation.ps1 new file mode 100644 index 0000000..084a5ce --- /dev/null +++ b/AzSentinel/Classes/MicrosoftSecurityIncidentCreation.ps1 @@ -0,0 +1,17 @@ +class MicrosoftSecurityIncidentCreation { + [string] $DisplayName + [string]$Description + [bool]$Enabled + [string]$ProductFilter + [Severity[]]$SeveritiesFilter + [string]$DisplayNamesFilter + + MicrosoftSecurityIncidentCreation ($DisplayName, $Description, $Enabled, $ProductFilter, $SeveritiesFilter, $DisplayNamesFilter) { + $this.displayName = $DisplayName + $this.description = $Description + $this.enabled = $Enabled + $this.productFilter = $ProductFilter + $this.severitiesFilter = $SeveritiesFilter + $this.displayNamesFilter = $DisplayNamesFilter + } +} diff --git a/AzSentinel/Classes/classes.psd1 b/AzSentinel/Classes/classes.psd1 index 980c7a3..44891de 100644 --- a/AzSentinel/Classes/classes.psd1 +++ b/AzSentinel/Classes/classes.psd1 @@ -1,5 +1,8 @@ @{ order = @( + ,'MicrosoftSecurityIncidentCreation' + ,'Fusion' + ,'MLBehaviorAnalytics' ,'groupingConfiguration' ,'IncidentConfiguration' ,'ScheduledAlertProp' diff --git a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 index 6714589..fecf112 100644 --- a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 @@ -13,6 +13,8 @@ function Get-AzSentinelAlertRule { Enter the Workspace name .PARAMETER RuleName Enter the name of the Alert rule + .PARAMETER Kind + The alert rule kind .EXAMPLE Get-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" In this example you can get configuration of multiple alert rules in once @@ -32,7 +34,12 @@ function Get-AzSentinelAlertRule { [Parameter(Mandatory = $false, ValueFromPipeline)] [ValidateNotNullOrEmpty()] - [string[]]$RuleName + [string[]]$RuleName, + + [Parameter(Mandatory = $false, + ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [Kind[]]$Kind ) begin { @@ -67,7 +74,6 @@ function Get-AzSentinelAlertRule { } $return = @() - if ($alertRules.value) { Write-Verbose "Found $($alertRules.value.count) Alert rules" @@ -88,12 +94,45 @@ function Get-AzSentinelAlertRule { $temp.properties | Add-Member -NotePropertyName name -NotePropertyValue $temp.name -Force $temp.properties | Add-Member -NotePropertyName etag -NotePropertyValue $temp.etag -Force $temp.properties | Add-Member -NotePropertyName id -NotePropertyValue $temp.id -Force - $temp.properties | Add-Member -NotePropertyName playbookName -NotePropertyValue $playbookName -Force + $temp.properties | Add-Member -NotePropertyName kind -NotePropertyValue $temp.kind -Force + if ($temp.kind -eq "Scheduled") { + $temp.properties | Add-Member -NotePropertyName playbookName -NotePropertyValue $playbookName -Force + } + + $return += $temp.properties + } + else { + Write-Verbose "Unable to find Rule: $rule" + } + } + return $return + } + elseif ($Kind.Count -ge 1) { + foreach ($rule in $Kind) { + [PSCustomObject]$temp = $alertRules.value | Where-Object { $_.Kind -eq $rule } + if ($null -ne $temp) { + + $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId ($temp.name) + + if ($playbook) { + $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] + } + else { + $playbookName = "" + } + + $temp.properties | Add-Member -NotePropertyName name -NotePropertyValue $temp.name -Force + $temp.properties | Add-Member -NotePropertyName etag -NotePropertyValue $temp.etag -Force + $temp.properties | Add-Member -NotePropertyName id -NotePropertyValue $temp.id -Force + $temp.properties | Add-Member -NotePropertyName kind -NotePropertyValue $temp.kind -Force + if ($temp.kind -eq "Scheduled") { + $temp.properties | Add-Member -NotePropertyName playbookName -NotePropertyValue $playbookName -Force + } $return += $temp.properties } else { - Write-Error "Unable to find Rule: $rule" + Write-Verbose "Unable to find Rule: $rule" } } return $return @@ -109,14 +148,18 @@ function Get-AzSentinelAlertRule { $playbookName = "" } $_.properties | Add-Member -NotePropertyName name -NotePropertyValue $_.name -Force - $_.properties | Add-Member -NotePropertyName playbookName -NotePropertyValue $playbookName -Force + $_.properties | Add-Member -NotePropertyName id -NotePropertyValue $_.id -Force + $_.properties | Add-Member -NotePropertyName kind -NotePropertyValue $_.kind -Force + if ($_.kind -eq "Scheduled") { + $_.properties | Add-Member -NotePropertyName playbookName -NotePropertyValue $playbookName -Force + } return $_.properties } } } else { - Write-Warning "No rules found on $($WorkspaceName)" + Write-Verbose "No rules found on $($WorkspaceName)" } } } diff --git a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 index 8e56701..18203d3 100644 --- a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 @@ -84,18 +84,19 @@ function Import-AzSentinelAlertRule { if ($SettingsFile.Extension -eq '.json') { try { - $analytics = (Get-Content $SettingsFile -Raw | ConvertFrom-Json -ErrorAction Stop).analytics - Write-Verbose -Message "Found $($analytics.count) rules" + $rulesRaw = Get-Content $SettingsFile -Raw + $rules = $rulesRaw | ConvertFrom-Json -Depth 99 + Write-Verbose -Message "Found $($rules.count) rules" } catch { Write-Verbose $_ - Write-Error -Message 'Unable to convert JSON file' -ErrorAction Stop + Write-Error -Message 'Unable to import JSON file' -ErrorAction Stop } } elseif ($SettingsFile.Extension -in '.yaml', '.yml') { try { - $analytics = [pscustomobject](Get-Content $SettingsFile -Raw | ConvertFrom-Yaml -ErrorAction Stop) - $analytics | Add-Member -MemberType NoteProperty -Name DisplayName -Value $analytics.name + $rules = [pscustomobject](Get-Content $SettingsFile -Raw | ConvertFrom-Yaml -ErrorAction Stop) + $rules | Add-Member -MemberType NoteProperty -Name DisplayName -Value $rules.name Write-Verbose -Message 'Found compatibel yaml file' } catch { @@ -109,39 +110,49 @@ function Import-AzSentinelAlertRule { $return = @() - foreach ($item in $analytics) { + <# + Test All rules first + #> + $allRules = $rules.analytics + $rules.Scheduled + $rules.fusion + $rules.MLBehaviorAnalytics + $rules.MicrosoftSecurityIncidentCreation | Select-Object displayName + $allRulesContent = Get-AzSentinelAlertRule @arguments -RuleName $($allRules.displayName) + + <# + analytics rule + #> + if ($rules.analytics) { + $scheduled = $rules.analytics + } + else { + $scheduled = $rules.Scheduled + } + foreach ($item in $scheduled) { Write-Verbose -Message "Started with rule: $($item.displayName)" $guid = (New-Guid).Guid - try { - Write-Verbose -Message "Get rule $($item.description)" - $content = Get-AzSentinelAlertRule @arguments -RuleName $($item.displayName) -ErrorAction SilentlyContinue + $content = $allRulesContent | Where-Object displayName -eq $item.displayName - if ($content) { - Write-Verbose "Rule $($item.displayName) exists in Azure Sentinel" + Write-Verbose -Message "Get rule $($item.description)" - $item | Add-Member -NotePropertyName name -NotePropertyValue $content.name -Force - $item | Add-Member -NotePropertyName etag -NotePropertyValue $content.etag -Force - $item | Add-Member -NotePropertyName Id -NotePropertyValue $content.id -Force + if ($content) { + Write-Verbose "Rule $($item.displayName) exists in Azure Sentinel" - $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($content.name)?api-version=2019-01-01-preview" - } - else { - Write-Verbose -Message "Rule $($item.displayName) doesn't exist in Azure Sentinel" + $item | Add-Member -NotePropertyName name -NotePropertyValue $content.name -Force + $item | Add-Member -NotePropertyName etag -NotePropertyValue $content.etag -Force + $item | Add-Member -NotePropertyName Id -NotePropertyValue $content.id -Force - $item | Add-Member -NotePropertyName name -NotePropertyValue $guid -Force - $item | Add-Member -NotePropertyName etag -NotePropertyValue $null -Force - $item | Add-Member -NotePropertyName Id -NotePropertyValue "$script:Workspace/providers/Microsoft.SecurityInsights/alertRules/$guid" -Force - $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($guid)?api-version=2019-01-01-preview" - } + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($content.name)?api-version=2019-01-01-preview" } - catch { - Write-Verbose $_ - Write-Error "Unable to connect to APi to get Analytic rules with message: $($_.Exception.Message)" -ErrorAction Stop + else { + Write-Verbose -Message "Rule $($item.displayName) doesn't exist in Azure Sentinel" + + $item | Add-Member -NotePropertyName name -NotePropertyValue $guid -Force + $item | Add-Member -NotePropertyName etag -NotePropertyValue $null -Force + $item | Add-Member -NotePropertyName Id -NotePropertyValue "$script:Workspace/providers/Microsoft.SecurityInsights/alertRules/$guid" -Force + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($guid)?api-version=2019-01-01-preview" } - try { + try { $groupingConfiguration = [GroupingConfiguration]::new( $item.groupingConfiguration.enabled, $item.groupingConfiguration.reopenClosedIncident, @@ -171,17 +182,15 @@ function Import-AzSentinelAlertRule { $IncidentConfiguration, $item.aggregationKind ) - $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id) + $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id, 'Scheduled') } catch { Write-Error "Unable to initiate class with error: $($_.Exception.Message)" -ErrorAction Stop } - if ($content) { - if ($item.playbookName -or $content.playbookName) { - $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id,incidentConfiguration, queryResultsAggregationSettings) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id,incidentConfiguration, queryResultsAggregationSettings) + $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id, incidentConfiguration, queryResultsAggregationSettings) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id, incidentConfiguration, queryResultsAggregationSettings) } else { $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id, PlaybookName, incidentConfiguration, queryResultsAggregationSettings) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name, PlaybookName, incidentConfiguration, queryResultsAggregationSettings) @@ -190,19 +199,23 @@ function Import-AzSentinelAlertRule { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | Select-Object * -ExcludeProperty Properties.PlaybookName | ConvertTo-Json -Depth 10 -EnumsAsStrings) if (($compareResult | Where-Object PropertyName -eq "playbookName").DiffValue) { - New-AzSentinelAlertRuleAction @arguments -PlayBookName ($body.Properties.playbookName) -RuleId $($body.Name) + $PlaybookResult = New-AzSentinelAlertRuleAction @arguments -PlayBookName ($body.Properties.playbookName) -RuleId $($body.Name) + $body.Properties | Add-Member -NotePropertyName PlaybookStatus -NotePropertyValue $PlaybookResult -Force } elseif (($compareResult | Where-Object PropertyName -eq "playbookName").RefValue) { - Remove-AzSentinelAlertRuleAction @arguments -RuleId $($body.Name) -Confirm:$false + $PlaybookResult = Remove-AzSentinelAlertRuleAction @arguments -RuleId $($body.Name) -Confirm:$false + $body.Properties | Add-Member -NotePropertyName PlaybookStatus -NotePropertyValue $PlaybookResult -Force } else { #nothing } $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "Scheduled" -Force $return += $body.Properties } catch { $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue "failed" -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "Scheduled" -Force $return += $body.Properties Write-Verbose $_ @@ -214,14 +227,19 @@ function Import-AzSentinelAlertRule { try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | Select-Object * -ExcludeProperty Properties.PlaybookName | ConvertTo-Json -Depth 10 -EnumsAsStrings) + if ($body.Properties.playbookName) { - New-AzSentinelAlertRuleAction @arguments -PlayBookName $($body.Properties.playbookName) -RuleId $($body.Properties.Name) -confirm:$false + $PlaybookResult = New-AzSentinelAlertRuleAction @arguments -PlayBookName $($body.Properties.playbookName) -RuleId $($body.Properties.Name) -confirm:$false + $body.Properties | Add-Member -NotePropertyName PlaybookStatus -NotePropertyValue $PlaybookResult -Force } + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "Scheduled" -Force $return += $body.Properties } catch { $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue "failed" -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "Scheduled" -Force $return += $body.Properties Write-Verbose $_ @@ -229,6 +247,173 @@ function Import-AzSentinelAlertRule { } } } + + <# + Fusion rule + #> + foreach ($item in $rules.fusion) { + Write-Verbose "Rule type is Fusion" + + $guid = (New-Guid).Guid + + $content = $allRulesContent | Where-Object displayName -eq $item.displayName + + Write-Verbose -Message "Get rule $($item.description)" + + if ($content) { + Write-Verbose "Rule $($item.displayName) exists in Azure Sentinel" + + $item | Add-Member -NotePropertyName name -NotePropertyValue $content.name -Force + $item | Add-Member -NotePropertyName etag -NotePropertyValue $content.etag -Force + $item | Add-Member -NotePropertyName Id -NotePropertyValue $content.id -Force + + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($content.name)?api-version=2019-01-01-preview" + } + else { + Write-Verbose -Message "Rule $($item.displayName) doesn't exist in Azure Sentinel" + + $item | Add-Member -NotePropertyName name -NotePropertyValue $guid -Force + $item | Add-Member -NotePropertyName etag -NotePropertyValue $null -Force + $item | Add-Member -NotePropertyName Id -NotePropertyValue "$script:Workspace/providers/Microsoft.SecurityInsights/alertRules/$guid" -Force + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($guid)?api-version=2019-01-01-preview" + } + + $bodyAlertProp = [Fusion]::new( + $item.enabled, + $item.alertRuleTemplateName + ) + + $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id, 'Fusion') + + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "Fusion" -Force + $return += $body.Properties + } + catch { + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue "failed" -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "Fusion" -Force + $return += $body.Properties + + Write-Verbose $_ + Write-Verbose "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue + } + } + + <# + MLBehaviorAnalytics + #> + foreach ($item in $rules.MLBehaviorAnalytics) { + Write-Verbose "Rule type is ML Behavior Analytics" + + $guid = (New-Guid).Guid + + $content = $allRulesContent | Where-Object displayName -eq $item.displayName + + Write-Verbose -Message "Get rule $($item.description)" + + if ($content) { + Write-Verbose "Rule $($item.displayName) exists in Azure Sentinel" + + $item | Add-Member -NotePropertyName name -NotePropertyValue $content.name -Force + $item | Add-Member -NotePropertyName etag -NotePropertyValue $content.etag -Force + $item | Add-Member -NotePropertyName Id -NotePropertyValue $content.id -Force + + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($content.name)?api-version=2019-01-01-preview" + } + else { + Write-Verbose -Message "Rule $($item.displayName) doesn't exist in Azure Sentinel" + + $item | Add-Member -NotePropertyName name -NotePropertyValue $guid -Force + $item | Add-Member -NotePropertyName etag -NotePropertyValue $null -Force + $item | Add-Member -NotePropertyName Id -NotePropertyValue "$script:Workspace/providers/Microsoft.SecurityInsights/alertRules/$guid" -Force + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($guid)?api-version=2019-01-01-preview" + } + + $bodyAlertProp = [MLBehaviorAnalytics]::new( + $item.enabled, + $item.alertRuleTemplateName + ) + + $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id, 'MLBehaviorAnalytics') + + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "MLBehaviorAnalytics" -Force + + $return += $body.Properties + } + catch { + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue "failed" -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "MLBehaviorAnalytics" -Force + $return += $body.Properties + + Write-Verbose $_ + Write-Verbose "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue + } + } + + <# + MicrosoftSecurityIncidentCreation + #> + foreach ($item in $rules.MicrosoftSecurityIncidentCreation) { + Write-Verbose "Rule type is Microsoft Security" + + $guid = (New-Guid).Guid + + $content = $allRulesContent | Where-Object displayName -eq $item.displayName + + Write-Verbose -Message "Get rule $($item.description)" + $content = Get-AzSentinelAlertRule @arguments -RuleName $($item.displayName) -ErrorAction SilentlyContinue + + if ($content) { + Write-Verbose "Rule $($item.displayName) exists in Azure Sentinel" + + $item | Add-Member -NotePropertyName name -NotePropertyValue $content.name -Force + $item | Add-Member -NotePropertyName etag -NotePropertyValue $content.etag -Force + $item | Add-Member -NotePropertyName Id -NotePropertyValue $content.id -Force + + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($content.name)?api-version=2019-01-01-preview" + } + else { + Write-Verbose -Message "Rule $($item.displayName) doesn't exist in Azure Sentinel" + + $item | Add-Member -NotePropertyName name -NotePropertyValue $guid -Force + $item | Add-Member -NotePropertyName etag -NotePropertyValue $null -Force + $item | Add-Member -NotePropertyName Id -NotePropertyValue "$script:Workspace/providers/Microsoft.SecurityInsights/alertRules/$guid" -Force + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($guid)?api-version=2019-01-01-preview" + } + + $bodyAlertProp = [MicrosoftSecurityIncidentCreation]::new( + $item.displayName, + $item.description, + $item.enabled, + $item.productFilter, + $item.severitiesFilter, + $item.displayNamesFilter + ) + + $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id, 'MicrosoftSecurityIncidentCreation') + + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) + + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "MicrosoftSecurityIncidentCreation" -Force + $return += $body.Properties + } + catch { + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue "failed" -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "MicrosoftSecurityIncidentCreation" -Force + $return += $body.Properties + + Write-Verbose $_ + Write-Verbose "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue + } + } + return $return } } diff --git a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 index 33b23e1..20aba07 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 @@ -11,16 +11,18 @@ function New-AzSentinelAlertRule { Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used .PARAMETER WorkspaceName Enter the Workspace name + .PARAMETER Kind + The alert rule kind .PARAMETER DisplayName - Enter the Display name for the Alert rule + The display name for alerts created by this alert rule. .PARAMETER Description - Enter the Description for the Alert rule + The description of the alert rule. .PARAMETER Severity Enter the Severity, valid values: Medium", "High", "Low", "Informational" .PARAMETER Enabled - Set $true to enable the Alert Rule or $false to disable Alert Rule + Determines whether this alert rule is enabled or disabled. .PARAMETER Query - Enter the Query that you want to use + The query that creates alerts for this rule. .PARAMETER QueryFrequency Enter the query frequency, example: 5H, 5M, 5D (H stands for Hour, M stands for Minute and D stands for Day) .PARAMETER QueryPeriod @@ -51,99 +53,116 @@ function New-AzSentinelAlertRule { Grouping alerts into a single incident if the selected entities match: .PARAMETER AggregationKind Configure how rule query results are grouped into alerts + .PARAMETER AlertRuleTemplateName + The Name of the alert rule template used to create this rule + .PARAMETER ProductFilter + The alerts' productName on which the cases will be generated + .PARAMETER SeveritiesFilter + The alerts' severities on which the cases will be generated + .PARAMETER DisplayNamesFilter + The alerts' displayNames on which the cases will be generated .EXAMPLE New-AzSentinelAlertRule -WorkspaceName "" -DisplayName "" -Description "" -Severity -Enabled $true -Query '' -QueryFrequency "" -QueryPeriod "" -TriggerOperator -TriggerThreshold -SuppressionDuration "" -SuppressionEnabled $false -Tactics @("","") -PlaybookName "" - In this example you create a new Alert rule by defining the rule properties from CMDLET + Example on how to create a scheduled rule + .EXAMPLE + New-AzSentinelAlertRule -WorkspaceName "" -Kind Fusion -DisplayName "Advanced Multistage Attack Detection" -Enabled $true -AlertRuleTemplateName "f71aba3d-28fb-450b-b192-4e76a83015c8" + Example on how to create a Fusion rule + .EXAMPLE + New-AzSentinelAlertRule -WorkspaceName "" -Kind MLBehaviorAnalytics -DisplayName "(Preview) Anomalous SSH Login Detection" -Enabled $true -AlertRuleTemplateName "fa118b98-de46-4e94-87f9-8e6d5060b60b" + Example on how to create a MLBehaviorAnalytics rule + .EXAMPLE + New-AzSentinelAlertRule -WorkspaceName "" -Kind MicrosoftSecurityIncidentCreation -DisplayName "" -Description "" -Enabled $true -ProductFilter "" -SeveritiesFilter "","" -DisplayNamesFilter "" + Example on how to create a MicrosoftSecurityIncidentCreation rule #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $false, ParameterSetName = "Sub")] [ValidateNotNullOrEmpty()] - [string] $SubscriptionId, + [string]$SubscriptionId, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] - [string] $WorkspaceName, + [string]$WorkspaceName, - [Parameter(Mandatory)] - [string] $DisplayName, + [Parameter(Mandatory = $false)] + [Kind]$Kind = 'Scheduled', - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string] $Description, + [Parameter(Mandatory = $false)] + [string]$DisplayName, - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [Severity] $Severity, + [Parameter(Mandatory = $false)] + [string]$Description, - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [bool] $Enabled, + [Parameter(Mandatory = $false)] + [Severity]$Severity, - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string] $Query, + [Parameter(Mandatory = $false)] + [bool]$Enabled, - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string] $QueryFrequency, + [Parameter(Mandatory = $false)] + [string]$Query, - [ValidateNotNullOrEmpty()] - [string] $QueryPeriod, + [Parameter(Mandatory = $false)] + [string]$QueryFrequency, - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [TriggerOperator] $TriggerOperator, + [parameter(Mandatory = $false)] + [string]$QueryPeriod, - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [Int] $TriggerThreshold, + [Parameter(Mandatory = $false)] + [TriggerOperator]$TriggerOperator, - [Parameter(Mandatory)] - [AllowEmptyString()] - [string] $SuppressionDuration, + [Parameter(Mandatory = $false)] + [Int]$TriggerThreshold, - [Parameter(Mandatory)] - [bool] $SuppressionEnabled, + [Parameter(Mandatory = $false)] + [string]$SuppressionDuration, - [Parameter(Mandatory)] - [AllowEmptyCollection()] + [Parameter(Mandatory = $false)] + [bool]$SuppressionEnabled, + + [Parameter(Mandatory = $false)] #[Tactics[]] $Tactics, - [string[]] $Tactics, + [string[]]$Tactics, [Parameter(Mandatory = $false)] - [AllowEmptyString()] - [string] $PlaybookName = $null, + [string[]]$PlaybookName = $null, [Parameter(Mandatory = $false)] - [AllowEmptyString()] [bool]$CreateIncident, [Parameter(Mandatory = $false)] - [AllowEmptyString()] [bool]$GroupingConfigurationEnabled, [Parameter(Mandatory = $false)] - [AllowEmptyString()] [bool]$ReopenClosedIncident, [Parameter(Mandatory = $false)] - [AllowEmptyString()] [string]$LookbackDuration, [Parameter(Mandatory = $false)] - [AllowEmptyString()] [MatchingMethod]$EntitiesMatchingMethod, [Parameter(Mandatory = $false)] - [AllowEmptyString()] #[groupByEntities[]]$GroupByEntities, [string[]]$GroupByEntities, [Parameter(Mandatory = $false)] - [AllowEmptyString()] - [string]$AggregationKind + [string]$AggregationKind, + + #Fusion & MLBehaviorAnalytics + [Parameter(Mandatory = $false)] + [string]$AlertRuleTemplateName, + + #MicrosoftSecurityIncidentCreation + [Parameter(Mandatory = $false)] + [string]$ProductFilter, + + [Parameter(Mandatory = $false)] + [string]$SeveritiesFilter, + + [Parameter(Mandatory = $false)] + [string]$DisplayNamesFilter ) begin { @@ -168,129 +187,231 @@ function New-AzSentinelAlertRule { $item = @{ } Write-Verbose -Message "Creating new rule: $($DisplayName)" - try { - Write-Verbose -Message "Get rule $DisplayName" - $content = Get-AzSentinelAlertRule @arguments -RuleName $DisplayName -ErrorAction SilentlyContinue - if ($content) { - Write-Verbose -Message "Rule $($DisplayName) exists in Azure Sentinel" + $content = Get-AzSentinelAlertRule @arguments -RuleName $DisplayName - $item | Add-Member -NotePropertyName name -NotePropertyValue $content.name -Force - $item | Add-Member -NotePropertyName etag -NotePropertyValue $content.eTag -Force - $item | Add-Member -NotePropertyName Id -NotePropertyValue $content.id -Force + if ($content) { + Write-Verbose -Message "Rule $($DisplayName) exists in Azure Sentinel" - $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($content.name)?api-version=2019-01-01-preview" - } - else { - Write-Verbose -Message "Rule $($DisplayName) doesn't exists in Azure Sentinel" + $item | Add-Member -NotePropertyName name -NotePropertyValue $content.name -Force + $item | Add-Member -NotePropertyName etag -NotePropertyValue $content.eTag -Force + $item | Add-Member -NotePropertyName Id -NotePropertyValue $content.id -Force - $guid = (New-Guid).Guid + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($content.name)?api-version=2019-01-01-preview" + } + else { + Write-Verbose -Message "Rule $($DisplayName) doesn't exists in Azure Sentinel" - $item | Add-Member -NotePropertyName name -NotePropertyValue $guid -Force - $item | Add-Member -NotePropertyName etag -NotePropertyValue $null -Force - $item | Add-Member -NotePropertyName Id -NotePropertyValue "$script:Workspace/providers/Microsoft.SecurityInsights/alertRules/$guid" -Force + $guid = (New-Guid).Guid - $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($guid)?api-version=2019-01-01-preview" - } - } - catch { - Write-Verbose $_ - Write-Error "Unable to connect to APi to get Analytic rules with message: $($_.Exception.Message)" -ErrorAction Stop + $item | Add-Member -NotePropertyName name -NotePropertyValue $guid -Force + $item | Add-Member -NotePropertyName etag -NotePropertyValue $null -Force + $item | Add-Member -NotePropertyName Id -NotePropertyValue "$script:Workspace/providers/Microsoft.SecurityInsights/alertRules/$guid" -Force + + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($guid)?api-version=2019-01-01-preview" } - try { - $groupingConfiguration = [GroupingConfiguration]::new( - $GroupingConfigurationEnabled, - $ReopenClosedIncident, - $LookbackDuration, - $EntitiesMatchingMethod, - $GroupByEntities - ) + if ($Kind -eq 'Scheduled') { - $incidentConfiguration = [IncidentConfiguration]::new( - $CreateIncident, - $groupingConfiguration - ) + try { + $groupingConfiguration = [GroupingConfiguration]::new( + $GroupingConfigurationEnabled, + $ReopenClosedIncident, + $LookbackDuration, + $EntitiesMatchingMethod, + $GroupByEntities + ) + + $incidentConfiguration = [IncidentConfiguration]::new( + $CreateIncident, + $groupingConfiguration + ) + + $bodyAlertProp = [ScheduledAlertProp]::new( + $item.name, + $DisplayName, + $Description, + $Severity, + $Enabled, + $Query, + $QueryFrequency, + $QueryPeriod, + $TriggerOperator, + $TriggerThreshold, + $SuppressionDuration, + $SuppressionEnabled, + $Tactics, + $PlaybookName, + $incidentConfiguration, + $AggregationKind + ) + + $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id) + } + catch { + Write-Error "Unable to initiate class with error: $($_.Exception.Message)" -ErrorAction Stop + } - $bodyAlertProp = [ScheduledAlertProp]::new( - $item.name, - $DisplayName, - $Description, - $Severity, - $Enabled, - $Query, - $QueryFrequency, - $QueryPeriod, - $TriggerOperator, - $TriggerThreshold, - $SuppressionDuration, - $SuppressionEnabled, - $Tactics, - $PlaybookName, - $incidentConfiguration, - $AggregationKind - ) + if ($content) { + if ($PlaybookName -or $content.playbookName) { + $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name) + } + else { + $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id, PlaybookName) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name, PlaybookName) + } - $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id) - } - catch { - Write-Error "Unable to initiate class with error: $($_.Exception.Message)" -ErrorAction Stop - } + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) + + if (($compareResult | Where-Object PropertyName -eq "playbookName").DiffValue) { + foreach ($playbook in ($body.Properties.PlaybookName)) { + New-AzSentinelAlertRuleAction @arguments -PlayBookName $playbook -RuleId $($body.Properties.Name) -confirm:$false + $body.Properties | Add-Member -NotePropertyName PlaybookStatus -NotePropertyValue $PlaybookResult -Force + } + } + elseif (($compareResult | Where-Object PropertyName -eq "playbookName").RefValue) { + Remove-AzSentinelAlertRuleAction @arguments -RuleId $($body.Name) -Confirm:$false + $body.Properties | Add-Member -NotePropertyName PlaybookStatus -NotePropertyValue $PlaybookResult -Force + } + else { + #nothing + } + + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "Scheduled" -Force + $return += $body.Properties + + return $return + } + catch { + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue "failed" -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "Scheduled" -Force + $return += $body.Properties - if ($content) { - if ($PlaybookName -or $content.playbookName) { - $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name) + return $return + + Write-Verbose $_ + Write-Error "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue + } } else { - $compareResult = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id, PlaybookName) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name, PlaybookName) + Write-Verbose "Creating new rule: $($DisplayName)" + + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) + if (($body.Properties.PlaybookName)) { + foreach ($playbook in ($body.Properties.PlaybookName)) { + New-AzSentinelAlertRuleAction @arguments -PlayBookName $playbook -RuleId $($body.Properties.Name) -confirm:$false + $body.Properties | Add-Member -NotePropertyName PlaybookStatus -NotePropertyValue $PlaybookResult -Force + } + } + + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "Scheduled" -Force + $return += $body.Properties + return $return + } + catch { + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue "failed" -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "Scheduled" -Force + $return += $body.Properties + return $return + + Write-Verbose $_ + Write-Error "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue + } } + } + + if ($Kind -eq 'Fusion') { + + $bodyAlertProp = [Fusion]::new( + $Enabled, + $AlertRuleTemplateName + ) + + $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id, 'Fusion') try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "Fusion" -Force + $return += $body.Properties - if (($compareResult | Where-Object PropertyName -eq "playbookName").DiffValue) { - New-AzSentinelAlertRuleAction @arguments -PlayBookName ($body.Properties.playbookName) -RuleId $($body.Name) - } - elseif (($compareResult | Where-Object PropertyName -eq "playbookName").RefValue) { - Remove-AzSentinelAlertRuleAction @arguments -RuleId $($body.Name) -Confirm:$false - } - else { - #nothing - } + return $return + } + catch { + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue "failed" -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "Fusion" -Force + $return += $body.Properties + + return $return + Write-Verbose $_ + Write-Verbose "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue + } + } + + if ($Kind -eq 'MLBehaviorAnalytics') { + + $bodyAlertProp = [MLBehaviorAnalytics]::new( + $Enabled, + $AlertRuleTemplateName + ) + + $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id, 'MLBehaviorAnalytics') + + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "MLBehaviorAnalytics" -Force $return += $body.Properties + return $return } catch { $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue "failed" -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "MLBehaviorAnalytics" -Force $return += $body.Properties + return $return Write-Verbose $_ - Write-Error "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue + Write-Verbose "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue } } - else { - Write-Verbose "Creating new rule: $($DisplayName)" + + if ($Kind -eq 'MicrosoftSecurityIncidentCreation') { + + $bodyAlertProp = [MicrosoftSecurityIncidentCreation]::new( + $DisplayName, + $Description, + $Enabled, + $ProductFilter, + $SeveritiesFilter, + $DisplayNamesFilter + ) + + $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id, 'MicrosoftSecurityIncidentCreation') try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) - if (($body.Properties.PlaybookName)) { - New-AzSentinelAlertRuleAction @arguments -PlayBookName ($body.Properties.PlaybookName) -RuleId $($body.Properties.Name) -confirm:$false - } $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "MicrosoftSecurityIncidentCreation" -Force $return += $body.Properties + return $return } catch { $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue "failed" -Force + $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "MicrosoftSecurityIncidentCreation" -Force $return += $body.Properties + return $return Write-Verbose $_ - Write-Error "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue + Write-Verbose "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue } } } diff --git a/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 b/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 index 7bb8190..40fc496 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 @@ -100,7 +100,7 @@ function New-AzSentinelAlertRuleAction { $uri = "$($Script:baseUri)/providers/Microsoft.SecurityInsights/alertRules/$($alertId)/actions/$($guid)?api-version=2019-01-01-preview" try { $return = Invoke-WebRequest -Method Put -Uri $uri -Headers $Script:authHeader -Body ($body | ConvertTo-Json -Depth 10) - Write-Output "Successfully created Action for Rule: $($RuleName) with Playbook $($PlayBookName) Status: $($return.StatusDescription)" + Write-Verbose "Successfully created Action for Rule: $($RuleName) with Playbook $($PlayBookName) Status: $($return.StatusDescription)" return $return.StatusDescription } catch { diff --git a/AzSentinel/Public/Remove-AzSentinelAlertRuleAction.ps1 b/AzSentinel/Public/Remove-AzSentinelAlertRuleAction.ps1 index e72e841..ead2569 100644 --- a/AzSentinel/Public/Remove-AzSentinelAlertRuleAction.ps1 +++ b/AzSentinel/Public/Remove-AzSentinelAlertRuleAction.ps1 @@ -79,7 +79,7 @@ function Remove-AzSentinelAlertRuleAction { try { $return = Invoke-WebRequest -Uri $uri -Method DELETE -Headers $script:authHeader Write-Verbose $return - Write-Output "Rule action $($result.properties.logicAppResourceId.Split('/')[-1]) removed for rule $($RuleName) with status: $($return.StatusCode)" + Write-Verbose "Rule action $($result.properties.logicAppResourceId.Split('/')[-1]) removed for rule $($RuleName) with status: $($return.StatusCode)" return $return.StatusCode } catch { diff --git a/AzSentinel/docs/Get-AzSentinelAlertRule.md b/AzSentinel/docs/Get-AzSentinelAlertRule.md index 23e92c1..a020fa8 100644 --- a/AzSentinel/docs/Get-AzSentinelAlertRule.md +++ b/AzSentinel/docs/Get-AzSentinelAlertRule.md @@ -13,8 +13,8 @@ Get Azure Sentinel Alert Rules ## SYNTAX ``` -Get-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-RuleName ] [-WhatIf] - [-Confirm] [] +Get-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-RuleName ] + [-Kind ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -75,6 +75,22 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -Kind +The alert rule kind + +```yaml +Type: Kind[] +Parameter Sets: (All) +Aliases: +Accepted values: Scheduled, Fusion, MLBehaviorAnalytics, MicrosoftSecurityIncidentCreation + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. diff --git a/AzSentinel/docs/New-AzSentinelAlertRule.md b/AzSentinel/docs/New-AzSentinelAlertRule.md index 778b1b8..3e12b8a 100644 --- a/AzSentinel/docs/New-AzSentinelAlertRule.md +++ b/AzSentinel/docs/New-AzSentinelAlertRule.md @@ -13,13 +13,15 @@ Create Azure Sentinal Alert Rules ## SYNTAX ``` -New-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName -DisplayName - -Description -Severity -Enabled -Query -QueryFrequency - [-QueryPeriod ] -TriggerOperator -TriggerThreshold - -SuppressionDuration -SuppressionEnabled -Tactics [-PlaybookName ] - [-CreateIncident ] [-GroupingConfigurationEnabled ] [-ReopenClosedIncident ] - [-LookbackDuration ] [-EntitiesMatchingMethod ] [-GroupByEntities ] - [-AggregationKind ] [-WhatIf] [-Confirm] [] +New-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-Kind ] + [-DisplayName ] [-Description ] [-Severity ] [-Enabled ] [-Query ] + [-QueryFrequency ] [-QueryPeriod ] [-TriggerOperator ] + [-TriggerThreshold ] [-SuppressionDuration ] [-SuppressionEnabled ] + [-Tactics ] [-PlaybookName ] [-CreateIncident ] + [-GroupingConfigurationEnabled ] [-ReopenClosedIncident ] [-LookbackDuration ] + [-EntitiesMatchingMethod ] [-GroupByEntities ] [-AggregationKind ] + [-AlertRuleTemplateName ] [-ProductFilter ] [-SeveritiesFilter ] + [-DisplayNamesFilter ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -30,7 +32,25 @@ Use this function creates Azure Sentinal Alert rules from provided CMDLET ### EXAMPLE 1 ``` New-AzSentinelAlertRule -WorkspaceName "" -DisplayName "" -Description "" -Severity -Enabled $true -Query '' -QueryFrequency "" -QueryPeriod "" -TriggerOperator -TriggerThreshold -SuppressionDuration "" -SuppressionEnabled $false -Tactics @("","") -PlaybookName "" -In this example you create a new Alert rule by defining the rule properties from CMDLET +Example on how to create a scheduled rule +``` + +### EXAMPLE 2 +``` +New-AzSentinelAlertRule -WorkspaceName "" -Kind Fusion -DisplayName "Advanced Multistage Attack Detection" -Enabled $true -AlertRuleTemplateName "f71aba3d-28fb-450b-b192-4e76a83015c8" +Example on how to create a Fusion rule +``` + +### EXAMPLE 3 +``` +New-AzSentinelAlertRule -WorkspaceName "" -Kind MLBehaviorAnalytics -DisplayName "(Preview) Anomalous SSH Login Detection" -Enabled $true -AlertRuleTemplateName "fa118b98-de46-4e94-87f9-8e6d5060b60b" +Example on how to create a MLBehaviorAnalytics rule +``` + +### EXAMPLE 4 +``` +New-AzSentinelAlertRule -WorkspaceName "" -Kind MicrosoftSecurityIncidentCreation -DisplayName "" -Description "" -Enabled $true -ProductFilter "" -SeveritiesFilter "","" -DisplayNamesFilter "" +Example on how to create a MicrosoftSecurityIncidentCreation rule ``` ## PARAMETERS @@ -65,15 +85,31 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Kind +The alert rule kind + +```yaml +Type: Kind +Parameter Sets: (All) +Aliases: +Accepted values: Scheduled, Fusion, MLBehaviorAnalytics, MicrosoftSecurityIncidentCreation + +Required: False +Position: Named +Default value: Scheduled +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -DisplayName -Enter the Display name for the Alert rule +The display name for alerts created by this alert rule. ```yaml Type: String Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -81,14 +117,14 @@ Accept wildcard characters: False ``` ### -Description -Enter the Description for the Alert rule +The description of the alert rule. ```yaml Type: String Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -104,7 +140,7 @@ Parameter Sets: (All) Aliases: Accepted values: Medium, High, Low, Informational -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -112,14 +148,14 @@ Accept wildcard characters: False ``` ### -Enabled -Set $true to enable the Alert Rule or $false to disable Alert Rule +Determines whether this alert rule is enabled or disabled. ```yaml Type: Boolean Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: False Accept pipeline input: False @@ -127,14 +163,14 @@ Accept wildcard characters: False ``` ### -Query -Enter the Query that you want to use +The query that creates alerts for this rule. ```yaml Type: String Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -149,7 +185,7 @@ Type: String Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -180,7 +216,7 @@ Parameter Sets: (All) Aliases: Accepted values: GreaterThan, LessThan, Equal, NotEqual, gt, lt, eq, ne -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -195,7 +231,7 @@ Type: Int32 Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: 0 Accept pipeline input: False @@ -210,7 +246,7 @@ Type: String Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -225,7 +261,7 @@ Type: Boolean Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: False Accept pipeline input: False @@ -240,7 +276,7 @@ Type: String[] Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -251,7 +287,7 @@ Accept wildcard characters: False Enter the Logic App name that you want to configure as playbook trigger ```yaml -Type: String +Type: String[] Parameter Sets: (All) Aliases: @@ -368,6 +404,66 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -AlertRuleTemplateName +The Name of the alert rule template used to create this rule + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ProductFilter +The alerts' productName on which the cases will be generated + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SeveritiesFilter +The alerts' severities on which the cases will be generated + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DisplayNamesFilter +The alerts' displayNames on which the cases will be generated + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. diff --git a/AzSentinel/docs/Rename-AzSentinelAlertRule.md b/AzSentinel/docs/Rename-AzSentinelAlertRule.md index bfcbcf9..e0ed7c6 100644 --- a/AzSentinel/docs/Rename-AzSentinelAlertRule.md +++ b/AzSentinel/docs/Rename-AzSentinelAlertRule.md @@ -128,6 +128,7 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable ## OUTPUTS +### System.String ## NOTES ## RELATED LINKS diff --git a/AzSentinel/enums/Kind.ps1 b/AzSentinel/enums/Kind.ps1 new file mode 100644 index 0000000..fa188af --- /dev/null +++ b/AzSentinel/enums/Kind.ps1 @@ -0,0 +1,6 @@ +enum Kind { + Scheduled + Fusion + MLBehaviorAnalytics + MicrosoftSecurityIncidentCreation +} diff --git a/README.md b/README.md index da3b0a0..1b116c4 100644 --- a/README.md +++ b/README.md @@ -39,47 +39,63 @@ See [docs](https://github.com/wortell/AzSentinel/tree/master/docs) folder for do To create a Azure Sentinel Rule, use the following JSON format. +### Roor schema ```JSON { - "analytics": [ - { - "displayName": "string", - "description": "string", - "severity": "High", - "enabled": true, - "query": "SecurityEvent | where EventID == \"4688\" | where CommandLine contains \"-noni -ep bypass $\"", - "queryFrequency": "5H", - "queryPeriod": "5H", - "triggerOperator": "GreaterThan", - "triggerThreshold": 5, - "suppressionDuration": "6H", - "suppressionEnabled": false, - "tactics": [ - "Persistence", - "LateralMovement", - "Collection" - ], - "playbookName": "string", - "aggregationKind": "string", - "createIncident": true, - "groupingConfiguration": { - "GroupingConfigurationEnabled": true, - "reopenClosedIncident": true, - "lookbackDuration": "PT6H", - "entitiesMatchingMethod": "string", - "groupByEntities": [ - "Account", - "Ip", - "Host", - "Url" - ] - } - } + "Scheduled": [ + ... + ], + "Fusion": [ + ... + ], + "MLBehaviorAnalytics": [ + ... + ], + "MicrosoftSecurityIncidentCreation": [ + ... ] } ``` -### Property values +### Scheduled rule + +```JSON + { + "displayName": "string", + "description": "string", + "severity": "High", + "enabled": true, + "query": "SecurityEvent | where EventID == \"4688\" | where CommandLine contains \"-noni -ep bypass $\"", + "queryFrequency": "5H", + "queryPeriod": "5H", + "triggerOperator": "GreaterThan", + "triggerThreshold": 5, + "suppressionDuration": "6H", + "suppressionEnabled": false, + "tactics": [ + "Persistence", + "LateralMovement", + "Collection" + ], + "playbookName": "string", + "aggregationKind": "string", + "createIncident": true, + "groupingConfiguration": { + "GroupingConfigurationEnabled": true, + "reopenClosedIncident": true, + "lookbackDuration": "PT6H", + "entitiesMatchingMethod": "string", + "groupByEntities": [ + "Account", + "Ip", + "Host", + "Url" + ] + } + } +``` + +#### Scheduled property values The following tables describe the values you need to set in the schema. @@ -106,9 +122,84 @@ The following tables describe the values you need to set in the schema. | entitiesMatchingMethod | string | false | All, None, Custom | All | | groupByEntities | string | false | Account, Ip, Host, Url | Account | + +### Fusion rule +```JSON + { + "displayName": "Advanced Multistage Attack Detection", + "enabled": true, + "alertRuleTemplateName": "f71aba3d-28fb-450b-b192-4e76a83015c8" + } +``` + +#### Scheduled property values + +The following tables describe the values you need to set in the schema. + +| Name | Type | Required | Allowed Values | Example | +| --------------------- | ------ | -------- | -------------- | ------------------------------------ | +| displayName | string | true | | Advanced Multistage Attack Detection | +| enabled | bool | true | | true | +| alertRuleTemplateName | string | true | | f71aba3d-28fb-450b-b192-4e76a83015c8 | +| | | | | + + + +### MLBehaviorAnalytics rules + +```JSON + { + "displayName": "(Preview) Anomalous SSH Login Detection", + "enabled": true, + "alertRuleTemplateName": "fa118b98-de46-4e94-87f9-8e6d5060b60b" + } +``` + +#### Scheduled property values + +The following tables describe the values you need to set in the schema. + +| Name | Type | Required | Allowed Values | Example | +| --------------------- | ------ | -------- | -------------- | ------------------------------------ | +| displayName | string | true | | Advanced Multistage Attack Detection | +| enabled | bool | true | | true | +| alertRuleTemplateName | string | true | | f71aba3d-28fb-450b-b192-4e76a83015c8 | +| | | | | + + +### MicrosoftSecurityIncidentCreation rules +```JSON + { + "displayName": "Create incidents based on Azure Active Directory Identity Protection alerts", + "description": "Create incidents based on all alerts generated in Azure Active Directory Identity Protection", + "enabled": true, + "productFilter": "Microsoft Cloud App Security", + "severitiesFilter": [ + "High", + "Medium", + "Low" + ], + "displayNamesFilter": null + } +``` +#### Scheduled property values + +The following tables describe the values you need to set in the schema. + +| Name | Type | Required | Allowed Values | Example | +| ------------------ | ------ | -------- | ----------------- | -------------------------------------------------------------------------------------------- | +| displayName | string | true | | Create incidents based on Azure Active Directory Identity Protection alerts | +| enabled | bool | true | | true | +| description | string | true | | Create incidents based on all alerts generated in Azure Active Directory Identity Protection | +| productFilter | string | true | | Microsoft Cloud App Security | +| severitiesFilter | string | true | High, Medium, Low | High | +| displayNamesFilter | string | false | | | +| | | | | | + + + ## Find us -* [Wortell](https://security.wortell.nl/) * [GitHub](https://github.com/wortell/AZSentinel) * [PowerShell Gallery](https://www.powershellgallery.com/packages/AzSentinel) diff --git a/docs/Get-AzSentinelAlertRule.md b/docs/Get-AzSentinelAlertRule.md index 23e92c1..a020fa8 100644 --- a/docs/Get-AzSentinelAlertRule.md +++ b/docs/Get-AzSentinelAlertRule.md @@ -13,8 +13,8 @@ Get Azure Sentinel Alert Rules ## SYNTAX ``` -Get-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-RuleName ] [-WhatIf] - [-Confirm] [] +Get-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-RuleName ] + [-Kind ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -75,6 +75,22 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -Kind +The alert rule kind + +```yaml +Type: Kind[] +Parameter Sets: (All) +Aliases: +Accepted values: Scheduled, Fusion, MLBehaviorAnalytics, MicrosoftSecurityIncidentCreation + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. diff --git a/docs/New-AzSentinelAlertRule.md b/docs/New-AzSentinelAlertRule.md index 778b1b8..3e12b8a 100644 --- a/docs/New-AzSentinelAlertRule.md +++ b/docs/New-AzSentinelAlertRule.md @@ -13,13 +13,15 @@ Create Azure Sentinal Alert Rules ## SYNTAX ``` -New-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName -DisplayName - -Description -Severity -Enabled -Query -QueryFrequency - [-QueryPeriod ] -TriggerOperator -TriggerThreshold - -SuppressionDuration -SuppressionEnabled -Tactics [-PlaybookName ] - [-CreateIncident ] [-GroupingConfigurationEnabled ] [-ReopenClosedIncident ] - [-LookbackDuration ] [-EntitiesMatchingMethod ] [-GroupByEntities ] - [-AggregationKind ] [-WhatIf] [-Confirm] [] +New-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-Kind ] + [-DisplayName ] [-Description ] [-Severity ] [-Enabled ] [-Query ] + [-QueryFrequency ] [-QueryPeriod ] [-TriggerOperator ] + [-TriggerThreshold ] [-SuppressionDuration ] [-SuppressionEnabled ] + [-Tactics ] [-PlaybookName ] [-CreateIncident ] + [-GroupingConfigurationEnabled ] [-ReopenClosedIncident ] [-LookbackDuration ] + [-EntitiesMatchingMethod ] [-GroupByEntities ] [-AggregationKind ] + [-AlertRuleTemplateName ] [-ProductFilter ] [-SeveritiesFilter ] + [-DisplayNamesFilter ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -30,7 +32,25 @@ Use this function creates Azure Sentinal Alert rules from provided CMDLET ### EXAMPLE 1 ``` New-AzSentinelAlertRule -WorkspaceName "" -DisplayName "" -Description "" -Severity -Enabled $true -Query '' -QueryFrequency "" -QueryPeriod "" -TriggerOperator -TriggerThreshold -SuppressionDuration "" -SuppressionEnabled $false -Tactics @("","") -PlaybookName "" -In this example you create a new Alert rule by defining the rule properties from CMDLET +Example on how to create a scheduled rule +``` + +### EXAMPLE 2 +``` +New-AzSentinelAlertRule -WorkspaceName "" -Kind Fusion -DisplayName "Advanced Multistage Attack Detection" -Enabled $true -AlertRuleTemplateName "f71aba3d-28fb-450b-b192-4e76a83015c8" +Example on how to create a Fusion rule +``` + +### EXAMPLE 3 +``` +New-AzSentinelAlertRule -WorkspaceName "" -Kind MLBehaviorAnalytics -DisplayName "(Preview) Anomalous SSH Login Detection" -Enabled $true -AlertRuleTemplateName "fa118b98-de46-4e94-87f9-8e6d5060b60b" +Example on how to create a MLBehaviorAnalytics rule +``` + +### EXAMPLE 4 +``` +New-AzSentinelAlertRule -WorkspaceName "" -Kind MicrosoftSecurityIncidentCreation -DisplayName "" -Description "" -Enabled $true -ProductFilter "" -SeveritiesFilter "","" -DisplayNamesFilter "" +Example on how to create a MicrosoftSecurityIncidentCreation rule ``` ## PARAMETERS @@ -65,15 +85,31 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Kind +The alert rule kind + +```yaml +Type: Kind +Parameter Sets: (All) +Aliases: +Accepted values: Scheduled, Fusion, MLBehaviorAnalytics, MicrosoftSecurityIncidentCreation + +Required: False +Position: Named +Default value: Scheduled +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -DisplayName -Enter the Display name for the Alert rule +The display name for alerts created by this alert rule. ```yaml Type: String Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -81,14 +117,14 @@ Accept wildcard characters: False ``` ### -Description -Enter the Description for the Alert rule +The description of the alert rule. ```yaml Type: String Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -104,7 +140,7 @@ Parameter Sets: (All) Aliases: Accepted values: Medium, High, Low, Informational -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -112,14 +148,14 @@ Accept wildcard characters: False ``` ### -Enabled -Set $true to enable the Alert Rule or $false to disable Alert Rule +Determines whether this alert rule is enabled or disabled. ```yaml Type: Boolean Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: False Accept pipeline input: False @@ -127,14 +163,14 @@ Accept wildcard characters: False ``` ### -Query -Enter the Query that you want to use +The query that creates alerts for this rule. ```yaml Type: String Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -149,7 +185,7 @@ Type: String Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -180,7 +216,7 @@ Parameter Sets: (All) Aliases: Accepted values: GreaterThan, LessThan, Equal, NotEqual, gt, lt, eq, ne -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -195,7 +231,7 @@ Type: Int32 Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: 0 Accept pipeline input: False @@ -210,7 +246,7 @@ Type: String Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -225,7 +261,7 @@ Type: Boolean Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: False Accept pipeline input: False @@ -240,7 +276,7 @@ Type: String[] Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -251,7 +287,7 @@ Accept wildcard characters: False Enter the Logic App name that you want to configure as playbook trigger ```yaml -Type: String +Type: String[] Parameter Sets: (All) Aliases: @@ -368,6 +404,66 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -AlertRuleTemplateName +The Name of the alert rule template used to create this rule + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ProductFilter +The alerts' productName on which the cases will be generated + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SeveritiesFilter +The alerts' severities on which the cases will be generated + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DisplayNamesFilter +The alerts' displayNames on which the cases will be generated + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. diff --git a/docs/Rename-AzSentinelAlertRule.md b/docs/Rename-AzSentinelAlertRule.md index bfcbcf9..e0ed7c6 100644 --- a/docs/Rename-AzSentinelAlertRule.md +++ b/docs/Rename-AzSentinelAlertRule.md @@ -128,6 +128,7 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable ## OUTPUTS +### System.String ## NOTES ## RELATED LINKS diff --git a/examples/AlertRules.json b/examples/AlertRules.json index 15ed4db..5f60bb2 100644 --- a/examples/AlertRules.json +++ b/examples/AlertRules.json @@ -1,5 +1,5 @@ { - "analytics": [ + "Scheduled": [ { "displayName": "AlertRule01", "description": "", @@ -52,5 +52,33 @@ ], "playbookName": "Playbook01" } + ], + "Fusion": [ + { + "displayName": "Advanced Multistage Attack Detection", + "enabled": true, + "alertRuleTemplateName": "f71aba3d-28fb-450b-b192-4e76a83015c8" + } + ], + "MLBehaviorAnalytics": [ + { + "displayName": "(Preview) Anomalous SSH Login Detection", + "enabled": true, + "alertRuleTemplateName": "fa118b98-de46-4e94-87f9-8e6d5060b60b" + } + ], + "MicrosoftSecurityIncidentCreation": [ + { + "displayName": "Create incidents based on Azure Active Directory Identity Protection alerts", + "description": "Create incidents based on all alerts generated in Azure Active Directory Identity Protection", + "enabled": true, + "productFilter": "Microsoft Cloud App Security", + "severitiesFilter": [ + "High", + "Medium", + "Low" + ], + "displayNamesFilter": null + } ] } From 88b234b32bcd353cbd67a9efce56bd3599a01c6c Mon Sep 17 00:00:00 2001 From: ramirezversion <34833071+ramirezversion@users.noreply.github.com> Date: Wed, 16 Sep 2020 11:38:21 +0200 Subject: [PATCH 21/53] New Functionality to get alert rule templates provided by Microsoft (#94) Co-authored-by: Antonio Ramirez --- .../Get-AzSentinelAlertRuleTemplates.ps1 | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100755 AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 diff --git a/AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 new file mode 100755 index 0000000..add2ae8 --- /dev/null +++ b/AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 @@ -0,0 +1,79 @@ +#requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} +#requires -version 6.2 + +function Get-AzSentinelAlertRuleTemplates { + <# + .SYNOPSIS + Get Azure Sentinel Alert Rules Templates + .DESCRIPTION + With this function you can get the configuration of the Azure Sentinel Alert Rules Templates from Azure Sentinel + .PARAMETER SubscriptionId + Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + .PARAMETER WorkspaceName + Enter the Workspace name + .EXAMPLE + Get-AzSentinelAlertRuleTemplates -WorkspaceName "" + In this example you can get Sentinel alert rules templates in once + #> + + [cmdletbinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory = $false, + ParameterSetName = "Sub")] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$WorkspaceName + ) + + begin { + precheck + } + + process { + switch ($PsCmdlet.ParameterSetName) { + Sub { + $arguments = @{ + WorkspaceName = $WorkspaceName + SubscriptionId = $SubscriptionId + } + } + default { + $arguments = @{ + WorkspaceName = $WorkspaceName + } + } + } + Get-LogAnalyticWorkspace @arguments + + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRuleTemplates?api-version=2019-01-01-preview" + + Write-Verbose -Message "Using URI: $($uri)" + + try { + $alertRulesTemplates = Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader + } + catch { + Write-Verbose $_ + Write-Error "Unable to get alert rules with error code: $($_.Exception.Message)" -ErrorAction Stop + } + + $return = @() + + if ($alertRulesTemplates.value) { + Write-Verbose "Found $($alertRulesTemplates.value.count) Alert rules templates" + + # This returns the objects for the alert rule templates which contains id, name, type, kind and properties[severity, query, ...] + # $alertRulesTemplates.Value | ForEach-Object { + # $return += $_.properties + # } + $return = $alertRulesTemplates.value + return $return + } + else { + Write-Warning "No rules templates found on $($WorkspaceName)" + } + } +} From ddc9c0a262a38575c4281799d3a4a902187735b3 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Fri, 18 Sep 2020 15:26:57 +0200 Subject: [PATCH 22/53] Update/get az sentinel alert rule templates (#95) * udating Get-AzSentinelAlertRuleTemplates * updated Co-authored-by: Khabazi --- AzSentinel/AzSentinel.psd1 | 3 +- .../Get-AzSentinelAlertRuleTemplates.ps1 | 31 ++++-- .../docs/Get-AzSentinelAlertRuleTemplates.md | 94 +++++++++++++++++++ AzSentinel/docs/README.md | 1 + AzSentinel/docs/Update-AzSentinelIncident.md | 17 +++- .../Get-AzSentinelAlertRuleTemplates.ps1 | 0 docs/Get-AzSentinelAlertRuleTemplates.md | 94 +++++++++++++++++++ docs/README.md | 1 + docs/Update-AzSentinelIncident.md | 17 +++- 9 files changed, 246 insertions(+), 12 deletions(-) create mode 100644 AzSentinel/docs/Get-AzSentinelAlertRuleTemplates.md create mode 100644 AzSentinel/tests/Unit/public/Get-AzSentinelAlertRuleTemplates.ps1 create mode 100644 docs/Get-AzSentinelAlertRuleTemplates.md diff --git a/AzSentinel/AzSentinel.psd1 b/AzSentinel/AzSentinel.psd1 index ae052e3..d5e912f 100644 --- a/AzSentinel/AzSentinel.psd1 +++ b/AzSentinel/AzSentinel.psd1 @@ -92,7 +92,8 @@ 'Update-AzSentinelIncident', 'Get-AzSentinelAlertRuleAction', 'New-AzSentinelAlertRuleAction', - 'Remove-AzSentinelAlertRuleAction' + 'Remove-AzSentinelAlertRuleAction', + 'Get-AzSentinelAlertRuleTemplates' ) # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. diff --git a/AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 index add2ae8..fa85a51 100755 --- a/AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 @@ -11,12 +11,16 @@ function Get-AzSentinelAlertRuleTemplates { Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used .PARAMETER WorkspaceName Enter the Workspace name + .PARAMETER Kind + Enter the Kind to filter on the templates .EXAMPLE Get-AzSentinelAlertRuleTemplates -WorkspaceName "" In this example you can get Sentinel alert rules templates in once + .EXAMPLE + Get-AzSentinelAlertRuleTemplates -WorkspaceName "" -Kind Fusion, MicrosoftSecurityIncidentCreation + Filter on the Kind #> - [cmdletbinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $false, ParameterSetName = "Sub")] @@ -25,7 +29,11 @@ function Get-AzSentinelAlertRuleTemplates { [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceName + [string]$WorkspaceName, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [Kind[]]$Kind ) begin { @@ -65,15 +73,20 @@ function Get-AzSentinelAlertRuleTemplates { if ($alertRulesTemplates.value) { Write-Verbose "Found $($alertRulesTemplates.value.count) Alert rules templates" - # This returns the objects for the alert rule templates which contains id, name, type, kind and properties[severity, query, ...] - # $alertRulesTemplates.Value | ForEach-Object { - # $return += $_.properties - # } - $return = $alertRulesTemplates.value - return $return + if ($Kind) { + foreach ($item in $Kind) { + $return += $alertRulesTemplates.value | Where-Object Kind -eq $item + } + } + else { + $return += $alertRulesTemplates.value + } + + return $return + } else { - Write-Warning "No rules templates found on $($WorkspaceName)" + Write-Host "No rules templates found on $($WorkspaceName)" } } } diff --git a/AzSentinel/docs/Get-AzSentinelAlertRuleTemplates.md b/AzSentinel/docs/Get-AzSentinelAlertRuleTemplates.md new file mode 100644 index 0000000..a19acea --- /dev/null +++ b/AzSentinel/docs/Get-AzSentinelAlertRuleTemplates.md @@ -0,0 +1,94 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Get-AzSentinelAlertRuleTemplates + +## SYNOPSIS +Get Azure Sentinel Alert Rules Templates + +## SYNTAX + +``` +Get-AzSentinelAlertRuleTemplates [-SubscriptionId ] -WorkspaceName [-Kind ] + [] +``` + +## DESCRIPTION +With this function you can get the configuration of the Azure Sentinel Alert Rules Templates from Azure Sentinel + +## EXAMPLES + +### EXAMPLE 1 +``` +Get-AzSentinelAlertRuleTemplates -WorkspaceName "" +In this example you can get Sentinel alert rules templates in once +``` + +### EXAMPLE 2 +``` +Get-AzSentinelAlertRuleTemplates -WorkspaceName "" -Kind Fusion, MicrosoftSecurityIncidentCreation +Filter on the Kind +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Kind +Enter the Kind to filter on the templates + +```yaml +Type: Kind[] +Parameter Sets: (All) +Aliases: +Accepted values: Scheduled, Fusion, MLBehaviorAnalytics, MicrosoftSecurityIncidentCreation + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/AzSentinel/docs/README.md b/AzSentinel/docs/README.md index e766892..ca399d1 100644 --- a/AzSentinel/docs/README.md +++ b/AzSentinel/docs/README.md @@ -17,3 +17,4 @@ 15. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) 16. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) 17. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) +18. [Get-AzSentinelAlertRuleTemplates](Get-AzSentinelAlertRuleTemplates.md) diff --git a/AzSentinel/docs/Update-AzSentinelIncident.md b/AzSentinel/docs/Update-AzSentinelIncident.md index 8b4709a..3edbe10 100644 --- a/AzSentinel/docs/Update-AzSentinelIncident.md +++ b/AzSentinel/docs/Update-AzSentinelIncident.md @@ -15,7 +15,7 @@ Update Azure Sentinel Incident ``` Update-AzSentinelIncident [-SubscriptionId ] -WorkspaceName -CaseNumber [-Severity ] [-Status ] [-Comment ] [-Labels ] [-CloseReason ] - [-ClosedReasonText ] [-WhatIf] [-Confirm] [] + [-ClosedReasonText ] [-Description ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -181,6 +181,21 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Description +{{ Fill Description Description }} + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. diff --git a/AzSentinel/tests/Unit/public/Get-AzSentinelAlertRuleTemplates.ps1 b/AzSentinel/tests/Unit/public/Get-AzSentinelAlertRuleTemplates.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/docs/Get-AzSentinelAlertRuleTemplates.md b/docs/Get-AzSentinelAlertRuleTemplates.md new file mode 100644 index 0000000..a19acea --- /dev/null +++ b/docs/Get-AzSentinelAlertRuleTemplates.md @@ -0,0 +1,94 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Get-AzSentinelAlertRuleTemplates + +## SYNOPSIS +Get Azure Sentinel Alert Rules Templates + +## SYNTAX + +``` +Get-AzSentinelAlertRuleTemplates [-SubscriptionId ] -WorkspaceName [-Kind ] + [] +``` + +## DESCRIPTION +With this function you can get the configuration of the Azure Sentinel Alert Rules Templates from Azure Sentinel + +## EXAMPLES + +### EXAMPLE 1 +``` +Get-AzSentinelAlertRuleTemplates -WorkspaceName "" +In this example you can get Sentinel alert rules templates in once +``` + +### EXAMPLE 2 +``` +Get-AzSentinelAlertRuleTemplates -WorkspaceName "" -Kind Fusion, MicrosoftSecurityIncidentCreation +Filter on the Kind +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Kind +Enter the Kind to filter on the templates + +```yaml +Type: Kind[] +Parameter Sets: (All) +Aliases: +Accepted values: Scheduled, Fusion, MLBehaviorAnalytics, MicrosoftSecurityIncidentCreation + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/docs/README.md b/docs/README.md index e766892..ca399d1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -17,3 +17,4 @@ 15. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) 16. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) 17. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) +18. [Get-AzSentinelAlertRuleTemplates](Get-AzSentinelAlertRuleTemplates.md) diff --git a/docs/Update-AzSentinelIncident.md b/docs/Update-AzSentinelIncident.md index 8b4709a..3edbe10 100644 --- a/docs/Update-AzSentinelIncident.md +++ b/docs/Update-AzSentinelIncident.md @@ -15,7 +15,7 @@ Update Azure Sentinel Incident ``` Update-AzSentinelIncident [-SubscriptionId ] -WorkspaceName -CaseNumber [-Severity ] [-Status ] [-Comment ] [-Labels ] [-CloseReason ] - [-ClosedReasonText ] [-WhatIf] [-Confirm] [] + [-ClosedReasonText ] [-Description ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -181,6 +181,21 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Description +{{ Fill Description Description }} + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. From ec36613155ab07b49d36a57fd6add4d0e223d547 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Fri, 18 Sep 2020 16:11:02 +0200 Subject: [PATCH 23/53] Feature/add az sentinel incident comment (#96) * udating Get-AzSentinelAlertRuleTemplates * updated * fixing playbook issue * Add-AzSentinelIncidentComment * release Co-authored-by: Khabazi --- AzSentinel/AzSentinel.psd1 | 3 +- .../Public/Add-AzSentinelIncidentComment.ps1 | 104 ++++++++++++ AzSentinel/Public/Get-AzSentinelAlertRule.ps1 | 7 +- .../docs/Add-AzSentinelIncidentComment.md | 148 ++++++++++++++++++ AzSentinel/docs/README.md | 19 +-- .../public/Add-AzSentinelIncidentComment.ps1 | 0 docs/Add-AzSentinelIncidentComment.md | 148 ++++++++++++++++++ docs/README.md | 19 +-- 8 files changed, 426 insertions(+), 22 deletions(-) create mode 100644 AzSentinel/Public/Add-AzSentinelIncidentComment.ps1 create mode 100644 AzSentinel/docs/Add-AzSentinelIncidentComment.md create mode 100644 AzSentinel/tests/Unit/public/Add-AzSentinelIncidentComment.ps1 create mode 100644 docs/Add-AzSentinelIncidentComment.md diff --git a/AzSentinel/AzSentinel.psd1 b/AzSentinel/AzSentinel.psd1 index d5e912f..54b50f5 100644 --- a/AzSentinel/AzSentinel.psd1 +++ b/AzSentinel/AzSentinel.psd1 @@ -93,7 +93,8 @@ 'Get-AzSentinelAlertRuleAction', 'New-AzSentinelAlertRuleAction', 'Remove-AzSentinelAlertRuleAction', - 'Get-AzSentinelAlertRuleTemplates' + 'Get-AzSentinelAlertRuleTemplates', + 'Add-AzSentinelIncidentComment' ) # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. diff --git a/AzSentinel/Public/Add-AzSentinelIncidentComment.ps1 b/AzSentinel/Public/Add-AzSentinelIncidentComment.ps1 new file mode 100644 index 0000000..5ebd11f --- /dev/null +++ b/AzSentinel/Public/Add-AzSentinelIncidentComment.ps1 @@ -0,0 +1,104 @@ +#requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} +#requires -version 6.2 + +function Add-AzSentinelIncidentComment { + <# + .SYNOPSIS + Add Azure Sentinel Incident comment + .DESCRIPTION + With this function you can add comment to existing Azure Sentinel Incident. + .PARAMETER SubscriptionId + Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + .PARAMETER WorkspaceName + Enter the Workspace name + .PARAMETER Name + Enter the name of the incidnet in GUID format + .PARAMETER CaseNumber + Enter the case number to get specfiek details of a open case + .PARAMETER Comment + Enter Comment tekst to add comment to the incident + .EXAMPLE + Add-AzSentinelIncidentComment -WorkspaceName "" CaseNumber "" -Comment + Add a comment to existing incidnet + #> + + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory = $false, + ParameterSetName = "Sub")] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$WorkspaceName, + + [Parameter(Mandatory = $false, + ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [guid]$Name, + + [Parameter(Mandatory = $false, + ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [int]$CaseNumber, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Comment + ) + + begin { + precheck + } + + process { + switch ($PsCmdlet.ParameterSetName) { + Sub { + $arguments = @{ + WorkspaceName = $WorkspaceName + SubscriptionId = $SubscriptionId + } + } + default { + $arguments = @{ + WorkspaceName = $WorkspaceName + } + } + } + + Write-Verbose -Message "Using URI: $($uri)" + + if ($CaseNumber) { + $incident = Get-AzSentinelIncident @arguments -CaseNumber $CaseNumber -All + } + elseif ($Name) { + $incident = Get-AzSentinelIncident @arguments -Name $Name + } + else { + Write-Error "Both CaseNumber and Name are empty" -ErrorAction Stop + } + + if ($incident) { + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/Cases/$($incident.name)/comments/$(New-Guid)?api-version=2019-01-01-preview" + $body = @{ + "properties" = @{ + "message" = $Comment + } + } + + Write-Verbose "Found incident with case number: $($incident.caseNumber)" + + try { + $return = Invoke-WebRequest -Uri $uri -Method Put -Body ($body | ConvertTo-Json -Depth 99 -EnumsAsStrings) -Headers $script:authHeader + return ($return.Content | ConvertFrom-Json).properties + } + catch { + $return = $_.Exception.Message + Write-Verbose $_ + Write-Error "Unable to update Incident $($incident.caseNumber) with error message $return" + return $return + } + } + } +} diff --git a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 index fecf112..2ef5c6e 100644 --- a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 @@ -82,7 +82,7 @@ function Get-AzSentinelAlertRule { [PSCustomObject]$temp = $alertRules.value | Where-Object { $_.properties.displayName -eq $rule } if ($null -ne $temp) { - $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId ($temp.name) + $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId ($temp.name)[0] if ($playbook) { $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] @@ -95,6 +95,7 @@ function Get-AzSentinelAlertRule { $temp.properties | Add-Member -NotePropertyName etag -NotePropertyValue $temp.etag -Force $temp.properties | Add-Member -NotePropertyName id -NotePropertyValue $temp.id -Force $temp.properties | Add-Member -NotePropertyName kind -NotePropertyValue $temp.kind -Force + if ($temp.kind -eq "Scheduled") { $temp.properties | Add-Member -NotePropertyName playbookName -NotePropertyValue $playbookName -Force } @@ -112,7 +113,7 @@ function Get-AzSentinelAlertRule { [PSCustomObject]$temp = $alertRules.value | Where-Object { $_.Kind -eq $rule } if ($null -ne $temp) { - $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId ($temp.name) + $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId ($temp.name)[0] if ($playbook) { $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] @@ -139,7 +140,7 @@ function Get-AzSentinelAlertRule { } else { $alertRules.value | ForEach-Object { - $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId ($_.name) + $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId ($temp.name)[0] if ($playbook) { $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] diff --git a/AzSentinel/docs/Add-AzSentinelIncidentComment.md b/AzSentinel/docs/Add-AzSentinelIncidentComment.md new file mode 100644 index 0000000..da8913f --- /dev/null +++ b/AzSentinel/docs/Add-AzSentinelIncidentComment.md @@ -0,0 +1,148 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Add-AzSentinelIncidentComment + +## SYNOPSIS +Add Azure Sentinel Incident comment + +## SYNTAX + +``` +Add-AzSentinelIncidentComment [-SubscriptionId ] -WorkspaceName [-Name ] + [-CaseNumber ] -Comment [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +With this function you can add comment to existing Azure Sentinel Incident. + +## EXAMPLES + +### EXAMPLE 1 +``` +Add-AzSentinelIncidentComment -WorkspaceName "" CaseNumber "" -Comment +Add a comment to existing incidnet +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Name +Enter the name of the incidnet in GUID format + +```yaml +Type: Guid +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -CaseNumber +Enter the case number to get specfiek details of a open case + +```yaml +Type: Int32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: 0 +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Comment +Enter Comment tekst to add comment to the incident + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/AzSentinel/docs/README.md b/AzSentinel/docs/README.md index ca399d1..546131e 100644 --- a/AzSentinel/docs/README.md +++ b/AzSentinel/docs/README.md @@ -9,12 +9,13 @@ 7. [Disable-AzSentinelAlertRule](Disable-AzSentinelAlertRule.md) 8. [Rename-AzSentinelAlertRule](Rename-AzSentinelAlertRule.md) 9. [Get-AzSentinelIncident](Get-AzSentinelIncident.md) -10. [Update-AzSentinelIncident](Update-AzSentinelIncident.md) -11. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) -12. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) -13. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) -14. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) -15. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) -16. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) -17. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) -18. [Get-AzSentinelAlertRuleTemplates](Get-AzSentinelAlertRuleTemplates.md) +10. [Add-AzSentinelIncidentComment](Add-AzSentinelIncidentComment.md) +11. [Update-AzSentinelIncident](Update-AzSentinelIncident.md) +12. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) +13. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) +14. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) +15. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) +16. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) +17. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) +18. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) +19. [Get-AzSentinelAlertRuleTemplates](Get-AzSentinelAlertRuleTemplates.md) diff --git a/AzSentinel/tests/Unit/public/Add-AzSentinelIncidentComment.ps1 b/AzSentinel/tests/Unit/public/Add-AzSentinelIncidentComment.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/docs/Add-AzSentinelIncidentComment.md b/docs/Add-AzSentinelIncidentComment.md new file mode 100644 index 0000000..da8913f --- /dev/null +++ b/docs/Add-AzSentinelIncidentComment.md @@ -0,0 +1,148 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Add-AzSentinelIncidentComment + +## SYNOPSIS +Add Azure Sentinel Incident comment + +## SYNTAX + +``` +Add-AzSentinelIncidentComment [-SubscriptionId ] -WorkspaceName [-Name ] + [-CaseNumber ] -Comment [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +With this function you can add comment to existing Azure Sentinel Incident. + +## EXAMPLES + +### EXAMPLE 1 +``` +Add-AzSentinelIncidentComment -WorkspaceName "" CaseNumber "" -Comment +Add a comment to existing incidnet +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Name +Enter the name of the incidnet in GUID format + +```yaml +Type: Guid +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -CaseNumber +Enter the case number to get specfiek details of a open case + +```yaml +Type: Int32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: 0 +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Comment +Enter Comment tekst to add comment to the incident + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/docs/README.md b/docs/README.md index ca399d1..546131e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,12 +9,13 @@ 7. [Disable-AzSentinelAlertRule](Disable-AzSentinelAlertRule.md) 8. [Rename-AzSentinelAlertRule](Rename-AzSentinelAlertRule.md) 9. [Get-AzSentinelIncident](Get-AzSentinelIncident.md) -10. [Update-AzSentinelIncident](Update-AzSentinelIncident.md) -11. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) -12. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) -13. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) -14. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) -15. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) -16. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) -17. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) -18. [Get-AzSentinelAlertRuleTemplates](Get-AzSentinelAlertRuleTemplates.md) +10. [Add-AzSentinelIncidentComment](Add-AzSentinelIncidentComment.md) +11. [Update-AzSentinelIncident](Update-AzSentinelIncident.md) +12. [Get-AzSentinelHuntingRule](Get-AzSentinelHuntingRule.md) +13. [New-AzSentinelHuntingRule](New-AzSentinelHuntingRule.md) +14. [Remove-AzSentinelHuntingRule](Remove-AzSentinelHuntingRule.md) +15. [Import-AzSentinelHuntingRule](Import-AzSentinelHuntingRule.md) +16. [Get-AzSentinelAlertRuleAction](Get-AzSentinelAlertRuleAction.md) +17. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) +18. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) +19. [Get-AzSentinelAlertRuleTemplates](Get-AzSentinelAlertRuleTemplates.md) From e69f329671593c3ae9ef6063510f6901cc06a50f Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Tue, 22 Sep 2020 22:39:00 +0200 Subject: [PATCH 24/53] fixing class error (#99) --- AzSentinel/Public/New-AzSentinelAlertRule.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 index 20aba07..a883814 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 @@ -246,7 +246,7 @@ function New-AzSentinelAlertRule { $AggregationKind ) - $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id) + $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id, 'Scheduled') } catch { Write-Error "Unable to initiate class with error: $($_.Exception.Message)" -ErrorAction Stop From 1fdb1ffd30a2c9858d3647de7238d1967181578a Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Thu, 24 Sep 2020 16:06:25 +0200 Subject: [PATCH 25/53] updating example files, ncluding multi rule yaml file (#104) --- examples/AlertRules.yaml | 84 +++++++++++++++++++++++++ examples/SuspectApplicationConsent.yaml | 2 +- 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 examples/AlertRules.yaml diff --git a/examples/AlertRules.yaml b/examples/AlertRules.yaml new file mode 100644 index 0000000..aab69f9 --- /dev/null +++ b/examples/AlertRules.yaml @@ -0,0 +1,84 @@ +Scheduled: + - id: 83ba3057-9ea3-4759-bf6a-933f2e5bc7ee + displayname: Suspect Application Consent + description: | + This will alert when the "Consent to application" operation occurs by a user that has not done this operation before or rarely does this. + This could indicate that permissions to access the listed Azure App were provided to a malicious actor. + Consent to application, Add service principal and Add OAuth2PermissionGrant should typically be rare events. + This may help detect the Oauth2 attack that can be initiated by this publicly available tool - https://github.com/fireeye/PwnAuth + For further information on AuditLogs please see https://docs.microsoft.com/en-us/azure/active-directory/reports-monitoring/reference-audit-activities + severity: High + requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - AuditLogs + queryFrequency: 2H + queryPeriod: 7H + triggerOperator: GreaterThan + triggerThreshold: 3 + tactics: + - Persistence + - LateralMovement + - Collection + playbookName: Playbook01 + query: | + + AzureActivity + | where TimeGenerated >= startofday(ago(7d)) + | where OperationName == "Create or Update Virtual Machine" or OperationName == "Create Deployment" + | where ActivityStatus == "Succeeded" + | make-series dResourceCount=dcount(ResourceId) default=0 on EventSubmissionTimestamp in range(startofday(ago(7d)), now(), 1d) by Caller + | extend (RSquare,Slope,Variance,RVariance,Interception,LineFit)=series_fit_line(dResourceCount) + | where Slope > 0.2 + | join kind=leftsemi ( + // Last day's activity is anomalous + AzureActivity + | where TimeGenerated >= startofday(ago(1d)) + | where OperationName == "Create or Update Virtual Machine" or OperationName == "Create Deployment" + | where ActivityStatus == "Succeeded" + | make-series dResourceCount=dcount(ResourceId) default=0 on EventSubmissionTimestamp in range(startofday(ago(1d)), now(), 1d) by Caller + | extend (RSquare,Slope,Variance,RVariance,Interception,LineFit)=series_fit_line(dResourceCount) + | where Slope >0.2 + ) on Caller + + - id: 83ba3057-9ea3-4759-bf6a-933f2e5bc7ee + displayname: Suspect Application Consent 02 + description: | + This will alert when the "Consent to application" operation occurs by a user that has not done this operation before or rarely does this. + This could indicate that permissions to access the listed Azure App were provided to a malicious actor. + Consent to application, Add service principal and Add OAuth2PermissionGrant should typically be rare events. + This may help detect the Oauth2 attack that can be initiated by this publicly available tool - https://github.com/fireeye/PwnAuth + For further information on AuditLogs please see https://docs.microsoft.com/en-us/azure/active-directory/reports-monitoring/reference-audit-activities + severity: High + requiredDataConnectors: + - connectorId: AzureActiveDirectory + dataTypes: + - AuditLogs + queryFrequency: 2H + queryPeriod: 7H + triggerOperator: GreaterThan + triggerThreshold: 3 + tactics: + - Persistence + - LateralMovement + - Collection + playbookName: Playbook01 + query: | + + AzureActivity + | where TimeGenerated >= startofday(ago(7d)) + | where OperationName == "Create or Update Virtual Machine" or OperationName == "Create Deployment" + | where ActivityStatus == "Succeeded" + | make-series dResourceCount=dcount(ResourceId) default=0 on EventSubmissionTimestamp in range(startofday(ago(7d)), now(), 1d) by Caller + | extend (RSquare,Slope,Variance,RVariance,Interception,LineFit)=series_fit_line(dResourceCount) + | where Slope > 0.2 + | join kind=leftsemi ( + // Last day's activity is anomalous + AzureActivity + | where TimeGenerated >= startofday(ago(1d)) + | where OperationName == "Create or Update Virtual Machine" or OperationName == "Create Deployment" + | where ActivityStatus == "Succeeded" + | make-series dResourceCount=dcount(ResourceId) default=0 on EventSubmissionTimestamp in range(startofday(ago(1d)), now(), 1d) by Caller + | extend (RSquare,Slope,Variance,RVariance,Interception,LineFit)=series_fit_line(dResourceCount) + | where Slope >0.2 + ) on Caller diff --git a/examples/SuspectApplicationConsent.yaml b/examples/SuspectApplicationConsent.yaml index a7ebbad..44cde9e 100644 --- a/examples/SuspectApplicationConsent.yaml +++ b/examples/SuspectApplicationConsent.yaml @@ -1,5 +1,5 @@ id: 83ba3057-9ea3-4759-bf6a-933f2e5bc7ee -name: Suspect Application Consent +displayName: Suspect Application Consent description: | This will alert when the "Consent to application" operation occurs by a user that has not done this operation before or rarely does this. This could indicate that permissions to access the listed Azure App were provided to a malicious actor. From e4c37e8688cd8eb3e7909307f7b109058098e74a Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Thu, 24 Sep 2020 20:50:08 +0200 Subject: [PATCH 26/53] Fix - Get-AzSentinelAlertRuleAction doesn't return playbookName (#102) * fixing return issue * fixing playbook issue --- AzSentinel/Public/Get-AzSentinelAlertRule.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 index 2ef5c6e..4993e10 100644 --- a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 @@ -62,7 +62,7 @@ function Get-AzSentinelAlertRule { } Get-LogAnalyticWorkspace @arguments - $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules?api-version=2019-01-01-preview" + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules?api-version=2020-01-01" Write-Verbose -Message "Using URI: $($uri)" try { @@ -82,7 +82,7 @@ function Get-AzSentinelAlertRule { [PSCustomObject]$temp = $alertRules.value | Where-Object { $_.properties.displayName -eq $rule } if ($null -ne $temp) { - $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId ($temp.name)[0] + $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId $temp.name if ($playbook) { $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] @@ -140,7 +140,7 @@ function Get-AzSentinelAlertRule { } else { $alertRules.value | ForEach-Object { - $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId ($temp.name)[0] + $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId $_.name if ($playbook) { $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] From c3cfca28959fb0f14cdeefb917347860a68d5d75 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Thu, 24 Sep 2020 20:50:46 +0200 Subject: [PATCH 27/53] init release Get-AzSentinelDataConnector function (#103) --- AzSentinel/AzSentinel.psd1 | 3 +- .../Public/Get-AzSentinelDataConnector.ps1 | 92 ++++++++++++++++++ .../docs/Get-AzSentinelDataConnector.md | 93 +++++++++++++++++++ AzSentinel/docs/README.md | 1 + ...> Add-AzSentinelIncidentComment.tests.ps1} | 0 .../Get-AzSentinelDataConnector.tests.ps1 | 0 docs/Get-AzSentinelDataConnector.md | 93 +++++++++++++++++++ docs/README.md | 1 + 8 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 AzSentinel/Public/Get-AzSentinelDataConnector.ps1 create mode 100644 AzSentinel/docs/Get-AzSentinelDataConnector.md rename AzSentinel/tests/Unit/public/{Add-AzSentinelIncidentComment.ps1 => Add-AzSentinelIncidentComment.tests.ps1} (100%) create mode 100644 AzSentinel/tests/Unit/public/Get-AzSentinelDataConnector.tests.ps1 create mode 100644 docs/Get-AzSentinelDataConnector.md diff --git a/AzSentinel/AzSentinel.psd1 b/AzSentinel/AzSentinel.psd1 index 54b50f5..64b58ae 100644 --- a/AzSentinel/AzSentinel.psd1 +++ b/AzSentinel/AzSentinel.psd1 @@ -94,7 +94,8 @@ 'New-AzSentinelAlertRuleAction', 'Remove-AzSentinelAlertRuleAction', 'Get-AzSentinelAlertRuleTemplates', - 'Add-AzSentinelIncidentComment' + 'Add-AzSentinelIncidentComment', + 'Get-AzSentinelDataConnector' ) # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. diff --git a/AzSentinel/Public/Get-AzSentinelDataConnector.ps1 b/AzSentinel/Public/Get-AzSentinelDataConnector.ps1 new file mode 100644 index 0000000..50a0b5f --- /dev/null +++ b/AzSentinel/Public/Get-AzSentinelDataConnector.ps1 @@ -0,0 +1,92 @@ +#requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} +#requires -version 6.2 + +function Get-AzSentinelDataConnector { + <# + .SYNOPSIS + Get Azure Sentinel Data connector + .DESCRIPTION + With this function you can get Azure Sentinel data connectors that are enabled on the workspace + .PARAMETER SubscriptionId + Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + .PARAMETER WorkspaceName + Enter the Workspace name + .PARAMETER DataConnectorName + Enter the Connector ID + .EXAMPLE + Get-AzSentinelDataConnector -WorkspaceName "" + List all enabled dataconnector + .EXAMPLE + Get-AzSentinelDataConnector -WorkspaceName "" -DataConnectorName "","" + Get specific dataconnectors + #> + + param ( + [Parameter(Mandatory = $false, + ParameterSetName = "Sub")] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$WorkspaceName, + + [Parameter(Mandatory = $false, + ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]]$DataConnectorName + ) + + begin { + precheck + } + + process { + switch ($PsCmdlet.ParameterSetName) { + Sub { + $arguments = @{ + WorkspaceName = $WorkspaceName + SubscriptionId = $SubscriptionId + } + } + default { + $arguments = @{ + WorkspaceName = $WorkspaceName + } + } + } + Get-LogAnalyticWorkspace @arguments + + if ($DataConnectorName) { + $dataConnectors = @() + + foreach ($item in $DataConnectorName){ + + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/dataConnectors/$($item)?api-version=2020-01-01" + + try { + $result = Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader + + $dataConnectors += $result + } + catch { + Write-Verbose $_ + Write-Error "Unable to get alert rules with error code: $($_.Exception.Message)" -ErrorAction Stop + } + } + return $dataConnectors + } + else { + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/dataConnectors?api-version=2020-01-01" + + try { + $result = Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader + } + catch { + Write-Verbose $_ + Write-Error "Unable to get alert rules with error code: $($_.Exception.Message)" -ErrorAction Stop + } + return $result.value + } + } +} diff --git a/AzSentinel/docs/Get-AzSentinelDataConnector.md b/AzSentinel/docs/Get-AzSentinelDataConnector.md new file mode 100644 index 0000000..3dc6517 --- /dev/null +++ b/AzSentinel/docs/Get-AzSentinelDataConnector.md @@ -0,0 +1,93 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Get-AzSentinelDataConnector + +## SYNOPSIS +Get Azure Sentinel Data connector + +## SYNTAX + +``` +Get-AzSentinelDataConnector [-SubscriptionId ] -WorkspaceName [-DataConnectorName ] + [] +``` + +## DESCRIPTION +With this function you can get Azure Sentinel data connectors that are enabled on the workspace + +## EXAMPLES + +### EXAMPLE 1 +``` +Get-AzSentinelDataConnector -WorkspaceName "" +List all enabled dataconnector +``` + +### EXAMPLE 2 +``` +Get-AzSentinelDataConnector -WorkspaceName "" -DataConnectorName "","" +Get specific dataconnectors +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DataConnectorName +Enter the Connector ID + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/AzSentinel/docs/README.md b/AzSentinel/docs/README.md index 546131e..7c56fa5 100644 --- a/AzSentinel/docs/README.md +++ b/AzSentinel/docs/README.md @@ -19,3 +19,4 @@ 17. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) 18. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) 19. [Get-AzSentinelAlertRuleTemplates](Get-AzSentinelAlertRuleTemplates.md) +20. [Get-AzSentinelDataConnector](Get-AzSentinelDataConnector.md) diff --git a/AzSentinel/tests/Unit/public/Add-AzSentinelIncidentComment.ps1 b/AzSentinel/tests/Unit/public/Add-AzSentinelIncidentComment.tests.ps1 similarity index 100% rename from AzSentinel/tests/Unit/public/Add-AzSentinelIncidentComment.ps1 rename to AzSentinel/tests/Unit/public/Add-AzSentinelIncidentComment.tests.ps1 diff --git a/AzSentinel/tests/Unit/public/Get-AzSentinelDataConnector.tests.ps1 b/AzSentinel/tests/Unit/public/Get-AzSentinelDataConnector.tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/docs/Get-AzSentinelDataConnector.md b/docs/Get-AzSentinelDataConnector.md new file mode 100644 index 0000000..3dc6517 --- /dev/null +++ b/docs/Get-AzSentinelDataConnector.md @@ -0,0 +1,93 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Get-AzSentinelDataConnector + +## SYNOPSIS +Get Azure Sentinel Data connector + +## SYNTAX + +``` +Get-AzSentinelDataConnector [-SubscriptionId ] -WorkspaceName [-DataConnectorName ] + [] +``` + +## DESCRIPTION +With this function you can get Azure Sentinel data connectors that are enabled on the workspace + +## EXAMPLES + +### EXAMPLE 1 +``` +Get-AzSentinelDataConnector -WorkspaceName "" +List all enabled dataconnector +``` + +### EXAMPLE 2 +``` +Get-AzSentinelDataConnector -WorkspaceName "" -DataConnectorName "","" +Get specific dataconnectors +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DataConnectorName +Enter the Connector ID + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/docs/README.md b/docs/README.md index 546131e..7c56fa5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,3 +19,4 @@ 17. [New-AzSentinelAlertRuleAction](New-AzSentinelAlertRuleAction.md) 18. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) 19. [Get-AzSentinelAlertRuleTemplates](Get-AzSentinelAlertRuleTemplates.md) +20. [Get-AzSentinelDataConnector](Get-AzSentinelDataConnector.md) From 91ea0e377637d401f18c806bdbd83fc1e91ef758 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Thu, 24 Sep 2020 21:44:08 +0200 Subject: [PATCH 28/53] Fix - get-azsentinelhuntingrule updated get and remove function (#106) * fixing hunitng rule get and remove issue * cleaning up * updating filters --- .../Public/Get-AzSentinelHuntingRule.ps1 | 27 ++++++++++--------- .../Public/Remove-AzSentinelHuntingRule.ps1 | 4 +-- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 index 8305000..eaf049c 100644 --- a/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 @@ -41,7 +41,7 @@ function Get-AzSentinelHuntingRule { [Parameter(Mandatory = $false, ValueFromPipeline)] - [ValidateNotNullOrEmpty()] + [validateset("Hunting Queries", "Log Management", "General Exploration")] [string]$Filter ) @@ -72,11 +72,10 @@ function Get-AzSentinelHuntingRule { try { if ($Filter) { - $huntingRules = (Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader) | Where-Object $_.Category -eq $Filter + $huntingRules = (Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader).value | Where-Object { $_.properties.Category -eq $Filter } } else { - $huntingRules = (Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader) - + $huntingRules = (Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader).value } } catch { @@ -86,11 +85,13 @@ function Get-AzSentinelHuntingRule { $return = @() - if ($huntingRules.value) { - Write-Verbose "Found $($huntingRules.value.count) hunting rules" + if ($huntingRules) { + Write-Verbose "Found $($huntingRules.count) hunting rules" if ($RuleName.Count -ge 1) { foreach ($rule in $RuleName) { - [PSCustomObject]$temp = $huntingRules.value | Where-Object { $_.displayName -eq $rule } + $temp = @() + [PSCustomObject]$temp = $huntingRules | Where-Object { ($_.properties).DisplayName -eq $rule } + if ($null -ne $temp) { $temp.properties | Add-Member -NotePropertyName name -NotePropertyValue $temp.name -Force $temp.properties | Add-Member -NotePropertyName id -NotePropertyValue $temp.id -Force @@ -98,23 +99,23 @@ function Get-AzSentinelHuntingRule { $return += $temp.Properties } - else { - Write-Warning "Unable to find hunting rule: $rule" - } } return $return } else { - $huntingRules.value | ForEach-Object { + $huntingRules | ForEach-Object { + $_.properties | Add-Member -NotePropertyName name -NotePropertyValue $_.name -Force $_.properties | Add-Member -NotePropertyName id -NotePropertyValue $_.id -Force $_.properties | Add-Member -NotePropertyName etag -NotePropertyValue $_.etag -Force - return $_.properties + + $return += $_.properties } + return $return } } else { - Write-Warning "No hunting rules found on $($WorkspaceName)" + Write-Verbose "No hunting rules found on $($WorkspaceName)" } } } diff --git a/AzSentinel/Public/Remove-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/Remove-AzSentinelHuntingRule.ps1 index 3549ee7..d2ed182 100644 --- a/AzSentinel/Public/Remove-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/Remove-AzSentinelHuntingRule.ps1 @@ -64,7 +64,7 @@ function Remove-AzSentinelHuntingRule { if ($RuleName) { # remove defined rules foreach ($rule in $RuleName) { - $item = Get-AzSentinelHuntingRule @arguments -Filter 'HuntingQueries' -RuleName $rule + $item = Get-AzSentinelHuntingRule @arguments -RuleName $rule if ($item) { $uri = "$script:baseUri/savedSearches/$($item.name)?api-version=2017-04-26-preview" @@ -90,7 +90,7 @@ function Remove-AzSentinelHuntingRule { } else { Write-Warning "No hunting rule selected, All hunting rules will be removed one by one!" - Get-AzSentinelHuntingRule @arguments -Filter 'HuntingQueries' | ForEach-Object { + Get-AzSentinelHuntingRule @arguments -Filter "Hunting Queries" | ForEach-Object { $uri = "$script:baseUri/savedSearches/$($_.name)?api-version=2017-04-26-preview" if ($PSCmdlet.ShouldProcess("Do you want to remove: $($_.displayName)")) { try { From c1a5db093e5c6bffc7a9c95bdf9a93658632e17c Mon Sep 17 00:00:00 2001 From: pemontto <939704+pemontto@users.noreply.github.com> Date: Wed, 30 Sep 2020 21:52:11 +0100 Subject: [PATCH 29/53] Add filtering by lastModified (#107) --- AzSentinel/Public/Get-AzSentinelAlertRule.ps1 | 16 ++++++++++++- AzSentinel/docs/Get-AzSentinelAlertRule.md | 23 ++++++++++++++++++- docs/Get-AzSentinelAlertRule.md | 23 ++++++++++++++++++- 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 index 4993e10..6ee1c59 100644 --- a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 @@ -15,9 +15,14 @@ function Get-AzSentinelAlertRule { Enter the name of the Alert rule .PARAMETER Kind The alert rule kind + .PARAMETER LastModified + Filter for rules modified after this date/time .EXAMPLE Get-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" In this example you can get configuration of multiple alert rules in once + .EXAMPLE + Get-LogAnalyticWorkspace -SubscriptionId "" -WorkspaceName "" -LastModified 2020-09-21 + In this example you can get configuration of multiple alert rules only if modified after the 21st September 2020. The datetime must be in ISO8601 format. #> [cmdletbinding(SupportsShouldProcess)] @@ -39,7 +44,12 @@ function Get-AzSentinelAlertRule { [Parameter(Mandatory = $false, ValueFromPipeline)] [ValidateNotNullOrEmpty()] - [Kind[]]$Kind + [Kind[]]$Kind, + + [Parameter(Mandatory = $false, + ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [DateTime]$LastModified ) begin { @@ -74,6 +84,10 @@ function Get-AzSentinelAlertRule { } $return = @() + if ($alertRules.value -and $LastModified) { + Write-Verbose "Filtering for rules modified after $LastModified" + $alertRules.value = $alertRules.value | Where-Object { $_.properties.lastModifiedUtc -gt $LastModified } + } if ($alertRules.value) { Write-Verbose "Found $($alertRules.value.count) Alert rules" diff --git a/AzSentinel/docs/Get-AzSentinelAlertRule.md b/AzSentinel/docs/Get-AzSentinelAlertRule.md index a020fa8..468db9e 100644 --- a/AzSentinel/docs/Get-AzSentinelAlertRule.md +++ b/AzSentinel/docs/Get-AzSentinelAlertRule.md @@ -14,7 +14,7 @@ Get Azure Sentinel Alert Rules ``` Get-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-RuleName ] - [-Kind ] [-WhatIf] [-Confirm] [] + [-Kind ] [-LastModified ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -28,6 +28,12 @@ Get-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" In this example you can get configuration of multiple alert rules in once ``` +### EXAMPLE 2 +``` +Get-LogAnalyticWorkspace -SubscriptionId "" -WorkspaceName "" -LastModified 2020-09-21 +In this example you can get configuration of multiple alert rules only if modified after the 21st September 2020. The datetime must be in ISO8601 format. +``` + ## PARAMETERS ### -SubscriptionId @@ -91,6 +97,21 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -LastModified +The minimum lastModified time to return + +```yaml +Type: DateTime +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. diff --git a/docs/Get-AzSentinelAlertRule.md b/docs/Get-AzSentinelAlertRule.md index a020fa8..468db9e 100644 --- a/docs/Get-AzSentinelAlertRule.md +++ b/docs/Get-AzSentinelAlertRule.md @@ -14,7 +14,7 @@ Get Azure Sentinel Alert Rules ``` Get-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-RuleName ] - [-Kind ] [-WhatIf] [-Confirm] [] + [-Kind ] [-LastModified ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -28,6 +28,12 @@ Get-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" In this example you can get configuration of multiple alert rules in once ``` +### EXAMPLE 2 +``` +Get-LogAnalyticWorkspace -SubscriptionId "" -WorkspaceName "" -LastModified 2020-09-21 +In this example you can get configuration of multiple alert rules only if modified after the 21st September 2020. The datetime must be in ISO8601 format. +``` + ## PARAMETERS ### -SubscriptionId @@ -91,6 +97,21 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -LastModified +The minimum lastModified time to return + +```yaml +Type: DateTime +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. From 6be4a6e313427da47df541ddb287c0dffec44abe Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Mon, 5 Oct 2020 21:52:59 +0200 Subject: [PATCH 30/53] updating AggregationKind class and enum (#111) --- AzSentinel/Classes/ScheduledAlertProp.ps1 | 4 ++-- AzSentinel/Public/New-AzSentinelAlertRule.ps1 | 2 +- AzSentinel/enums/aggregationKind.ps1 | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AzSentinel/Classes/ScheduledAlertProp.ps1 b/AzSentinel/Classes/ScheduledAlertProp.ps1 index 672154e..28eb100 100644 --- a/AzSentinel/Classes/ScheduledAlertProp.ps1 +++ b/AzSentinel/Classes/ScheduledAlertProp.ps1 @@ -30,7 +30,7 @@ class ScheduledAlertProp { [IncidentConfiguration]$IncidentConfiguration - $queryResultsAggregationSettings + $eventGroupingSettings hidden [AggregationKind]$aggregationKind @@ -94,7 +94,7 @@ class ScheduledAlertProp { $this.Tactics = $Tactics $this.PlaybookName = $PlaybookName $this.IncidentConfiguration = $IncidentConfiguration - $this.queryResultsAggregationSettings = @{ + $this.eventGroupingSettings = @{ aggregationKind = if ($aggregationKind) { $aggregationKind } else { "SingleAlert" } } } diff --git a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 index a883814..26cbdc2 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 @@ -148,7 +148,7 @@ function New-AzSentinelAlertRule { [string[]]$GroupByEntities, [Parameter(Mandatory = $false)] - [string]$AggregationKind, + [AggregationKind]$AggregationKind, #Fusion & MLBehaviorAnalytics [Parameter(Mandatory = $false)] diff --git a/AzSentinel/enums/aggregationKind.ps1 b/AzSentinel/enums/aggregationKind.ps1 index 950314d..fb80236 100644 --- a/AzSentinel/enums/aggregationKind.ps1 +++ b/AzSentinel/enums/aggregationKind.ps1 @@ -1,4 +1,4 @@ enum AggregationKind { SingleAlert - AlertPerRow + AlertPerResult } From 83c4013ccabfda48f45cef4bd5b199ad6837d8b9 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Thu, 8 Oct 2020 21:19:33 +0200 Subject: [PATCH 31/53] Release of Import-AzSentinelDataConnector function (#116) --- AzSentinel/AzSentinel.psd1 | 3 +- .../Private/Get-LogAnalyticWorkspace.ps1 | 83 +++--- .../Public/Get-AzSentinelDataConnector.ps1 | 25 +- .../Public/Import-AzSentinelDataConnector.ps1 | 260 ++++++++++++++++++ AzSentinel/docs/Get-AzSentinelAlertRule.md | 2 +- .../docs/Get-AzSentinelDataConnector.md | 18 +- .../docs/Import-AzSentinelDataConnector.md | 87 ++++++ AzSentinel/docs/New-AzSentinelAlertRule.md | 5 +- AzSentinel/docs/README.md | 1 + AzSentinel/enums/DataSourceName.ps1 | 35 +++ .../Import-AzSentinelDataConnector.tests.ps1 | 0 README.md | 2 +- docs/Get-AzSentinelAlertRule.md | 2 +- docs/Get-AzSentinelDataConnector.md | 18 +- docs/Import-AzSentinelDataConnector.md | 87 ++++++ docs/New-AzSentinelAlertRule.md | 5 +- docs/README.md | 1 + examples/DataConnectors.json | 30 ++ 18 files changed, 613 insertions(+), 51 deletions(-) create mode 100644 AzSentinel/Public/Import-AzSentinelDataConnector.ps1 create mode 100644 AzSentinel/docs/Import-AzSentinelDataConnector.md create mode 100644 AzSentinel/enums/DataSourceName.ps1 create mode 100644 AzSentinel/tests/Unit/public/Import-AzSentinelDataConnector.tests.ps1 create mode 100644 docs/Import-AzSentinelDataConnector.md create mode 100644 examples/DataConnectors.json diff --git a/AzSentinel/AzSentinel.psd1 b/AzSentinel/AzSentinel.psd1 index 64b58ae..0a71d87 100644 --- a/AzSentinel/AzSentinel.psd1 +++ b/AzSentinel/AzSentinel.psd1 @@ -95,7 +95,8 @@ 'Remove-AzSentinelAlertRuleAction', 'Get-AzSentinelAlertRuleTemplates', 'Add-AzSentinelIncidentComment', - 'Get-AzSentinelDataConnector' + 'Get-AzSentinelDataConnector', + 'Import-AzSentinelDataConnector' ) # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. diff --git a/AzSentinel/Private/Get-LogAnalyticWorkspace.ps1 b/AzSentinel/Private/Get-LogAnalyticWorkspace.ps1 index cdb53cb..651ca37 100644 --- a/AzSentinel/Private/Get-LogAnalyticWorkspace.ps1 +++ b/AzSentinel/Private/Get-LogAnalyticWorkspace.ps1 @@ -1,7 +1,7 @@ #requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} #requires -version 6.2 function Get-LogAnalyticWorkspace { - <# + <# .SYNOPSIS Get log analytic workspace .DESCRIPTION @@ -24,50 +24,53 @@ function Get-LogAnalyticWorkspace { .NOTES NAME: Get-LogAnalyticWorkspace #> - param ( - [Parameter(Mandatory = $false)] - [ValidateNotNullOrEmpty()] - [string] $SubscriptionId, + param ( + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$WorkspaceName, + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$WorkspaceName, - [Parameter(Mandatory = $false)] - [ValidateNotNullOrEmpty()] - [Switch]$FullObject - ) + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [Switch]$FullObject + ) - begin { - precheck - } - - process { - if ($SubscriptionId) { - Write-Verbose "Getting Worspace from Subscription $($subscriptionId)" - $uri = "https://management.azure.com/subscriptions/$($subscriptionId)/providers/Microsoft.OperationalInsights/workspaces?api-version=2015-11-01-preview" - } - elseif ($script:subscriptionId) { - Write-Verbose "Getting Worspace from Subscription $($script:subscriptionId)" - $uri = "https://management.azure.com/subscriptions/$($script:subscriptionId)/providers/Microsoft.OperationalInsights/workspaces?api-version=2015-11-01-preview" - } - else { - Write-Error "No SubscriptionID provided" -ErrorAction Stop + begin { + precheck } - $workspaces = Invoke-webrequest -Uri $uri -Method get -Headers $script:authHeader - $workspaceObject = ($workspaces.Content | ConvertFrom-Json).value | Where-Object { $_.name -eq $WorkspaceName } + process { + if ($SubscriptionId) { + Write-Verbose "Getting Worspace from Subscription $($subscriptionId)" + $uri = "https://management.azure.com/subscriptions/$($subscriptionId)/providers/Microsoft.OperationalInsights/workspaces?api-version=2015-11-01-preview" + } + elseif ($script:subscriptionId) { + Write-Verbose "Getting Worspace from Subscription $($script:subscriptionId)" + $uri = "https://management.azure.com/subscriptions/$($script:subscriptionId)/providers/Microsoft.OperationalInsights/workspaces?api-version=2015-11-01-preview" + } + else { + Write-Error "No SubscriptionID provided" -ErrorAction Stop + } - if ($workspaceObject) { - $Script:workspace = ($workspaceObject.id).trim() - Write-Verbose "Workspace is: $($Script:workspace)" - $script:baseUri = "https://management.azure.com$($Script:workspace)" - if ($FullObject) { return $workspaceObject } - Write-Verbose ($workspaceObject | Format-List | Format-Table | Out-String) - Write-Verbose "Found Workspace $WorkspaceName in RG $($workspaceObject.id.Split('/')[4])" - } - else { - Write-Error "Unable to find workspace $WorkspaceName under Subscription Id: $($script:subscriptionId)" -ErrorAction Stop + $workspaces = Invoke-webrequest -Uri $uri -Method get -Headers $script:authHeader + $workspaceObject = ($workspaces.Content | ConvertFrom-Json).value | Where-Object { $_.name -eq $WorkspaceName } + + if ($workspaceObject) { + $Script:workspace = ($workspaceObject.id).trim() + $script:workspaceId = $workspaceObject.properties.customerId + Write-Verbose "Workspace is: $($Script:workspace)" + $script:baseUri = "https://management.azure.com$($Script:workspace)" + if ($FullObject) { + return $workspaceObject + } + Write-Verbose ($workspaceObject | Format-List | Format-Table | Out-String) + Write-Verbose "Found Workspace $WorkspaceName in RG $($workspaceObject.id.Split('/')[4])" + } + else { + Write-Error "Unable to find workspace $WorkspaceName under Subscription Id: $($script:subscriptionId)" -ErrorAction Stop + } } - } } diff --git a/AzSentinel/Public/Get-AzSentinelDataConnector.ps1 b/AzSentinel/Public/Get-AzSentinelDataConnector.ps1 index 50a0b5f..1f8d5a1 100644 --- a/AzSentinel/Public/Get-AzSentinelDataConnector.ps1 +++ b/AzSentinel/Public/Get-AzSentinelDataConnector.ps1 @@ -34,7 +34,12 @@ function Get-AzSentinelDataConnector { [Parameter(Mandatory = $false, ValueFromPipeline)] [ValidateNotNullOrEmpty()] - [string[]]$DataConnectorName + [string[]]$DataConnectorName, + + [Parameter(Mandatory = $false, + ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [DataSourceName[]]$DataSourceName ) begin { @@ -76,6 +81,24 @@ function Get-AzSentinelDataConnector { } return $dataConnectors } + elseif ($DataSourceName) { + $dataSources = @() + + foreach ($dataSource in $DataSourceName){ + $uri = $($script:baseUri)+ "/dataSources?"+'$'+"filter=kind+eq+'"+$dataSource+"'&api-version=2020-08-01" + + try { + $result = Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader + + $dataSources += $result + } + catch { + Write-Verbose $_ + Write-Error "Unable to get alert rules with error code: $($_.Exception.Message)" -ErrorAction Stop + } + } + return $dataSources.value + } else { $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/dataConnectors?api-version=2020-01-01" diff --git a/AzSentinel/Public/Import-AzSentinelDataConnector.ps1 b/AzSentinel/Public/Import-AzSentinelDataConnector.ps1 new file mode 100644 index 0000000..ac3f7a5 --- /dev/null +++ b/AzSentinel/Public/Import-AzSentinelDataConnector.ps1 @@ -0,0 +1,260 @@ +#requires -module @{ModuleName = 'Az.Accounts'; ModuleVersion = '1.5.2'} +#requires -version 6.2 + +function Import-AzSentinelDataConnector { + <# + .SYNOPSIS + Import Azure Sentinel Data Connectors + .DESCRIPTION + This function imports Azure Sentinel Data Connectors + .PARAMETER SubscriptionId + Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + .PARAMETER WorkspaceName + Enter the Workspace name + .PARAMETER SettingsFile + Path to the JSON file for the Data Connectors + .EXAMPLE + Import-AzSentinelDataConnector -WorkspaceName "" -SettingsFile ".\examples\DataConnectors.json" + In this example all the Data Conenctors configured in the JSON file will be created or updated + #> + + param ( + [Parameter(Mandatory = $false, + ParameterSetName = "Sub")] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $WorkspaceName, + + [Parameter(Mandatory, ValueFromPipeline)] + [ValidateScript( { (Test-Path -Path $_) -and ($_.Extension -in '.json') })] + [System.IO.FileInfo] $SettingsFile + ) + + begin { + precheck + } + + process { + switch ($PsCmdlet.ParameterSetName) { + Sub { + $arguments = @{ + WorkspaceName = $WorkspaceName + SubscriptionId = $SubscriptionId + } + } + default { + $arguments = @{ + WorkspaceName = $WorkspaceName + SubscriptionId = $script:SubscriptionId + } + } + } + Get-LogAnalyticWorkspace @arguments + + if ($SettingsFile.Extension -eq '.json') { + try { + $connectors = Get-Content -Raw $SettingsFile | ConvertFrom-Json -Depth 99 + } + catch { + Write-Verbose $_ + Write-Error -Message 'Unable to import JSON file' -ErrorAction Stop + } + } + else { + Write-Error -Message 'Unsupported extension for SettingsFile' -ErrorAction Stop + } + + <# + Get all the DataConenctors + #> + $enabledDataConnectors = Get-AzSentinelDataConnector @arguments -ErrorAction SilentlyContinue + + <# + Get AzureActivityLog connector data + #> + $azureActivityLog = Get-AzSentinelDataConnector @arguments -DataSourceName 'AzureActivityLog' -ErrorAction SilentlyContinue + + foreach ($item in $connectors.AzureActivityLog) { + $azureActivity = $azureActivityLog | Where-Object { $_.properties.linkedResourceId.Split('/')[2] -eq $item.subscriptionId } + + if ($azureActivity) { + Write-Host "AzureActivityLog is already enabled on '$($item.subscriptionId)'" + } + else { + $name = ($item.subscriptionId).Replace('-', '') + $connectorBody = @{ + id = "$script:Workspace/datasources/$name" + name = $name + type = 'Microsoft.OperationalInsights/workspaces/datasources' + kind = 'AzureActivityLog' + properties = @{ + linkedResourceId = "/subscriptions/$($item.subscriptionId)/providers/microsoft.insights/eventtypes/management" + } + } + + $uri = "$baseUri/datasources/$($name)?api-version=2020-03-01-preview" + + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($connectorBody | ConvertTo-Json -Depth 4 -EnumsAsStrings) + + Write-Host "Successfully enabled AzureActivityLog for: $($item.subscriptionId) with status: $($result.StatusDescription)" + } + catch { + $errorReturn = $_.Exception.Message + Write-Verbose $_ + Write-Error "Unable to invoke webrequest with error message: $($errorReturn)" -ErrorAction Stop + } + } + } + + #AzureSecurityCenter connector + foreach ($item in $connectors.AzureSecurityCenter) { + + $azureSecurityCenter = $enabledDataConnectors | Where-Object { $_.Kind -eq "AzureSecurityCenter" -and $_.properties.subscriptionId -eq $item.subscriptionId } + $skip = $false + + if ($null -ne $azureSecurityCenter) { + if ($azureSecurityCenter.properties.dataTypes.alerts.state -eq $item.state) { + Write-Host "AzureSecurityCenter is already '$($item.state)' for subscription '$($azureSecurityCenter.properties.subscriptionId)'" + $skip = $true + } + else { + $connectorBody = @{ + id = $azureSecurityCenter.id + name = $azureSecurityCenter.name + etag = $azureSecurityCenter.etag + type = 'Microsoft.SecurityInsights/dataConnectors' + kind = 'AzureSecurityCenter' + properties = @{ + subscriptionId = $azureSecurityCenter.properties.subscriptionId + dataTypes = @{ + alerts = @{ + state = $item.state + } + } + } + } + } + } + else { + $guid = (New-Guid).Guid + + $connectorBody = @{ + id = "$script:Workspace/providers/Microsoft.SecurityInsights/dataConnectors/$guid" + name = $guid + type = 'Microsoft.SecurityInsights/dataConnectors' + kind = 'AzureSecurityCenter' + properties = @{ + subscriptionId = $item.subscriptionId + dataTypes = @{ + alerts = @{ + state = $item.state + } + } + } + } + } + + if ($skip -eq $false) { + # Enable or update AzureSecurityCenter with http put method + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/dataConnectors/$($connectorBody.name)?api-version=2020-01-01" + + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($connectorBody | ConvertTo-Json -Depth 4 -EnumsAsStrings) + + Write-Host "Successfully enabled AzureSecurityCenter for: $($item.subscriptionId) with status: $($result.StatusDescription)" + + } + catch { + $errorReturn = $_ + $errorResult = ($errorReturn | ConvertFrom-Json ).error + Write-Verbose $_ + Write-Error "Unable to invoke webrequest with error message: $($errorResult.message)" -ErrorAction Stop + } + } + } + + #ThreatIntelligenceTaxii + foreach ($item in $connectors.ThreatIntelligenceTaxii) { + $threatIntelligenceTaxii = $enabledDataConnectors | Where-Object { $_.Kind -eq "ThreatIntelligenceTaxii" -and $_.properties.friendlyName -eq $item.friendlyName } + $skip = $false + + if ($null -ne $threatIntelligenceTaxii) { + + if ($threatIntelligenceTaxii.properties.dataTypes.taxiiClient.state -eq $item.state) { + Write-Host "ThreatIntelligenceTaxii is already $($item.state) for '$($item.friendlyName)'" + $skip = $true + } + else { + # Compose body for connector update scenario + $connectorBody = @{ + id = $threatIntelligenceTaxii.id + name = $threatIntelligenceTaxii.name + etag = $threatIntelligenceTaxii.etag + type = 'Microsoft.SecurityInsights/dataConnectors' + kind = 'ThreatIntelligenceTaxii' + properties = @{ + tenantId = $script:tenantId + workspaceId = $script:workspaceId + friendlyName = $item.friendlyName + taxiiServer = $item.taxiiServer + collectionId = $item.collectionId + username = $item.username + password = $item.password + taxiiClients = $null + dataTypes = @{ + taxiiClient = @{ + state = $item.state + } + } + } + } + } + } + else { + $guid = (New-Guid).Guid + # Compose body for connector enable scenario + $connectorBody = @{ + id = "$script:Workspace/providers/Microsoft.SecurityInsights/dataConnectors/$guid" + name = $guid + type = 'Microsoft.SecurityInsights/dataConnectors' + kind = 'ThreatIntelligenceTaxii' + properties = @{ + tenantId = $script:tenantId + workspaceId = $script:workspaceId + friendlyName = $item.friendlyName + taxiiServer = $item.taxiiServer + collectionId = $item.collectionId + username = $item.username + password = $item.password + taxiiClients = $null + dataTypes = @{ + taxiiClient = @{ + state = $item.state + } + } + } + } + } + + if ($skip -eq $false) { + # Enable or update ThreatIntelligenceTaxii + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/dataConnectors/$($connectorBody.name)?api-version=2020-01-01" + + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($connectorBody | ConvertTo-Json -Depth 4 -EnumsAsStrings) + + Write-Host "Successfully enabled ThreatIntelligenceTaxii for: $($item.friendlyName) with status: $($result.StatusDescription)" + } + catch { + $errorReturn = $_.Exception.Message + Write-Verbose $_ + Write-Error "Unable to invoke webrequest with error message: $($errorReturn)" -ErrorAction Stop + } + } + } + } +} diff --git a/AzSentinel/docs/Get-AzSentinelAlertRule.md b/AzSentinel/docs/Get-AzSentinelAlertRule.md index 468db9e..ba1aac7 100644 --- a/AzSentinel/docs/Get-AzSentinelAlertRule.md +++ b/AzSentinel/docs/Get-AzSentinelAlertRule.md @@ -98,7 +98,7 @@ Accept wildcard characters: False ``` ### -LastModified -The minimum lastModified time to return +Filter for rules modified after this date/time ```yaml Type: DateTime diff --git a/AzSentinel/docs/Get-AzSentinelDataConnector.md b/AzSentinel/docs/Get-AzSentinelDataConnector.md index 3dc6517..072ed26 100644 --- a/AzSentinel/docs/Get-AzSentinelDataConnector.md +++ b/AzSentinel/docs/Get-AzSentinelDataConnector.md @@ -14,7 +14,7 @@ Get Azure Sentinel Data connector ``` Get-AzSentinelDataConnector [-SubscriptionId ] -WorkspaceName [-DataConnectorName ] - [] + [-DataSourceName ] [] ``` ## DESCRIPTION @@ -81,6 +81,22 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -DataSourceName +{{ Fill DataSourceName Description }} + +```yaml +Type: DataSourceName[] +Parameter Sets: (All) +Aliases: +Accepted values: ApplicationInsights, AzureActivityLog, AzureAuditLog, ChangeTrackingContentLocation, ChangeTrackingCustomPath, ChangeTrackingDataTypeConfiguration, ChangeTrackingDefaultRegistry, ChangeTrackingLinuxPath, ChangeTrackingPath, ChangeTrackingRegistry, ChangeTrackingServices, CustomLog, CustomLogCollection, DnsAnalytics, GenericDataSource, IISLogs, ImportComputerGroup, Itsm, LinuxChangeTrackingPath, LinuxPerformanceCollection, LinuxPerformanceObject, LinuxSyslog, LinuxSyslogCollection, NetworkMonitoring, Office365, SecurityCenterSecurityWindowsBaselineConfiguration, SecurityEventCollectionConfiguration, SecurityInsightsSecurityEventCollectionConfiguration, SecurityWindowsBaselineConfiguration, SqlDataClassification, WindowsEvent, WindowsPerformanceCounter, WindowsTelemetry + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). diff --git a/AzSentinel/docs/Import-AzSentinelDataConnector.md b/AzSentinel/docs/Import-AzSentinelDataConnector.md new file mode 100644 index 0000000..5c0c703 --- /dev/null +++ b/AzSentinel/docs/Import-AzSentinelDataConnector.md @@ -0,0 +1,87 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Import-AzSentinelDataConnector + +## SYNOPSIS +Import Azure Sentinel Data Connectors + +## SYNTAX + +``` +Import-AzSentinelDataConnector [-SubscriptionId ] -WorkspaceName -SettingsFile + [] +``` + +## DESCRIPTION +This function imports Azure Sentinel Data Connectors + +## EXAMPLES + +### EXAMPLE 1 +``` +Import-AzSentinelDataConnector -WorkspaceName "" -SettingsFile ".\examples\DataConnectors.json" +In this example all the Data Conenctors configured in the JSON file will be created or updated +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SettingsFile +Path to the JSON file for the Data Connectors + +```yaml +Type: FileInfo +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/AzSentinel/docs/New-AzSentinelAlertRule.md b/AzSentinel/docs/New-AzSentinelAlertRule.md index 3e12b8a..ff7c868 100644 --- a/AzSentinel/docs/New-AzSentinelAlertRule.md +++ b/AzSentinel/docs/New-AzSentinelAlertRule.md @@ -19,7 +19,7 @@ New-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-Kin [-TriggerThreshold ] [-SuppressionDuration ] [-SuppressionEnabled ] [-Tactics ] [-PlaybookName ] [-CreateIncident ] [-GroupingConfigurationEnabled ] [-ReopenClosedIncident ] [-LookbackDuration ] - [-EntitiesMatchingMethod ] [-GroupByEntities ] [-AggregationKind ] + [-EntitiesMatchingMethod ] [-GroupByEntities ] [-AggregationKind ] [-AlertRuleTemplateName ] [-ProductFilter ] [-SeveritiesFilter ] [-DisplayNamesFilter ] [-WhatIf] [-Confirm] [] ``` @@ -393,9 +393,10 @@ Accept wildcard characters: False Configure how rule query results are grouped into alerts ```yaml -Type: String +Type: AggregationKind Parameter Sets: (All) Aliases: +Accepted values: SingleAlert, AlertPerResult Required: False Position: Named diff --git a/AzSentinel/docs/README.md b/AzSentinel/docs/README.md index 7c56fa5..155eec8 100644 --- a/AzSentinel/docs/README.md +++ b/AzSentinel/docs/README.md @@ -20,3 +20,4 @@ 18. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) 19. [Get-AzSentinelAlertRuleTemplates](Get-AzSentinelAlertRuleTemplates.md) 20. [Get-AzSentinelDataConnector](Get-AzSentinelDataConnector.md) +21. [Import-AzSentinelDataConnector](Import-AzSentinelDataConnector.md) diff --git a/AzSentinel/enums/DataSourceName.ps1 b/AzSentinel/enums/DataSourceName.ps1 new file mode 100644 index 0000000..f892dff --- /dev/null +++ b/AzSentinel/enums/DataSourceName.ps1 @@ -0,0 +1,35 @@ +enum DataSourceName { + ApplicationInsights + AzureActivityLog + AzureAuditLog + ChangeTrackingContentLocation + ChangeTrackingCustomPath + ChangeTrackingDataTypeConfiguration + ChangeTrackingDefaultRegistry + ChangeTrackingLinuxPath + ChangeTrackingPath + ChangeTrackingRegistry + ChangeTrackingServices + CustomLog + CustomLogCollection + DnsAnalytics + GenericDataSource + IISLogs + ImportComputerGroup + Itsm + LinuxChangeTrackingPath + LinuxPerformanceCollection + LinuxPerformanceObject + LinuxSyslog + LinuxSyslogCollection + NetworkMonitoring + Office365 + SecurityCenterSecurityWindowsBaselineConfiguration + SecurityEventCollectionConfiguration + SecurityInsightsSecurityEventCollectionConfiguration + SecurityWindowsBaselineConfiguration + SqlDataClassification + WindowsEvent + WindowsPerformanceCounter + WindowsTelemetry +} diff --git a/AzSentinel/tests/Unit/public/Import-AzSentinelDataConnector.tests.ps1 b/AzSentinel/tests/Unit/public/Import-AzSentinelDataConnector.tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md index 1b116c4..547c861 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ See [docs](https://github.com/wortell/AzSentinel/tree/master/docs) folder for do To create a Azure Sentinel Rule, use the following JSON format. -### Roor schema +### Root schema ```JSON { "Scheduled": [ diff --git a/docs/Get-AzSentinelAlertRule.md b/docs/Get-AzSentinelAlertRule.md index 468db9e..ba1aac7 100644 --- a/docs/Get-AzSentinelAlertRule.md +++ b/docs/Get-AzSentinelAlertRule.md @@ -98,7 +98,7 @@ Accept wildcard characters: False ``` ### -LastModified -The minimum lastModified time to return +Filter for rules modified after this date/time ```yaml Type: DateTime diff --git a/docs/Get-AzSentinelDataConnector.md b/docs/Get-AzSentinelDataConnector.md index 3dc6517..072ed26 100644 --- a/docs/Get-AzSentinelDataConnector.md +++ b/docs/Get-AzSentinelDataConnector.md @@ -14,7 +14,7 @@ Get Azure Sentinel Data connector ``` Get-AzSentinelDataConnector [-SubscriptionId ] -WorkspaceName [-DataConnectorName ] - [] + [-DataSourceName ] [] ``` ## DESCRIPTION @@ -81,6 +81,22 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -DataSourceName +{{ Fill DataSourceName Description }} + +```yaml +Type: DataSourceName[] +Parameter Sets: (All) +Aliases: +Accepted values: ApplicationInsights, AzureActivityLog, AzureAuditLog, ChangeTrackingContentLocation, ChangeTrackingCustomPath, ChangeTrackingDataTypeConfiguration, ChangeTrackingDefaultRegistry, ChangeTrackingLinuxPath, ChangeTrackingPath, ChangeTrackingRegistry, ChangeTrackingServices, CustomLog, CustomLogCollection, DnsAnalytics, GenericDataSource, IISLogs, ImportComputerGroup, Itsm, LinuxChangeTrackingPath, LinuxPerformanceCollection, LinuxPerformanceObject, LinuxSyslog, LinuxSyslogCollection, NetworkMonitoring, Office365, SecurityCenterSecurityWindowsBaselineConfiguration, SecurityEventCollectionConfiguration, SecurityInsightsSecurityEventCollectionConfiguration, SecurityWindowsBaselineConfiguration, SqlDataClassification, WindowsEvent, WindowsPerformanceCounter, WindowsTelemetry + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). diff --git a/docs/Import-AzSentinelDataConnector.md b/docs/Import-AzSentinelDataConnector.md new file mode 100644 index 0000000..5c0c703 --- /dev/null +++ b/docs/Import-AzSentinelDataConnector.md @@ -0,0 +1,87 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Import-AzSentinelDataConnector + +## SYNOPSIS +Import Azure Sentinel Data Connectors + +## SYNTAX + +``` +Import-AzSentinelDataConnector [-SubscriptionId ] -WorkspaceName -SettingsFile + [] +``` + +## DESCRIPTION +This function imports Azure Sentinel Data Connectors + +## EXAMPLES + +### EXAMPLE 1 +``` +Import-AzSentinelDataConnector -WorkspaceName "" -SettingsFile ".\examples\DataConnectors.json" +In this example all the Data Conenctors configured in the JSON file will be created or updated +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SettingsFile +Path to the JSON file for the Data Connectors + +```yaml +Type: FileInfo +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/docs/New-AzSentinelAlertRule.md b/docs/New-AzSentinelAlertRule.md index 3e12b8a..ff7c868 100644 --- a/docs/New-AzSentinelAlertRule.md +++ b/docs/New-AzSentinelAlertRule.md @@ -19,7 +19,7 @@ New-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-Kin [-TriggerThreshold ] [-SuppressionDuration ] [-SuppressionEnabled ] [-Tactics ] [-PlaybookName ] [-CreateIncident ] [-GroupingConfigurationEnabled ] [-ReopenClosedIncident ] [-LookbackDuration ] - [-EntitiesMatchingMethod ] [-GroupByEntities ] [-AggregationKind ] + [-EntitiesMatchingMethod ] [-GroupByEntities ] [-AggregationKind ] [-AlertRuleTemplateName ] [-ProductFilter ] [-SeveritiesFilter ] [-DisplayNamesFilter ] [-WhatIf] [-Confirm] [] ``` @@ -393,9 +393,10 @@ Accept wildcard characters: False Configure how rule query results are grouped into alerts ```yaml -Type: String +Type: AggregationKind Parameter Sets: (All) Aliases: +Accepted values: SingleAlert, AlertPerResult Required: False Position: Named diff --git a/docs/README.md b/docs/README.md index 7c56fa5..155eec8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -20,3 +20,4 @@ 18. [Remove-AzSentinelAlertRuleAction](Remove-AzSentinelAlertRuleAction.md) 19. [Get-AzSentinelAlertRuleTemplates](Get-AzSentinelAlertRuleTemplates.md) 20. [Get-AzSentinelDataConnector](Get-AzSentinelDataConnector.md) +21. [Import-AzSentinelDataConnector](Import-AzSentinelDataConnector.md) diff --git a/examples/DataConnectors.json b/examples/DataConnectors.json new file mode 100644 index 0000000..2039468 --- /dev/null +++ b/examples/DataConnectors.json @@ -0,0 +1,30 @@ +{ + "AzureSecurityCenter": [ + { + "subscriptionId": "ebdab2f1-0b79-4181-a70d-82f0ff39243e", + "state": "Enabled" + }, + { + "subscriptionId": "ebdab2f1-0b79-4181-a70d-82f0ff39243e", + "state": "Enabled" + } + ], + "AzureActivityLog": [ + { + "subscriptionId": "ebdab2f1-0b79-4181-a70d-82f0ff39243e" + }, + { + "subscriptionId": "ebdab2f1-0b79-4181-a70d-82f0ff39243e" + } + ], + "ThreatIntelligenceTaxii": [ + { + "friendlyName": "testserver", + "taxiiServer": "https://limo.anomali.com/api/v1/taxii2/feeds/", + "collectionId": "107", + "username": "guest", + "password": "guest", + "state": "enabled" + } + ] +} From 417b86ceb3ce2854a4a6e04b2501bc53caca9482 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Thu, 8 Oct 2020 22:08:00 +0200 Subject: [PATCH 32/53] extra check for Import-AzSentinelDataConnector --- .../Public/Import-AzSentinelDataConnector.ps1 | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/AzSentinel/Public/Import-AzSentinelDataConnector.ps1 b/AzSentinel/Public/Import-AzSentinelDataConnector.ps1 index ac3f7a5..d3c2b08 100644 --- a/AzSentinel/Public/Import-AzSentinelDataConnector.ps1 +++ b/AzSentinel/Public/Import-AzSentinelDataConnector.ps1 @@ -78,7 +78,12 @@ function Import-AzSentinelDataConnector { $azureActivityLog = Get-AzSentinelDataConnector @arguments -DataSourceName 'AzureActivityLog' -ErrorAction SilentlyContinue foreach ($item in $connectors.AzureActivityLog) { - $azureActivity = $azureActivityLog | Where-Object { $_.properties.linkedResourceId.Split('/')[2] -eq $item.subscriptionId } + if ($null -ne $azureActivityLog){ + $azureActivity = $azureActivityLog | Where-Object { $_.properties.linkedResourceId.Split('/')[2] -eq $item.subscriptionId } + } + else { + $azureActivity + } if ($azureActivity) { Write-Host "AzureActivityLog is already enabled on '$($item.subscriptionId)'" @@ -112,8 +117,12 @@ function Import-AzSentinelDataConnector { #AzureSecurityCenter connector foreach ($item in $connectors.AzureSecurityCenter) { - - $azureSecurityCenter = $enabledDataConnectors | Where-Object { $_.Kind -eq "AzureSecurityCenter" -and $_.properties.subscriptionId -eq $item.subscriptionId } + if ($null -ne $enabledDataConnectors){ + $azureSecurityCenter = $enabledDataConnectors | Where-Object { $_.Kind -eq "AzureSecurityCenter" -and $_.properties.subscriptionId -eq $item.subscriptionId } + } + else { + $azureSecurityCenter + } $skip = $false if ($null -ne $azureSecurityCenter) { @@ -179,7 +188,12 @@ function Import-AzSentinelDataConnector { #ThreatIntelligenceTaxii foreach ($item in $connectors.ThreatIntelligenceTaxii) { - $threatIntelligenceTaxii = $enabledDataConnectors | Where-Object { $_.Kind -eq "ThreatIntelligenceTaxii" -and $_.properties.friendlyName -eq $item.friendlyName } + if ($enabledDataConnectors){ + $threatIntelligenceTaxii = $enabledDataConnectors | Where-Object { $_.Kind -eq "ThreatIntelligenceTaxii" -and $_.properties.friendlyName -eq $item.friendlyName } + } + else { + $threatIntelligenceTaxii + } $skip = $false if ($null -ne $threatIntelligenceTaxii) { From 46a84174e2b424c6ff574a5b3d042b1cf884af09 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Fri, 16 Oct 2020 16:30:31 +0200 Subject: [PATCH 33/53] fixing class issue (#118) --- .../Public/Disable-AzSentinelAlertRule.ps1 | 36 ++++++++++++++++--- .../Public/Enable-AzSentinelAlertRule.ps1 | 35 +++++++++++++++--- 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 index 2f5fb45..33f8821 100644 --- a/AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 @@ -60,15 +60,41 @@ function Disable-AzSentinelAlertRule { $rule.enabled = $false $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($rule.name)?api-version=2019-01-01-preview" - $bodyAlertProp = [AlertProp]::new( - ($rule | Select-Object * -ExcludeProperty lastModifiedUtc, etag, id) + $groupingConfiguration = [GroupingConfiguration]::new( + $rule.incidentConfiguration.groupingConfiguration.GroupingConfigurationEnabled, + $rule.incidentConfiguration.groupingConfiguration.ReopenClosedIncident, + $rule.incidentConfiguration.groupingConfiguration.LookbackDuration, + $rule.incidentConfiguration.groupingConfiguration.EntitiesMatchingMethod, + $rule.incidentConfiguration.groupingConfiguration.GroupByEntities ) - $body = [AlertRule]::new( - ($rule | Select-Object lastModifiedUtc, etag, id, name), - $bodyAlertProp + $incidentConfiguration = [IncidentConfiguration]::new( + $rule.incidentConfiguration.CreateIncident, + $groupingConfiguration ) + $bodyAlertProp = [ScheduledAlertProp]::new( + $rule.name, + $rule.DisplayName, + $rule.Description, + $rule.Severity, + $rule.Enabled, + $rule.Query, + $rule.QueryFrequency, + $rule.QueryPeriod, + $rule.TriggerOperator, + $rule.TriggerThreshold, + $rule.SuppressionDuration, + $rule.SuppressionEnabled, + $rule.Tactics, + $rule.PlaybookName, + $incidentConfiguration, + $rule.AggregationKind + ) + + $body = [AlertRule]::new( $rule.name, $rule.etag, $bodyAlertProp, $rule.Id, 'Scheduled') + + try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) Write-Verbose $result diff --git a/AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 index 43826bd..3e2b7c5 100644 --- a/AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 @@ -60,15 +60,40 @@ function Enable-AzSentinelAlertRule { $rule.enabled = $true $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($rule.name)?api-version=2019-01-01-preview" - $bodyAlertProp = [AlertProp]::new( - ($rule | Select-Object * -ExcludeProperty lastModifiedUtc, etag, id) + $groupingConfiguration = [GroupingConfiguration]::new( + $rule.incidentConfiguration.groupingConfiguration.GroupingConfigurationEnabled, + $rule.incidentConfiguration.groupingConfiguration.ReopenClosedIncident, + $rule.incidentConfiguration.groupingConfiguration.LookbackDuration, + $rule.incidentConfiguration.groupingConfiguration.EntitiesMatchingMethod, + $rule.incidentConfiguration.groupingConfiguration.GroupByEntities ) - $body = [AlertRule]::new( - ($rule | Select-Object lastModifiedUtc, etag, id, name), - $bodyAlertProp + $incidentConfiguration = [IncidentConfiguration]::new( + $rule.incidentConfiguration.CreateIncident, + $groupingConfiguration ) + $bodyAlertProp = [ScheduledAlertProp]::new( + $rule.name, + $rule.DisplayName, + $rule.Description, + $rule.Severity, + $rule.Enabled, + $rule.Query, + $rule.QueryFrequency, + $rule.QueryPeriod, + $rule.TriggerOperator, + $rule.TriggerThreshold, + $rule.SuppressionDuration, + $rule.SuppressionEnabled, + $rule.Tactics, + $rule.PlaybookName, + $incidentConfiguration, + $rule.AggregationKind + ) + + $body = [AlertRule]::new( $rule.name, $rule.etag, $bodyAlertProp, $rule.Id, 'Scheduled') + try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) Write-Verbose $result From 94af32cf261877647264920699bb283916852749 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Tue, 20 Oct 2020 16:30:31 +0200 Subject: [PATCH 34/53] New function: Export-AzSentinel (#121) * init code * Release Export-AzSentinel and some small fixes/updates --- AzSentinel/AzSentinel.psd1 | 1 + AzSentinel/Public/Export-AzSentinel.ps1 | 182 ++++++++++++++++++ .../Get-AzSentinelAlertRuleTemplates.ps1 | 22 ++- .../Public/Import-AzSentinelHuntingRule.ps1 | 17 +- AzSentinel/docs/Export-AzSentinel.md | 131 +++++++++++++ AzSentinel/enums/ExportType.ps1 | 6 + ...plates.ps1 => Export-AzSentinel.tests.ps1} | 0 ...Get-AzSentinelAlertRuleTemplates.tests.ps1 | 0 docs/Export-AzSentinel.md | 131 +++++++++++++ examples/HuntingRules.json | 2 +- 10 files changed, 481 insertions(+), 11 deletions(-) create mode 100644 AzSentinel/Public/Export-AzSentinel.ps1 create mode 100644 AzSentinel/docs/Export-AzSentinel.md create mode 100644 AzSentinel/enums/ExportType.ps1 rename AzSentinel/tests/Unit/public/{Get-AzSentinelAlertRuleTemplates.ps1 => Export-AzSentinel.tests.ps1} (100%) create mode 100644 AzSentinel/tests/Unit/public/Get-AzSentinelAlertRuleTemplates.tests.ps1 create mode 100644 docs/Export-AzSentinel.md diff --git a/AzSentinel/AzSentinel.psd1 b/AzSentinel/AzSentinel.psd1 index 0a71d87..7726dc4 100644 --- a/AzSentinel/AzSentinel.psd1 +++ b/AzSentinel/AzSentinel.psd1 @@ -96,6 +96,7 @@ 'Get-AzSentinelAlertRuleTemplates', 'Add-AzSentinelIncidentComment', 'Get-AzSentinelDataConnector', + 'Export-AzSentinel' 'Import-AzSentinelDataConnector' ) diff --git a/AzSentinel/Public/Export-AzSentinel.ps1 b/AzSentinel/Public/Export-AzSentinel.ps1 new file mode 100644 index 0000000..7bd5f29 --- /dev/null +++ b/AzSentinel/Public/Export-AzSentinel.ps1 @@ -0,0 +1,182 @@ +function Export-AzSentinel { + <# + .SYNOPSIS + Export Azure Sentinel + .DESCRIPTION + With this function you can export Azure Sentinel configuration + .PARAMETER SubscriptionId + Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + .PARAMETER WorkspaceName + Enter the Workspace name + .PARAMETER Kind + Select what you want to export: Alert, Hunting, Templates or All + .PARAMETER OutputFolder + The Path where you want to export the JSON files + .PARAMETER TemplatesKind + Select which Kind of templates you want to export, if empy all Templates will be exported + .EXAMPLE + Export-AzSentinel -WorkspaceName '' -Path C:\Temp\ -Kind All + In this example you export Alert, Hunting and Template rules + .EXAMPLE + Export-AzSentinel -WorkspaceName '' -Path C:\Temp\ -Kind Templates + In this example you export only the Templates + .EXAMPLE + Export-AzSentinel -WorkspaceName '' -Path C:\Temp\ -Kind Alert + In this example you export only the Scheduled Alert rules + #> + + param ( + [Parameter(Mandatory = $false, + ParameterSetName = "Sub")] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$WorkspaceName, + + [Parameter(Mandatory)] + [System.IO.FileInfo]$OutputFolder, + + [Parameter(Mandatory, + ValueFromPipeline)] + [ExportType[]]$Kind, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [Kind[]]$TemplatesKind + ) + + begin { + precheck + } + + process { + switch ($PsCmdlet.ParameterSetName) { + Sub { + $arguments = @{ + WorkspaceName = $WorkspaceName + SubscriptionId = $SubscriptionId + } + } + default { + $arguments = @{ + WorkspaceName = $WorkspaceName + } + } + } + + $date = Get-Date -Format HHmmss_ddMMyyyy + + <# + Test export path + #> + if (Test-Path $OutputFolder) { + Write-Verbose "Path Exists" + } + else { + try { + $null = New-Item -Path $OutputFolder -Force -ItemType Directory -ErrorAction Stop + } + catch { + $ErrorMessage = $_.Exception.Message + Write-Error $ErrorMessage + Write-Verbose $_ + Break + } + } + + <# + Export Alert rules section + #> + if (($Kind -like 'Alert') -or ($Kind -like 'All')) { + + $rules = Get-AzSentinelAlertRule @arguments + if ($rules) { + $output = @{ + Scheduled = @() + Fusion = @() + MLBehaviorAnalytics = @() + MicrosoftSecurityIncidentCreation = @() + } + $rules.Kind | ForEach-Object { + $output.$_ += $rules | Where-Object kind -eq $_ + } + + try { + $fullPath = "$($OutputFolder)AlertRules_$date.json" + $output | ConvertTo-Json -EnumsAsStrings -Depth 15 | Out-File $fullPath -ErrorAction Stop + Write-Output "Alert rules exported to: $fullPath" + } + catch { + $ErrorMessage = $_.Exception.Message + Write-Error $ErrorMessage + Write-Verbose $_ + Break + } + } + } + + <# + Export Hunting rules section + #> + if (($Kind -like 'Hunting') -or ($Kind -like 'All')) { + $rules = Get-AzSentinelHuntingRule @arguments + + if ($rules) { + $output = @{ + Hunting = @() + } + $output.Hunting += $rules + try { + $fullPath = "$($OutputFolder)HuntingRules_$date.json" + $output | ConvertTo-Json -EnumsAsStrings -Depth 15 | Out-File $fullPath -ErrorAction Stop + Write-Output "Hunting rules exported to: $fullPath" + } + catch { + $ErrorMessage = $_.Exception.Message + Write-Error $ErrorMessage + Write-Verbose $_ + Break + } + } + } + + <# + Export Templates section + #> + if (($Kind -like 'Templates') -or ($Kind -like 'All')) { + + if ($TemplatesKind) { + $templates = Get-AzSentinelAlertRuleTemplates @arguments -Kind $TemplatesKind + } + else { + $templates = Get-AzSentinelAlertRuleTemplates @arguments + } + + if ($templates) { + $output = @{ + Scheduled = @() + Fusion = @() + MLBehaviorAnalytics = @() + MicrosoftSecurityIncidentCreation = @() + } + $templates.Kind | ForEach-Object { + $output.$_ += $templates | Where-Object kind -eq $_ + } + + try { + $fullPath = "$($OutputFolder)Templates_$date.json" + $output | ConvertTo-Json -EnumsAsStrings -Depth 15 | Out-File $fullPath -ErrorAction Stop + Write-Output "Templates xported to: $fullPath" + } + catch { + $ErrorMessage = $_.Exception.Message + Write-Error $ErrorMessage + Write-Verbose $_ + Break + } + } + } + } +} diff --git a/AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 index fa85a51..a522274 100755 --- a/AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 @@ -61,7 +61,7 @@ function Get-AzSentinelAlertRuleTemplates { Write-Verbose -Message "Using URI: $($uri)" try { - $alertRulesTemplates = Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader + $alertRulesTemplates = (Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader).value } catch { Write-Verbose $_ @@ -70,16 +70,28 @@ function Get-AzSentinelAlertRuleTemplates { $return = @() - if ($alertRulesTemplates.value) { - Write-Verbose "Found $($alertRulesTemplates.value.count) Alert rules templates" + if ($alertRulesTemplates) { + Write-Verbose "Found $($alertRulesTemplates.count) Alert rules templates" if ($Kind) { foreach ($item in $Kind) { - $return += $alertRulesTemplates.value | Where-Object Kind -eq $item + $alertRulesTemplates | Where-Object Kind -eq $item | ForEach-Object { + $_.properties | Add-Member -NotePropertyName name -NotePropertyValue $_.name -Force + $_.properties | Add-Member -NotePropertyName id -NotePropertyValue $_.id -Force + $_.properties | Add-Member -NotePropertyName kind -NotePropertyValue $_.kind -Force + + $return += $_.properties + } } } else { - $return += $alertRulesTemplates.value + $alertRulesTemplates | ForEach-Object { + $_.properties | Add-Member -NotePropertyName name -NotePropertyValue $_.name -Force + $_.properties | Add-Member -NotePropertyName id -NotePropertyValue $_.id -Force + $_.properties | Add-Member -NotePropertyName kind -NotePropertyValue $_.kind -Force + + $return += $_.properties + } } return $return diff --git a/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 index be51e6d..8439227 100644 --- a/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 @@ -65,8 +65,15 @@ function Import-AzSentinelHuntingRule { if ($SettingsFile.Extension -eq '.json') { try { - $analytics = (Get-Content $SettingsFile -Raw | ConvertFrom-Json -ErrorAction Stop).analytics - Write-Verbose -Message "Found $($analytics.count) rules" + $content = (Get-Content $SettingsFile -Raw | ConvertFrom-Json -ErrorAction Stop) + if ($content.analytics){ + $hunting = $content.analytics + } + else { + $hunting = $content.Hunting + } + + Write-Verbose -Message "Found $($hunting.count) rules" } catch { Write-Verbose $_ @@ -75,8 +82,8 @@ function Import-AzSentinelHuntingRule { } elseif ($SettingsFile.Extension -in '.yaml', 'yml') { try { - $analytics = [pscustomobject](Get-Content $SettingsFile -Raw | ConvertFrom-Yaml -ErrorAction Stop) - $analytics | Add-Member -MemberType NoteProperty -Name DisplayName -Value $analytics.name + $hunting = [pscustomobject](Get-Content $SettingsFile -Raw | ConvertFrom-Yaml -ErrorAction Stop) + $hunting | Add-Member -MemberType NoteProperty -Name DisplayName -Value $hunting.name Write-Verbose -Message 'Found compatibel yaml file' } catch { @@ -85,7 +92,7 @@ function Import-AzSentinelHuntingRule { } } - foreach ($item in $analytics) { + foreach ($item in $hunting) { Write-Output "Started with Hunting rule: $($item.displayName)" try { diff --git a/AzSentinel/docs/Export-AzSentinel.md b/AzSentinel/docs/Export-AzSentinel.md new file mode 100644 index 0000000..03f01a3 --- /dev/null +++ b/AzSentinel/docs/Export-AzSentinel.md @@ -0,0 +1,131 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Export-AzSentinel + +## SYNOPSIS +Export Azure Sentinel + +## SYNTAX + +``` +Export-AzSentinel [-SubscriptionId ] -WorkspaceName -OutputFolder + -Kind [-TemplatesKind ] [] +``` + +## DESCRIPTION +With this function you can export Azure Sentinel configuration + +## EXAMPLES + +### EXAMPLE 1 +``` +Export-AzSentinel -WorkspaceName '' -Path C:\Temp\ -Kind All +In this example you export Alert, Hunting and Template rules +``` + +### EXAMPLE 2 +``` +Export-AzSentinel -WorkspaceName '' -Path C:\Temp\ -Kind Templates +In this example you export only the Templates +``` + +### EXAMPLE 3 +``` +Export-AzSentinel -WorkspaceName '' -Path C:\Temp\ -Kind Alert +In this example you export only the Scheduled Alert rules +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -OutputFolder +The Path where you want to export the JSON files + +```yaml +Type: FileInfo +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Kind +Select what you want to export: Alert, Hunting, Templates or All + +```yaml +Type: ExportType[] +Parameter Sets: (All) +Aliases: +Accepted values: Alert, Hunting, All, Templates + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -TemplatesKind +Select which Kind of templates you want to export, if empy all Templates will be exported + +```yaml +Type: Kind[] +Parameter Sets: (All) +Aliases: +Accepted values: Scheduled, Fusion, MLBehaviorAnalytics, MicrosoftSecurityIncidentCreation + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/AzSentinel/enums/ExportType.ps1 b/AzSentinel/enums/ExportType.ps1 new file mode 100644 index 0000000..ce58a41 --- /dev/null +++ b/AzSentinel/enums/ExportType.ps1 @@ -0,0 +1,6 @@ +enum ExportType { + Alert + Hunting + All + Templates +} diff --git a/AzSentinel/tests/Unit/public/Get-AzSentinelAlertRuleTemplates.ps1 b/AzSentinel/tests/Unit/public/Export-AzSentinel.tests.ps1 similarity index 100% rename from AzSentinel/tests/Unit/public/Get-AzSentinelAlertRuleTemplates.ps1 rename to AzSentinel/tests/Unit/public/Export-AzSentinel.tests.ps1 diff --git a/AzSentinel/tests/Unit/public/Get-AzSentinelAlertRuleTemplates.tests.ps1 b/AzSentinel/tests/Unit/public/Get-AzSentinelAlertRuleTemplates.tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/docs/Export-AzSentinel.md b/docs/Export-AzSentinel.md new file mode 100644 index 0000000..03f01a3 --- /dev/null +++ b/docs/Export-AzSentinel.md @@ -0,0 +1,131 @@ +--- +external help file: AzSentinel-help.xml +Module Name: AzSentinel +online version: +schema: 2.0.0 +--- + +# Export-AzSentinel + +## SYNOPSIS +Export Azure Sentinel + +## SYNTAX + +``` +Export-AzSentinel [-SubscriptionId ] -WorkspaceName -OutputFolder + -Kind [-TemplatesKind ] [] +``` + +## DESCRIPTION +With this function you can export Azure Sentinel configuration + +## EXAMPLES + +### EXAMPLE 1 +``` +Export-AzSentinel -WorkspaceName '' -Path C:\Temp\ -Kind All +In this example you export Alert, Hunting and Template rules +``` + +### EXAMPLE 2 +``` +Export-AzSentinel -WorkspaceName '' -Path C:\Temp\ -Kind Templates +In this example you export only the Templates +``` + +### EXAMPLE 3 +``` +Export-AzSentinel -WorkspaceName '' -Path C:\Temp\ -Kind Alert +In this example you export only the Scheduled Alert rules +``` + +## PARAMETERS + +### -SubscriptionId +Enter the subscription ID, if no subscription ID is provided then current AZContext subscription will be used + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WorkspaceName +Enter the Workspace name + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -OutputFolder +The Path where you want to export the JSON files + +```yaml +Type: FileInfo +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Kind +Select what you want to export: Alert, Hunting, Templates or All + +```yaml +Type: ExportType[] +Parameter Sets: (All) +Aliases: +Accepted values: Alert, Hunting, All, Templates + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -TemplatesKind +Select which Kind of templates you want to export, if empy all Templates will be exported + +```yaml +Type: Kind[] +Parameter Sets: (All) +Aliases: +Accepted values: Scheduled, Fusion, MLBehaviorAnalytics, MicrosoftSecurityIncidentCreation + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/examples/HuntingRules.json b/examples/HuntingRules.json index 28c351f..fb37421 100644 --- a/examples/HuntingRules.json +++ b/examples/HuntingRules.json @@ -1,5 +1,5 @@ { - "analytics": [ + "Hunting": [ { "displayName": "HuntingRule01", "description": "test", From a4cd0be70042b7e3adeb75f446063420e91b5cef Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Wed, 21 Oct 2020 15:40:41 +0200 Subject: [PATCH 35/53] fixing SeveritiesFilter issue for MicrosoftSecurityIncidentCreation (#122) --- AzSentinel/Public/New-AzSentinelAlertRule.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 index 26cbdc2..53cc36c 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 @@ -159,7 +159,7 @@ function New-AzSentinelAlertRule { [string]$ProductFilter, [Parameter(Mandatory = $false)] - [string]$SeveritiesFilter, + [Severity[]]$SeveritiesFilter, [Parameter(Mandatory = $false)] [string]$DisplayNamesFilter From 82062b42e0722f0c93864aead5bdb3c9a67ea5f2 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Wed, 21 Oct 2020 20:55:41 +0200 Subject: [PATCH 36/53] updating Get-AzSentinelAlertRule function and docs (#125) --- AzSentinel/Public/Get-AzSentinelAlertRule.ps1 | 106 ++++++++++-------- AzSentinel/docs/Get-AzSentinelAlertRule.md | 19 +++- docs/Get-AzSentinelAlertRule.md | 19 +++- 3 files changed, 91 insertions(+), 53 deletions(-) diff --git a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 index 6ee1c59..6dd8585 100644 --- a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 @@ -17,11 +17,13 @@ function Get-AzSentinelAlertRule { The alert rule kind .PARAMETER LastModified Filter for rules modified after this date/time + .PARAMETER SkipPlaybook + Use SkipPlaybook switch to only return the rule properties, this skips the Playbook resolve step. .EXAMPLE Get-AzSentinelAlertRule -WorkspaceName "" -RuleName "","" In this example you can get configuration of multiple alert rules in once .EXAMPLE - Get-LogAnalyticWorkspace -SubscriptionId "" -WorkspaceName "" -LastModified 2020-09-21 + Get-AzSentinelAlertRule -SubscriptionId "" -WorkspaceName "" -LastModified 2020-09-21 In this example you can get configuration of multiple alert rules only if modified after the 21st September 2020. The datetime must be in ISO8601 format. #> @@ -49,7 +51,12 @@ function Get-AzSentinelAlertRule { [Parameter(Mandatory = $false, ValueFromPipeline)] [ValidateNotNullOrEmpty()] - [DateTime]$LastModified + [DateTime]$LastModified, + + [Parameter(Mandatory = $false, + ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [switch]$SkipPlaybook ) begin { @@ -93,84 +100,85 @@ function Get-AzSentinelAlertRule { if ($RuleName.Count -ge 1) { foreach ($rule in $RuleName) { - [PSCustomObject]$temp = $alertRules.value | Where-Object { $_.properties.displayName -eq $rule } - if ($null -ne $temp) { + $alertRules.value | Where-Object { $_.properties.displayName -eq $rule } | ForEach-Object { - $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId $temp.name + $_.properties | Add-Member -NotePropertyName name -NotePropertyValue $_.name -Force + $_.properties | Add-Member -NotePropertyName etag -NotePropertyValue $_.etag -Force + $_.properties | Add-Member -NotePropertyName id -NotePropertyValue $_.id -Force + $_.properties | Add-Member -NotePropertyName kind -NotePropertyValue $_.kind -Force - if ($playbook) { - $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] - } - else { - $playbookName = "" - } + if (! $SkipPlaybook) { + + $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId $_.name - $temp.properties | Add-Member -NotePropertyName name -NotePropertyValue $temp.name -Force - $temp.properties | Add-Member -NotePropertyName etag -NotePropertyValue $temp.etag -Force - $temp.properties | Add-Member -NotePropertyName id -NotePropertyValue $temp.id -Force - $temp.properties | Add-Member -NotePropertyName kind -NotePropertyValue $temp.kind -Force + if ($playbook) { + $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] + } + else { + $playbookName = "" + } - if ($temp.kind -eq "Scheduled") { - $temp.properties | Add-Member -NotePropertyName playbookName -NotePropertyValue $playbookName -Force + $_.properties | Add-Member -NotePropertyName playbookName -NotePropertyValue $playbookName -Force } - $return += $temp.properties - } - else { - Write-Verbose "Unable to find Rule: $rule" + + $return += $_.properties } } return $return } elseif ($Kind.Count -ge 1) { foreach ($rule in $Kind) { - [PSCustomObject]$temp = $alertRules.value | Where-Object { $_.Kind -eq $rule } - if ($null -ne $temp) { + $alertRules.value | Where-Object { $_.Kind -eq $rule } | ForEach-Object { - $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId ($temp.name)[0] + $_.properties | Add-Member -NotePropertyName name -NotePropertyValue $_.name -Force + $_.properties | Add-Member -NotePropertyName etag -NotePropertyValue $_.etag -Force + $_.properties | Add-Member -NotePropertyName id -NotePropertyValue $_.id -Force + $_.properties | Add-Member -NotePropertyName kind -NotePropertyValue $_.kind -Force - if ($playbook) { - $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] - } - else { - $playbookName = "" - } + if (! $SkipPlaybook) { + + $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId $_.name + + if ($playbook) { + $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] + } + else { + $playbookName = "" + } - $temp.properties | Add-Member -NotePropertyName name -NotePropertyValue $temp.name -Force - $temp.properties | Add-Member -NotePropertyName etag -NotePropertyValue $temp.etag -Force - $temp.properties | Add-Member -NotePropertyName id -NotePropertyValue $temp.id -Force - $temp.properties | Add-Member -NotePropertyName kind -NotePropertyValue $temp.kind -Force - if ($temp.kind -eq "Scheduled") { - $temp.properties | Add-Member -NotePropertyName playbookName -NotePropertyValue $playbookName -Force + $_.properties | Add-Member -NotePropertyName playbookName -NotePropertyValue $playbookName -Force } - $return += $temp.properties - } - else { - Write-Verbose "Unable to find Rule: $rule" + $return += $_.properties } } return $return } else { $alertRules.value | ForEach-Object { - $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId $_.name - if ($playbook) { - $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] - } - else { - $playbookName = "" - } $_.properties | Add-Member -NotePropertyName name -NotePropertyValue $_.name -Force $_.properties | Add-Member -NotePropertyName id -NotePropertyValue $_.id -Force $_.properties | Add-Member -NotePropertyName kind -NotePropertyValue $_.kind -Force - if ($_.kind -eq "Scheduled") { + + if (! $SkipPlaybook) { + + $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId $_.name + + if ($playbook) { + $playbookName = ($playbook.properties.logicAppResourceId).Split('/')[-1] + } + else { + $playbookName = "" + } + $_.properties | Add-Member -NotePropertyName playbookName -NotePropertyValue $playbookName -Force } - return $_.properties + $return += $_.properties } + return $return } } else { diff --git a/AzSentinel/docs/Get-AzSentinelAlertRule.md b/AzSentinel/docs/Get-AzSentinelAlertRule.md index ba1aac7..605429f 100644 --- a/AzSentinel/docs/Get-AzSentinelAlertRule.md +++ b/AzSentinel/docs/Get-AzSentinelAlertRule.md @@ -14,7 +14,7 @@ Get Azure Sentinel Alert Rules ``` Get-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-RuleName ] - [-Kind ] [-LastModified ] [-WhatIf] [-Confirm] [] + [-Kind ] [-LastModified ] [-SkipPlaybook] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -30,7 +30,7 @@ In this example you can get configuration of multiple alert rules in once ### EXAMPLE 2 ``` -Get-LogAnalyticWorkspace -SubscriptionId "" -WorkspaceName "" -LastModified 2020-09-21 +Get-AzSentinelAlertRule -SubscriptionId "" -WorkspaceName "" -LastModified 2020-09-21 In this example you can get configuration of multiple alert rules only if modified after the 21st September 2020. The datetime must be in ISO8601 format. ``` @@ -112,6 +112,21 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -SkipPlaybook +Use SkipPlaybook switch to only return the rule properties, this skips the Playbook resolve step. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. diff --git a/docs/Get-AzSentinelAlertRule.md b/docs/Get-AzSentinelAlertRule.md index ba1aac7..605429f 100644 --- a/docs/Get-AzSentinelAlertRule.md +++ b/docs/Get-AzSentinelAlertRule.md @@ -14,7 +14,7 @@ Get Azure Sentinel Alert Rules ``` Get-AzSentinelAlertRule [-SubscriptionId ] -WorkspaceName [-RuleName ] - [-Kind ] [-LastModified ] [-WhatIf] [-Confirm] [] + [-Kind ] [-LastModified ] [-SkipPlaybook] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -30,7 +30,7 @@ In this example you can get configuration of multiple alert rules in once ### EXAMPLE 2 ``` -Get-LogAnalyticWorkspace -SubscriptionId "" -WorkspaceName "" -LastModified 2020-09-21 +Get-AzSentinelAlertRule -SubscriptionId "" -WorkspaceName "" -LastModified 2020-09-21 In this example you can get configuration of multiple alert rules only if modified after the 21st September 2020. The datetime must be in ISO8601 format. ``` @@ -112,6 +112,21 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -SkipPlaybook +Use SkipPlaybook switch to only return the rule properties, this skips the Playbook resolve step. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. From 1bc9c892751261b1ec0d89a4991378e50c36994a Mon Sep 17 00:00:00 2001 From: John Crouch <50185606+john-crouch@users.noreply.github.com> Date: Tue, 10 Nov 2020 08:55:38 -0600 Subject: [PATCH 37/53] modified token expiration logic (#135) Co-authored-by: John Crouch --- AzSentinel/Private/precheck.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AzSentinel/Private/precheck.ps1 b/AzSentinel/Private/precheck.ps1 index 0718601..b6cb88c 100644 --- a/AzSentinel/Private/precheck.ps1 +++ b/AzSentinel/Private/precheck.ps1 @@ -16,7 +16,7 @@ function precheck { if ($null -eq $script:accessToken) { Get-AuthToken - } elseif ([datetime]::UtcNow.AddMinutes(5) -lt $script:accessToken.ExpiresOn.DateTime ) { + } elseif ($script:accessToken.ExpiresOn.DateTime - [datetime]::UtcNow.AddMinutes(-5) -le 0) { # if token expires within 5 minutes, request a new one Get-AuthToken } From 8fa4c6a480d79c1d5d2923e2d5cacad352337bff Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Tue, 10 Nov 2020 15:58:22 +0100 Subject: [PATCH 38/53] fixing small issues (#136) --- AzSentinel/Classes/groupingConfiguration.ps1 | 2 +- .../Private/Get-LogAnalyticWorkspace.ps1 | 12 +++- AzSentinel/Private/precheck.ps1 | 27 ++++---- .../Public/Disable-AzSentinelAlertRule.ps1 | 10 ++- .../Public/Enable-AzSentinelAlertRule.ps1 | 8 ++- AzSentinel/Public/Export-AzSentinel.ps1 | 34 +++++++++-- AzSentinel/Public/Get-AzSentinelAlertRule.ps1 | 9 ++- .../Public/Get-AzSentinelAlertRuleAction.ps1 | 8 ++- .../Get-AzSentinelAlertRuleTemplates.ps1 | 9 ++- .../Public/Get-AzSentinelDataConnector.ps1 | 9 ++- .../Public/Get-AzSentinelHuntingRule.ps1 | 9 ++- AzSentinel/Public/Get-AzSentinelIncident.ps1 | 9 ++- .../Public/Import-AzSentinelAlertRule.ps1 | 9 ++- .../Public/Import-AzSentinelDataConnector.ps1 | 9 ++- .../Public/Import-AzSentinelHuntingRule.ps1 | 12 +++- AzSentinel/Public/New-AzSentinelAlertRule.ps1 | 10 ++- .../Public/New-AzSentinelHuntingRule.ps1 | 2 +- .../Public/Remove-AzSentinelAlertRule.ps1 | 11 +++- .../Public/Remove-AzSentinelHuntingRule.ps1 | 10 ++- .../Public/Rename-AzSentinelAlertRule.ps1 | 61 +++++++++++++------ AzSentinel/Public/Set-AzSentinel.ps1 | 9 ++- .../Public/Update-AzSentinelIncident.ps1 | 50 ++++++++------- 22 files changed, 242 insertions(+), 87 deletions(-) diff --git a/AzSentinel/Classes/groupingConfiguration.ps1 b/AzSentinel/Classes/groupingConfiguration.ps1 index 5be2398..420c7c6 100644 --- a/AzSentinel/Classes/groupingConfiguration.ps1 +++ b/AzSentinel/Classes/groupingConfiguration.ps1 @@ -36,7 +36,7 @@ class GroupingConfiguration { } groupingConfiguration ($Enabled, $reopenClosedIncident, $lookbackDuration, $entitiesMatchingMethod, $groupByEntities) { - $this.enabled = if ($null -ne $Enabled ) { $Enabled } else { $true } + $this.enabled = if ($null -ne $Enabled ) { $Enabled } else { $false } $this.reopenClosedIncident = if ($null -ne $reopenClosedIncident) { $reopenClosedIncident } else { $false } $this.lookbackDuration = if ($lookbackDuration) { [groupingConfiguration]::TimeString($lookbackDuration) } else { "PT5H" } $this.entitiesMatchingMethod = if ($entitiesMatchingMethod) { $entitiesMatchingMethod } else { "All" } diff --git a/AzSentinel/Private/Get-LogAnalyticWorkspace.ps1 b/AzSentinel/Private/Get-LogAnalyticWorkspace.ps1 index 651ca37..2a1706f 100644 --- a/AzSentinel/Private/Get-LogAnalyticWorkspace.ps1 +++ b/AzSentinel/Private/Get-LogAnalyticWorkspace.ps1 @@ -55,8 +55,14 @@ function Get-LogAnalyticWorkspace { Write-Error "No SubscriptionID provided" -ErrorAction Stop } - $workspaces = Invoke-webrequest -Uri $uri -Method get -Headers $script:authHeader - $workspaceObject = ($workspaces.Content | ConvertFrom-Json).value | Where-Object { $_.name -eq $WorkspaceName } + try { + $workspaces = Invoke-webrequest -Uri $uri -Method get -Headers $script:authHeader -ErrorAction Stop + $workspaceObject = ($workspaces.Content | ConvertFrom-Json).value | Where-Object { $_.name -eq $WorkspaceName } + } + catch { + Write-Error $_.Exception.Message + break + } if ($workspaceObject) { $Script:workspace = ($workspaceObject.id).trim() @@ -70,7 +76,7 @@ function Get-LogAnalyticWorkspace { Write-Verbose "Found Workspace $WorkspaceName in RG $($workspaceObject.id.Split('/')[4])" } else { - Write-Error "Unable to find workspace $WorkspaceName under Subscription Id: $($script:subscriptionId)" -ErrorAction Stop + Write-Error "Unable to find workspace $WorkspaceName under Subscription Id: $($script:subscriptionId)" } } } diff --git a/AzSentinel/Private/precheck.ps1 b/AzSentinel/Private/precheck.ps1 index b6cb88c..19b89f7 100644 --- a/AzSentinel/Private/precheck.ps1 +++ b/AzSentinel/Private/precheck.ps1 @@ -2,27 +2,28 @@ #requires -version 6.2 function precheck { - <# - .SYNOPSIS - PreCheck - .DESCRIPTION - This function is used as a precheck step by all the functions to test all the required authentication and properties. - .EXAMPLE - precheck - Run the test - .NOTES - NAME: precheck - #> + <# + .SYNOPSIS + PreCheck + .DESCRIPTION + This function is used as a precheck step by all the functions to test all the required authentication and properties. + .EXAMPLE + precheck + Run the test + .NOTES + NAME: precheck + #> if ($null -eq $script:accessToken) { Get-AuthToken - } elseif ($script:accessToken.ExpiresOn.DateTime - [datetime]::UtcNow.AddMinutes(-5) -le 0) { + } + elseif ($script:accessToken.ExpiresOn.DateTime - [datetime]::UtcNow.AddMinutes(-5) -le 0) { # if token expires within 5 minutes, request a new one Get-AuthToken } $script:authHeader = @{ 'Content-Type' = 'application/json' - Authorization = 'Bearer ' + $script:accessToken.AccessToken + Authorization = 'Bearer ' + $script:accessToken.AccessToken } } diff --git a/AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 index 33f8821..023b373 100644 --- a/AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Disable-AzSentinelAlertRule.ps1 @@ -50,7 +50,13 @@ function Disable-AzSentinelAlertRule { } } - $rules = Get-AzSentinelAlertRule @arguments -RuleName $RuleName + try { + $rules = Get-AzSentinelAlertRule @arguments -RuleName $RuleName -ErrorAction Stop + } + catch { + $return = $_.Exception.Message + Write-Error $return + } foreach ($rule in $rules) { if ($rule.enabled -eq $false) { @@ -94,12 +100,10 @@ function Disable-AzSentinelAlertRule { $body = [AlertRule]::new( $rule.name, $rule.etag, $bodyAlertProp, $rule.Id, 'Scheduled') - try { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) Write-Verbose $result Write-Output "Status of '$($rule.DisplayName)' changed to '$($rule.enabled)'" - } catch { Write-Error $_.Exception.Message diff --git a/AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 index 3e2b7c5..d87fdc9 100644 --- a/AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Enable-AzSentinelAlertRule.ps1 @@ -50,7 +50,13 @@ function Enable-AzSentinelAlertRule { } } - $rules = Get-AzSentinelAlertRule @arguments -RuleName $RuleName -ErrorAction Stop + try { + $rules = Get-AzSentinelAlertRule @arguments -RuleName $RuleName -ErrorAction Stop + } + catch { + $return = $_.Exception.Message + Write-Error $return + } foreach ($rule in $rules) { if ($rule.enabled -eq $true) { diff --git a/AzSentinel/Public/Export-AzSentinel.ps1 b/AzSentinel/Public/Export-AzSentinel.ps1 index e21a841..da24335 100644 --- a/AzSentinel/Public/Export-AzSentinel.ps1 +++ b/AzSentinel/Public/Export-AzSentinel.ps1 @@ -91,7 +91,14 @@ function Export-AzSentinel { #> if (($Kind -like 'Alert') -or ($Kind -like 'All')) { - $rules = Get-AzSentinelAlertRule @arguments + try { + $rules = Get-AzSentinelAlertRule @arguments -ErrorAction Stop + } + catch { + $return = $_.Exception.Message + Write-Error $return + } + if ($rules) { $output = @{ Scheduled = @( @@ -126,8 +133,13 @@ function Export-AzSentinel { Export Hunting rules section #> if (($Kind -like 'Hunting') -or ($Kind -like 'All')) { - $rules = Get-AzSentinelHuntingRule @arguments - + try { + $rules = Get-AzSentinelHuntingRule @arguments -ErrorAction Stop + } + catch { + $return = $_.Exception.Message + Write-Error $return + } if ($rules) { $output = @{ Hunting = @() @@ -153,10 +165,22 @@ function Export-AzSentinel { if (($Kind -like 'Templates') -or ($Kind -like 'All')) { if ($TemplatesKind) { - $templates = Get-AzSentinelAlertRuleTemplates @arguments -Kind $TemplatesKind + try { + $templates = Get-AzSentinelAlertRuleTemplates @arguments -Kind $TemplatesKind + } + catch { + $return = $_.Exception.Message + Write-Error $return + } } else { - $templates = Get-AzSentinelAlertRuleTemplates @arguments + try { + $templates = Get-AzSentinelAlertRuleTemplates @arguments + } + catch { + $return = $_.Exception.Message + Write-Error $return + } } if ($templates) { diff --git a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 index 6dd8585..004082c 100644 --- a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 @@ -77,7 +77,13 @@ function Get-AzSentinelAlertRule { } } } - Get-LogAnalyticWorkspace @arguments + try { + Get-LogAnalyticWorkspace @arguments -ErrorAction Stop + } + catch { + Write-Error $_.Exception.Message + break + } $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules?api-version=2020-01-01" Write-Verbose -Message "Using URI: $($uri)" @@ -121,7 +127,6 @@ function Get-AzSentinelAlertRule { $_.properties | Add-Member -NotePropertyName playbookName -NotePropertyValue $playbookName -Force } - $return += $_.properties } } diff --git a/AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 index 3e298ac..75cc743 100644 --- a/AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 @@ -60,7 +60,13 @@ function Get-AzSentinelAlertRuleAction { } if ($RuleName) { - $alertId = (Get-AzSentinelAlertRule @arguments -RuleName $RuleName).name + try { + $alertId = (Get-AzSentinelAlertRule @arguments -RuleName $RuleName -ErrorAction Stop).name + } + catch { + Write-Error $_.Exception.Message + break + } } elseif ($RuleId) { $alertId = $RuleId diff --git a/AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 index a522274..90a8a0d 100755 --- a/AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRuleTemplates.ps1 @@ -54,7 +54,14 @@ function Get-AzSentinelAlertRuleTemplates { } } } - Get-LogAnalyticWorkspace @arguments + + try { + Get-LogAnalyticWorkspace @arguments -ErrorAction Stop + } + catch { + Write-Error $_.Exception.Message + break + } $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRuleTemplates?api-version=2019-01-01-preview" diff --git a/AzSentinel/Public/Get-AzSentinelDataConnector.ps1 b/AzSentinel/Public/Get-AzSentinelDataConnector.ps1 index 1f8d5a1..46d6d92 100644 --- a/AzSentinel/Public/Get-AzSentinelDataConnector.ps1 +++ b/AzSentinel/Public/Get-AzSentinelDataConnector.ps1 @@ -60,7 +60,14 @@ function Get-AzSentinelDataConnector { } } } - Get-LogAnalyticWorkspace @arguments + + try { + Get-LogAnalyticWorkspace @arguments -ErrorAction Stop + } + catch { + Write-Error $_.Exception.Message + break + } if ($DataConnectorName) { $dataConnectors = @() diff --git a/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 index eaf049c..ea6e645 100644 --- a/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/Get-AzSentinelHuntingRule.ps1 @@ -63,7 +63,14 @@ function Get-AzSentinelHuntingRule { } } } - Get-LogAnalyticWorkspace @arguments + + try { + Get-LogAnalyticWorkspace @arguments -ErrorAction Stop + } + catch { + Write-Error $_.Exception.Message + break + } $uri = "$script:baseUri/savedSearches?api-version=2017-04-26-preview" diff --git a/AzSentinel/Public/Get-AzSentinelIncident.ps1 b/AzSentinel/Public/Get-AzSentinelIncident.ps1 index bd5ef30..032123a 100644 --- a/AzSentinel/Public/Get-AzSentinelIncident.ps1 +++ b/AzSentinel/Public/Get-AzSentinelIncident.ps1 @@ -76,7 +76,14 @@ function Get-AzSentinelIncident { } } } - Get-LogAnalyticWorkspace @arguments + + try { + Get-LogAnalyticWorkspace @arguments -ErrorAction Stop + } + catch { + Write-Error $_.Exception.Message + break + } $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/Cases?api-version=2019-01-01-preview" Write-Verbose -Message "Using URI: $($uri)" diff --git a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 index 18203d3..22ad279 100644 --- a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 @@ -114,8 +114,13 @@ function Import-AzSentinelAlertRule { Test All rules first #> $allRules = $rules.analytics + $rules.Scheduled + $rules.fusion + $rules.MLBehaviorAnalytics + $rules.MicrosoftSecurityIncidentCreation | Select-Object displayName - $allRulesContent = Get-AzSentinelAlertRule @arguments -RuleName $($allRules.displayName) - + try { + $allRulesContent = Get-AzSentinelAlertRule @arguments -RuleName $($allRules.displayName) -ErrorAction Stop + } + catch { + Write-Error $_.Exception.Message + break + } <# analytics rule #> diff --git a/AzSentinel/Public/Import-AzSentinelDataConnector.ps1 b/AzSentinel/Public/Import-AzSentinelDataConnector.ps1 index d3c2b08..ad3e776 100644 --- a/AzSentinel/Public/Import-AzSentinelDataConnector.ps1 +++ b/AzSentinel/Public/Import-AzSentinelDataConnector.ps1 @@ -52,7 +52,14 @@ function Import-AzSentinelDataConnector { } } } - Get-LogAnalyticWorkspace @arguments + + try { + Get-LogAnalyticWorkspace @arguments -ErrorAction Stop + } + catch { + Write-Error $_.Exception.Message + break + } if ($SettingsFile.Extension -eq '.json') { try { diff --git a/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 index 8439227..1b4d99f 100644 --- a/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 @@ -59,7 +59,6 @@ function Import-AzSentinelHuntingRule { } } } - Get-LogAnalyticWorkspace @arguments $item = @{ } @@ -92,12 +91,21 @@ function Import-AzSentinelHuntingRule { } } + try { + $allRulesContent = Get-AzSentinelHuntingRule @arguments -RuleName $($hunting.displayName) -WarningAction SilentlyContinue -ErrorAction Stop + } + catch { + Write-Error $_.Exception.Message + break + } + foreach ($item in $hunting) { Write-Output "Started with Hunting rule: $($item.displayName)" try { Write-Verbose -Message "Get rule $($item.description)" - $content = Get-AzSentinelHuntingRule @arguments -RuleName $($item.displayName) -WarningAction SilentlyContinue + + $content = $allRulesContent | Where-Object displayName -eq $item.displayName if ($content) { Write-Verbose -Message "Hunting rule $($item.displayName) exists in Azure Sentinel" diff --git a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 index 53cc36c..ddccbaa 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 @@ -188,8 +188,14 @@ function New-AzSentinelAlertRule { Write-Verbose -Message "Creating new rule: $($DisplayName)" - $content = Get-AzSentinelAlertRule @arguments -RuleName $DisplayName - + try { + $content = Get-AzSentinelAlertRule @arguments -RuleName $DisplayName -ErrorAction Stop + } + catch { + Write-Error $_.Exception.Message + break + } + if ($content) { Write-Verbose -Message "Rule $($DisplayName) exists in Azure Sentinel" diff --git a/AzSentinel/Public/New-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/New-AzSentinelHuntingRule.ps1 index 3a5132a..3345286 100644 --- a/AzSentinel/Public/New-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelHuntingRule.ps1 @@ -69,7 +69,7 @@ function New-AzSentinelHuntingRule { } } } - Get-LogAnalyticWorkspace @arguments + $item = @{ } $content = $null diff --git a/AzSentinel/Public/Remove-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Remove-AzSentinelAlertRule.ps1 index 381302e..8a880ae 100644 --- a/AzSentinel/Public/Remove-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Remove-AzSentinelAlertRule.ps1 @@ -59,12 +59,19 @@ function Remove-AzSentinelAlertRule { } } } - Get-LogAnalyticWorkspace @arguments if ($RuleName) { # remove defined rules foreach ($rule in $RuleName) { - $item = Get-AzSentinelAlertRule @arguments -RuleName $rule -WarningAction SilentlyContinue + + try { + $item = Get-AzSentinelAlertRule @arguments -RuleName $rule -WarningAction SilentlyContinue -ErrorAction Stop + } + catch { + $return = $_.Exception.Message + Write-Error $return + } + if ($item) { $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($item.name)?api-version=2019-01-01-preview" diff --git a/AzSentinel/Public/Remove-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/Remove-AzSentinelHuntingRule.ps1 index d2ed182..b5f443d 100644 --- a/AzSentinel/Public/Remove-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/Remove-AzSentinelHuntingRule.ps1 @@ -59,12 +59,18 @@ function Remove-AzSentinelHuntingRule { } } } - Get-LogAnalyticWorkspace @arguments if ($RuleName) { # remove defined rules foreach ($rule in $RuleName) { - $item = Get-AzSentinelHuntingRule @arguments -RuleName $rule + try { + $item = Get-AzSentinelHuntingRule @arguments -RuleName $rule -ErrorAction Stop + } + catch { + Write-Error $_.Exception.Message + break + } + if ($item) { $uri = "$script:baseUri/savedSearches/$($item.name)?api-version=2017-04-26-preview" diff --git a/AzSentinel/Public/Rename-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Rename-AzSentinelAlertRule.ps1 index 80b6207..1f1adc0 100644 --- a/AzSentinel/Public/Rename-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Rename-AzSentinelAlertRule.ps1 @@ -64,33 +64,56 @@ function Rename-AzSentinelAlertRule { try { $rule = Get-AzSentinelAlertRule @arguments -RuleName $CurrentRuleName -ErrorAction Stop + } + catch { + $return = $_.Exception.Message + Write-Error $return + } - $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($currentRule.name)?api-version=2019-01-01-preview" + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($rule.name)?api-version=2019-01-01-preview" - $rule.displayName = $NewRuleName + $groupingConfiguration = [GroupingConfiguration]::new( + $rule.incidentConfiguration.groupingConfiguration.GroupingConfigurationEnabled, + $rule.incidentConfiguration.groupingConfiguration.ReopenClosedIncident, + $rule.incidentConfiguration.groupingConfiguration.LookbackDuration, + $rule.incidentConfiguration.groupingConfiguration.EntitiesMatchingMethod, + $rule.incidentConfiguration.groupingConfiguration.GroupByEntities + ) - $bodyAlertProp = [AlertProp]::new( - ($rule | Select-Object * -ExcludeProperty lastModifiedUtc, etag, id) - ) + $incidentConfiguration = [IncidentConfiguration]::new( + $rule.incidentConfiguration.CreateIncident, + $groupingConfiguration + ) - $body = [AlertRule]::new( - ($rule | Select-Object lastModifiedUtc, etag, id, name), - $bodyAlertProp - ) + $bodyAlertProp = [ScheduledAlertProp]::new( + $rule.name, + $NewRuleName, + $rule.Description, + $rule.Severity, + $rule.Enabled, + $rule.Query, + $rule.QueryFrequency, + $rule.QueryPeriod, + $rule.TriggerOperator, + $rule.TriggerThreshold, + $rule.SuppressionDuration, + $rule.SuppressionEnabled, + $rule.Tactics, + $rule.PlaybookName, + $incidentConfiguration, + $rule.AggregationKind + ) - try { - $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -EnumsAsStrings) - $return = "Successfully renamed rule $($CurrentRuleName) to $($NewRuleName) with status: $($result.StatusDescription)" - return $return - } - catch { - $return = $_.Exception.Message - Write-Error "Rename failed with error $return" - } + $body = [AlertRule]::new( $rule.name, $rule.etag, $bodyAlertProp, $rule.Id, 'Scheduled') + + try { + $result = Invoke-RestMethod -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) -ErrorAction Stop + $return = "Successfully renamed rule $($CurrentRuleName) to $($NewRuleName) with status: $($result.StatusDescription)" + return $return } catch { $return = $_.Exception.Message - Write-Error $return + Write-Error "Rename failed with error $return" } } } diff --git a/AzSentinel/Public/Set-AzSentinel.ps1 b/AzSentinel/Public/Set-AzSentinel.ps1 index 97c7ecf..f81a0ce 100644 --- a/AzSentinel/Public/Set-AzSentinel.ps1 +++ b/AzSentinel/Public/Set-AzSentinel.ps1 @@ -46,7 +46,14 @@ function Set-AzSentinel { } } } - $workspaceResult = Get-LogAnalyticWorkspace @arguments -FullObject + + try { + $workspaceResult = Get-LogAnalyticWorkspace @arguments -FullObject -ErrorAction Stop + } + catch { + Write-Error $_.Exception.Message + break + } # Variables $errorResult = '' diff --git a/AzSentinel/Public/Update-AzSentinelIncident.ps1 b/AzSentinel/Public/Update-AzSentinelIncident.ps1 index 6449343..d8b195b 100644 --- a/AzSentinel/Public/Update-AzSentinelIncident.ps1 +++ b/AzSentinel/Public/Update-AzSentinelIncident.ps1 @@ -75,7 +75,7 @@ function Update-AzSentinelIncident { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$ClosedReasonText, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$Description @@ -101,7 +101,13 @@ function Update-AzSentinelIncident { } Write-Verbose -Message "Using URI: $($uri)" - $incident = Get-AzSentinelIncident @arguments -CaseNumber $CaseNumber + try { + $incident = Get-AzSentinelIncident @arguments -CaseNumber $CaseNumber -ErrorAction Stop + } + catch { + Write-Error $_.Exception.Message + break + } if ($incident) { if ($Comment) { @@ -118,26 +124,26 @@ function Update-AzSentinelIncident { $body = @{ "etag" = $($incident.etag) "properties" = @{ - "caseNumber" = $CaseNumber - "createdTimeUtc" = $($incident.incidentcreatedTimeUtc) - "endTimeUtc" = $($incident.endTimeUtc) - "lastUpdatedTimeUtc" = $($incident.lastUpdatedTimeUtc) - "lastComment" = "" - "totalComments" = $incident.TotalComments - "metrics" = $incident.Metrics - "relatedAlertIds" = $incident.RelatedAlertIds - "relatedAlertProductNames" = $incident.RelatedAlertProductNames - "severity" = if ($Severity) { $Severity } else { $incident.severity } - "startTimeUtc" = $($incident.startTimeUtc) - "status" = if ($Status) { $Status } else { $incident.status } - "closeReason" = if ($Status -eq 'Closed') { if ($null -ne [CloseReason]$CloseReason) { $CloseReason } else { Write-Error "No close reason provided" -ErrorAction Stop } } else { $null } - "closedReasonText" = if ($Status -eq 'Closed') { if ($ClosedReasonText) { $ClosedReasonText } else { Write-Error 'No closed comment provided' } } else { $null } - [pscustomobject]"labels" = @( $LabelsUnique) - "title" = $($incident.title) - "description" = if ($Description) { $Description } else { $incident.Description } - "firstAlertTimeGenerated" = $incident.FirstAlertTimeGenerated - "lastAlertTimeGenerated" = $incident.LastAlertTimeGenerated - "owner" = @{ + "caseNumber" = $CaseNumber + "createdTimeUtc" = $($incident.incidentcreatedTimeUtc) + "endTimeUtc" = $($incident.endTimeUtc) + "lastUpdatedTimeUtc" = $($incident.lastUpdatedTimeUtc) + "lastComment" = "" + "totalComments" = $incident.TotalComments + "metrics" = $incident.Metrics + "relatedAlertIds" = $incident.RelatedAlertIds + "relatedAlertProductNames" = $incident.RelatedAlertProductNames + "severity" = if ($Severity) { $Severity } else { $incident.severity } + "startTimeUtc" = $($incident.startTimeUtc) + "status" = if ($Status) { $Status } else { $incident.status } + "closeReason" = if ($Status -eq 'Closed') { if ($null -ne [CloseReason]$CloseReason) { $CloseReason } else { Write-Error "No close reason provided" -ErrorAction Stop } } else { $null } + "closedReasonText" = if ($Status -eq 'Closed') { if ($ClosedReasonText) { $ClosedReasonText } else { Write-Error 'No closed comment provided' } } else { $null } + [pscustomobject]"labels" = @( $LabelsUnique) + "title" = $($incident.title) + "description" = if ($Description) { $Description } else { $incident.Description } + "firstAlertTimeGenerated" = $incident.FirstAlertTimeGenerated + "lastAlertTimeGenerated" = $incident.LastAlertTimeGenerated + "owner" = @{ "name" = $incident.Owner.Name "email" = $incident.Owner.Email "objectId" = $incident.Owner.ObjectId From b321bfeab0131596404d8f00bdbbd0efa3cf1e3c Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Tue, 17 Nov 2020 16:14:31 +0100 Subject: [PATCH 39/53] Fixing issue when switching from subscription (#140) * !Deploy Release version 0.6.14 (#137) * Release '0.6.2' (#31) * updating get alert and hunting rule function * updated error handling * Create Get-PlayBook.ps1 * cleaning up * Release Update Incident function (#37) * init release update incident function * cleaning up * updating * updating incident function * code cleanup * Cleaning up and ready for release * updating final docs folder * Release Feature playbook configuration (#33) * updating get alert and hunting rule function * updated error handling * Create Get-PlayBook.ps1 * init release for playbook * cleaning up * finishing playbook * adding get alert rule action function * releasing get logic app function * release new- az sen alert action and some codue update * init release playbook function * uppdated gitignore * init release remove azsentinel action rule * fixed compare issue * Merge branch 'development' of github.com:wortell/AZSentinel into feature/playbook * updating pester test result * updating readme * updating readme * updated docs and pester test results * restoring version * Fix/smallconflicts (#40) * updating docs * updating examples * updating pipeline * fixing Subscribtion parameter for playbook (#43) * fixing Subscribtion parameter for playbook (#45) * Fix- get-Azsentinalhuntingrule - Cannot validate argument on parameter "Property" (#50) * fix huntng rule * fixing hunting rule issue * Fix - new-azsentinelalertrule playbook property (#49) * fixing the if statement * fixing the if statement * Feature - get all incidents (#51) * updating get incident * updating get incident function and docs * updating powershell-yaml * updating importmodule error * workaround * removing powershell-yaml depending * fixing logicapp sas token (#52) * Add support for day time periods (#61) * Add missing dot to yml file extension (#59) The Import-AZSentinelAlertRule function is not able to import yml files due to missing dot in the file extension. * adding support for resource provider in set-azsentinel (#69) * New function for enabling and disabling Alert rules (#71) * init release enable and disable function * adding empty test files * updating return message * New feature change the displayName of an alert (#68) * Release Rename Alert rule function * updating rename function * Handle nextLink for Playbooks (#78) When retrieving playbooks not all are being returned. Code copied from Issue #35 Retrieving all incidents. * adding support for alert aggregation (#65) * adding support for alert aggregation, classes created * updaing classes * updated the class and created first rule wih no error * update class and made import function backwards compatible * small changes * tested with import method * updating new function * checking working code, starting cleanup * updating documentation * updating docs and cleaning up * updating build errors * change pester version * updating pester version * Update groupingConfiguration.ps1 (#87) * Fix bug that causes loss of certain incident properties, add option to set incident description (#91) * Feature - Adding support for all alert rule types (#90) * init release * updating docs Co-authored-by: Khabazi * New Functionality to get alert rule templates provided by Microsoft (#94) Co-authored-by: Antonio Ramirez * Update/get az sentinel alert rule templates (#95) * udating Get-AzSentinelAlertRuleTemplates * updated Co-authored-by: Khabazi * Feature/add az sentinel incident comment (#96) * udating Get-AzSentinelAlertRuleTemplates * updated * fixing playbook issue * Add-AzSentinelIncidentComment * release Co-authored-by: Khabazi * fixing class error (#99) * updating example files, ncluding multi rule yaml file (#104) * Fix - Get-AzSentinelAlertRuleAction doesn't return playbookName (#102) * fixing return issue * fixing playbook issue * init release Get-AzSentinelDataConnector function (#103) * Fix - get-azsentinelhuntingrule updated get and remove function (#106) * fixing hunitng rule get and remove issue * cleaning up * updating filters * Add filtering by lastModified (#107) * updating AggregationKind class and enum (#111) * Release of Import-AzSentinelDataConnector function (#116) * extra check for Import-AzSentinelDataConnector * fixing class issue (#118) * New function: Export-AzSentinel (#121) * init code * Release Export-AzSentinel and some small fixes/updates * fixing SeveritiesFilter issue for MicrosoftSecurityIncidentCreation (#122) * updating Get-AzSentinelAlertRule function and docs (#125) * modified token expiration logic (#135) Co-authored-by: John Crouch * fixing small issues (#136) Co-authored-by: pemontto <939704+pemontto@users.noreply.github.com> Co-authored-by: NVolcz Co-authored-by: stehod <34159548+stehod@users.noreply.github.com> Co-authored-by: ThijsLecomte <42153270+ThijsLecomte@users.noreply.github.com> Co-authored-by: Jonathan Holtmann Co-authored-by: Khabazi Co-authored-by: ramirezversion <34833071+ramirezversion@users.noreply.github.com> Co-authored-by: Antonio Ramirez Co-authored-by: John Crouch <50185606+john-crouch@users.noreply.github.com> Co-authored-by: John Crouch * fixing issue when switching from subscription * fixing subscription precheck issue * restore Co-authored-by: pemontto <939704+pemontto@users.noreply.github.com> Co-authored-by: NVolcz Co-authored-by: stehod <34159548+stehod@users.noreply.github.com> Co-authored-by: ThijsLecomte <42153270+ThijsLecomte@users.noreply.github.com> Co-authored-by: Jonathan Holtmann Co-authored-by: Khabazi Co-authored-by: ramirezversion <34833071+ramirezversion@users.noreply.github.com> Co-authored-by: Antonio Ramirez Co-authored-by: John Crouch <50185606+john-crouch@users.noreply.github.com> Co-authored-by: John Crouch --- AzSentinel/Private/Get-AuthToken.ps1 | 31 +++++++++++++--------------- AzSentinel/Private/precheck.ps1 | 26 ++++++++++++++--------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/AzSentinel/Private/Get-AuthToken.ps1 b/AzSentinel/Private/Get-AuthToken.ps1 index cc8d395..a809b36 100644 --- a/AzSentinel/Private/Get-AuthToken.ps1 +++ b/AzSentinel/Private/Get-AuthToken.ps1 @@ -13,24 +13,21 @@ function Get-AuthToken { [CmdletBinding()] param ( - ) - try { - $azContext = Get-AzContext - - if ($null -ne $azContext) { - Write-Verbose -Message "Using Subscription: $($azContext.Subscription.Name) from tenant $($azContext.Tenant.Id)" - - $azProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile - $profileClient = [Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient]::new($azProfile) - $script:accessToken = $profileClient.AcquireAccessToken($azContext.Subscription.TenantId) - $script:subscriptionId = $azContext.Subscription.Id - $script:tenantId = $azContext.Tenant.Id - } else { - throw 'No subscription available, Please use Connect-AzAccount to login and select the right subscription' - } - } catch { - $PSCmdlet.ThrowTerminatingError($_) + $azProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile + + Write-Verbose -Message "Using Subscription: $($azProfile.DefaultContext.Subscription.Name) from tenant $($azProfile.DefaultContext.Tenant.Id)" + + $script:subscriptionId = $azProfile.DefaultContext.Subscription.Id + $script:tenantId = $azProfile.DefaultContext.Tenant.Id + + $profileClient = [Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient]::new($azProfile) + $script:accessToken = $profileClient.AcquireAccessToken($script:tenantId) + + $script:authHeader = @{ + 'Content-Type' = 'application/json' + Authorization = 'Bearer ' + $script:accessToken.AccessToken } + } diff --git a/AzSentinel/Private/precheck.ps1 b/AzSentinel/Private/precheck.ps1 index 19b89f7..f8c3d91 100644 --- a/AzSentinel/Private/precheck.ps1 +++ b/AzSentinel/Private/precheck.ps1 @@ -14,16 +14,22 @@ function precheck { NAME: precheck #> - if ($null -eq $script:accessToken) { - Get-AuthToken - } - elseif ($script:accessToken.ExpiresOn.DateTime - [datetime]::UtcNow.AddMinutes(-5) -le 0) { - # if token expires within 5 minutes, request a new one - Get-AuthToken - } + $azProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile - $script:authHeader = @{ - 'Content-Type' = 'application/json' - Authorization = 'Bearer ' + $script:accessToken.AccessToken + if ($azProfile.Contexts.Count -ne 0) { + if ($null -eq $script:accessToken ) { + Get-AuthToken + } + elseif ($script:accessToken.ExpiresOn.DateTime - [datetime]::UtcNow.AddMinutes(-5) -le 0) { + # if token expires within 5 minutes, request a new one + Get-AuthToken + } + + # Set the subscription from AzContext + $script:subscriptionId = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext.Subscription.Id + } + else { + Write-Error 'No subscription available, Please use Connect-AzAccount to login and select the right subscription' + break } } From f8a4cbe4ad049102227c7b0ca5a70ed6e5e330f4 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Wed, 18 Nov 2020 13:55:40 +0100 Subject: [PATCH 40/53] Fixing issue with Fusion rules (#143) --- AzSentinel/Classes/AlertRule.ps1 | 2 +- AzSentinel/Public/Import-AzSentinelAlertRule.ps1 | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/AzSentinel/Classes/AlertRule.ps1 b/AzSentinel/Classes/AlertRule.ps1 index 1677ea7..0be892e 100644 --- a/AzSentinel/Classes/AlertRule.ps1 +++ b/AzSentinel/Classes/AlertRule.ps1 @@ -1,5 +1,5 @@ class AlertRule { - [guid] $Name + [string] $Name [string] $Etag diff --git a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 index 22ad279..bb1558b 100644 --- a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 @@ -135,7 +135,7 @@ function Import-AzSentinelAlertRule { $guid = (New-Guid).Guid - $content = $allRulesContent | Where-Object displayName -eq $item.displayName + $content = $allRulesContent | Where-Object {$_.kind -eq 'Scheduled' -and $_.displayName -eq $item.displayName} Write-Verbose -Message "Get rule $($item.description)" @@ -261,7 +261,7 @@ function Import-AzSentinelAlertRule { $guid = (New-Guid).Guid - $content = $allRulesContent | Where-Object displayName -eq $item.displayName + $content = $allRulesContent | Where-Object {$_.kind -eq 'Fusion' -and $_.displayName -eq $item.displayName} Write-Verbose -Message "Get rule $($item.description)" @@ -314,7 +314,7 @@ function Import-AzSentinelAlertRule { $guid = (New-Guid).Guid - $content = $allRulesContent | Where-Object displayName -eq $item.displayName + $content = $allRulesContent | Where-Object {$_.kind -eq 'MLBehaviorAnalytics' -and $_.displayName -eq $item.displayName} Write-Verbose -Message "Get rule $($item.description)" @@ -368,7 +368,7 @@ function Import-AzSentinelAlertRule { $guid = (New-Guid).Guid - $content = $allRulesContent | Where-Object displayName -eq $item.displayName + $content = $allRulesContent | Where-Object {$_.kind -eq 'MicrosoftSecurityIncidentCreation' -and $_.displayName -eq $item.displayName} Write-Verbose -Message "Get rule $($item.description)" $content = Get-AzSentinelAlertRule @arguments -RuleName $($item.displayName) -ErrorAction SilentlyContinue From ee34dcf54ed49f260ae6ee16ab2a5668bc00dc00 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Thu, 19 Nov 2020 19:50:47 +0100 Subject: [PATCH 41/53] MSSP Playbook (#142) * !Deploy Release version 0.6.14 (#137) * Release '0.6.2' (#31) * updating get alert and hunting rule function * updated error handling * Create Get-PlayBook.ps1 * cleaning up * Release Update Incident function (#37) * init release update incident function * cleaning up * updating * updating incident function * code cleanup * Cleaning up and ready for release * updating final docs folder * Release Feature playbook configuration (#33) * updating get alert and hunting rule function * updated error handling * Create Get-PlayBook.ps1 * init release for playbook * cleaning up * finishing playbook * adding get alert rule action function * releasing get logic app function * release new- az sen alert action and some codue update * init release playbook function * uppdated gitignore * init release remove azsentinel action rule * fixed compare issue * Merge branch 'development' of github.com:wortell/AZSentinel into feature/playbook * updating pester test result * updating readme * updating readme * updated docs and pester test results * restoring version * Fix/smallconflicts (#40) * updating docs * updating examples * updating pipeline * fixing Subscribtion parameter for playbook (#43) * fixing Subscribtion parameter for playbook (#45) * Fix- get-Azsentinalhuntingrule - Cannot validate argument on parameter "Property" (#50) * fix huntng rule * fixing hunting rule issue * Fix - new-azsentinelalertrule playbook property (#49) * fixing the if statement * fixing the if statement * Feature - get all incidents (#51) * updating get incident * updating get incident function and docs * updating powershell-yaml * updating importmodule error * workaround * removing powershell-yaml depending * fixing logicapp sas token (#52) * Add support for day time periods (#61) * Add missing dot to yml file extension (#59) The Import-AZSentinelAlertRule function is not able to import yml files due to missing dot in the file extension. * adding support for resource provider in set-azsentinel (#69) * New function for enabling and disabling Alert rules (#71) * init release enable and disable function * adding empty test files * updating return message * New feature change the displayName of an alert (#68) * Release Rename Alert rule function * updating rename function * Handle nextLink for Playbooks (#78) When retrieving playbooks not all are being returned. Code copied from Issue #35 Retrieving all incidents. * adding support for alert aggregation (#65) * adding support for alert aggregation, classes created * updaing classes * updated the class and created first rule wih no error * update class and made import function backwards compatible * small changes * tested with import method * updating new function * checking working code, starting cleanup * updating documentation * updating docs and cleaning up * updating build errors * change pester version * updating pester version * Update groupingConfiguration.ps1 (#87) * Fix bug that causes loss of certain incident properties, add option to set incident description (#91) * Feature - Adding support for all alert rule types (#90) * init release * updating docs Co-authored-by: Khabazi * New Functionality to get alert rule templates provided by Microsoft (#94) Co-authored-by: Antonio Ramirez * Update/get az sentinel alert rule templates (#95) * udating Get-AzSentinelAlertRuleTemplates * updated Co-authored-by: Khabazi * Feature/add az sentinel incident comment (#96) * udating Get-AzSentinelAlertRuleTemplates * updated * fixing playbook issue * Add-AzSentinelIncidentComment * release Co-authored-by: Khabazi * fixing class error (#99) * updating example files, ncluding multi rule yaml file (#104) * Fix - Get-AzSentinelAlertRuleAction doesn't return playbookName (#102) * fixing return issue * fixing playbook issue * init release Get-AzSentinelDataConnector function (#103) * Fix - get-azsentinelhuntingrule updated get and remove function (#106) * fixing hunitng rule get and remove issue * cleaning up * updating filters * Add filtering by lastModified (#107) * updating AggregationKind class and enum (#111) * Release of Import-AzSentinelDataConnector function (#116) * extra check for Import-AzSentinelDataConnector * fixing class issue (#118) * New function: Export-AzSentinel (#121) * init code * Release Export-AzSentinel and some small fixes/updates * fixing SeveritiesFilter issue for MicrosoftSecurityIncidentCreation (#122) * updating Get-AzSentinelAlertRule function and docs (#125) * modified token expiration logic (#135) Co-authored-by: John Crouch * fixing small issues (#136) Co-authored-by: pemontto <939704+pemontto@users.noreply.github.com> Co-authored-by: NVolcz Co-authored-by: stehod <34159548+stehod@users.noreply.github.com> Co-authored-by: ThijsLecomte <42153270+ThijsLecomte@users.noreply.github.com> Co-authored-by: Jonathan Holtmann Co-authored-by: Khabazi Co-authored-by: ramirezversion <34833071+ramirezversion@users.noreply.github.com> Co-authored-by: Antonio Ramirez Co-authored-by: John Crouch <50185606+john-crouch@users.noreply.github.com> Co-authored-by: John Crouch * init test * ready for release Co-authored-by: pemontto <939704+pemontto@users.noreply.github.com> Co-authored-by: NVolcz Co-authored-by: stehod <34159548+stehod@users.noreply.github.com> Co-authored-by: ThijsLecomte <42153270+ThijsLecomte@users.noreply.github.com> Co-authored-by: Jonathan Holtmann Co-authored-by: Khabazi Co-authored-by: ramirezversion <34833071+ramirezversion@users.noreply.github.com> Co-authored-by: Antonio Ramirez Co-authored-by: John Crouch <50185606+john-crouch@users.noreply.github.com> Co-authored-by: John Crouch --- AzSentinel/Classes/ScheduledAlertProp.ps1 | 6 ++- AzSentinel/Private/Get-AzSentinelPlayBook.ps1 | 15 +++++-- .../Public/Get-AzSentinelAlertRuleAction.ps1 | 4 +- .../Public/Import-AzSentinelAlertRule.ps1 | 4 +- .../Public/New-AzSentinelAlertRuleAction.ps1 | 19 ++++---- README.md | 44 +++++++++---------- examples/AlertRules.json | 4 +- 7 files changed, 53 insertions(+), 43 deletions(-) diff --git a/AzSentinel/Classes/ScheduledAlertProp.ps1 b/AzSentinel/Classes/ScheduledAlertProp.ps1 index 28eb100..c5f4176 100644 --- a/AzSentinel/Classes/ScheduledAlertProp.ps1 +++ b/AzSentinel/Classes/ScheduledAlertProp.ps1 @@ -92,7 +92,11 @@ class ScheduledAlertProp { } $this.SuppressionEnabled = if ($suppressionEnabled) { $suppressionEnabled } else { $false } $this.Tactics = $Tactics - $this.PlaybookName = $PlaybookName + $this.PlaybookName = if ($PlaybookName.Split('/').count -gt 1){ + $PlaybookName.Split('/')[-1] + } else { + $PlaybookName + } $this.IncidentConfiguration = $IncidentConfiguration $this.eventGroupingSettings = @{ aggregationKind = if ($aggregationKind) { $aggregationKind } else { "SingleAlert" } diff --git a/AzSentinel/Private/Get-AzSentinelPlayBook.ps1 b/AzSentinel/Private/Get-AzSentinelPlayBook.ps1 index 2e2cbb2..7332bc7 100644 --- a/AzSentinel/Private/Get-AzSentinelPlayBook.ps1 +++ b/AzSentinel/Private/Get-AzSentinelPlayBook.ps1 @@ -20,9 +20,9 @@ function Get-AzSentinelPlayBook { param ( [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] - [string] $SubscriptionId, + [string]$SubscriptionId, - [Parameter(Mandatory)] + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$Name ) @@ -35,7 +35,11 @@ function Get-AzSentinelPlayBook { $triggerName = 'When_a_response_to_an_Azure_Sentinel_alert_is_triggered' - if ($SubscriptionId) { + if ($Name.Split('/').count -gt 1) { + $uri = "https://management.azure.com/subscriptions/$($Name.Split('/')[2])/providers/Microsoft.Logic/workflows?api-version=2016-06-01" + $Name = $Name.Split('/')[-1] + } + elseif ($SubscriptionId) { Write-Verbose "Getting LogicApp from Subscription $($subscriptionId)" $uri = "https://management.azure.com/subscriptions/$($subscriptionId)/providers/Microsoft.Logic/workflows?api-version=2016-06-01" } @@ -51,12 +55,15 @@ function Get-AzSentinelPlayBook { try { $logicappRaw = (Invoke-RestMethod -Uri $uri -Method Get -Headers $script:authHeader) $logicapp = $logicappRaw.value + while ($logicappRaw.nextLink) { $logicappRaw = (Invoke-RestMethod -Uri $($logicappRaw.nextLink) -Headers $script:authHeader -Method Get) $logicapp += $logicappRaw.value } + $playBook = $logicapp | Where-Object { $_.name -eq $Name } - if ($playBook){ + + if ($playBook) { $uri1 = "https://management.azure.com$($playBook.id)/triggers/$($triggerName)/listCallbackUrl?api-version=2016-06-01" try { $playbookTrigger = (Invoke-RestMethod -Uri $uri1 -Method Post -Headers $script:authHeader) diff --git a/AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 index 75cc743..fffbf15 100644 --- a/AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRuleAction.ps1 @@ -25,11 +25,11 @@ function Get-AzSentinelAlertRuleAction { [Parameter(Mandatory = $false, ParameterSetName = "Sub")] [ValidateNotNullOrEmpty()] - [string] $SubscriptionId, + [string]$SubscriptionId, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] - [string] $WorkspaceName, + [string]$WorkspaceName, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] diff --git a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 index bb1558b..2fcf59a 100644 --- a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 @@ -204,11 +204,11 @@ function Import-AzSentinelAlertRule { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | Select-Object * -ExcludeProperty Properties.PlaybookName | ConvertTo-Json -Depth 10 -EnumsAsStrings) if (($compareResult | Where-Object PropertyName -eq "playbookName").DiffValue) { - $PlaybookResult = New-AzSentinelAlertRuleAction @arguments -PlayBookName ($body.Properties.playbookName) -RuleId $($body.Name) + $PlaybookResult = New-AzSentinelAlertRuleAction @arguments -PlayBookName ($item.playbookName) -RuleId $($body.Name) $body.Properties | Add-Member -NotePropertyName PlaybookStatus -NotePropertyValue $PlaybookResult -Force } elseif (($compareResult | Where-Object PropertyName -eq "playbookName").RefValue) { - $PlaybookResult = Remove-AzSentinelAlertRuleAction @arguments -RuleId $($body.Name) -Confirm:$false + $PlaybookResult = Remove-AzSentinelAlertRuleAction @arguments -RuleId $body.Name -Confirm:$false $body.Properties | Add-Member -NotePropertyName PlaybookStatus -NotePropertyValue $PlaybookResult -Force } else { diff --git a/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 b/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 index 40fc496..d73f04f 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRuleAction.ps1 @@ -28,15 +28,15 @@ function New-AzSentinelAlertRuleAction { [Parameter(Mandatory = $false, ParameterSetName = "Sub")] [ValidateNotNullOrEmpty()] - [string] $SubscriptionId, + [string]$SubscriptionId, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] - [string] $WorkspaceName, + [string]$WorkspaceName, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] - [string] $PlayBookName, + [string]$PlayBookName, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] @@ -73,6 +73,7 @@ function New-AzSentinelAlertRuleAction { $action = $null + if ($SubscriptionId) { $playBook = Get-AzSentinelPlayBook -SubscriptionId $SubscriptionId -Name $PlayBookName } @@ -82,8 +83,7 @@ function New-AzSentinelAlertRuleAction { $action = Get-AzSentinelAlertRuleAction @arguments -RuleId $alertId -ErrorAction SilentlyContinue - - if ($null -eq $action) { + if (($null -eq $action) -or ((($action.properties.logicAppResourceId).Split('/')[-1]) -ne $PlayBookName.Split('/')[-1])) { $guid = New-Guid $body = @{ @@ -98,6 +98,7 @@ function New-AzSentinelAlertRuleAction { } $uri = "$($Script:baseUri)/providers/Microsoft.SecurityInsights/alertRules/$($alertId)/actions/$($guid)?api-version=2019-01-01-preview" + try { $return = Invoke-WebRequest -Method Put -Uri $uri -Headers $Script:authHeader -Body ($body | ConvertTo-Json -Depth 10) Write-Verbose "Successfully created Action for Rule: $($RuleName) with Playbook $($PlayBookName) Status: $($return.StatusDescription)" @@ -105,16 +106,14 @@ function New-AzSentinelAlertRuleAction { } catch { Write-Error "Unable to create Action for Rule: $($RuleName) with Playbook $($PlayBookName) Error: $($_.Exception.Message)" + Write-Verbose $_ return $_.Exception.Message - Write-Verbose $_. } } elseif ((($action.properties.logicAppResourceId).Split('/')[-1]) -eq $PlayBookName) { - Write-Output "Alert Rule: $($alertId) has already playbook assigned: $(($action.properties.logicAppResourceId).Split('/')[-1])" - } - elseif ((($action.properties.logicAppResourceId).Split('/')[-1]) -ne $PlayBookName) { - Write-Output "Alert rule $($RuleName) assigned to a different playbook with name $(($action.properties.logicAppResourceId).Split('/')[-1])" + Write-Output "Alert Rule: $($alertId) is already assigned to playbook: $(($action.properties.logicAppResourceId).Split('/')[-1])" } + else { #nothing? } diff --git a/README.md b/README.md index 547c861..5f9e60a 100644 --- a/README.md +++ b/README.md @@ -99,28 +99,28 @@ To create a Azure Sentinel Rule, use the following JSON format. The following tables describe the values you need to set in the schema. -| Name | Type | Required | Allowed Values | Example | -| ---------------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -| displayName | string | true | * | DisplayName | -| description | string | true | * | Description | -| severity | string | true | Medium, High, Low, Informational | Medium | -| enabled | bool | true | true, false | true | -| query | string | true | special character need to be escaped by \ | SecurityEvent \| where EventID == \"4688\" \| where CommandLine contains \\"-noni -ep bypass $\\" | -| queryFrequency | string | true | Value must be between 5 minutes and 24 hours | 30M | -| queryPeriod | string | true | Value must be between 5 minutes and 14 days | 6H | -| triggerOperator | string | true | GreaterThan, FewerThan, EqualTo, NotEqualTo | GreaterThan | -| triggerThreshold | int | true | The value must be between 0 and 10000 | 5 | -| suppressionDuration | string | true | Value must be greater than 5 minutes | 1D | -| suppressionEnabled | bool | true | true, false | true | -| tactics | array | true | InitialAccess, Persistence,Execution,PrivilegeEscalation,DefenseEvasion,CredentialAccess,LateralMovement,Discovery,Collection,Exfiltration,CommandAndControl,Impact | true | -| playbookName | string | false | Enter the Logic App name that you want to configure as playbook trigger | LogicApp01 | -| aggregationKind | string | false | SingleAlert, AlertPerRow | SingleAlert | -| createIncident | bool | false | true, false | true | -| GroupingConfigurationEnabled | bool | false | true, false | true | -| reopenClosedIncident | bool | false | true, false | true | -| lookbackDuration | string | false | Value must be between 5 minutes and 24 hours. | PT6H | -| entitiesMatchingMethod | string | false | All, None, Custom | All | -| groupByEntities | string | false | Account, Ip, Host, Url | Account | +| Name | Type | Required | Allowed Values | Example | +| ---------------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | +| displayName | string | true | * | DisplayName | +| description | string | true | * | Description | +| severity | string | true | Medium, High, Low, Informational | Medium | +| enabled | bool | true | true, false | true | +| query | string | true | special character need to be escaped by \ | SecurityEvent \| where EventID == \"4688\" \| where CommandLine contains \\"-noni -ep bypass $\\" | +| queryFrequency | string | true | Value must be between 5 minutes and 24 hours | 30M | +| queryPeriod | string | true | Value must be between 5 minutes and 14 days | 6H | +| triggerOperator | string | true | GreaterThan, FewerThan, EqualTo, NotEqualTo | GreaterThan | +| triggerThreshold | int | true | The value must be between 0 and 10000 | 5 | +| suppressionDuration | string | true | Value must be greater than 5 minutes | 1D | +| suppressionEnabled | bool | true | true, false | true | +| tactics | array | true | InitialAccess, Persistence,Execution,PrivilegeEscalation,DefenseEvasion,CredentialAccess,LateralMovement,Discovery,Collection,Exfiltration,CommandAndControl,Impact | true | +| playbookName | string | false | Enter the Logic App name or Resource ID | LogicApp01 / /subscriptions/SUBSCRIPTIONID/resourceGroups/RESOURCEGROUPNAME/providers/Microsoft.Logic/workflows/playbook02 | +| aggregationKind | string | false | SingleAlert, AlertPerRow | SingleAlert | +| createIncident | bool | false | true, false | true | +| GroupingConfigurationEnabled | bool | false | true, false | true | +| reopenClosedIncident | bool | false | true, false | true | +| lookbackDuration | string | false | Value must be between 5 minutes and 24 hours. | PT6H | +| entitiesMatchingMethod | string | false | All, None, Custom | All | +| groupByEntities | string | false | Account, Ip, Host, Url | Account | ### Fusion rule diff --git a/examples/AlertRules.json b/examples/AlertRules.json index 5f60bb2..297f94c 100644 --- a/examples/AlertRules.json +++ b/examples/AlertRules.json @@ -17,7 +17,7 @@ "LateralMovement", "Collection" ], - "playbookName": "Playbook01", + "playbookName": "playbook01", "aggregationKind": "SingleAlert", "createIncident": true, "groupingConfiguration": { @@ -50,7 +50,7 @@ "LateralMovement", "Collection" ], - "playbookName": "Playbook01" + "playbookName": "/subscriptions/SUBSCRIPTIONID/resourceGroups/RESOURCEGROUPNAME/providers/Microsoft.Logic/workflows/playbook02" } ], "Fusion": [ From 7a1bf343718ef15b22570ffab45f67aa047c453d Mon Sep 17 00:00:00 2001 From: Luke Fritz Date: Thu, 3 Dec 2020 14:34:30 -0600 Subject: [PATCH 42/53] Prevent null reference of non-required argument; fixes #148 (#149) * !Deploy Release Version 0.6.16 (#146) * Release '0.6.2' (#31) * updating get alert and hunting rule function * updated error handling * Create Get-PlayBook.ps1 * cleaning up * Release Update Incident function (#37) * init release update incident function * cleaning up * updating * updating incident function * code cleanup * Cleaning up and ready for release * updating final docs folder * Release Feature playbook configuration (#33) * updating get alert and hunting rule function * updated error handling * Create Get-PlayBook.ps1 * init release for playbook * cleaning up * finishing playbook * adding get alert rule action function * releasing get logic app function * release new- az sen alert action and some codue update * init release playbook function * uppdated gitignore * init release remove azsentinel action rule * fixed compare issue * Merge branch 'development' of github.com:wortell/AZSentinel into feature/playbook * updating pester test result * updating readme * updating readme * updated docs and pester test results * restoring version * Fix/smallconflicts (#40) * updating docs * updating examples * updating pipeline * fixing Subscribtion parameter for playbook (#43) * fixing Subscribtion parameter for playbook (#45) * Fix- get-Azsentinalhuntingrule - Cannot validate argument on parameter "Property" (#50) * fix huntng rule * fixing hunting rule issue * Fix - new-azsentinelalertrule playbook property (#49) * fixing the if statement * fixing the if statement * Feature - get all incidents (#51) * updating get incident * updating get incident function and docs * updating powershell-yaml * updating importmodule error * workaround * removing powershell-yaml depending * fixing logicapp sas token (#52) * Add support for day time periods (#61) * Add missing dot to yml file extension (#59) The Import-AZSentinelAlertRule function is not able to import yml files due to missing dot in the file extension. * adding support for resource provider in set-azsentinel (#69) * New function for enabling and disabling Alert rules (#71) * init release enable and disable function * adding empty test files * updating return message * New feature change the displayName of an alert (#68) * Release Rename Alert rule function * updating rename function * Handle nextLink for Playbooks (#78) When retrieving playbooks not all are being returned. Code copied from Issue #35 Retrieving all incidents. * adding support for alert aggregation (#65) * adding support for alert aggregation, classes created * updaing classes * updated the class and created first rule wih no error * update class and made import function backwards compatible * small changes * tested with import method * updating new function * checking working code, starting cleanup * updating documentation * updating docs and cleaning up * updating build errors * change pester version * updating pester version * Update groupingConfiguration.ps1 (#87) * Fix bug that causes loss of certain incident properties, add option to set incident description (#91) * Feature - Adding support for all alert rule types (#90) * init release * updating docs Co-authored-by: Khabazi * New Functionality to get alert rule templates provided by Microsoft (#94) Co-authored-by: Antonio Ramirez * Update/get az sentinel alert rule templates (#95) * udating Get-AzSentinelAlertRuleTemplates * updated Co-authored-by: Khabazi * Feature/add az sentinel incident comment (#96) * udating Get-AzSentinelAlertRuleTemplates * updated * fixing playbook issue * Add-AzSentinelIncidentComment * release Co-authored-by: Khabazi * fixing class error (#99) * updating example files, ncluding multi rule yaml file (#104) * Fix - Get-AzSentinelAlertRuleAction doesn't return playbookName (#102) * fixing return issue * fixing playbook issue * init release Get-AzSentinelDataConnector function (#103) * Fix - get-azsentinelhuntingrule updated get and remove function (#106) * fixing hunitng rule get and remove issue * cleaning up * updating filters * Add filtering by lastModified (#107) * updating AggregationKind class and enum (#111) * Release of Import-AzSentinelDataConnector function (#116) * extra check for Import-AzSentinelDataConnector * fixing class issue (#118) * New function: Export-AzSentinel (#121) * init code * Release Export-AzSentinel and some small fixes/updates * fixing SeveritiesFilter issue for MicrosoftSecurityIncidentCreation (#122) * updating Get-AzSentinelAlertRule function and docs (#125) * modified token expiration logic (#135) Co-authored-by: John Crouch * fixing small issues (#136) * Fixing issue when switching from subscription (#140) * !Deploy Release version 0.6.14 (#137) * Release '0.6.2' (#31) * updating get alert and hunting rule function * updated error handling * Create Get-PlayBook.ps1 * cleaning up * Release Update Incident function (#37) * init release update incident function * cleaning up * updating * updating incident function * code cleanup * Cleaning up and ready for release * updating final docs folder * Release Feature playbook configuration (#33) * updating get alert and hunting rule function * updated error handling * Create Get-PlayBook.ps1 * init release for playbook * cleaning up * finishing playbook * adding get alert rule action function * releasing get logic app function * release new- az sen alert action and some codue update * init release playbook function * uppdated gitignore * init release remove azsentinel action rule * fixed compare issue * Merge branch 'development' of github.com:wortell/AZSentinel into feature/playbook * updating pester test result * updating readme * updating readme * updated docs and pester test results * restoring version * Fix/smallconflicts (#40) * updating docs * updating examples * updating pipeline * fixing Subscribtion parameter for playbook (#43) * fixing Subscribtion parameter for playbook (#45) * Fix- get-Azsentinalhuntingrule - Cannot validate argument on parameter "Property" (#50) * fix huntng rule * fixing hunting rule issue * Fix - new-azsentinelalertrule playbook property (#49) * fixing the if statement * fixing the if statement * Feature - get all incidents (#51) * updating get incident * updating get incident function and docs * updating powershell-yaml * updating importmodule error * workaround * removing powershell-yaml depending * fixing logicapp sas token (#52) * Add support for day time periods (#61) * Add missing dot to yml file extension (#59) The Import-AZSentinelAlertRule function is not able to import yml files due to missing dot in the file extension. * adding support for resource provider in set-azsentinel (#69) * New function for enabling and disabling Alert rules (#71) * init release enable and disable function * adding empty test files * updating return message * New feature change the displayName of an alert (#68) * Release Rename Alert rule function * updating rename function * Handle nextLink for Playbooks (#78) When retrieving playbooks not all are being returned. Code copied from Issue #35 Retrieving all incidents. * adding support for alert aggregation (#65) * adding support for alert aggregation, classes created * updaing classes * updated the class and created first rule wih no error * update class and made import function backwards compatible * small changes * tested with import method * updating new function * checking working code, starting cleanup * updating documentation * updating docs and cleaning up * updating build errors * change pester version * updating pester version * Update groupingConfiguration.ps1 (#87) * Fix bug that causes loss of certain incident properties, add option to set incident description (#91) * Feature - Adding support for all alert rule types (#90) * init release * updating docs Co-authored-by: Khabazi * New Functionality to get alert rule templates provided by Microsoft (#94) Co-authored-by: Antonio Ramirez * Update/get az sentinel alert rule templates (#95) * udating Get-AzSentinelAlertRuleTemplates * updated Co-authored-by: Khabazi * Feature/add az sentinel incident comment (#96) * udating Get-AzSentinelAlertRuleTemplates * updated * fixing playbook issue * Add-AzSentinelIncidentComment * release Co-authored-by: Khabazi * fixing class error (#99) * updating example files, ncluding multi rule yaml file (#104) * Fix - Get-AzSentinelAlertRuleAction doesn't return playbookName (#102) * fixing return issue * fixing playbook issue * init release Get-AzSentinelDataConnector function (#103) * Fix - get-azsentinelhuntingrule updated get and remove function (#106) * fixing hunitng rule get and remove issue * cleaning up * updating filters * Add filtering by lastModified (#107) * updating AggregationKind class and enum (#111) * Release of Import-AzSentinelDataConnector function (#116) * extra check for Import-AzSentinelDataConnector * fixing class issue (#118) * New function: Export-AzSentinel (#121) * init code * Release Export-AzSentinel and some small fixes/updates * fixing SeveritiesFilter issue for MicrosoftSecurityIncidentCreation (#122) * updating Get-AzSentinelAlertRule function and docs (#125) * modified token expiration logic (#135) Co-authored-by: John Crouch * fixing small issues (#136) Co-authored-by: pemontto <939704+pemontto@users.noreply.github.com> Co-authored-by: NVolcz Co-authored-by: stehod <34159548+stehod@users.noreply.github.com> Co-authored-by: ThijsLecomte <42153270+ThijsLecomte@users.noreply.github.com> Co-authored-by: Jonathan Holtmann Co-authored-by: Khabazi Co-authored-by: ramirezversion <34833071+ramirezversion@users.noreply.github.com> Co-authored-by: Antonio Ramirez Co-authored-by: John Crouch <50185606+john-crouch@users.noreply.github.com> Co-authored-by: John Crouch * fixing issue when switching from subscription * fixing subscription precheck issue * restore Co-authored-by: pemontto <939704+pemontto@users.noreply.github.com> Co-authored-by: NVolcz Co-authored-by: stehod <34159548+stehod@users.noreply.github.com> Co-authored-by: ThijsLecomte <42153270+ThijsLecomte@users.noreply.github.com> Co-authored-by: Jonathan Holtmann Co-authored-by: Khabazi Co-authored-by: ramirezversion <34833071+ramirezversion@users.noreply.github.com> Co-authored-by: Antonio Ramirez Co-authored-by: John Crouch <50185606+john-crouch@users.noreply.github.com> Co-authored-by: John Crouch * Fixing issue with Fusion rules (#143) * MSSP Playbook (#142) * !Deploy Release version 0.6.14 (#137) * Release '0.6.2' (#31) * updating get alert and hunting rule function * updated error handling * Create Get-PlayBook.ps1 * cleaning up * Release Update Incident function (#37) * init release update incident function * cleaning up * updating * updating incident function * code cleanup * Cleaning up and ready for release * updating final docs folder * Release Feature playbook configuration (#33) * updating get alert and hunting rule function * updated error handling * Create Get-PlayBook.ps1 * init release for playbook * cleaning up * finishing playbook * adding get alert rule action function * releasing get logic app function * release new- az sen alert action and some codue update * init release playbook function * uppdated gitignore * init release remove azsentinel action rule * fixed compare issue * Merge branch 'development' of github.com:wortell/AZSentinel into feature/playbook * updating pester test result * updating readme * updating readme * updated docs and pester test results * restoring version * Fix/smallconflicts (#40) * updating docs * updating examples * updating pipeline * fixing Subscribtion parameter for playbook (#43) * fixing Subscribtion parameter for playbook (#45) * Fix- get-Azsentinalhuntingrule - Cannot validate argument on parameter "Property" (#50) * fix huntng rule * fixing hunting rule issue * Fix - new-azsentinelalertrule playbook property (#49) * fixing the if statement * fixing the if statement * Feature - get all incidents (#51) * updating get incident * updating get incident function and docs * updating powershell-yaml * updating importmodule error * workaround * removing powershell-yaml depending * fixing logicapp sas token (#52) * Add support for day time periods (#61) * Add missing dot to yml file extension (#59) The Import-AZSentinelAlertRule function is not able to import yml files due to missing dot in the file extension. * adding support for resource provider in set-azsentinel (#69) * New function for enabling and disabling Alert rules (#71) * init release enable and disable function * adding empty test files * updating return message * New feature change the displayName of an alert (#68) * Release Rename Alert rule function * updating rename function * Handle nextLink for Playbooks (#78) When retrieving playbooks not all are being returned. Code copied from Issue #35 Retrieving all incidents. * adding support for alert aggregation (#65) * adding support for alert aggregation, classes created * updaing classes * updated the class and created first rule wih no error * update class and made import function backwards compatible * small changes * tested with import method * updating new function * checking working code, starting cleanup * updating documentation * updating docs and cleaning up * updating build errors * change pester version * updating pester version * Update groupingConfiguration.ps1 (#87) * Fix bug that causes loss of certain incident properties, add option to set incident description (#91) * Feature - Adding support for all alert rule types (#90) * init release * updating docs Co-authored-by: Khabazi * New Functionality to get alert rule templates provided by Microsoft (#94) Co-authored-by: Antonio Ramirez * Update/get az sentinel alert rule templates (#95) * udating Get-AzSentinelAlertRuleTemplates * updated Co-authored-by: Khabazi * Feature/add az sentinel incident comment (#96) * udating Get-AzSentinelAlertRuleTemplates * updated * fixing playbook issue * Add-AzSentinelIncidentComment * release Co-authored-by: Khabazi * fixing class error (#99) * updating example files, ncluding multi rule yaml file (#104) * Fix - Get-AzSentinelAlertRuleAction doesn't return playbookName (#102) * fixing return issue * fixing playbook issue * init release Get-AzSentinelDataConnector function (#103) * Fix - get-azsentinelhuntingrule updated get and remove function (#106) * fixing hunitng rule get and remove issue * cleaning up * updating filters * Add filtering by lastModified (#107) * updating AggregationKind class and enum (#111) * Release of Import-AzSentinelDataConnector function (#116) * extra check for Import-AzSentinelDataConnector * fixing class issue (#118) * New function: Export-AzSentinel (#121) * init code * Release Export-AzSentinel and some small fixes/updates * fixing SeveritiesFilter issue for MicrosoftSecurityIncidentCreation (#122) * updating Get-AzSentinelAlertRule function and docs (#125) * modified token expiration logic (#135) Co-authored-by: John Crouch * fixing small issues (#136) Co-authored-by: pemontto <939704+pemontto@users.noreply.github.com> Co-authored-by: NVolcz Co-authored-by: stehod <34159548+stehod@users.noreply.github.com> Co-authored-by: ThijsLecomte <42153270+ThijsLecomte@users.noreply.github.com> Co-authored-by: Jonathan Holtmann Co-authored-by: Khabazi Co-authored-by: ramirezversion <34833071+ramirezversion@users.noreply.github.com> Co-authored-by: Antonio Ramirez Co-authored-by: John Crouch <50185606+john-crouch@users.noreply.github.com> Co-authored-by: John Crouch * init test * ready for release Co-authored-by: pemontto <939704+pemontto@users.noreply.github.com> Co-authored-by: NVolcz Co-authored-by: stehod <34159548+stehod@users.noreply.github.com> Co-authored-by: ThijsLecomte <42153270+ThijsLecomte@users.noreply.github.com> Co-authored-by: Jonathan Holtmann Co-authored-by: Khabazi Co-authored-by: ramirezversion <34833071+ramirezversion@users.noreply.github.com> Co-authored-by: Antonio Ramirez Co-authored-by: John Crouch <50185606+john-crouch@users.noreply.github.com> Co-authored-by: John Crouch * fix bug 145 Co-authored-by: pemontto <939704+pemontto@users.noreply.github.com> Co-authored-by: NVolcz Co-authored-by: stehod <34159548+stehod@users.noreply.github.com> Co-authored-by: ThijsLecomte <42153270+ThijsLecomte@users.noreply.github.com> Co-authored-by: Jonathan Holtmann Co-authored-by: Khabazi Co-authored-by: ramirezversion <34833071+ramirezversion@users.noreply.github.com> Co-authored-by: Antonio Ramirez Co-authored-by: John Crouch <50185606+john-crouch@users.noreply.github.com> Co-authored-by: John Crouch * Prevent null reference of non-required argument; fixes #148 Co-authored-by: Pouyan Khabazi Co-authored-by: pemontto <939704+pemontto@users.noreply.github.com> Co-authored-by: NVolcz Co-authored-by: stehod <34159548+stehod@users.noreply.github.com> Co-authored-by: ThijsLecomte <42153270+ThijsLecomte@users.noreply.github.com> Co-authored-by: Jonathan Holtmann Co-authored-by: Khabazi Co-authored-by: ramirezversion <34833071+ramirezversion@users.noreply.github.com> Co-authored-by: Antonio Ramirez Co-authored-by: John Crouch <50185606+john-crouch@users.noreply.github.com> Co-authored-by: John Crouch --- AzSentinel/Classes/ScheduledAlertProp.ps1 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/AzSentinel/Classes/ScheduledAlertProp.ps1 b/AzSentinel/Classes/ScheduledAlertProp.ps1 index c5f4176..c9af247 100644 --- a/AzSentinel/Classes/ScheduledAlertProp.ps1 +++ b/AzSentinel/Classes/ScheduledAlertProp.ps1 @@ -92,10 +92,12 @@ class ScheduledAlertProp { } $this.SuppressionEnabled = if ($suppressionEnabled) { $suppressionEnabled } else { $false } $this.Tactics = $Tactics - $this.PlaybookName = if ($PlaybookName.Split('/').count -gt 1){ - $PlaybookName.Split('/')[-1] - } else { - $PlaybookName + if ($PlaybookName) { + $this.PlaybookName = if ($PlaybookName.Split('/').count -gt 1){ + $PlaybookName.Split('/')[-1] + } else { + $PlaybookName + } } $this.IncidentConfiguration = $IncidentConfiguration $this.eventGroupingSettings = @{ From 6e9c232580dd85f8df5b37fe04c04c22c826b4c5 Mon Sep 17 00:00:00 2001 From: pemontto <939704+pemontto@users.noreply.github.com> Date: Thu, 3 Dec 2020 21:38:57 +0000 Subject: [PATCH 43/53] Add support for FileHash entity (#147) --- AzSentinel/Classes/groupingConfiguration.ps1 | 3 ++- AzSentinel/enums/GroupByEntities.ps1 | 1 + AzSentinel/tests/Unit/classes/AlertRule.tests.ps1 | 6 ++++-- README.md | 5 +++-- examples/AlertRules.json | 3 ++- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/AzSentinel/Classes/groupingConfiguration.ps1 b/AzSentinel/Classes/groupingConfiguration.ps1 index 420c7c6..d9ed457 100644 --- a/AzSentinel/Classes/groupingConfiguration.ps1 +++ b/AzSentinel/Classes/groupingConfiguration.ps1 @@ -45,7 +45,8 @@ class GroupingConfiguration { "Account", "Ip", "Host", - "Url" + "Url", + "FileHash" ) } } diff --git a/AzSentinel/enums/GroupByEntities.ps1 b/AzSentinel/enums/GroupByEntities.ps1 index 05a6441..cf05ab8 100644 --- a/AzSentinel/enums/GroupByEntities.ps1 +++ b/AzSentinel/enums/GroupByEntities.ps1 @@ -3,4 +3,5 @@ enum GroupByEntities { Ip Host Url + FileHash } diff --git a/AzSentinel/tests/Unit/classes/AlertRule.tests.ps1 b/AzSentinel/tests/Unit/classes/AlertRule.tests.ps1 index 4b9e1e3..57fe463 100644 --- a/AzSentinel/tests/Unit/classes/AlertRule.tests.ps1 +++ b/AzSentinel/tests/Unit/classes/AlertRule.tests.ps1 @@ -24,7 +24,8 @@ Describe ScheduledAlertProp { "Account", "Ip", "Host", - "Url" + "Url", + "FileHash" ) ) $groupingConfiguration | Should -Not -BeNullOrEmpty @@ -42,7 +43,8 @@ Describe ScheduledAlertProp { "Account", "Ip", "Host", - "Url" + "Url", + "FileHash" ) ) $groupingConfiguration | Should -Not -BeNullOrEmpty diff --git a/README.md b/README.md index 5f9e60a..1859bbd 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,8 @@ To create a Azure Sentinel Rule, use the following JSON format. "Account", "Ip", "Host", - "Url" + "Url", + "FileHash" ] } } @@ -120,7 +121,7 @@ The following tables describe the values you need to set in the schema. | reopenClosedIncident | bool | false | true, false | true | | lookbackDuration | string | false | Value must be between 5 minutes and 24 hours. | PT6H | | entitiesMatchingMethod | string | false | All, None, Custom | All | -| groupByEntities | string | false | Account, Ip, Host, Url | Account | +| groupByEntities | string | false | Account, Ip, Host, Url, FileHash | Account | ### Fusion rule diff --git a/examples/AlertRules.json b/examples/AlertRules.json index 297f94c..9116342 100644 --- a/examples/AlertRules.json +++ b/examples/AlertRules.json @@ -29,7 +29,8 @@ "Account", "Ip", "Host", - "Url" + "Url", + "FileHash" ] } }, From b42e8a68565d18870ca927ea71ebe8f8cdd2eb27 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Wed, 9 Dec 2020 11:33:53 +0100 Subject: [PATCH 44/53] update enums folder name (#156) --- AzSentinel/AzSentinel.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AzSentinel/AzSentinel.psm1 b/AzSentinel/AzSentinel.psm1 index e5477c4..49e5a11 100644 --- a/AzSentinel/AzSentinel.psm1 +++ b/AzSentinel/AzSentinel.psm1 @@ -1,4 +1,4 @@ -$Enums = @( Get-ChildItem -Path $PSScriptRoot\Enums\*.ps1 -ErrorAction SilentlyContinue ) +$Enums = @( Get-ChildItem -Path $PSScriptRoot\enums\*.ps1 -ErrorAction SilentlyContinue ) Foreach ($import in @($Enums)) { Try { Write-Verbose "Importing $($import.FullName)" From f21cc7da2d4b26a3c93d917018d815280a63cc4f Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Mon, 14 Dec 2020 21:30:40 +0100 Subject: [PATCH 45/53] Updating alertrule output format (#157) --- AzSentinel/Public/Get-AzSentinelAlertRule.ps1 | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 index 004082c..5181262 100644 --- a/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Get-AzSentinelAlertRule.ps1 @@ -113,6 +113,13 @@ function Get-AzSentinelAlertRule { $_.properties | Add-Member -NotePropertyName id -NotePropertyValue $_.id -Force $_.properties | Add-Member -NotePropertyName kind -NotePropertyValue $_.kind -Force + # Updating incidentConfiguration output to match JSON input + if ($_.properties.kind -eq 'Scheduled'){ + $_.properties | Add-Member -NotePropertyName createIncident -NotePropertyValue $_.properties.incidentConfiguration.createIncident -Force + $_.properties | Add-Member -NotePropertyName groupingConfiguration -NotePropertyValue $_.properties.incidentConfiguration.groupingConfiguration -Force + $_.properties.PSObject.Properties.Remove('incidentConfiguration') + } + if (! $SkipPlaybook) { $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId $_.name @@ -141,6 +148,13 @@ function Get-AzSentinelAlertRule { $_.properties | Add-Member -NotePropertyName id -NotePropertyValue $_.id -Force $_.properties | Add-Member -NotePropertyName kind -NotePropertyValue $_.kind -Force + # Updating incidentConfiguration output to match JSON input + if ($_.properties.kind -eq 'Scheduled'){ + $_.properties | Add-Member -NotePropertyName createIncident -NotePropertyValue $_.properties.incidentConfiguration.createIncident -Force + $_.properties | Add-Member -NotePropertyName groupingConfiguration -NotePropertyValue $_.properties.incidentConfiguration.groupingConfiguration -Force + $_.properties.PSObject.Properties.Remove('incidentConfiguration') + } + if (! $SkipPlaybook) { $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId $_.name @@ -167,6 +181,13 @@ function Get-AzSentinelAlertRule { $_.properties | Add-Member -NotePropertyName id -NotePropertyValue $_.id -Force $_.properties | Add-Member -NotePropertyName kind -NotePropertyValue $_.kind -Force + # Updating incidentConfiguration output to match JSON input + if ($_.properties.kind -eq 'Scheduled'){ + $_.properties | Add-Member -NotePropertyName createIncident -NotePropertyValue $_.properties.incidentConfiguration.createIncident -Force + $_.properties | Add-Member -NotePropertyName groupingConfiguration -NotePropertyValue $_.properties.incidentConfiguration.groupingConfiguration -Force + $_.properties.PSObject.Properties.Remove('incidentConfiguration') + } + if (! $SkipPlaybook) { $playbook = Get-AzSentinelAlertRuleAction @arguments -RuleId $_.name From 8e3fe1b378d37770f8fa7900c76f9796a73872ad Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Mon, 14 Dec 2020 22:01:49 +0100 Subject: [PATCH 46/53] adding support for AlertRuleTemplate property (#160) --- AzSentinel/Classes/ScheduledAlertProp.ps1 | 43 +++++++++++ .../Public/Import-AzSentinelAlertRule.ps1 | 66 ++++++++++++----- AzSentinel/Public/New-AzSentinelAlertRule.ps1 | 71 +++++++++++++------ README.md | 4 +- examples/AlertRules.json | 20 ++++++ 5 files changed, 163 insertions(+), 41 deletions(-) diff --git a/AzSentinel/Classes/ScheduledAlertProp.ps1 b/AzSentinel/Classes/ScheduledAlertProp.ps1 index c9af247..fd266b7 100644 --- a/AzSentinel/Classes/ScheduledAlertProp.ps1 +++ b/AzSentinel/Classes/ScheduledAlertProp.ps1 @@ -32,6 +32,8 @@ class ScheduledAlertProp { $eventGroupingSettings + [string] $AlertRuleTemplateName + hidden [AggregationKind]$aggregationKind static [string] TriggerOperatorSwitch([string]$value) { @@ -104,4 +106,45 @@ class ScheduledAlertProp { aggregationKind = if ($aggregationKind) { $aggregationKind } else { "SingleAlert" } } } + + ScheduledAlertProp ($Name, $DisplayName, $Description, $Severity, $Enabled, $Query, $QueryFrequency, ` + $QueryPeriod, $TriggerOperator, $TriggerThreshold, $suppressionDuration, ` + $suppressionEnabled, $Tactics, $PlaybookName, $IncidentConfiguration, ` + $aggregationKind, $AlertRuleTemplateName) { + $this.name = $Name + $this.DisplayName = $DisplayName + $this.Description = $Description + $this.Severity = $Severity + $this.Enabled = $Enabled + $this.Query = $Query + $this.QueryFrequency = [ScheduledAlertProp]::TimeString($QueryFrequency) + $this.QueryPeriod = [ScheduledAlertProp]::TimeString($QueryPeriod) + $this.TriggerOperator = [ScheduledAlertProp]::TriggerOperatorSwitch($TriggerOperator) + $this.TriggerThreshold = $TriggerThreshold + $this.SuppressionDuration = if (($null -eq $suppressionDuration) -or ( $false -eq $suppressionEnabled)) { + "PT1H" + } + else { + if ( [ScheduledAlertProp]::TimeString($suppressionDuration) -ge [ScheduledAlertProp]::TimeString($QueryFrequency) ) { + [ScheduledAlertProp]::TimeString($suppressionDuration) + } + else { + Write-Error "Invalid Properties for Scheduled alert rule: 'suppressionDuration' should be greater than or equal to 'queryFrequency'" -ErrorAction Stop + } + } + $this.SuppressionEnabled = if ($suppressionEnabled) { $suppressionEnabled } else { $false } + $this.Tactics = $Tactics + if ($PlaybookName) { + $this.PlaybookName = if ($PlaybookName.Split('/').count -gt 1){ + $PlaybookName.Split('/')[-1] + } else { + $PlaybookName + } + } + $this.IncidentConfiguration = $IncidentConfiguration + $this.eventGroupingSettings = @{ + aggregationKind = if ($aggregationKind) { $aggregationKind } else { "SingleAlert" } + } + $this.AlertRuleTemplateName = $AlertRuleTemplateName + } } diff --git a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 index 2fcf59a..830e479 100644 --- a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 @@ -169,24 +169,54 @@ function Import-AzSentinelAlertRule { $item.createIncident, $groupingConfiguration ) - $bodyAlertProp = [ScheduledAlertProp]::new( - $item.name, - $item.displayName, - $item.description, - $item.severity, - $item.enabled, - $item.query, - $item.queryFrequency, - $item.queryPeriod, - $item.triggerOperator, - $item.triggerThreshold, - $item.suppressionDuration, - $item.suppressionEnabled, - $item.Tactics, - $item.playbookName, - $IncidentConfiguration, - $item.aggregationKind - ) + + if (($item.AlertRuleTemplateName -and ! $content) -or $content.AlertRuleTemplateName){ + if ($content.AlertRuleTemplateName){ + <# + If alertRule is already created with a TemplateName then Always use template name from existing rule. + You can't attach existing scheduled rule to another templatename or remove the link to the template + #> + $item | Add-Member -NotePropertyName AlertRuleTemplateName -NotePropertyValue $content.AlertRuleTemplateName -Force + } + $bodyAlertProp = [ScheduledAlertProp]::new( + $item.name, + $item.displayName, + $item.description, + $item.severity, + $item.enabled, + $item.query, + $item.queryFrequency, + $item.queryPeriod, + $item.triggerOperator, + $item.triggerThreshold, + $item.suppressionDuration, + $item.suppressionEnabled, + $item.Tactics, + $item.playbookName, + $IncidentConfiguration, + $item.aggregationKind, + $item.AlertRuleTemplateName + ) + } else { + $bodyAlertProp = [ScheduledAlertProp]::new( + $item.name, + $item.displayName, + $item.description, + $item.severity, + $item.enabled, + $item.query, + $item.queryFrequency, + $item.queryPeriod, + $item.triggerOperator, + $item.triggerThreshold, + $item.suppressionDuration, + $item.suppressionEnabled, + $item.Tactics, + $item.playbookName, + $IncidentConfiguration, + $item.aggregationKind + ) + } $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id, 'Scheduled') } catch { diff --git a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 index 2722514..67c11ae 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 @@ -150,7 +150,7 @@ function New-AzSentinelAlertRule { [Parameter(Mandatory = $false)] [AggregationKind]$AggregationKind, - #Fusion & MLBehaviorAnalytics + #Fusion & MLBehaviorAnalytics & Scheduled [Parameter(Mandatory = $false)] [string]$AlertRuleTemplateName, @@ -233,24 +233,53 @@ function New-AzSentinelAlertRule { $groupingConfiguration ) - $bodyAlertProp = [ScheduledAlertProp]::new( - $item.name, - $DisplayName, - $Description, - $Severity, - $Enabled, - $Query, - $QueryFrequency, - $QueryPeriod, - $TriggerOperator, - $TriggerThreshold, - $SuppressionDuration, - $SuppressionEnabled, - $Tactics, - $PlaybookName, - $incidentConfiguration, - $AggregationKind - ) + if (($AlertRuleTemplateName -and ! $content) -or $content.AlertRuleTemplateName) { + if ($content.AlertRuleTemplateName){ + <# + If alertRule is already created with a TemplateName then Always use template name from existing rule. + You can't attach existing scheduled rule to another templatename or remove the link to the template + #> + $AlertRuleTemplateName = $content.AlertRuleTemplateName + } + $bodyAlertProp = [ScheduledAlertProp]::new( + $item.name, + $DisplayName, + $Description, + $Severity, + $Enabled, + $Query, + $QueryFrequency, + $QueryPeriod, + $TriggerOperator, + $TriggerThreshold, + $SuppressionDuration, + $SuppressionEnabled, + $Tactics, + $PlaybookName, + $incidentConfiguration, + $AggregationKind, + $AlertRuleTemplateName + ) + } else { + $bodyAlertProp = [ScheduledAlertProp]::new( + $item.name, + $DisplayName, + $Description, + $Severity, + $Enabled, + $Query, + $QueryFrequency, + $QueryPeriod, + $TriggerOperator, + $TriggerThreshold, + $SuppressionDuration, + $SuppressionEnabled, + $Tactics, + $PlaybookName, + $incidentConfiguration, + $AggregationKind + ) + } $body = [AlertRule]::new( $item.name, $item.etag, $bodyAlertProp, $item.Id, 'Scheduled') } @@ -294,10 +323,10 @@ function New-AzSentinelAlertRule { $body.Properties | Add-Member -NotePropertyName Kind -NotePropertyValue "Scheduled" -Force $return += $body.Properties - return $return - Write-Verbose $_ Write-Error "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue + + return $return } } else { diff --git a/README.md b/README.md index 1859bbd..f366986 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ To create a Azure Sentinel Rule, use the following JSON format. { "displayName": "string", "description": "string", + "AlertRuleTemplateName": "string", "severity": "High", "enabled": true, "query": "SecurityEvent | where EventID == \"4688\" | where CommandLine contains \"-noni -ep bypass $\"", @@ -122,7 +123,7 @@ The following tables describe the values you need to set in the schema. | lookbackDuration | string | false | Value must be between 5 minutes and 24 hours. | PT6H | | entitiesMatchingMethod | string | false | All, None, Custom | All | | groupByEntities | string | false | Account, Ip, Host, Url, FileHash | Account | - +| AlertRuleTemplateName | string | false | Name of the alert rule template | 826bb2f8-7894-4785-9a6b-a8a855d8366f | ### Fusion rule ```JSON @@ -198,7 +199,6 @@ The following tables describe the values you need to set in the schema. | | | | | | - ## Find us * [GitHub](https://github.com/wortell/AZSentinel) diff --git a/examples/AlertRules.json b/examples/AlertRules.json index 9116342..f7b0e6d 100644 --- a/examples/AlertRules.json +++ b/examples/AlertRules.json @@ -2,6 +2,7 @@ "Scheduled": [ { "displayName": "AlertRule01", + "AlertRuleTemplateName": "826bb2f8-7894-4785-9a6b-a8a855d8366f", "description": "", "severity": "Medium", "enabled": true, @@ -37,6 +38,7 @@ { "displayName": "AlertRule02", "description": "", + "AlertRuleTemplateName": "", "severity": "Medium", "enabled": true, "query": "SecurityEvent | where EventID == \"4688\" | where CommandLine contains \"-noni -ep bypass $\"", @@ -52,6 +54,24 @@ "Collection" ], "playbookName": "/subscriptions/SUBSCRIPTIONID/resourceGroups/RESOURCEGROUPNAME/providers/Microsoft.Logic/workflows/playbook02" + }, + { + "displayName": "AlertRule03", + "description": "", + "severity": "Medium", + "enabled": true, + "query": "SecurityEvent | where EventID == \"4688\" | where CommandLine contains \"-noni -ep bypass $\"", + "queryFrequency": "5H", + "queryPeriod": "6H", + "triggerOperator": "GreaterThan", + "triggerThreshold": 5, + "suppressionDuration": "6H", + "suppressionEnabled": false, + "tactics": [ + "Persistence", + "LateralMovement", + "Collection" + ] } ], "Fusion": [ From d370c18ea4ea4882b1f8102c36f8bd7eee7192c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Wadstr=C3=B6m?= <36885853+wadstromtech@users.noreply.github.com> Date: Tue, 22 Dec 2020 21:18:25 +0100 Subject: [PATCH 47/53] Follow official api schema (#162) * Update groupingConfiguration.ps1 * Update Import-AzSentinelAlertRule.ps1 * Support importing raw rule configuration This update makes it possible to import a rule without nesting it within "Scheduled", "analytics", "fusion", "MLBehaviorAnalytics" or "MicrosoftSecurityIncidentCreation" * Update Import-AzSentinelAlertRule.ps1 * Update Import-AzSentinelAlertRule.ps1 Added backwards compatibility support, fix for non-nested settings files (row 133), and added some verbose logging. --- AzSentinel/Classes/groupingConfiguration.ps1 | 8 +- .../Public/Import-AzSentinelAlertRule.ps1 | 86 +++++++++++++------ 2 files changed, 64 insertions(+), 30 deletions(-) diff --git a/AzSentinel/Classes/groupingConfiguration.ps1 b/AzSentinel/Classes/groupingConfiguration.ps1 index d9ed457..a61a5b3 100644 --- a/AzSentinel/Classes/groupingConfiguration.ps1 +++ b/AzSentinel/Classes/groupingConfiguration.ps1 @@ -1,5 +1,5 @@ class GroupingConfiguration { - [bool]$Enabled + [bool]$enabled [bool]$reopenClosedIncident @@ -27,7 +27,7 @@ class GroupingConfiguration { return $value } - groupingConfiguration ($properties) { + GroupingConfiguration ($properties) { $this.enabled = $properties.enabled $this.reopenClosedIncident = $properties.reopenClosedIncident $this.lookbackDuration = $properties.lookbackDuration @@ -35,8 +35,8 @@ class GroupingConfiguration { $this.groupByEntities = $properties.groupByEntities } - groupingConfiguration ($Enabled, $reopenClosedIncident, $lookbackDuration, $entitiesMatchingMethod, $groupByEntities) { - $this.enabled = if ($null -ne $Enabled ) { $Enabled } else { $false } + GroupingConfiguration ($enabled, $reopenClosedIncident, $lookbackDuration, $entitiesMatchingMethod, $groupByEntities) { + $this.enabled = if ($null -ne $enabled ) { $enabled } else { $false } $this.reopenClosedIncident = if ($null -ne $reopenClosedIncident) { $reopenClosedIncident } else { $false } $this.lookbackDuration = if ($lookbackDuration) { [groupingConfiguration]::TimeString($lookbackDuration) } else { "PT5H" } $this.entitiesMatchingMethod = if ($entitiesMatchingMethod) { $entitiesMatchingMethod } else { "All" } diff --git a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 index 830e479..5f0105f 100644 --- a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 @@ -113,29 +113,45 @@ function Import-AzSentinelAlertRule { <# Test All rules first #> - $allRules = $rules.analytics + $rules.Scheduled + $rules.fusion + $rules.MLBehaviorAnalytics + $rules.MicrosoftSecurityIncidentCreation | Select-Object displayName - try { - $allRulesContent = Get-AzSentinelAlertRule @arguments -RuleName $($allRules.displayName) -ErrorAction Stop - } - catch { - Write-Error $_.Exception.Message - break + if($rules.analytics -or $rules.Scheduled -or $rules.fusion -or $rules.MLBehaviorAnalytics -or $rules.MicrosoftSecurityIncidentCreation) + { + $allRules = $rules.analytics + $rules.Scheduled + $rules.fusion + $rules.MLBehaviorAnalytics + $rules.MicrosoftSecurityIncidentCreation | Select-Object displayName + try { + Write-Verbose -Message "Found $($allRules.displayName.Count) rules in the settings file." + $allRulesContent = Get-AzSentinelAlertRule @arguments -RuleName $($allRules.displayName) -ErrorAction Stop + } + catch { + Write-Error $_.Exception.Message + break + } } + <# - analytics rule + Analytics rule + Take the raw rule configuration if it is not nested in "analytics", "Scheduled", "fusion", "MLBehaviorAnalytics" or "MicrosoftIncidentCreation" #> - if ($rules.analytics) { + if (-not $rules.analytics -and -not $rules.Scheduled -and -not $rules.fusion -and -not $rules.MLBehaviorAnalytics -and -not $rules.MicrosoftSecurityIncidentCreation){ + Write-Verbose -Message "Settings file is not nested in root schema, using raw configuration." + $scheduled = $rules + } + elseif ($rules.analytics) { $scheduled = $rules.analytics } - else { + else{ $scheduled = $rules.Scheduled } + foreach ($item in $scheduled) { Write-Verbose -Message "Started with rule: $($item.displayName)" $guid = (New-Guid).Guid - - $content = $allRulesContent | Where-Object {$_.kind -eq 'Scheduled' -and $_.displayName -eq $item.displayName} + if($allRulesContent) + { + $content = $allRulesContent | Where-Object {$_.kind -eq 'Scheduled' -and $_.displayName -eq $item.displayName} + } + else{ + $content = Get-AzSentinelAlertRule @arguments -RuleName $($item.displayName) -ErrorAction Stop + } Write-Verbose -Message "Get rule $($item.description)" @@ -157,19 +173,37 @@ function Import-AzSentinelAlertRule { $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/alertRules/$($guid)?api-version=2019-01-01-preview" } + # The official API schema indicates that the grouping configuration is part of the incident configuration try { - $groupingConfiguration = [GroupingConfiguration]::new( - $item.groupingConfiguration.enabled, - $item.groupingConfiguration.reopenClosedIncident, - $item.groupingConfiguration.lookbackDuration, - $item.groupingConfiguration.entitiesMatchingMethod, - $item.groupingConfiguration.groupByEntities - ) - $IncidentConfiguration = [IncidentConfiguration]::new( - $item.createIncident, - $groupingConfiguration - ) - + # Added if/else statement for backwards compatibility + if($item.incidentConfiguration){ + $groupingConfiguration = [GroupingConfiguration]::new( + $item.incidentConfiguration.groupingConfiguration.enabled, + $item.incidentConfiguration.groupingConfiguration.reopenClosedIncident, + $item.incidentConfiguration.groupingConfiguration.lookbackDuration, + $item.incidentConfiguration.groupingConfiguration.entitiesMatchingMethod, + $item.incidentConfiguration.groupingConfiguration.groupByEntities + ) + $incidentConfiguration = [IncidentConfiguration]::new( + $item.incidentConfiguration.createIncident, + $groupingConfiguration + ) + } + else{ + $groupingConfiguration = [GroupingConfiguration]::new( + $item.groupingConfiguration.enabled, + $item.groupingConfiguration.reopenClosedIncident, + $item.groupingConfiguration.lookbackDuration, + $item.groupingConfiguration.entitiesMatchingMethod, + $item.groupingConfiguration.groupByEntities + ) + $incidentConfiguration = [IncidentConfiguration]::new( + $item.createIncident, + $groupingConfiguration + ) + Write-Warning -Message "`"$($item.displayName)`" configuration is not following the official API schema, consider updating the incident and grouping configuration." + } + if (($item.AlertRuleTemplateName -and ! $content) -or $content.AlertRuleTemplateName){ if ($content.AlertRuleTemplateName){ <# @@ -193,7 +227,7 @@ function Import-AzSentinelAlertRule { $item.suppressionEnabled, $item.Tactics, $item.playbookName, - $IncidentConfiguration, + $incidentConfiguration, $item.aggregationKind, $item.AlertRuleTemplateName ) @@ -213,7 +247,7 @@ function Import-AzSentinelAlertRule { $item.suppressionEnabled, $item.Tactics, $item.playbookName, - $IncidentConfiguration, + $incidentConfiguration, $item.aggregationKind ) } From 9b38eb6846fe49c465df99e2bfb0d35e6b966897 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Tue, 22 Dec 2020 21:18:44 +0100 Subject: [PATCH 48/53] fixing playbook reference (#163) --- AzSentinel/Public/Import-AzSentinelAlertRule.ps1 | 4 ++-- AzSentinel/Public/New-AzSentinelAlertRule.ps1 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 index 5f0105f..f89c36c 100644 --- a/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelAlertRule.ps1 @@ -268,7 +268,7 @@ function Import-AzSentinelAlertRule { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | Select-Object * -ExcludeProperty Properties.PlaybookName | ConvertTo-Json -Depth 10 -EnumsAsStrings) if (($compareResult | Where-Object PropertyName -eq "playbookName").DiffValue) { - $PlaybookResult = New-AzSentinelAlertRuleAction @arguments -PlayBookName ($item.playbookName) -RuleId $($body.Name) + $PlaybookResult = New-AzSentinelAlertRuleAction @arguments -PlayBookName $($item.playbookName) -RuleId $($body.Name) $body.Properties | Add-Member -NotePropertyName PlaybookStatus -NotePropertyValue $PlaybookResult -Force } elseif (($compareResult | Where-Object PropertyName -eq "playbookName").RefValue) { @@ -298,7 +298,7 @@ function Import-AzSentinelAlertRule { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | Select-Object * -ExcludeProperty Properties.PlaybookName | ConvertTo-Json -Depth 10 -EnumsAsStrings) if ($body.Properties.playbookName) { - $PlaybookResult = New-AzSentinelAlertRuleAction @arguments -PlayBookName $($body.Properties.playbookName) -RuleId $($body.Properties.Name) -confirm:$false + $PlaybookResult = New-AzSentinelAlertRuleAction @arguments -PlayBookName $($item.playbookName) -RuleId $($body.Name) -confirm:$false $body.Properties | Add-Member -NotePropertyName PlaybookStatus -NotePropertyValue $PlaybookResult -Force } diff --git a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 index 67c11ae..998d585 100644 --- a/AzSentinel/Public/New-AzSentinelAlertRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelAlertRule.ps1 @@ -300,7 +300,7 @@ function New-AzSentinelAlertRule { if (($compareResult | Where-Object PropertyName -eq "playbookName").DiffValue) { foreach ($playbook in ($body.Properties.PlaybookName)) { - $PlaybookResult = New-AzSentinelAlertRuleAction @arguments -PlayBookName $playbook -RuleId $($body.Properties.Name) -confirm:$false + $PlaybookResult = New-AzSentinelAlertRuleAction @arguments -PlayBookName $playbook -RuleId $($body.Name) -confirm:$false $body.Properties | Add-Member -NotePropertyName PlaybookStatus -NotePropertyValue $PlaybookResult -Force } } @@ -336,7 +336,7 @@ function New-AzSentinelAlertRule { $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) if (($body.Properties.PlaybookName)) { foreach ($playbook in ($body.Properties.PlaybookName)) { - New-AzSentinelAlertRuleAction @arguments -PlayBookName $playbook -RuleId $($body.Properties.Name) -confirm:$false + New-AzSentinelAlertRuleAction @arguments -PlayBookName $playbook -RuleId $($body.Name) -confirm:$false $body.Properties | Add-Member -NotePropertyName PlaybookStatus -NotePropertyValue $PlaybookResult -Force } } From 789d44df62dc2c5a1d92201b360879ae9492b80f Mon Sep 17 00:00:00 2001 From: wez3 Date: Wed, 30 Dec 2020 13:47:43 +0100 Subject: [PATCH 49/53] Add Office 365 Data Connector (#154) --- .../Public/Import-AzSentinelDataConnector.ps1 | 88 +++++++++++++++++++ examples/DataConnectors.json | 8 ++ 2 files changed, 96 insertions(+) diff --git a/AzSentinel/Public/Import-AzSentinelDataConnector.ps1 b/AzSentinel/Public/Import-AzSentinelDataConnector.ps1 index ad3e776..d2acedc 100644 --- a/AzSentinel/Public/Import-AzSentinelDataConnector.ps1 +++ b/AzSentinel/Public/Import-AzSentinelDataConnector.ps1 @@ -193,6 +193,94 @@ function Import-AzSentinelDataConnector { } } + # Office365 connector + foreach ($item in $connectors.Office365) { + if (-Not (Get-Member -InputObject $item -Name "tenantId" -MemberType Properties)) { + Write-Error "TenantId missing" + break + } + + if ($null -ne $enabledDataConnectors){ + $office365 = $enabledDataConnectors | Where-Object { $_.kind -eq "Office365" -and $_.properties.tenantId -eq $item.tenantId } + } + else { + $office365 + } + $skip = $false + + if ($null -ne $office365) { + if ($office365) { + Write-Host "Office365 is already enabled on tenant '$($office365.properties.tenantId)'" + $skip = $true + } + else { + $connectorBody = @{ + id = $office365.id + name = $office365.name + etag = $office365.etag + type = 'Microsoft.SecurityInsights/dataConnectors' + kind = 'Office365' + properties = @{ + tenantId = $item.tenantId + dataTypes = @{ + exchange = @{ + state = $item.exchange_state + } + sharepoint = @{ + state = $item.sharepoint_state + } + teams = @{ + state = $item.teams_state + } + } + } + } + } + } + else { + $guid = (New-Guid).Guid + + $connectorBody = @{ + id = "$script:Workspace/providers/Microsoft.SecurityInsights/dataConnectors/$guid" + name = $guid + type = 'Microsoft.SecurityInsights/dataConnectors' + kind = 'Office365' + properties = @{ + tenantId = $item.tenantId + dataTypes = @{ + exchange = @{ + state = $item.exchange_state + } + sharepoint = @{ + state = $item.sharepoint_state + } + teams = @{ + state = $item.teams_state + } + } + } + } + } + + if ($skip -eq $false) { + # Enable or update Office365 with http put method + $uri = "$script:baseUri/providers/Microsoft.SecurityInsights/dataConnectors/$($connectorBody.name)?api-version=2020-01-01" + + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($connectorBody | ConvertTo-Json -Depth 4 -EnumsAsStrings) + + Write-Host "Successfully enabled Office365 with status: $($result.StatusDescription) for tenant '$($item.tenantId)'" + + } + catch { + $errorReturn = $_ + $errorResult = ($errorReturn | ConvertFrom-Json ).error + Write-Verbose $_ + Write-Error "Unable to invoke webrequest with error message: $($errorResult.message)" -ErrorAction Stop + } + } + } + #ThreatIntelligenceTaxii foreach ($item in $connectors.ThreatIntelligenceTaxii) { if ($enabledDataConnectors){ diff --git a/examples/DataConnectors.json b/examples/DataConnectors.json index 2039468..9c88958 100644 --- a/examples/DataConnectors.json +++ b/examples/DataConnectors.json @@ -17,6 +17,14 @@ "subscriptionId": "ebdab2f1-0b79-4181-a70d-82f0ff39243e" } ], + "Office365": [ + { + "exchange_state": "Enabled", + "sharepoint_state": "Enabled", + "teams_state": "Enabled", + "tenantId": "ebdab2f1-0b79-4181-a70d-82f0ff39243e" + } + ], "ThreatIntelligenceTaxii": [ { "friendlyName": "testserver", From ce517662b11655e54ce55a9d83adf2e4c5865cb1 Mon Sep 17 00:00:00 2001 From: nodauf Date: Tue, 2 Feb 2021 22:54:08 +0100 Subject: [PATCH 50/53] Typo xported -> exported (#169) Templates xported -> Templates exported --- AzSentinel/Public/Export-AzSentinel.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AzSentinel/Public/Export-AzSentinel.ps1 b/AzSentinel/Public/Export-AzSentinel.ps1 index da24335..a0d8af2 100644 --- a/AzSentinel/Public/Export-AzSentinel.ps1 +++ b/AzSentinel/Public/Export-AzSentinel.ps1 @@ -202,7 +202,7 @@ function Export-AzSentinel { try { $fullPath = "$($OutputFolder)Templates_$date.json" $output | ConvertTo-Json -EnumsAsStrings -Depth 15 | Out-File $fullPath -ErrorAction Stop - Write-Output "Templates xported to: $fullPath" + Write-Output "Templates exported to: $fullPath" } catch { $ErrorMessage = $_.Exception.Message From 8058e5afdd9872200f73d9c1e2d6a8137824cfb5 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Tue, 2 Feb 2021 23:02:51 +0100 Subject: [PATCH 51/53] Hunting rules function updated (#170) * init update * update example --- AzSentinel/Classes/Hunting.ps1 | 35 ++++++ AzSentinel/Classes/HuntingRule.ps1 | 15 +++ AzSentinel/Classes/classes.psd1 | 2 + .../Public/Import-AzSentinelHuntingRule.ps1 | 100 ++++++----------- .../Public/New-AzSentinelHuntingRule.ps1 | 102 +++++------------- examples/HuntingRules.json | 4 +- 6 files changed, 113 insertions(+), 145 deletions(-) create mode 100644 AzSentinel/Classes/Hunting.ps1 create mode 100644 AzSentinel/Classes/HuntingRule.ps1 diff --git a/AzSentinel/Classes/Hunting.ps1 b/AzSentinel/Classes/Hunting.ps1 new file mode 100644 index 0000000..5806be4 --- /dev/null +++ b/AzSentinel/Classes/Hunting.ps1 @@ -0,0 +1,35 @@ +class Hunting { + [string]$DisplayName + [string]$Query + + [string]$Description + [Tactics[]]$Tactics + + [string]$Category + [pscustomobject]$Tags + + Hunting($DisplayName, $Query, $Description, $Tactics) { + $this.Category = 'Hunting Queries' + $this.DisplayName = $DisplayName + $this.Query = $Query + $this.Tags = @( + @{ + 'Name' = "description" + 'Value' = $Description + }, + @{ + "Name" = "tactics" + "Value" = $Tactics -join ',' + }, + @{ + "Name" = "createdBy" + "Value" = "" + }, + @{ + "Name" = "createdTimeUtc" + "Value" = "" + } + ) + + } +} diff --git a/AzSentinel/Classes/HuntingRule.ps1 b/AzSentinel/Classes/HuntingRule.ps1 new file mode 100644 index 0000000..bcaa151 --- /dev/null +++ b/AzSentinel/Classes/HuntingRule.ps1 @@ -0,0 +1,15 @@ +class HuntingRule { + [string]$name + $eTag + [string]$id + + [pscustomobject]$properties + + HuntingRule ($name, $eTag, $id, $properties ) { + $this.name = $name + $this.eTag = $eTag + $this.id = $id + $this.properties = $properties + + } +} diff --git a/AzSentinel/Classes/classes.psd1 b/AzSentinel/Classes/classes.psd1 index 44891de..5f280e6 100644 --- a/AzSentinel/Classes/classes.psd1 +++ b/AzSentinel/Classes/classes.psd1 @@ -7,5 +7,7 @@ ,'IncidentConfiguration' ,'ScheduledAlertProp' ,'AlertRule' + , 'Hunting' + , 'HuntingRule' ) } diff --git a/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 index 1b4d99f..2c945e1 100644 --- a/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/Import-AzSentinelHuntingRule.ps1 @@ -65,7 +65,7 @@ function Import-AzSentinelHuntingRule { if ($SettingsFile.Extension -eq '.json') { try { $content = (Get-Content $SettingsFile -Raw | ConvertFrom-Json -ErrorAction Stop) - if ($content.analytics){ + if ($content.analytics) { $hunting = $content.analytics } else { @@ -99,8 +99,10 @@ function Import-AzSentinelHuntingRule { break } + $return = @() + foreach ($item in $hunting) { - Write-Output "Started with Hunting rule: $($item.displayName)" + Write-Verbose "Started with Hunting rule: $($item.displayName)" try { Write-Verbose -Message "Get rule $($item.description)" @@ -133,77 +135,39 @@ function Import-AzSentinelHuntingRule { Write-Error "Unable to connect to APi to get Analytic rules with message: $($_.Exception.Message)" -ErrorAction Stop } - [PSCustomObject]$body = @{ - "name" = $item.name - "eTag" = $item.etag - "id" = $item.id - "properties" = @{ - 'Category' = 'Hunting Queries' - 'DisplayName' = [string]$item.displayName - 'Query' = [string]$item.query - [pscustomobject]'Tags' = @( - @{ - 'Name' = "description" - 'Value' = [string]$item.description - }, - @{ - "Name" = "tactics" - "Value" = [Tactics[]] $item.tactics -join ',' - }, - @{ - "Name" = "createdBy" - "Value" = "" - }, - @{ - "Name" = "createdTimeUtc" - "Value" = "" - } - ) - } + <# + Build Class + #> + try { + $bodyProp = [Hunting]::new( + $item.displayName, + $item.query, + $item.description, + $item.tactics + ) + + $body = [HuntingRule]::new( $item.name, $item.eTag, $item.Id, $bodyProp) + } + catch { + Write-Error "Unable to initiate class with error: $($_.Exception.Message)" -ErrorAction Continue } - if ($content) { - $compareResult1 = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id, Tags, Version) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name, Tags, Version) - $compareResult2 = Compare-Policy -ReferenceTemplate ($content.Tags | Where-Object { $_.name -eq "tactics" }) -DifferenceTemplate ($body.Properties.Tags | Where-Object { $_.name -eq "tactics" }) - $compareResult = [PSCustomObject]$compareResult1 + [PSCustomObject]$compareResult2 - - if ($compareResult) { - Write-Output "Found Differences for hunting rule: $($item.displayName)" - Write-Output ($compareResult | Format-Table | Out-String) - - if ($PSCmdlet.ShouldProcess("Do you want to update hunting rule: $($body.Properties.DisplayName)")) { - try { - $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) - Write-Output "Successfully updated hunting rule: $($item.displayName) with status: $($result.StatusDescription)" - Write-Output ($body.Properties | Format-List | Format-Table | Out-String) - } - catch { - Write-Verbose $_ - Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Continue - } - } - else { - Write-Output "No change have been made for hunting rule $($item.displayName), deployment aborted" - } - } - else { - Write-Output "Hunting rule $($item.displayName) is compliance, nothing to do" - Write-Output ($body.Properties | Format-List | Format-Table | Out-String) - } + <# + Try to create or update Hunting Rule + #> + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + $return += $body.Properties + + Write-Verbose "Successfully updated hunting rule: $($item.displayName) with status: $($result.StatusDescription)" } - else { - Write-Verbose "Creating new rule: $($item.displayName)" + catch { + Write-Verbose $_ + Write-Error "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue - try { - $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) - Write-Output "Successfully created hunting rule: $($item.displayName) with status: $($result.StatusDescription)" - Write-Output ($body.Properties | Format-List | Format-Table | Out-String) - } - catch { - Write-Verbose $_ - Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Continue - } } } + return $return } } diff --git a/AzSentinel/Public/New-AzSentinelHuntingRule.ps1 b/AzSentinel/Public/New-AzSentinelHuntingRule.ps1 index 3345286..9644167 100644 --- a/AzSentinel/Public/New-AzSentinelHuntingRule.ps1 +++ b/AzSentinel/Public/New-AzSentinelHuntingRule.ps1 @@ -69,14 +69,8 @@ function New-AzSentinelHuntingRule { } } } - $item = @{ } - $content = $null - $body = @{ } - $compareResult1 = $null - $compareResult2 = $null - $compareResult = $null Write-Verbose -Message "Creating new Hunting rule: $($DisplayName)" @@ -111,79 +105,37 @@ function New-AzSentinelHuntingRule { Write-Error "Unable to connect to APi to get Analytic rules with message: $($_.Exception.Message)" -ErrorAction Stop } - [PSCustomObject]$body = @{ - "name" = $item.name - "eTag" = $item.etag - "id" = $item.id - "properties" = @{ - 'Category' = 'Hunting Queries' - 'DisplayName' = $DisplayName - 'Query' = $Query - [pscustomobject]'Tags' = @( - @{ - 'Name' = "description" - 'Value' = $Description - }, - @{ - "Name" = "tactics" - "Value" = $Tactics -join ',' - }, - @{ - "Name" = "createdBy" - "Value" = "" - }, - @{ - "Name" = "createdTimeUtc" - "Value" = "" - } - ) - } + <# + Build Class + #> + try { + $bodyProp = [Hunting]::new( + $DisplayName, + $Query, + $Description, + $Tactics + ) + + $body = [HuntingRule]::new( $item.name, $item.etag, $item.Id, $bodyProp) } - - #return $content - if ($content) { - $compareResult1 = Compare-Policy -ReferenceTemplate ($content | Select-Object * -ExcludeProperty lastModifiedUtc, alertRuleTemplateName, name, etag, id, Tags, Version) -DifferenceTemplate ($body.Properties | Select-Object * -ExcludeProperty name, Tags, Version) - $compareResult2 = Compare-Policy -ReferenceTemplate ($content.Tags | Where-Object { $_.name -eq "tactics" }) -DifferenceTemplate ($body.Properties.Tags | Where-Object { $_.name -eq "tactics" }) - $compareResult = [PSCustomObject]$compareResult1 + [PSCustomObject]$compareResult2 - - if ($compareResult) { - Write-Output "Found Differences for hunting rule: $($DisplayName)" - Write-Output ($compareResult | Format-Table | Out-String) - - if ($PSCmdlet.ShouldProcess("Do you want to update hunting rule: $($DisplayName)")) { - try { - Write-Output ($body.properties | Format-Table) - - $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) - Write-Output "Successfully updated hunting rule: $($DisplayName) with status: $($result.StatusDescription)" - } - catch { - Write-Verbose $_ - Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Stop - } - } - else { - Write-Output "No change have been made for rule $($DisplayName), deployment aborted" - } - } - else { - Write-Output "Hunting rule $($DisplayName) is compliance, nothing to do" - Write-Output ($body.properties | Format-Table) - } + catch { + Write-Error "Unable to initiate class with error: $($_.Exception.Message)" -ErrorAction Continue } - else { - Write-Verbose "Creating new hunting rule: $($DisplayName)" - try { + <# + Try to create or update Hunting Rule + #> + try { + $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) + $body.Properties | Add-Member -NotePropertyName status -NotePropertyValue $($result.StatusDescription) -Force + return $body.Properties + + Write-Verbose "Successfully updated hunting rule: $($item.displayName) with status: $($result.StatusDescription)" + } + catch { + Write-Verbose $_ + Write-Error "Unable to invoke webrequest for rule $($item.displayName) with error message: $($_.Exception.Message)" -ErrorAction Continue - $result = Invoke-webrequest -Uri $uri -Method Put -Headers $script:authHeader -Body ($body | ConvertTo-Json -Depth 10 -EnumsAsStrings) - Write-Output "Successfully created hunting rule: $($DisplayName) with status: $($result.StatusDescription)" - Write-Output ($body.properties | Format-Table) - } - catch { - Write-Verbose $_ - Write-Error "Unable to invoke webrequest with error message: $($_.Exception.Message)" -ErrorAction Stop - } } } } diff --git a/examples/HuntingRules.json b/examples/HuntingRules.json index fb37421..a697161 100644 --- a/examples/HuntingRules.json +++ b/examples/HuntingRules.json @@ -2,7 +2,7 @@ "Hunting": [ { "displayName": "HuntingRule01", - "description": "test", + "description": "test 1", "query": "SecurityEvent | where EventID == \"4688\" | where CommandLine contains \"-noni -ep bypass $\"", "tactics": [ "Persistence", @@ -12,7 +12,7 @@ }, { "displayName": "HuntingRule02", - "description": "test", + "description": "test2", "query": "SecurityEvent | where EventID == \"4688\" | where CommandLine contains \"-noni -ep bypass $\"", "tactics": [ "Persistence", From 17e070437183ce0e9c7df246d0d519a0b76fcd24 Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Mon, 2 Aug 2021 11:52:42 +0200 Subject: [PATCH 52/53] updating the group entity properties (#188) --- AzSentinel/enums/GroupByEntities.ps1 | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/AzSentinel/enums/GroupByEntities.ps1 b/AzSentinel/enums/GroupByEntities.ps1 index cf05ab8..aa033fc 100644 --- a/AzSentinel/enums/GroupByEntities.ps1 +++ b/AzSentinel/enums/GroupByEntities.ps1 @@ -1,7 +1,21 @@ enum GroupByEntities { Account - Ip Host - Url + IP + Malware + File + Process + CloudApplication + DNS + AzureResource FileHash + RegistryKey + RegistryValue + SecurityGroup + URL + IoTDevice + Mailbox + MailCluster + MailMessage + SubmissionMail } From da5482069b6b77511ed196cc5e2d48b54726cf2b Mon Sep 17 00:00:00 2001 From: Pouyan Khabazi Date: Mon, 2 Aug 2021 11:55:16 +0200 Subject: [PATCH 53/53] Error when multiple rules with the same name is found (#178) --- .DS_Store | Bin 0 -> 6148 bytes AzSentinel/Public/New-AzSentinelAlertRule.ps1 | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7b7e179148f56646b77dbfde510730a8dfc0adc8 GIT binary patch literal 6148 zcmeHK%}N6?5Kh{v*^1bMV2`!E*$9)zXVgEwJC4=Qz+7Q3+BE#0k)*2=zyzL8Jh z>o}97#Zm=NB6bEQ-()hAkZ+ev!WiSNaks`;i7_TX5pxzaUkHw)E=kUK5IN4_rSZ@U zA`kj+OVQ-`iwxkmE3kz1*nkZ`e?M;+_;FILzVlqZu(rM-L{SvCiZ@O_N}a?_#&OFX zU19IcP5Pd(&!Z@q+pc%v_tH*j=Qs)z*AIImmF#zW5ORI#hutV`MdPp=t9cwfAcQ5X zPN_VZG!B{-S#LC_6*)PqS1YpF*q=@(b`1)=JP@P!x>IG|p4N iP^B1Ru@sj