diff --git a/README.md b/README.md index 19bb73348..2126c478f 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,8 @@ The following is a list of static resources. - [azure_role_definitions](docs/resources/azure_role_definitions.md) - [azure_security_center_policy](docs/resources/azure_security_center_policy.md) - [azure_security_center_policies](docs/resources/azure_security_center_policies.md) +- [azure_sentinel_alert_rule_template](docs/resources/azure_sentinel_alert_rule_template.md) +- [azure_sentinel_alert_rule_templates](docs/resources/azure_sentinel_alert_rule_templates.md) - [azure_sql_database](docs/resources/azure_sql_database.md) - [azure_sql_databases](docs/resources/azure_sql_databases.md) - [azure_sql_server](docs/resources/azure_sql_server.md) diff --git a/docs/resources/azure_sentinel_alert_rule_template.md b/docs/resources/azure_sentinel_alert_rule_template.md new file mode 100644 index 000000000..06bb6dd97 --- /dev/null +++ b/docs/resources/azure_sentinel_alert_rule_template.md @@ -0,0 +1,107 @@ +--- +title: About the azure_sentinel_alert_rule_template Resource +platform: azure + --- + +# azure_sentinel_alert_rule_template + +Use the `azure_sentinel_alert_rule_template` InSpec audit resource to test properties of an Azure Sentinel alert rule template. + +See the [`Azure alert rule templates documentation`](https://docs.microsoft.com/en-us/rest/api/securityinsights/alert-rule-templates/list) for additional information. + +## Azure REST API version, endpoint, and HTTP client parameters + +This resource interacts with api versions supported by the resource provider. +The `api_version` can be defined as a resource parameter. +If not provided, the latest version will be used. +For more information, refer to [`azure_generic_resource`](azure_generic_resource.md). + +Unless defined, `azure_cloud` global endpoint, and default values for the HTTP client will be used. +For more information, refer to the resource pack [README](../../README.md). + +## Availability + +### Installation + +This resource is available in the [InSpec Azure resource pack](https://github.com/inspec/inspec-azure). +For an example `inspec.yml` file and how to set up your Azure credentials, refer to resource pack [README](../../README.md#Service-Principal). + +## Syntax + +`resource_group`, `alert_rule_template_id`, and `workspace_name` are required parameters. + +```ruby +describe azure_sentinel_alert_rule_template(resource_group: 'RESOURCE_GROUP', workspace_name: 'WORKSPACE_NAME', alert_rule_template_id: 'ALERT_RULE_TEMPLATE_ID') do + #... +end +``` + +## Parameters + +| Name | Description | +|--------------------------------|-----------------------------------------------------------------------------------| +| resource_group | Azure resource group that the targeted resource resides in. | +| workspace_name | Name for the workspace_name that you want to create your Sentinel alert rule template in. | +| alert_rule_template_id | The Sentinel alert rule template name. | + +All the parameter sets needs be provided for a valid query: + +- `resource_group` +- `workspace_name` +- `alert_rule_template_id` + +## Properties + +| Name | Description | +|--------------------------------|----------------------------------------------------------------------------------| +| name | Name of the Azure resource to test. | +| id | The Sentinel alert rule template type. | +| properties | The Properties of the Resource. | +| type | Azure resource type. | +| kind | The alert rule kind. | +| properties.severity | The severity for alerts created by this alert rule. | +| properties.status| The alert rule template status. | +| properties.triggerThreshold | The threshold triggers this alert rule. | +| properties.displayName| The display name for alert rule template. | +| properties.triggerOperator | The operation against the threshold that triggers alert rule. | +| properties.queryPeriod | The period (in ISO 8601 duration format) that this alert rule looks at. | +| properties.queryFrequency | The frequency (in ISO 8601 duration format) for this alert rule to run. | +## Examples + +### Test if properties match + + ```ruby +describe azure_sentinel_alert_rule_template(resource_group: 'RESOURCE_GROUP', workspace_name: 'WORKSPACE_NAME', alert_rule_template_id: 'ALERT_RULE_TEMPLATE_ID') do + its('name') { should eq 'RESOURCE_NAME' } + its('type') { should eq 'Microsoft.SecurityInsights/AlertRuleTemplates' } + its('kind') { should eq 'ALERT_RULE_KIND' } + its('properties.triggerThreshold') { should eq INTEGER } + its('properties.status') { should eq 'STATUS' } + its('properties.displayName') { should eq 'ALERT_RULE_DISPLAY_NAME' } + its('properties.triggerOperator') { should eq 'OPERATOR' } + its('properties.queryPeriod') { should eq 'PERIOD' } + its('properties.queryFrequency') { should eq 'FREQUENCY' } + its('properties.severity') { should eq 'ALERT_SEVERITY' } +end + ``` + + +### Test that a Sentinel alert rule template exists + +```ruby +describe azure_sentinel_alert_rule_template(resource_group: 'RESOURCE_GROUP', workspace_name: 'WORKSPACE_NAME', alert_rule_template_id: 'ALERT_RULE_TEMPLATE_ID') do + it { should exist } +end +``` + +### Test that a Sentinel alert rule template does not exist + +```ruby +describe azure_sentinel_alert_rule_template(resource_group: 'RESOURCE_GROUP', workspace_name: 'WORKSPACE_NAME', alert_rule_template_id: 'ALERT_RULE_TEMPLATE_ID') do + it { should_not exist } +end + ``` + +## Azure Permissions + +Your [Service Principal](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal) must be setup with a `contributor` role on the subscription you wish to test. \ No newline at end of file diff --git a/docs/resources/azure_sentinel_alert_rule_templates.md b/docs/resources/azure_sentinel_alert_rule_templates.md new file mode 100644 index 000000000..2303b59b8 --- /dev/null +++ b/docs/resources/azure_sentinel_alert_rule_templates.md @@ -0,0 +1,105 @@ +--- +title: About the azure_sentinel_alert_rule_templates Resource +platform: azure + --- + +# azure_sentinel_alert_rule_templates + +Use the `azure_sentinel_alert_rule_templates` InSpec audit resource to test properties related to alert rule templates for a resource group or the entire subscription. + +See the [`Azure alert rule templates documentation`](https://docs.microsoft.com/en-us/rest/api/securityinsights/alert-rule-templates/list) for additional information. + +## Azure Rest API Version, Endpoint, And HTTP Client Parameters + +This resource interacts with API versions supported by the resource provider. +The `api_version` can be defined as a resource parameter. +If not provided, the latest version will be used. +For more information, refer to [`azure_generic_resource`](azure_generic_resource.md). + +Unless defined, `azure_cloud` global endpoint, and default values for the HTTP client will be used. +For more information, refer to the resource pack [README](../../README.md). + +## Availability + +### Installation + +This resource is available in the [InSpec Azure resource pack](https://github.com/inspec/inspec-azure). +For an example `inspec.yml` file and how to set up your Azure credentials, refer to resource pack [README](../../README.md#Service-Principal). + +## Syntax + +An `azure_sentinel_alert_rule_templates` resource block returns all Azure alert rule templates, either within a Resource Group (if provided), or within an entire Subscription. + +```ruby +describe azure_sentinel_alert_rule_templates(resource_group: 'RESOURCE_GROUP', workspace_name: 'WORKSPACE_NAME') do + #... +end +``` + +`resource_group` and `workspace_name` are required parameters. + + +## Parameters + +| Name | Description | + |-----------------------------|-----------------------------------------------------------------------------------| +| resource_group | Azure resource group that the targeted resource resides in. | +| workspace_name | Azure workspace Name for which alert rule templates are being retrieved.| + +## Properties + +| Property | Description | Filter Criteria* | +|-----------------|--------------------------------------------------------|-----------------| +| names | A list of the unique resource names. | `name` | +| ids | A list of alert rule templates IDs . | `id` | +| properties | A list of properties for the resource | `properties` | +| types | A list of types for each resource. | `type` | +| severities | The severity for alerts created by this alert rule. | `severity` | +| statuses | The status of the alert rule. | `status` | +| triggerThresholds | The email of the user the incident is assigned to. | `triggerThreshold` | +| displayNames| The user principal name of the user the incident is assigned to. | `displayName` | +| triggerOperators | The name of the user the incident is assigned to. | `triggerOperator` | +|queryPeriods| The List of period (in ISO 8601 duration format) that this alert rule looks at. |`queryPeriod`| +|queryFrequencies| The List of frequency (in ISO 8601 duration format) for this alert rule to run.|`queryFrequency`| + +* For information on how to use filter criteria on plural resources refer to [FilterTable usage](https://github.com/inspec/inspec/blob/master/dev-docs/filtertable-usage.md). + +## Examples + +### Test if properties matches + +```ruby +describe azure_sentinel_alert_rule_templates(resource_group: 'RESOURCE_GROUP', workspace_name: 'WORKSPACE_NAME') do + its('names') { should include 'RESOURCE_NAME' } + its('types') { should include 'Microsoft.SecurityInsights/AlertRuleTemplates' } + its('kinds') { should include 'ALERT_RULE_KIND' } + its('triggerThresholds') { should include INTEGER } + its('statuses') { should include 'STATUS' } + its('severities') { should include 'ALERT_SEVERITY' } + its('queryFrequencies') { should include 'FREQUENCY' } + its('queryPeriods') { should include 'PERIOD' } + its('triggerOperators') { should include 'OPERATOR' } + its('displayNames') { should include 'ALERT_RULE_DISPLAY_NAME' } +end +``` + +### Test if any alert rule templates exist in the resource group + +```ruby +describe azure_sentinel_alert_rule_templates(resource_group: 'RESOURCE_GROUP', workspace_name: 'WORKSPACE_NAME') do + it { should exist } +end +``` + +### Test that there aren't any alert rule templates in a resource group + +```ruby +# Should not exist if no alert rule templates are in the resource group +describe azure_sentinel_alert_rule_templates(resource_group: 'RESOURCE_GROUP', workspace_name: 'WORKSPACE_NAME') do + it { should_not exist } +end +``` + +## Azure Permissions + +Your [Service Principal](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal) must be setup with a `contributor` role on the subscription you wish to test. diff --git a/libraries/azure_sentinel_alert_rule_template.rb b/libraries/azure_sentinel_alert_rule_template.rb new file mode 100644 index 000000000..f5319e019 --- /dev/null +++ b/libraries/azure_sentinel_alert_rule_template.rb @@ -0,0 +1,27 @@ +require 'azure_generic_resource' + +class AzureSentinelAlertRuleTemplate < AzureGenericResource + name 'azure_sentinel_alert_rule_template' + desc 'Verifies settings for an Sentinel Alert Rule Template' + example <<-EXAMPLE + describe azure_alert_rule_template(resource_group: 'example', workspace_name: 'workspaceName', alert_rule_template_id: 'alert_rule_template_id') do + it { should exit } + end + EXAMPLE + + def initialize(opts = {}) + # Options should be Hash type. Otherwise Ruby will raise an error when we try to access the keys. + raise ArgumentError, 'Parameters must be provided in an Hash object.' unless opts.is_a?(Hash) + + opts[:resource_provider] = specific_resource_constraint('Microsoft.OperationalInsights/workspaces', opts) + opts[:required_parameters] = %i(workspace_name) + opts[:resource_path] = [opts[:workspace_name], 'providers/Microsoft.SecurityInsights/alertRuleTemplates/'].join('/') + opts[:resource_identifiers] = %i(alert_rule_template_id) + # static_resource parameter must be true for setting the resource_provider in the backend. + super(opts, true) + end + + def to_s + super(AzureSentinelAlertRuleTemplate) + end +end diff --git a/libraries/azure_sentinel_alert_rule_templates.rb b/libraries/azure_sentinel_alert_rule_templates.rb new file mode 100644 index 000000000..04efc3d73 --- /dev/null +++ b/libraries/azure_sentinel_alert_rule_templates.rb @@ -0,0 +1,61 @@ +require 'azure_generic_resources' + +class AzureSentinelAlertRuleTemplates < AzureGenericResources + name 'azure_sentinel_alert_rule_templates' + desc 'Verifies settings for Azure Alert Rule Templates' + example <<-EXAMPLE + azure_alert_rule_templates(resource_group: 'example', workspace_name: 'workspaceName') do + it{ should exist } + end + EXAMPLE + + attr_reader :table + + def initialize(opts = {}) + # Options should be Hash type. Otherwise Ruby will raise an error when we try to access the keys. + raise ArgumentError, 'Parameters must be provided in an Hash object.' unless opts.is_a?(Hash) + + opts[:resource_provider] = specific_resource_constraint('Microsoft.OperationalInsights/workspaces', opts) + opts[:required_parameters] = %i(workspace_name) + opts[:resource_path] = [opts[:workspace_name], 'providers/Microsoft.SecurityInsights/alertRuleTemplates/'].join('/') + # static_resource parameter must be true for setting the resource_provider in the backend. + super(opts, true) + + # Check if the resource is failed. + # It is recommended to check that after every usage of superclass methods or API calls. + return if failed_resource? + + # Define the column and field names for FilterTable. + # - column: It is defined as an instance method, callable on the resource, and present `field` values in a list. + # - field: It has to be identical with the `key` names in @table items that will be presented in the FilterTable. + # @see https://github.com/inspec/inspec/blob/master/docs/dev/filtertable-usage.md + + # FilterTable is populated at the very end due to being an expensive operation. + populate_filter_table_from_response + end + + def to_s + super(AzureSentinelAlertRuleTemplates) + end + + private + + def flatten_hash(hash) + hash.each_with_object({}) do |(k, v), h| + if v.is_a? Hash + flatten_hash(v).map do |h_k, h_v| + h["#{k}_#{h_k}".to_sym] = h_v + end + else + h[k] = v + end + end + end + + def populate_table + @resources.each do |resource| + resource = resource.merge(resource[:properties]) + @table << flatten_hash(resource) + end + end +end diff --git a/test/unit/resources/azure_sentinel_alert_rule_template_test.rb b/test/unit/resources/azure_sentinel_alert_rule_template_test.rb new file mode 100644 index 000000000..020a49cd7 --- /dev/null +++ b/test/unit/resources/azure_sentinel_alert_rule_template_test.rb @@ -0,0 +1,17 @@ +require_relative 'helper' +require 'azure_sentinel_alert_rule_template' + +class AzureAlertRuleTemplateConstructorTest < Minitest::Test + def test_empty_param_not_ok + assert_raises(ArgumentError) { AzureSentinelAlertRuleTemplate.new } + end + + # resource_provider should not be allowed. + def test_resource_provider_not_ok + assert_raises(ArgumentError) { AzureSentinelAlertRuleTemplate.new(resource_provider: 'some_type') } + end + + def test_resource_group + assert_raises(ArgumentError) { AzureSentinelAlertRuleTemplate.new(name: 'my-name') } + end +end diff --git a/test/unit/resources/azure_sentinel_alert_rule_templates_test.rb b/test/unit/resources/azure_sentinel_alert_rule_templates_test.rb new file mode 100644 index 000000000..d1b766791 --- /dev/null +++ b/test/unit/resources/azure_sentinel_alert_rule_templates_test.rb @@ -0,0 +1,25 @@ +require_relative 'helper' +require 'azure_sentinel_alert_rule_templates' + +class AzureAlertRuleTemplatesConstructorTest < Minitest::Test + # resource_type should not be allowed. + def test_resource_type_not_ok + assert_raises(ArgumentError) { AzureSentinelAlertRuleTemplate.new(resource_provider: 'some_type') } + end + + def tag_value_not_ok + assert_raises(ArgumentError) { AzureSentinelAlertRuleTemplate.new(tag_value: 'some_tag_value') } + end + + def tag_name_not_ok + assert_raises(ArgumentError) { AzureSentinelAlertRuleTemplate.new(tag_name: 'some_tag_name') } + end + + def test_resource_id_not_ok + assert_raises(ArgumentError) { AzureSentinelAlertRuleTemplate.new(resource_id: 'some_id') } + end + + def test_name_not_ok + assert_raises(ArgumentError) { AzureSentinelAlertRuleTemplate.new(name: 'some_name') } + end +end