From 940d185e3dbf6973ad5f62588e2dbabbbba75647 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 22 Oct 2024 11:22:43 -0400 Subject: [PATCH 1/3] SentinelAlertRule - Initial Release --- .../MSFT_SentinelAlertRule.psm1 | 642 ++++++++++++++++++ .../MSFT_SentinelAlertRule.schema.mof | 109 +++ .../MSFT_SentinelAlertRule/readme.md | 6 + .../MSFT_SentinelAlertRule/settings.json | 32 + .../Resources/SentinelAlertRule/1-Create.ps1 | 26 + .../Resources/SentinelAlertRule/2-Update.ps1 | 26 + .../Resources/SentinelAlertRule/3-Remove.ps1 | 26 + ...icrosoft365DSC.SentinelAlertRule.Tests.ps1 | 178 +++++ 8 files changed, 1045 insertions(+) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/settings.json create mode 100644 Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/1-Create.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/2-Update.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/3-Remove.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.SentinelAlertRule.Tests.ps1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 new file mode 100644 index 0000000000..947211cb19 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 @@ -0,0 +1,642 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $DisplayName, + + [Parameter(Mandatory = $true)] + [System.String] + $SubscriptionId, + + [Parameter(Mandatory = $true)] + [System.String] + $ResourceGroupName, + + [Parameter(Mandatory = $true)] + [System.String] + $WorkspaceName, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + New-M365DSCConnection -Workload 'Azure' ` + -InboundParameters $PSBoundParameters | Out-Null + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $nullResult = $PSBoundParameters + $nullResult.Ensure = 'Absent' + try + { + if ([System.String]::IsNullOrEmpty($TenantId) -and -not $null -eq $Credential) + { + $TenantId = $Credential.UserName.Split('@')[1] + } + + if (-not [System.String]::IsNullOrEmpty($Id)) + { + $instance = Get-M365DSCSentinelAlertRule -SubscriptionId $SubscriptionId ` + -ResourceGroupName $ResourceGroupName ` + -WorkspaceName $WorkspaceName ` + -TenantId $TenantId ` + -Id $Id + } + if ($null -eq $instance) + { + $instances = Get-M365DSCSentinelAlertRule -SubscriptionId $SubscriptionId ` + -ResourceGroupName $ResourceGroupName ` + -WorkspaceName $WorkspaceName ` + -TenantId $TenantId + $instance = $instances | Where-Object -FilterScript {$_.properties.displayName -eq $DisplayName} + } + if ($null -eq $instance) + { + return $nullResult + } + + $results = @{ + ProductFilter = $instance.properties.ProductFilter + Enabled = $instance.properties.Enabled + Severity = $instance.properties.Severity + Tactics = $instance.properties.Tactics + Techniques = $instance.properties.Techniques + Query = $instance.properties.Query + QueryFrequency = $instance.properties.QueryFrequency + QueryPeriod = $instance.properties.QueryPeriod + TriggerOperator = $instance.properties.TriggerOperator + TriggerThreshold = $instance.properties.TriggerThreshold + SuppressionDuration = $instance.properties.SuppressionDuration + SuppresionEnabled = $instance.properties.SuppresionEnabled + AlertRuleTemplateName = $instance.properties.AlertRuleTemplateName + DisplayNamesExcludeFilter = $instance.properties.DisplayNamesExcludeFilter + DisplayNamesFilter = $instance.properties.DisplayNamesFilter + SeveritiesFilter = $instance.properties.SeveritiesFilter + DisplayName = $instance.properties.displayName + SubscriptionId = $SubscriptionId + ResourceGroupName = $ResourceGroupName + WorkspaceName = $WorkspaceName + Id = $instance.name + Description = $instance.properties.description + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + return [System.Collections.Hashtable] $results + } + catch + { + Write-Verbose -Message $_ + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return $nullResult + } +} + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + ##TODO - Replace the PrimaryKey + [Parameter(Mandatory = $true)] + [System.String] + $PrimaryKey, + + ##TODO - Add the list of Parameters + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $currentInstance = Get-TargetResource @PSBoundParameters + + $setParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters + + # CREATE + if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') + { + ##TODO - Replace by the New cmdlet for the resource + New-Cmdlet @SetParameters + } + # UPDATE + elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') + { + ##TODO - Replace by the Update/Set cmdlet for the resource + Set-cmdlet @SetParameters + } + # REMOVE + elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') + { + ##TODO - Replace by the Remove cmdlet for the resource + Remove-cmdlet @SetParameters + } +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + ##TODO - Replace the PrimaryKey + [Parameter(Mandatory = $true)] + [System.String] + $PrimaryKey, + + ##TODO - Add the list of Parameters + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $CurrentValues = Get-TargetResource @PSBoundParameters + $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + + Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" + Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" + + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + Write-Verbose -Message "Test-TargetResource returned $testResult" + + return $testResult +} + +function Export-TargetResource +{ + [CmdletBinding()] + [OutputType([System.String])] + param + ( + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + $ConnectionMode = New-M365DSCConnection -Workload 'Azure' ` + -InboundParameters $PSBoundParameters + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + try + { + $Script:ExportMode = $true + $workspaces = Get-AzResource -ResourceType 'Microsoft.OperationalInsights/workspaces' + $Script:exportedInstances = @() + $i = 1 + $dscContent = '' + if ($Script:exportedInstances.Length -eq 0) + { + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + else + { + Write-Host "`r`n" -NoNewline + } + + if ([System.String]::IsNullOrEmpty($TenantId) -and $null -ne $Credential) + { + $TenantId = $Credential.UserName.Split('@')[1] + } + foreach ($workspace in $workspaces) + { + if ($null -ne $Global:M365DSCExportResourceInstancesCount) + { + $Global:M365DSCExportResourceInstancesCount++ + } + + Write-Host " |---[$i/$($workspaces.Length)] $($workspace.Name)" -NoNewline + $subscriptionId = $workspace.ResourceId.Split('/')[2] + $resourceGroupName = $workspace.ResourceGroupName + $workspaceName = $workspace.Name + + $rules = Get-M365DSCSentinelAlertRule -SubscriptionId $subscriptionId ` + -ResourceGroupName $resourceGroupName ` + -WorkspaceName $workspaceName ` + -TenantId $TenantId + + $j = 1 + if ($currentWatchLists.Length -eq 0 ) + { + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + else + { + Write-Host "`r`n" -NoNewline + } + + foreach ($rule in $rules) + { + $displayedKey = $rule.properties.DisplayName + Write-Host " |---[$j/$($rules.Count)] $displayedKey" -NoNewline + $params = @{ + DisplayName = $indruleicator.properties.displayName + Id = $rule.name + SubscriptionId = $subscriptionId + ResourceGroupName = $resourceGroupName + WorkspaceName = $workspaceName + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + + $Results = Get-TargetResource @Params + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` + -Results $Results + + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` + -ConnectionMode $ConnectionMode ` + -ModulePath $PSScriptRoot ` + -Results $Results ` + -Credential $Credential + $dscContent += $currentDSCBlock + Save-M365DSCPartialExport -Content $currentDSCBlock ` + -FileName $Global:PartialExportFileName + $j++ + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + } + return $dscContent + } + catch + { + Write-Host $Global:M365DSCEmojiRedX + + New-M365DSCLogEntry -Message 'Error during Export:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return '' + } +} + +function Get-M365DSCSentinelAlertRule +{ + [CmdletBinding()] + [OutputType([Array])] + param( + [Parameter()] + [System.String] + $SubscriptionId, + + [Parameter()] + [System.String] + $ResourceGroupName, + + [Parameter()] + [System.String] + $WorkspaceName, + + [Parameter(Mandatory = $true)] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $Id + ) + + try + { + $hostUrl = Get-M365DSCAPIEndpoint -TenantId $TenantId + $uri = $hostUrl.AzureManagement + "/subscriptions/$($SubscriptionId)/resourceGroups/$($ResourceGroupName)/" + if (-not [System.String]::IsNullOrEmpty($Id)) + { + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/alertrules/$($Id)?api-version=2023-12-01-preview" + $response = Invoke-AzRest -Uri $uri -Method 'GET' + $result = ConvertFrom-Json $response.Content + return $result + } + else + { + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/alertrules?api-version=2023-12-01-preview" + $response = Invoke-AzRest -Uri $uri -Method 'GET' + $result = ConvertFrom-Json $response.Content + return $result.value + } + } + catch + { + Write-Verbose -Message $_ + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId + throw $_ + } +} + +function New-M365DSCSentinelThreatIntelligenceIndicator +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $SubscriptionId, + + [Parameter()] + [System.String] + $ResourceGroupName, + + [Parameter()] + [System.String] + $WorkspaceName, + + [Parameter(Mandatory = $true)] + [System.String] + $TenantId, + + [Parameter()] + [System.Collections.Hashtable] + $Body + ) + + try + { + $hostUrl = Get-M365DSCAPIEndpoint -TenantId $TenantId + $uri = $hostUrl.AzureManagement + "/subscriptions/$($SubscriptionId)/resourceGroups/$($ResourceGroupName)/" + + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/alertrules/$((New-GUID).ToString())?api-version=2023-12-01-preview" + $payload = ConvertTo-Json $Body -Depth 10 -Compress + $response = Invoke-AzRest -Uri $uri -Method 'PUT' -Payload $payload + } + catch + { + Write-Verbose -Message $_ + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId + throw $_ + } +} + +function Set-M365DSCSentinelThreatIntelligenceIndicator +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $SubscriptionId, + + [Parameter()] + [System.String] + $ResourceGroupName, + + [Parameter()] + [System.String] + $WorkspaceName, + + [Parameter(Mandatory = $true)] + [System.String] + $TenantId, + + [Parameter(Mandatory = $true)] + [System.String] + $Id, + + [Parameter()] + [System.Collections.Hashtable] + $Body + ) + + try + { + $hostUrl = Get-M365DSCAPIEndpoint -TenantId $TenantId + $uri = $hostUrl.AzureManagement + "/subscriptions/$($SubscriptionId)/resourceGroups/$($ResourceGroupName)/" + + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/threatIntelligence/main/indicators/$($Id)?api-version=2024-03-01" + $payload = ConvertTo-Json $Body -Depth 10 -Compress + $response = Invoke-AzRest -Uri $uri -Method 'PUT' -Payload $payload + } + catch + { + Write-Verbose -Message $_ + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId + throw $_ + } +} + +function Remove-M365DSCSentinelThreatIntelligenceIndicator +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $SubscriptionId, + + [Parameter()] + [System.String] + $ResourceGroupName, + + [Parameter()] + [System.String] + $WorkspaceName, + + [Parameter(Mandatory = $true)] + [System.String] + $TenantId, + + [Parameter(Mandatory = $true)] + [System.String] + $Id + ) + + try + { + $hostUrl = Get-M365DSCAPIEndpoint -TenantId $TenantId + $uri = $hostUrl.AzureManagement + "/subscriptions/$($SubscriptionId)/resourceGroups/$($ResourceGroupName)/" + + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/threatIntelligence/main/indicators/$($Id)?api-version=2024-03-01" + $response = Invoke-AzRest -Uri $uri -Method 'DELETE' + } + catch + { + Write-Verbose -Message $_ + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId + throw $_ + } +} + +Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof new file mode 100644 index 0000000000..30c37ecf58 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof @@ -0,0 +1,109 @@ +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleEventGroupSettings +{ + [Write, Description("")] String aggregationKind; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleCustomDetails +{ + [Write, Description("")] String DetailKey; + [Write, Description("")] String DetailValue; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleEntityMapping +{ + [Write, Description("")] String entityType; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEntityMappingFieldMapping")] String fieldMappings[]; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleEntityMappingFieldMapping +{ + [Write, Description("")] String columnName; + [Write, Description("")] String identifier; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleAlertDetailsOverride +{ + [Write, Description("")] String alertDescriptionFormat; + [Write, Description("")] String alertDisplayNameFormat; + [Write, Description("")] String alertColumnName; + [Write, Description("")] String alertTacticsColumnName; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty")] String alertDynamicProperties; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty +{ + [Write, Description("")] String alertProperty; + [Write, Description("")] String alertPropertyValue; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleIncidentConfiguration +{ + [Write, Description("")] Boolean createIncident; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration")] String groupingConfiguration; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration +{ + [Write, Description("")] Boolean enabled; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail")] groupByAlertDetails[]; + [Write, Description("")] String groupByCustomDetails[]; + [Write, Description("")] String groupByEntities[]; + [Write, Description("")] String lookbackDuration; + [Write, Description("")] String matchingMethod; + [Write, Description("")] Boolean reopenClosedIncident; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail +{ + [Write, Description("")] String DisplayName; + [Write, Description("")] String Severity; +}; + +[ClassVersion("1.0.0.0"), FriendlyName("ResourceName")] +class MSFT_ResourceName : OMI_BaseResource +{ + [Key, Description("The display name of the indicator")] String DisplayName; + [Write, Description("The name of the resource group. The name is case insensitive.")] String SubscriptionId; + [Write, Description("The name of the resource group. The name is case insensitive.")] String ResourceGroupName; + [Write, Description("The name of the workspace.")] String WorkspaceName; + [Write, Description("The unique id of the indicator.")] String Id; + [Write, Description("The name of the workspace.")] String Description; + [Write, Description("")] String ProductFilter; + [Write, Description("")] Boolean Enabled; + [Write, Description("")] String Severity; + [Write, Description("")] String Tactics[]; + [Write, Description("")] String Techniques[]; + [Write, Description("")] String Query; + [Write, Description("")] String QueryFrequency; + [Write, Description("")] String QueryPeriod; + [Write, Description("")] String TriggerOperator; + [Write, Description("")] UInt32 TriggerThreshold; + [Write, Description("")] String SuppressionDuration; + [Write, Description("")] String SuppresionEnabled; + [Write, Description("")] String AlertRuleTemplateName; + [Write, Description("")] String DisplayNamesExcludeFilter[]; + [Write, Description("")] String DisplayNamesFilter[]; + [Write, Description("")] String SeveritiesFilter[]; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEventGroupSettings")] String EventGroupSettings; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleCustomDetails")] String CustomDetails; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEntityMapping")] String EntityMappings[]; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverride")] String AlertDetailsOverride; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfiguration")] String IncidentConfiguration; + + [Write, Description("Present ensures the instance exists, absent ensures it is removed."), ValueMap{"Absent","Present"}, Values{"Absent","Present"}] string Ensure; + [Write, Description("Credentials of the workload's Admin"), EmbeddedInstance("MSFT_Credential")] string Credential; + [Write, Description("Id of the Azure Active Directory application to authenticate with.")] String ApplicationId; + [Write, Description("Id of the Azure Active Directory tenant used for authentication.")] String TenantId; + [Write, Description("Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication.")] String CertificateThumbprint; + [Write, Description("Managed ID being used for authentication.")] Boolean ManagedIdentity; + [Write, Description("Access token used for authentication.")] String AccessTokens[]; +}; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/readme.md new file mode 100644 index 0000000000..32e0e7fb27 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/readme.md @@ -0,0 +1,6 @@ + +# ResourceName + +## Description + +##TODO - Provide a short description of what the resource is set to configure. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/settings.json new file mode 100644 index 0000000000..edf14b05e4 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/settings.json @@ -0,0 +1,32 @@ +{ + "resourceName": "ResourceName", + "description": "Description of what the resource is about.", + "roles": { + "read": [ + "Role" + ], + "update": [ + "Role" + ] + }, + "permissions": { + "graph": { + "delegated": { + "read": [], + "update": [] + }, + "application": { + "read": [ + { + "name": "Permission for Monitoring and Export" + } + ], + "update": [ + { + "name": "Permission for deploying" + } + ] + } + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/1-Create.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/1-Create.ps1 @@ -0,0 +1,26 @@ +<# +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. +#> + +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/2-Update.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/2-Update.ps1 @@ -0,0 +1,26 @@ +<# +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. +#> + +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/3-Remove.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/3-Remove.ps1 @@ -0,0 +1,26 @@ +<# +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. +#> + +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + + } +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SentinelAlertRule.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SentinelAlertRule.Tests.ps1 new file mode 100644 index 0000000000..780e0f343d --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SentinelAlertRule.Tests.ps1 @@ -0,0 +1,178 @@ +[CmdletBinding()] +param( +) +$M365DSCTestFolder = Join-Path -Path $PSScriptRoot ` + -ChildPath '..\..\Unit' ` + -Resolve +$CmdletModule = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Microsoft365.psm1' ` + -Resolve) +$GenericStubPath = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Generic.psm1' ` + -Resolve) +Import-Module -Name (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\UnitTestHelper.psm1' ` + -Resolve) + +$CurrentScriptPath = $PSCommandPath.Split('\') +$CurrentScriptName = $CurrentScriptPath[$CurrentScriptPath.Length -1] +$ResourceName = $CurrentScriptName.Split('.')[1] +$Global:DscHelper = New-M365DscUnitTestHelper -StubModule $CmdletModule ` + -DscResource $ResourceName -GenericStubModule $GenericStubPath + +Describe -Name $Global:DscHelper.DescribeHeader -Fixture { + InModuleScope -ModuleName $Global:DscHelper.ModuleName -ScriptBlock { + Invoke-Command -ScriptBlock $Global:DscHelper.InitializeScript -NoNewScope + BeforeAll { + + $secpasswd = ConvertTo-SecureString (New-Guid | Out-String) -AsPlainText -Force + $Credential = New-Object System.Management.Automation.PSCredential ('tenantadmin@mydomain.com', $secpasswd) + + Mock -CommandName Confirm-M365DSCDependencies -MockWith { + } + + Mock -CommandName New-M365DSCConnection -MockWith { + return "Credentials" + } + + ##TODO - Mock any Remove/Set/New cmdlets + + # Mock Write-Host to hide output during the tests + Mock -CommandName Write-Host -MockWith { + } + $Script:exportedInstances =$null + $Script:ExportMode = $false + } + # Test contexts + Context -Name "The instance should exist but it DOES NOT" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Present' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return $null + Mock -CommandName Get-Cmdlet -MockWith { + return $null + } + } + It 'Should return Values from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Absent' + } + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should create a new instance from the Set method' { + ##TODO - Replace the New-Cmdlet by the appropriate one + Set-TargetResource @testParams + Should -Invoke -CommandName New-Cmdlet -Exactly 1 + } + } + + Context -Name "The instance exists but it SHOULD NOT" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Absent' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return an instance + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + It 'Should return Values from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + } + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should remove the instance from the Set method' { + Set-TargetResource @testParams + ##TODO - Replace the Remove-Cmdlet by the appropriate one + Should -Invoke -CommandName Remove-Cmdlet -Exactly 1 + } + } + + Context -Name "The instance exists and values are already in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Present' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return the desired values + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + + It 'Should return true from the Test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + + Context -Name "The instance exists and values are NOT in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Present' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return a drift + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + + It 'Should return Values from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + } + + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should call the Set method' { + Set-TargetResource @testParams + ##TODO - Replace the Update-Cmdlet by the appropriate one + Should -Invoke -CommandName Update-Cmdlet -Exactly 1 + } + } + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return an instance + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + It 'Should Reverse Engineer resource from the Export method' { + $result = Export-TargetResource @testParams + $result | Should -Not -BeNullOrEmpty + } + } + } +} + +Invoke-Command -ScriptBlock $Global:DscHelper.CleanupScript -NoNewScope From 47b1372de3a6ed932ff0f0598cca27cc5dbeec8b Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 22 Oct 2024 23:01:40 -0400 Subject: [PATCH 2/3] Ready for Review --- CHANGELOG.md | 2 + .../MSFT_SentinelAlertRule.psm1 | 889 ++++++++++++++++-- .../MSFT_SentinelAlertRule.schema.mof | 18 +- .../MSFT_SentinelAlertRule/readme.md | 4 +- .../MSFT_SentinelAlertRule/settings.json | 24 +- .../Dependencies/Manifest.psd1 | 42 +- .../Resources/SentinelAlertRule/1-Create.ps1 | 51 +- .../Resources/SentinelAlertRule/2-Update.ps1 | 51 +- .../Resources/SentinelAlertRule/3-Remove.ps1 | 14 +- ...icrosoft365DSC.SentinelAlertRule.Tests.ps1 | 347 ++++++- 10 files changed, 1286 insertions(+), 156 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c94a634efc..da4bf910c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ * Fixed missing permissions in settings.json * Intune workload * Fixed missing permissions in settings.json +* SentinelAlertRule + * Initial release. * SentinelThreatIntelligenceIndicator * Initial release. * SPOTenantSettings diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 index 947211cb19..77911ab204 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 @@ -28,6 +28,98 @@ function Get-TargetResource [System.String] $Description, + [Parameter()] + [System.String] + $ProductFilter, + + [Parameter()] + [System.Boolean] + $Enabled, + + [Parameter()] + [System.String] + $Severity, + + [Parameter()] + [System.String[]] + $Tactics, + + [Parameter()] + [System.String[]] + $Techniques, + + [Parameter()] + [System.String[]] + $SubTechniques, + + [Parameter()] + [System.String] + $Query, + + [Parameter()] + [System.String] + $QueryFrequency, + + [Parameter()] + [System.String] + $QueryPeriod, + + [Parameter()] + [System.String] + $TriggerOperator, + + [Parameter()] + [System.UInt32] + $TriggerThreshold, + + [Parameter()] + [System.String] + $SuppressionDuration, + + [Parameter()] + [System.String] + $SuppressionEnabled, + + [Parameter()] + [System.String] + $AlertRuleTemplateName, + + [Parameter()] + [System.String[]] + $DisplayNamesExcludeFilter, + + [Parameter()] + [System.String[]] + $DisplayNamesFilter, + + [Parameter()] + [System.String[]] + $SeveritiesFilter, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $EventGroupingSettings, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $CustomDetails, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $EntityMappings, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $AlertDetailsOverride, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $IncidentConfiguration, + + [Parameter()] + [System.String] + $Kind, + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] @@ -103,28 +195,119 @@ function Get-TargetResource return $nullResult } + # EventGroupingSettings + $EventGroupingValueSettingsValue = $null + if ($null -ne $instance.properties.eventGroupingSettings) + { + $EventGroupingValueSettingsValue = @{ + aggregationKind = $instance.properties.eventGroupingSettings.aggregationKind + } + } + + # CustomDetails + $CustomDetailsValue = @() + if ($null -ne $instance.properties.customDetails) + { + $detailAsHash = @{} + $instance.properties.customDetails.psobject.properties | foreach { $detailAsHash[$_.Name] = $_.Value } + foreach ($key in $detailAsHash.Keys) + { + $CustomDetailsValue += @{ + DetailKey = $key + DetailValue = $detailAsHash.$key + } + } + } + + #EntityMappings + $EntityMappingsValue = @() + if ($null -ne $instance.properties.entityMappings) + { + foreach ($mapping in $instance.properties.entityMappings) + { + $entity = @{ + entityType = $mapping.entityType + fieldMappings = @() + } + + foreach ($fieldMapping in $mapping.fieldMappings) + { + $entity.fieldMappings += @{ + identifier = $fieldMapping.identifier + columnName = $fieldMapping.columnName + } + } + + $EntityMappingsValue += $entity + } + } + + #AlertDetailsOverride + if ($null -ne $instance.properties.alertDetailsOverride) + { + $info = $instance.properties.alertDetailsOverride + $AlertDetailsOverrideValue = @{ + alertDisplayNameFormat = $info.alertDisplayNameFormat + alertDescriptionFormat = $info.alertDescriptionFormat + alertDynamicProperties = @() + } + + foreach ($propertyEntry in $info.alertDynamicProperties) + { + $AlertDetailsOverrideValue.alertDynamicProperties += @{ + alertProperty = $propertyEntry.alertProperty + alertPropertyValue = $propertyEntry.value + } + } + } + + #IncidentConfiguration + if ($null -ne $instance.properties.incidentConfiguration) + { + $info = $instance.properties.incidentConfiguration + $IncidentConfigurationValue = @{ + createIncident = [Boolean]::Parse($info.createIncident.ToString()) + groupingConfiguration = @{ + enabled = $info.groupingConfiguration.enabled + reopenClosedIncident = $info.groupingConfiguration.reopenClosedIncident + lookbackDuration = $info.groupingConfiguration.lookbackDuration + matchingMethod = $info.groupingConfiguration.matchingMethod + groupByEntities = $info.groupingConfiguration.groupByEntities + groupByAlertDetails = $info.groupingConfiguration.groupByAlertDetails + groupByCustomDetails = $info.groupingConfiguration.groupByCustomDetails + } + } + } + $results = @{ ProductFilter = $instance.properties.ProductFilter Enabled = $instance.properties.Enabled Severity = $instance.properties.Severity Tactics = $instance.properties.Tactics Techniques = $instance.properties.Techniques + SubTechniques = $instance.properties.SubTechniques Query = $instance.properties.Query QueryFrequency = $instance.properties.QueryFrequency QueryPeriod = $instance.properties.QueryPeriod TriggerOperator = $instance.properties.TriggerOperator TriggerThreshold = $instance.properties.TriggerThreshold SuppressionDuration = $instance.properties.SuppressionDuration - SuppresionEnabled = $instance.properties.SuppresionEnabled + SuppressionEnabled = $instance.properties.SuppressionEnabled AlertRuleTemplateName = $instance.properties.AlertRuleTemplateName DisplayNamesExcludeFilter = $instance.properties.DisplayNamesExcludeFilter DisplayNamesFilter = $instance.properties.DisplayNamesFilter SeveritiesFilter = $instance.properties.SeveritiesFilter DisplayName = $instance.properties.displayName + EventGroupingSettings = $EventGroupingValueSettingsValue + CustomDetails = $CustomDetailsValue + EntityMappings = $EntityMappingsValue + AlertDetailsOverride = $AlertDetailsOverrideValue + IncidentConfiguration = $IncidentConfigurationValue SubscriptionId = $SubscriptionId ResourceGroupName = $ResourceGroupName WorkspaceName = $WorkspaceName Id = $instance.name + Kind = $instance.kind Description = $instance.properties.description Ensure = 'Present' Credential = $Credential @@ -154,12 +337,121 @@ function Set-TargetResource [CmdletBinding()] param ( - ##TODO - Replace the PrimaryKey [Parameter(Mandatory = $true)] [System.String] - $PrimaryKey, + $DisplayName, + + [Parameter(Mandatory = $true)] + [System.String] + $SubscriptionId, + + [Parameter(Mandatory = $true)] + [System.String] + $ResourceGroupName, + + [Parameter(Mandatory = $true)] + [System.String] + $WorkspaceName, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + $ProductFilter, + + [Parameter()] + [System.Boolean] + $Enabled, + + [Parameter()] + [System.String] + $Severity, + + [Parameter()] + [System.String[]] + $Tactics, + + [Parameter()] + [System.String[]] + $Techniques, + + [Parameter()] + [System.String[]] + $SubTechniques, + + [Parameter()] + [System.String] + $Query, - ##TODO - Add the list of Parameters + [Parameter()] + [System.String] + $QueryFrequency, + + [Parameter()] + [System.String] + $QueryPeriod, + + [Parameter()] + [System.String] + $TriggerOperator, + + [Parameter()] + [System.UInt32] + $TriggerThreshold, + + [Parameter()] + [System.String] + $SuppressionDuration, + + [Parameter()] + [System.String] + $SuppressionEnabled, + + [Parameter()] + [System.String] + $AlertRuleTemplateName, + + [Parameter()] + [System.String[]] + $DisplayNamesExcludeFilter, + + [Parameter()] + [System.String[]] + $DisplayNamesFilter, + + [Parameter()] + [System.String[]] + $SeveritiesFilter, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $EventGroupingSettings, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $CustomDetails, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $EntityMappings, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $AlertDetailsOverride, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $IncidentConfiguration, + + [Parameter()] + [System.String] + $Kind, [Parameter()] [ValidateSet('Present', 'Absent')] @@ -205,25 +497,222 @@ function Set-TargetResource $currentInstance = Get-TargetResource @PSBoundParameters - $setParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters + if ([System.String]::IsNullOrEmpty($TenantId) -and -not $null -eq $Credential) + { + $TenantId = $Credential.UserName.Split('@')[1] + } + + $instance = @{} + if ($Kind -eq 'Fusion') + { + $instance = @{ + kind = $Kind + properties = @{ + alertRuleTemplateName = $AlertRuleTemplateName + enabled = $Enabled + } + } + } + elseif ($Kind -eq 'MicrosoftSecurityIncidentCreation') + { + $instance = @{ + kind = $Kind + properties = @{ + displayName = $DisplayName + description = $Description + productFilter = $ProductFilter + displayNamesExcludeFilter = $DisplayNamesExcludeFilter + displayNamesFilter = $DisplayNamesFilter + enabled = $Enabled + severitiesFilter = $AlertSeverity + } + } + } + elseif ($Kind -eq 'Scheduled') + { + $instance = @{ + kind = $Kind + properties = @{ + displayName = $DisplayName + enabled = $Enabled + description = $Description + query = $Query + queryFrequency = $QueryFrequency + queryPeriod = $QueryPeriod + severity = $Severity + suppressionDuration = $SuppressionDuration + suppressionEnabled = $SuppressionEnabled + triggerOperator = $TriggerOperator + triggerThreshold = $TriggerThreshold + eventGroupingSettings = @{ + aggregationKind = $EventGroupingSettings.aggregationKind + } + customDetails = @{} + alertDetailsOverride = @{ + alertDisplayNameFormat = $AlertDetailsOverride.alertDisplayNameFormat + alertDescriptionFormat = $AlertDetailsOverride.alertDescriptionFormat + alertDynamicProperties = @() + } + entityMappings = @() + incidentConfiguration = @{ + createIncident = $IncidentConfiguration.createIncident + groupingConfiguration = @{ + enabled = $IncidentConfiguration.groupingConfiguration.enabled + reopenClosedIncident = $IncidentConfiguration.groupingConfiguration.reopenClosedIncident + lookbackDuration = $IncidentConfiguration.groupingConfiguration.lookbackDuration + matchingMethod = $IncidentConfiguration.groupingConfiguration.matchingMethod + groupByEntities = $IncidentConfiguration.groupingConfiguration.groupByEntities + groupByAlertDetails = $IncidentConfiguration.groupingConfiguration.groupByAlertDetails + groupByCustomDetails = $IncidentConfiguration.groupingConfiguration.groupByCustomDetails + } + } + productFilter = $ProductFilter + displayNamesExcludeFilter = $DisplayNamesExcludeFilter + displayNamesFilter = $DisplayNamesFilter + severitiesFilter = $AlertSeverity + } + } + + foreach ($entity in $EntityMappings) + { + $entry = @{ + entityType = $entity.entityType + fieldMappings = @() + } + + foreach ($field in $entity.fieldMappings) + { + $entry.fieldMappings += @{ + identifier = $field.identifier + columnName = $field.columnName + } + } + + $instance.properties.entityMappings += $entry + } + + foreach ($detail in $CustomDetails) + { + $instance.properties.customDetails.Add($detail.DetailKey, $detail.DetailValue) + } + + foreach ($dynamicProp in $AlertDetailsOverride.alertDynamicProperties) + { + $instance.properties.alertDetailsOverride.alertDynamicProperties += @{ + alertProperty = $dynamicProp.alertProperty + value = $dynamicProp.alertPropertyValue + } + } + } + elseif ($Kind -eq 'NRT') + { + $instance = @{ + kind = $Kind + properties = @{ + displayName = $DisplayName + enabled = $Enabled + description = $Description + query = $Query + severity = $Severity + suppressionDuration = $SuppressionDuration + suppressionEnabled = $SuppressionEnabled + eventGroupingSettings = @{ + aggregationKind = $EventGroupingSettings.aggregationKind + } + alertDetailsOverride = @{ + alertDisplayNameFormat = $AlertDetailsOverride.alertDisplayNameFormat + alertDescriptionFormat = $AlertDetailsOverride.alertDescriptionFormat + alertDynamicProperties = @() + } + entityMappings = @() + customDetails = @{} + incidentConfiguration = @{ + createIncident = $IncidentConfiguration.createIncident + groupingConfiguration = @{ + enabled = $IncidentConfiguration.groupingConfiguration.enabled + reopenClosedIncident = $IncidentConfiguration.groupingConfiguration.reopenClosedIncident + lookbackDuration = $IncidentConfiguration.groupingConfiguration.lookbackDuration + matchingMethod = $IncidentConfiguration.groupingConfiguration.matchingMethod + groupByEntities = $IncidentConfiguration.groupingConfiguration.groupByEntities + groupByAlertDetails = $IncidentConfiguration.groupingConfiguration.groupByAlertDetails + groupByCustomDetails = $IncidentConfiguration.groupingConfiguration.groupByCustomDetails + } + } + techniques = $Techniques + subTechniques = $SubTechniques + tactics = $Tactics + } + } + + if ($null -eq $EntityMappings -or $EntityMappings.Length -eq 0) + { + $instance.properties.Remove('entityMappings') | Out-Null + } + else + { + foreach ($entity in $EntityMappings) + { + $entry = @{ + entityType = $entity.entityType + fieldMappings = @() + } + + foreach ($field in $entity.fieldMappings) + { + $entry.fieldMappings += @{ + identifier = $field.identifier + columnName = $field.columnName + } + } + + $instance.properties.entityMappings += $entry + } + } + + foreach ($detail in $CustomDetails) + { + $instance.properties.customDetails.Add($detail.DetailKey, $detail.DetailValue) + } + + foreach ($dynamicProp in $AlertDetailsOverride.alertDynamicProperties) + { + $instance.properties.alertDetailsOverride.alertDynamicProperties += @{ + alertProperty = $dynamicProp.alertProperty + value = $dynamicProp.alertPropertyValue + } + } + } # CREATE if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') { - ##TODO - Replace by the New cmdlet for the resource - New-Cmdlet @SetParameters + Write-Verbose -Message "Creating new Alert Rule {$DisplayName}" + New-M365DSCSentinelAlertRule -SubscriptionId $SubscriptionId ` + -ResourceGroupName $ResourceGroupName ` + -WorkspaceName $WorkspaceName ` + -TenantId $TenantId ` + -Body $instance } # UPDATE - elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') + elseif($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') { - ##TODO - Replace by the Update/Set cmdlet for the resource - Set-cmdlet @SetParameters + Write-Verbose -Message "Updating Alert Rule {$DisplayName}" + New-M365DSCSentinelAlertRule -SubscriptionId $SubscriptionId ` + -ResourceGroupName $ResourceGroupName ` + -WorkspaceName $WorkspaceName ` + -TenantId $TenantId ` + -Body $instance ` + -Id $currentInstance.Id } # REMOVE elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') { - ##TODO - Replace by the Remove cmdlet for the resource - Remove-cmdlet @SetParameters + Write-Verbose -Message "Removing Alert Rule {$DisplayName}" + Remove-M365DSCSentinelAlertRule -SubscriptionId $SubscriptionId ` + -ResourceGroupName $ResourceGroupName ` + -WorkspaceName $WorkspaceName ` + -TenantId $TenantId ` + -Id $currentInstance.Id } } @@ -233,12 +722,121 @@ function Test-TargetResource [OutputType([System.Boolean])] param ( - ##TODO - Replace the PrimaryKey [Parameter(Mandatory = $true)] [System.String] - $PrimaryKey, + $DisplayName, + + [Parameter(Mandatory = $true)] + [System.String] + $SubscriptionId, + + [Parameter(Mandatory = $true)] + [System.String] + $ResourceGroupName, + + [Parameter(Mandatory = $true)] + [System.String] + $WorkspaceName, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + $ProductFilter, + + [Parameter()] + [System.Boolean] + $Enabled, + + [Parameter()] + [System.String] + $Severity, + + [Parameter()] + [System.String[]] + $Tactics, + + [Parameter()] + [System.String[]] + $Techniques, + + [Parameter()] + [System.String[]] + $SubTechniques, + + [Parameter()] + [System.String] + $Query, + + [Parameter()] + [System.String] + $QueryFrequency, + + [Parameter()] + [System.String] + $QueryPeriod, + + [Parameter()] + [System.String] + $TriggerOperator, + + [Parameter()] + [System.UInt32] + $TriggerThreshold, + + [Parameter()] + [System.String] + $SuppressionDuration, + + [Parameter()] + [System.String] + $SuppressionEnabled, + + [Parameter()] + [System.String] + $AlertRuleTemplateName, - ##TODO - Add the list of Parameters + [Parameter()] + [System.String[]] + $DisplayNamesExcludeFilter, + + [Parameter()] + [System.String[]] + $DisplayNamesFilter, + + [Parameter()] + [System.String[]] + $SeveritiesFilter, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $EventGroupingSettings, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $CustomDetails, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $EntityMappings, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $AlertDetailsOverride, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $IncidentConfiguration, + + [Parameter()] + [System.String] + $Kind, [Parameter()] [ValidateSet('Present', 'Absent')] @@ -288,10 +886,33 @@ function Test-TargetResource Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + #Compare Cim instances + foreach ($key in $PSBoundParameters.Keys) + { + $source = $PSBoundParameters.$key + $target = $CurrentValues.$key + if ($null -ne $source -and $source.GetType().Name -like '*CimInstance*') + { + $testResult = Compare-M365DSCComplexObject ` + -Source ($source) ` + -Target ($target) + + if (-not $testResult) + { + break + } + + $ValuesToCheck.Remove($key) | Out-Null + } + } + + if ($testResult) + { + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + } Write-Verbose -Message "Test-TargetResource returned $testResult" @@ -400,7 +1021,7 @@ function Export-TargetResource $displayedKey = $rule.properties.DisplayName Write-Host " |---[$j/$($rules.Count)] $displayedKey" -NoNewline $params = @{ - DisplayName = $indruleicator.properties.displayName + DisplayName = $rule.properties.displayName Id = $rule.name SubscriptionId = $subscriptionId ResourceGroupName = $resourceGroupName @@ -417,11 +1038,173 @@ function Export-TargetResource $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` -Results $Results + if ( $null -ne $Results.EventGroupingSettings) + { + $complexMapping = @( + @{ + Name = 'EventGroupingSettings' + CimInstanceName = 'SentinelAlertRuleEventGroupingSettings' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.EventGroupingSettings ` + -CIMInstanceName 'SentinelAlertRuleEventGroupingSettings' ` + -ComplexTypeMapping $complexMapping + + if (-Not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.EventGroupingSettings = $complexTypeStringResult + } + else + { + $Results.Remove('EventGroupingSettings') | Out-Null + } + } + + if ($null -ne $Results.CustomDetails) + { + $complexMapping = @( + @{ + Name = 'CustomDetails' + CimInstanceName = 'SentinelAlertRuleCustomDetails' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.CustomDetails ` + -CIMInstanceName 'SentinelAlertRuleCustomDetails' ` + -ComplexTypeMapping $complexMapping + + if (-Not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.CustomDetails = $complexTypeStringResult + } + else + { + $Results.Remove('CustomDetails') | Out-Null + } + } + + if ( $null -ne $Results.EntityMappings) + { + $complexMapping = @( + @{ + Name = 'EntityMappings' + CimInstanceName = 'SentinelAlertRuleEntityMapping' + IsRequired = $False + }, + @{ + Name = 'fieldMappings' + CimInstanceName = 'SentinelAlertRuleEntityMappingFieldMapping' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.EntityMappings ` + -CIMInstanceName 'SentinelAlertRuleEntityMapping' ` + -ComplexTypeMapping $complexMapping + + if (-Not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.EntityMappings = $complexTypeStringResult + } + else + { + $Results.Remove('EntityMappings') | Out-Null + } + } + + if ($null -ne $Results.AlertDetailsOverride) + { + $complexMapping = @( + @{ + Name = 'AlertDetailsOverride' + CimInstanceName = 'SentinelAlertRuleAlertDetailsOverride' + IsRequired = $False + }, + @{ + Name = 'alertDynamicProperties' + CimInstanceName = 'SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.AlertDetailsOverride ` + -CIMInstanceName 'SentinelAlertRuleAlertDetailsOverride' ` + -ComplexTypeMapping $complexMapping + + if (-Not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.AlertDetailsOverride = $complexTypeStringResult + } + else + { + $Results.Remove('AlertDetailsOverride') | Out-Null + } + } + + if ($null -ne $Results.IncidentConfiguration) + { + $complexMapping = @( + @{ + Name = 'IncidentConfiguration' + CimInstanceName = 'SentinelAlertRuleIncidentConfiguration' + IsRequired = $False + }, + @{ + Name = 'groupingConfiguration' + CimInstanceName = 'SentinelAlertRuleIncidentConfigurationGroupingConfiguration' + IsRequired = $False + } + @{ + Name = 'groupByAlertDetails' + CimInstanceName = 'SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.IncidentConfiguration ` + -CIMInstanceName 'SentinelAlertRuleIncidentConfiguration' ` + -ComplexTypeMapping $complexMapping + + if (-Not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.IncidentConfiguration = $complexTypeStringResult + } + else + { + $Results.Remove('IncidentConfiguration') | Out-Null + } + } + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` -ConnectionMode $ConnectionMode ` -ModulePath $PSScriptRoot ` -Results $Results ` -Credential $Credential + + if ($Results.EventGroupingSettings) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'EventGroupingSettings' -IsCIMArray:$False + } + if ($Results.CustomDetails) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'CustomDetails' -IsCIMArray:$False + } + if ($Results.EntityMappings) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'EntityMappings' -IsCIMArray:$True + } + if ($Results.AlertDetailsOverride) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'AlertDetailsOverride' -IsCIMArray:$True + } + if ($Results.IncidentConfiguration) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'IncidentConfiguration' -IsCIMArray:$True + } + $dscContent += $currentDSCBlock Save-M365DSCPartialExport -Content $currentDSCBlock ` -FileName $Global:PartialExportFileName @@ -501,7 +1284,7 @@ function Get-M365DSCSentinelAlertRule } } -function New-M365DSCSentinelThreatIntelligenceIndicator +function New-M365DSCSentinelAlertRule { [CmdletBinding()] param( @@ -523,56 +1306,11 @@ function New-M365DSCSentinelThreatIntelligenceIndicator [Parameter()] [System.Collections.Hashtable] - $Body - ) + $Body, - try - { - $hostUrl = Get-M365DSCAPIEndpoint -TenantId $TenantId - $uri = $hostUrl.AzureManagement + "/subscriptions/$($SubscriptionId)/resourceGroups/$($ResourceGroupName)/" - - $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/alertrules/$((New-GUID).ToString())?api-version=2023-12-01-preview" - $payload = ConvertTo-Json $Body -Depth 10 -Compress - $response = Invoke-AzRest -Uri $uri -Method 'PUT' -Payload $payload - } - catch - { - Write-Verbose -Message $_ - New-M365DSCLogEntry -Message 'Error retrieving data:' ` - -Exception $_ ` - -Source $($MyInvocation.MyCommand.Source) ` - -TenantId $TenantId - throw $_ - } -} - -function Set-M365DSCSentinelThreatIntelligenceIndicator -{ - [CmdletBinding()] - param( [Parameter()] [System.String] - $SubscriptionId, - - [Parameter()] - [System.String] - $ResourceGroupName, - - [Parameter()] - [System.String] - $WorkspaceName, - - [Parameter(Mandatory = $true)] - [System.String] - $TenantId, - - [Parameter(Mandatory = $true)] - [System.String] - $Id, - - [Parameter()] - [System.Collections.Hashtable] - $Body + $Id ) try @@ -580,9 +1318,18 @@ function Set-M365DSCSentinelThreatIntelligenceIndicator $hostUrl = Get-M365DSCAPIEndpoint -TenantId $TenantId $uri = $hostUrl.AzureManagement + "/subscriptions/$($SubscriptionId)/resourceGroups/$($ResourceGroupName)/" - $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/threatIntelligence/main/indicators/$($Id)?api-version=2024-03-01" + if ($null -eq $Id) + { + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/alertrules/$((New-GUID).ToString())?api-version=2024-04-01-preview" + } + else + { + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/alertrules/$($Id)?api-version=2024-04-01-preview" + } $payload = ConvertTo-Json $Body -Depth 10 -Compress + Write-Verbose -Message "Creating new rule against URL:`r`n$($uri)`r`nWith payload:`r`n$payload" $response = Invoke-AzRest -Uri $uri -Method 'PUT' -Payload $payload + Write-Verbose -Message $response.Content } catch { @@ -595,7 +1342,7 @@ function Set-M365DSCSentinelThreatIntelligenceIndicator } } -function Remove-M365DSCSentinelThreatIntelligenceIndicator +function Remove-M365DSCSentinelAlertRule { [CmdletBinding()] param( @@ -625,7 +1372,7 @@ function Remove-M365DSCSentinelThreatIntelligenceIndicator $hostUrl = Get-M365DSCAPIEndpoint -TenantId $TenantId $uri = $hostUrl.AzureManagement + "/subscriptions/$($SubscriptionId)/resourceGroups/$($ResourceGroupName)/" - $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/threatIntelligence/main/indicators/$($Id)?api-version=2024-03-01" + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/alertRules/$($Id)?api-version=2024-04-01-preview" $response = Invoke-AzRest -Uri $uri -Method 'DELETE' } catch diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof index 30c37ecf58..3762c576f8 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof @@ -1,5 +1,5 @@ [ClassVersion("1.0.0")] -class MSFT_SentinelAlertRuleEventGroupSettings +class MSFT_SentinelAlertRuleEventGroupingSettings { [Write, Description("")] String aggregationKind; }; @@ -32,7 +32,7 @@ class MSFT_SentinelAlertRuleAlertDetailsOverride [Write, Description("")] String alertDisplayNameFormat; [Write, Description("")] String alertColumnName; [Write, Description("")] String alertTacticsColumnName; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty")] String alertDynamicProperties; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty")] String alertDynamicProperties[]; }; [ClassVersion("1.0.0")] @@ -53,7 +53,7 @@ class MSFT_SentinelAlertRuleIncidentConfiguration class MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration { [Write, Description("")] Boolean enabled; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail")] groupByAlertDetails[]; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail")] String groupByAlertDetails[]; [Write, Description("")] String groupByCustomDetails[]; [Write, Description("")] String groupByEntities[]; [Write, Description("")] String lookbackDuration; @@ -68,8 +68,8 @@ class MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetai [Write, Description("")] String Severity; }; -[ClassVersion("1.0.0.0"), FriendlyName("ResourceName")] -class MSFT_ResourceName : OMI_BaseResource +[ClassVersion("1.0.0.0"), FriendlyName("SentinelAlertRule")] +class MSFT_SentinelAlertRule : OMI_BaseResource { [Key, Description("The display name of the indicator")] String DisplayName; [Write, Description("The name of the resource group. The name is case insensitive.")] String SubscriptionId; @@ -82,22 +82,24 @@ class MSFT_ResourceName : OMI_BaseResource [Write, Description("")] String Severity; [Write, Description("")] String Tactics[]; [Write, Description("")] String Techniques[]; + [Write, Description("")] String SubTechniques[]; [Write, Description("")] String Query; [Write, Description("")] String QueryFrequency; [Write, Description("")] String QueryPeriod; [Write, Description("")] String TriggerOperator; [Write, Description("")] UInt32 TriggerThreshold; [Write, Description("")] String SuppressionDuration; - [Write, Description("")] String SuppresionEnabled; + [Write, Description("")] String SuppressionEnabled; [Write, Description("")] String AlertRuleTemplateName; [Write, Description("")] String DisplayNamesExcludeFilter[]; [Write, Description("")] String DisplayNamesFilter[]; [Write, Description("")] String SeveritiesFilter[]; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEventGroupSettings")] String EventGroupSettings; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleCustomDetails")] String CustomDetails; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEventGroupingSettings")] String EventGroupingSettings; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleCustomDetails")] String CustomDetails[]; [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEntityMapping")] String EntityMappings[]; [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverride")] String AlertDetailsOverride; [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfiguration")] String IncidentConfiguration; + [Write, Description("")] String Kind; [Write, Description("Present ensures the instance exists, absent ensures it is removed."), ValueMap{"Absent","Present"}, Values{"Absent","Present"}] string Ensure; [Write, Description("Credentials of the workload's Admin"), EmbeddedInstance("MSFT_Credential")] string Credential; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/readme.md index 32e0e7fb27..2bdf6ede04 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/readme.md +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/readme.md @@ -1,6 +1,6 @@ -# ResourceName +# SentinelAlertRule ## Description -##TODO - Provide a short description of what the resource is set to configure. +Configures alert rules in Azure Sentinel. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/settings.json index edf14b05e4..d01973dfcf 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/settings.json +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/settings.json @@ -1,13 +1,9 @@ { - "resourceName": "ResourceName", - "description": "Description of what the resource is about.", + "resourceName": "SentinelAlertRule", + "description": "Configures alert rules in Azure Sentinel.", "roles": { - "read": [ - "Role" - ], - "update": [ - "Role" - ] + "read": [], + "update": [] }, "permissions": { "graph": { @@ -16,16 +12,8 @@ "update": [] }, "application": { - "read": [ - { - "name": "Permission for Monitoring and Export" - } - ], - "update": [ - { - "name": "Permission for deploying" - } - ] + "read": [], + "update": [] } } } diff --git a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 index c9f78cc37b..bfb5342628 100644 --- a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 +++ b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 @@ -30,87 +30,87 @@ }, @{ ModuleName = 'Microsoft.Graph.Applications' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Applications' - Requiredversion = '2.23.0' + Requiredversion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Authentication' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DeviceManagement' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Devices.CorporateManagement' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DeviceManagement.Administration' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DeviceManagement.Enrollment' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Identity.DirectoryManagement' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Identity.Governance' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Identity.SignIns' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Reports' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Search' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Teams' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.DeviceManagement.Administration' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DirectoryObjects' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Groups' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Groups' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Planner' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Sites' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Users' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Users.Actions' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.PowerApps.Administration.PowerShell' diff --git a/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/1-Create.ps1 index b516274848..48bcd14ca7 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/1-Create.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/1-Create.ps1 @@ -21,6 +21,55 @@ Configuration Example Import-DscResource -ModuleName Microsoft365DSC node localhost { - + SentinelAlertRule "SentinelAlertRule-MyNRTRule" + { + AlertDetailsOverride = MSFT_SentinelAlertRuleAlertDetailsOverride{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + }; + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + CustomDetails = @( + MSFT_SentinelAlertRuleCustomDetails{ + DetailKey = 'Color' + DetailValue = 'TenantId' + } + ); + Description = "Test"; + DisplayName = "MyNRTRule"; + Enabled = $True; + Ensure = "Present"; + EntityMappings = @( + MSFT_SentinelAlertRuleEntityMapping{ + fieldMappings = @( + MSFT_SentinelAlertRuleEntityMappingFieldMapping{ + identifier = 'AppId' + columnName = 'Id' + } + ) + entityType = 'CloudApplication' + } + ); + IncidentConfiguration = MSFT_SentinelAlertRuleIncidentConfiguration{ + groupingConfiguration = MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } + createIncident = $True + }; + Query = "ThreatIntelIndicators"; + ResourceGroupName = "ResourceGroupName"; + Severity = "Medium"; + SubscriptionId = "xxxx"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + TenantId = $TenantId; + WorkspaceName = "SentinelWorkspace"; + } } } diff --git a/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/2-Update.ps1 index b516274848..f2ce0ff25e 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/2-Update.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/2-Update.ps1 @@ -21,6 +21,55 @@ Configuration Example Import-DscResource -ModuleName Microsoft365DSC node localhost { - + SentinelAlertRule "SentinelAlertRule-MyNRTRule" + { + AlertDetailsOverride = MSFT_SentinelAlertRuleAlertDetailsOverride{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + }; + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + CustomDetails = @( + MSFT_SentinelAlertRuleCustomDetails{ + DetailKey = 'Color' + DetailValue = 'TenantId' + } + ); + Description = "Test"; + DisplayName = "MyNRTRule"; + Enabled = $True; + Ensure = "Present"; + EntityMappings = @( + MSFT_SentinelAlertRuleEntityMapping{ + fieldMappings = @( + MSFT_SentinelAlertRuleEntityMappingFieldMapping{ + identifier = 'AppId' + columnName = 'Id' + } + ) + entityType = 'CloudApplication' + } + ); + IncidentConfiguration = MSFT_SentinelAlertRuleIncidentConfiguration{ + groupingConfiguration = MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } + createIncident = $True + }; + Query = "ThreatIntelIndicators"; + ResourceGroupName = "ResourceGroupName"; + Severity = "High"; #Drift + SubscriptionId = "xxxx"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + TenantId = $TenantId; + WorkspaceName = "SentinelWorkspace"; + } } } diff --git a/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/3-Remove.ps1 index b516274848..9d40a7633d 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/3-Remove.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/3-Remove.ps1 @@ -21,6 +21,18 @@ Configuration Example Import-DscResource -ModuleName Microsoft365DSC node localhost { - + SentinelAlertRule "SentinelAlertRule-MyNRTRule" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Description = "Test"; + DisplayName = "MyNRTRule"; + Ensure = "Absent"; + ResourceGroupName = "ResourceGroupName"; + Severity = "Medium"; + SubscriptionId = "xxxx"; + TenantId = $TenantId; + WorkspaceName = "SentinelWorkspace"; + } } } diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SentinelAlertRule.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SentinelAlertRule.Tests.ps1 index 780e0f343d..90786e49f9 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SentinelAlertRule.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SentinelAlertRule.Tests.ps1 @@ -35,7 +35,21 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { return "Credentials" } - ##TODO - Mock any Remove/Set/New cmdlets + Mock -CommandName Get-AzResource -MockWith { + return @{ + ResourceGroupName = "MyResourceGroup" + Name = 'MySentinelWorkspace' + ResourceId = "name/part/resourceId/" + } + } + + Mock -CommandName New-M365DSCSentinelAlertRule -MockWith { + + } + + Mock -CommandName Remove-M365DSCSentinelAlertRule -MockWith { + + } # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { @@ -47,13 +61,47 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Context -Name "The instance should exist but it DOES NOT" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; + AlertDetailsOverride = (New-CimInstance -ClassName MSFT_SentinelAlertRuleAlertDetailsOverride -Property @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } -ClientOnly) + CustomDetails = @( + (New-CimInstance -ClassName MSFT_SentinelAlertRuleCustomDetails -Property @{ + DetailKey = 'Color' + DetailValue = 'TenantId' + } -ClientOnly) + ) + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $True; + Ensure = "Present"; + EventGroupingSettings = (New-CimInstance -ClassName MSFT_SentinelAlertRuleEventGroupingSettings -Property @{ + aggregationKind = 'AlertPerResult' + } -ClientOnly) + IncidentConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfiguration -Property @{ + groupingConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration -Property @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } -ClientOnly) + createIncident = $True + } -ClientOnly) + Kind = "NRT"; + Query = "ThreatIntelIndicators"; + ResourceGroupName = "TBDSentinel"; + Severity = "Medium"; + SubscriptionId = "42136a41-5030-4140-aba0-7e6211115d3a"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + WorkspaceName = "SentinelWorkspace"; + Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return $null - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-M365DSCSentinelAlertRule -MockWith { return $null } } @@ -65,24 +113,91 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } It 'Should create a new instance from the Set method' { - ##TODO - Replace the New-Cmdlet by the appropriate one Set-TargetResource @testParams - Should -Invoke -CommandName New-Cmdlet -Exactly 1 + Should -Invoke -CommandName New-M365DSCSentinelAlertRule -Exactly 1 } } Context -Name "The instance exists but it SHOULD NOT" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Absent' - Credential = $Credential; + AlertDetailsOverride = (New-CimInstance -ClassName MSFT_SentinelAlertRuleAlertDetailsOverride -Property @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } -ClientOnly) + CustomDetails = @( + (New-CimInstance -ClassName MSFT_SentinelAlertRuleCustomDetails -Property @{ + DetailKey = 'Color' + DetailValue = 'TenantId' + } -ClientOnly) + ) + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $True; + Ensure = "Absent"; + EventGroupingSettings = (New-CimInstance -ClassName MSFT_SentinelAlertRuleEventGroupingSettings -Property @{ + aggregationKind = 'AlertPerResult' + } -ClientOnly) + IncidentConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfiguration -Property @{ + groupingConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration -Property @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } -ClientOnly) + createIncident = $True + } -ClientOnly) + Kind = "NRT"; + Query = "ThreatIntelIndicators"; + ResourceGroupName = "TBDSentinel"; + Severity = "Medium"; + SubscriptionId = "42136a41-5030-4140-aba0-7e6211115d3a"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + WorkspaceName = "SentinelWorkspace"; + Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return an instance - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-M365DSCSentinelAlertRule -MockWith { return @{ - + Kind = 'NRT' + name = '12345-12345-12345-12345-12345' + properties = @{ + Query = "ThreatIntelIndicators"; + Severity = "Medium"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $True; + AlertDetailsOverride = @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } + CustomDetails = @( + @{ + Color = 'TenantId' + } + ) + EventGroupingSettings = @{ + aggregationKind = 'AlertPerResult' + } + IncidentConfiguration = @{ + groupingConfiguration = @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } + createIncident = $True + } + } } } } @@ -95,23 +210,89 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { It 'Should remove the instance from the Set method' { Set-TargetResource @testParams - ##TODO - Replace the Remove-Cmdlet by the appropriate one - Should -Invoke -CommandName Remove-Cmdlet -Exactly 1 + Should -Invoke -CommandName Remove-M365DSCSentinelAlertRule -Exactly 1 } } Context -Name "The instance exists and values are already in the desired state" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; + AlertDetailsOverride = (New-CimInstance -ClassName MSFT_SentinelAlertRuleAlertDetailsOverride -Property @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } -ClientOnly) + CustomDetails = @( + (New-CimInstance -ClassName MSFT_SentinelAlertRuleCustomDetails -Property @{ + DetailKey = 'Color' + DetailValue = 'TenantId' + } -ClientOnly) + ) + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $True; + Ensure = "Present"; + EventGroupingSettings = (New-CimInstance -ClassName MSFT_SentinelAlertRuleEventGroupingSettings -Property @{ + aggregationKind = 'AlertPerResult' + } -ClientOnly) + IncidentConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfiguration -Property @{ + groupingConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration -Property @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } -ClientOnly) + createIncident = $True + } -ClientOnly) + Kind = "NRT"; + Query = "ThreatIntelIndicators"; + ResourceGroupName = "TBDSentinel"; + Severity = "Medium"; + SubscriptionId = "42136a41-5030-4140-aba0-7e6211115d3a"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + WorkspaceName = "SentinelWorkspace"; + Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return the desired values - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-M365DSCSentinelAlertRule -MockWith { return @{ - + Kind = 'NRT' + name = '12345-12345-12345-12345-12345' + properties = @{ + Query = "ThreatIntelIndicators"; + Severity = "Medium"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $True; + AlertDetailsOverride = @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } + CustomDetails = + [PSCustomObject]@{ + Color = 'TenantId' + } + EventGroupingSettings = @{ + aggregationKind = 'AlertPerResult' + } + IncidentConfiguration = @{ + groupingConfiguration = @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } + createIncident = $True + } + } } } } @@ -124,15 +305,83 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Context -Name "The instance exists and values are NOT in the desired state" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; + AlertDetailsOverride = (New-CimInstance -ClassName MSFT_SentinelAlertRuleAlertDetailsOverride -Property @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } -ClientOnly) + CustomDetails = @( + (New-CimInstance -ClassName MSFT_SentinelAlertRuleCustomDetails -Property @{ + DetailKey = 'Color' + DetailValue = 'TenantId' + } -ClientOnly) + ) + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $False; #Drift + Ensure = "Present"; + EventGroupingSettings = (New-CimInstance -ClassName MSFT_SentinelAlertRuleEventGroupingSettings -Property @{ + aggregationKind = 'AlertPerResult' + } -ClientOnly) + IncidentConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfiguration -Property @{ + groupingConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration -Property @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } -ClientOnly) + createIncident = $True + } -ClientOnly) + Kind = "NRT"; + Query = "ThreatIntelIndicators"; + ResourceGroupName = "TBDSentinel"; + Severity = "Medium"; + SubscriptionId = "42136a41-5030-4140-aba0-7e6211115d3a"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + WorkspaceName = "SentinelWorkspace"; + Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return a drift - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-M365DSCSentinelAlertRule -MockWith { return @{ - + Kind = 'NRT' + name = '12345-12345-12345-12345-12345' + properties = @{ + Query = "ThreatIntelIndicators"; + Severity = "Medium"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $True; + AlertDetailsOverride = @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } + CustomDetails = @( + @{ + Color = 'TenantId' + } + ) + EventGroupingSettings = @{ + aggregationKind = 'AlertPerResult' + } + IncidentConfiguration = @{ + groupingConfiguration = @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } + createIncident = $True + } + } } } } @@ -147,8 +396,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { It 'Should call the Set method' { Set-TargetResource @testParams - ##TODO - Replace the Update-Cmdlet by the appropriate one - Should -Invoke -CommandName Update-Cmdlet -Exactly 1 + Should -Invoke -CommandName New-M365DSCSentinelAlertRule -Exactly 1 } } @@ -160,10 +408,43 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return an instance - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-M365DSCSentinelAlertRule -MockWith { return @{ - + Kind = 'NRT' + name = '12345-12345-12345-12345-12345' + properties = @{ + Query = "ThreatIntelIndicators"; + Severity = "Medium"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $True; + AlertDetailsOverride = @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } + CustomDetails = @( + @{ + Color = 'TenantId' + } + ) + EventGroupingSettings = @{ + aggregationKind = 'AlertPerResult' + } + IncidentConfiguration = @{ + groupingConfiguration = @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } + createIncident = $True + } + } } } } From e6a170f947e06dd416fe81d1a0f2713cf1a50cac Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 22 Oct 2024 23:26:12 -0400 Subject: [PATCH 3/3] Fixes --- .../MSFT_SentinelAlertRule.schema.mof | 96 +++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof index 3762c576f8..e7fa79a05e 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof @@ -1,71 +1,71 @@ [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleEventGroupingSettings { - [Write, Description("")] String aggregationKind; + [Write, Description("The event grouping aggregation kinds")] String aggregationKind; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleCustomDetails { - [Write, Description("")] String DetailKey; - [Write, Description("")] String DetailValue; + [Write, Description("Key of the custom detail.")] String DetailKey; + [Write, Description("Associated value with the custom detail.")] String DetailValue; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleEntityMapping { - [Write, Description("")] String entityType; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEntityMappingFieldMapping")] String fieldMappings[]; + [Write, Description("Type of entity.")] String entityType; + [Write, Description("List of field mappings."), EmbeddedInstance("MSFT_SentinelAlertRuleEntityMappingFieldMapping")] String fieldMappings[]; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleEntityMappingFieldMapping { - [Write, Description("")] String columnName; - [Write, Description("")] String identifier; + [Write, Description("Name of the column")] String columnName; + [Write, Description("Identifier of the associated field.")] String identifier; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleAlertDetailsOverride { - [Write, Description("")] String alertDescriptionFormat; - [Write, Description("")] String alertDisplayNameFormat; - [Write, Description("")] String alertColumnName; - [Write, Description("")] String alertTacticsColumnName; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty")] String alertDynamicProperties[]; + [Write, Description("The format containing columns name(s) to override the alert description")] String alertDescriptionFormat; + [Write, Description("The format containing columns name(s) to override the alert name")] String alertDisplayNameFormat; + [Write, Description("The column name to take the alert severity from")] String alertSeverityColumnName; + [Write, Description("The column name to take the alert tactics from")] String alertTacticsColumnName; + [Write, Description("List of additional dynamic properties to override"), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty")] String alertDynamicProperties[]; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty { - [Write, Description("")] String alertProperty; - [Write, Description("")] String alertPropertyValue; + [Write, Description("Dynamic property key.")] String alertProperty; + [Write, Description("Dynamic property value.")] String alertPropertyValue; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleIncidentConfiguration { - [Write, Description("")] Boolean createIncident; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration")] String groupingConfiguration; + [Write, Description("Create incidents from alerts triggered by this analytics rule")] Boolean createIncident; + [Write, Description("Set how the alerts that are triggered by this analytics rule, are grouped into incidents"), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration")] String groupingConfiguration; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration { - [Write, Description("")] Boolean enabled; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail")] String groupByAlertDetails[]; - [Write, Description("")] String groupByCustomDetails[]; - [Write, Description("")] String groupByEntities[]; - [Write, Description("")] String lookbackDuration; - [Write, Description("")] String matchingMethod; - [Write, Description("")] Boolean reopenClosedIncident; + [Write, Description("Grouping enabled")] Boolean enabled; + [Write, Description("A list of alert details to group by (when matchingMethod is Selected)"), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail")] String groupByAlertDetails[]; + [Write, Description("A list of custom details keys to group by (when matchingMethod is Selected). Only keys defined in the current alert rule may be used.")] String groupByCustomDetails[]; + [Write, Description("A list of entity types to group by (when matchingMethod is Selected). Only entities defined in the current alert rule may be used.")] String groupByEntities[]; + [Write, Description("Limit the group to alerts created within the lookback duration (in ISO 8601 duration format)")] String lookbackDuration; + [Write, Description("Grouping matching method. When method is Selected at least one of groupByEntities, groupByAlertDetails, groupByCustomDetails must be provided and not empty.")] String matchingMethod; + [Write, Description("Re-open closed matching incidents")] Boolean reopenClosedIncident; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail { - [Write, Description("")] String DisplayName; - [Write, Description("")] String Severity; + [Write, Description("Display name of the alert detail.")] String DisplayName; + [Write, Description("Severity level associated with the alert detail.")] String Severity; }; [ClassVersion("1.0.0.0"), FriendlyName("SentinelAlertRule")] @@ -77,29 +77,29 @@ class MSFT_SentinelAlertRule : OMI_BaseResource [Write, Description("The name of the workspace.")] String WorkspaceName; [Write, Description("The unique id of the indicator.")] String Id; [Write, Description("The name of the workspace.")] String Description; - [Write, Description("")] String ProductFilter; - [Write, Description("")] Boolean Enabled; - [Write, Description("")] String Severity; - [Write, Description("")] String Tactics[]; - [Write, Description("")] String Techniques[]; - [Write, Description("")] String SubTechniques[]; - [Write, Description("")] String Query; - [Write, Description("")] String QueryFrequency; - [Write, Description("")] String QueryPeriod; - [Write, Description("")] String TriggerOperator; - [Write, Description("")] UInt32 TriggerThreshold; - [Write, Description("")] String SuppressionDuration; - [Write, Description("")] String SuppressionEnabled; - [Write, Description("")] String AlertRuleTemplateName; - [Write, Description("")] String DisplayNamesExcludeFilter[]; - [Write, Description("")] String DisplayNamesFilter[]; - [Write, Description("")] String SeveritiesFilter[]; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEventGroupingSettings")] String EventGroupingSettings; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleCustomDetails")] String CustomDetails[]; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEntityMapping")] String EntityMappings[]; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverride")] String AlertDetailsOverride; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfiguration")] String IncidentConfiguration; - [Write, Description("")] String Kind; + [Write, Description("The alerts' productName on which the cases will be generated")] String ProductFilter; + [Write, Description("Determines whether this alert rule is enabled or disabled.")] Boolean Enabled; + [Write, Description("The severity for alerts created by this alert rule.")] String Severity; + [Write, Description("The tactics of the alert rule")] String Tactics[]; + [Write, Description("The techniques of the alert rule")] String Techniques[]; + [Write, Description("The sub-techniques of the alert rule")] String SubTechniques[]; + [Write, Description("The query that creates alerts for this rule.")] String Query; + [Write, Description("The frequency (in ISO 8601 duration format) for this alert rule to run.")] String QueryFrequency; + [Write, Description("The period (in ISO 8601 duration format) that this alert rule looks at.")] String QueryPeriod; + [Write, Description("The operation against the threshold that triggers alert rule.")] String TriggerOperator; + [Write, Description("The threshold triggers this alert rule.")] UInt32 TriggerThreshold; + [Write, Description("The suppression (in ISO 8601 duration format) to wait since last time this alert rule been triggered.")] String SuppressionDuration; + [Write, Description("Determines whether the suppression for this alert rule is enabled or disabled.")] String SuppressionEnabled; + [Write, Description("The Name of the alert rule template used to create this rule.")] String AlertRuleTemplateName; + [Write, Description("The alerts' displayNames on which the cases will not be generated.")] String DisplayNamesExcludeFilter[]; + [Write, Description("The alerts' displayNames on which the cases will be generated.")] String DisplayNamesFilter[]; + [Write, Description("The alerts' severities on which the cases will be generated")] String SeveritiesFilter[]; + [Write, Description("The event grouping settings."), EmbeddedInstance("MSFT_SentinelAlertRuleEventGroupingSettings")] String EventGroupingSettings; + [Write, Description("Dictionary of string key-value pairs of columns to be attached to the alert"), EmbeddedInstance("MSFT_SentinelAlertRuleCustomDetails")] String CustomDetails[]; + [Write, Description("Array of the entity mappings of the alert rule"), EmbeddedInstance("MSFT_SentinelAlertRuleEntityMapping")] String EntityMappings[]; + [Write, Description("The alert details override settings"), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverride")] String AlertDetailsOverride; + [Write, Description("The settings of the incidents that created from alerts triggered by this analytics rule"), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfiguration")] String IncidentConfiguration; + [Write, Description("The kind of the alert rule")] String Kind; [Write, Description("Present ensures the instance exists, absent ensures it is removed."), ValueMap{"Absent","Present"}, Values{"Absent","Present"}] string Ensure; [Write, Description("Credentials of the workload's Admin"), EmbeddedInstance("MSFT_Credential")] string Credential;