diff --git a/README.md b/README.md index 54af98212..3e7173f5e 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,8 @@ The static resources derived from the generic resources prepended with `azure_` - [azure_load_balancers](docs/resources/azure_load_balancers.md) - [azure_mariadb_server](docs/resources/azure_mariadb_server.md) - [azure_mariadb_servers](docs/resources/azure_mariadb_servers.md) +- [azure_monitor_activity_log_alert](docs/resources/azure_monitor_activity_log_alert.md) +- [azure_monitor_activity_log_alerts](docs/resources/azure_monitor_activity_log_alerts.md) - [azure_mysql_database](docs/resources/azure_mysql_database.md) - [azure_mysql_databases](docs/resources/azure_mysql_databases.md) - [azure_mysql_server](docs/resources/azure_mysql_server.md) @@ -464,7 +466,7 @@ To run integration tests: ``` rake test:integration ``` -To run all tests: +To run lint and unit tests: ``` rake ``` diff --git a/Rakefile b/Rakefile index 14b499ca6..562f52fd4 100644 --- a/Rakefile +++ b/Rakefile @@ -19,7 +19,7 @@ INTEGRATION_DIR = 'test/integration/verify' task default: :test desc 'Testing tasks' -task test: %w{lint test:unit test:integration} +task test: %w{lint test:unit} desc 'Linting tasks' task lint: [:rubocop, :'syntax:ruby', :'syntax:inspec'] diff --git a/docs/resources/azure_monitor_activity_log_alert.md b/docs/resources/azure_monitor_activity_log_alert.md new file mode 100644 index 000000000..37c6a75a2 --- /dev/null +++ b/docs/resources/azure_monitor_activity_log_alert.md @@ -0,0 +1,106 @@ +--- +title: About the azure_monitor_activity_log_alert Resource +platform: azure +--- + +# azure_monitor_activity_log_alert + +Use the `azure_monitor_activity_log_alert` InSpec audit resource to test properties of an Azure Monitor Activity Log Alert. + +## 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_monitor_activity_log_alert` resource block identifies an Azure Monitor Activity Log Alert by `name` and `resource_group` or the `resource_id`. +```ruby +describe azure_monitor_activity_log_alert(resource_group: 'example', name: 'AlertName') do + it { should exist } +end +``` +```ruby +describe azure_monitor_activity_log_alert(resource_id: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/microsoft.insights/activityLogAlerts/{activityLogAlertName}') do + it { should exist } +end +``` +## Parameters + +| Name | Description | +|--------------------------------|-----------------------------------------------------------------------------------| +| resource_group | Azure resource group that the targeted resource resides in. `MyResourceGroup` | +| name | Name of the Activity Log Alert to test. `AlertName` | +| resource_id | The unique resource ID. `/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/microsoft.insights/activityLogAlerts/{activityLogAlertName}` | + +Either one of the parameter sets can be provided for a valid query: +- `resource_id` +- `resource_group` and `name` + +## Properties + +| Property | Description | +|-------------------|-------------| +| operations | The list of operations. `['Microsoft.Authorization/policyAssignments/write']` | +| conditions | The list of activity log alert conditions that will cause this alert to activate. | +| scopes | A list of resource id prefixes. The alert will only apply to activityLogs with resource ids that fall under one of these prefixes. | +| enabled? | Indicates whether this activity log alert is enabled. `true` or `false` | + +For properties applicable to all resources, such as `type`, `name`, `id`, `properties`, refer to [`azure_generic_resource`](azure_generic_resource.md#properties). + +Also, refer to [Azure documentation](https://docs.microsoft.com/en-us/rest/api/monitor/activitylogalerts/get#activitylogalertresource) for other properties available. +Any attribute in the response may be accessed with the key names separated by dots (`.`), eg. `properties.<attribute>`. + +## Examples + +### Test an Activity Log Alert Has the Correct Operation +```ruby +describe azure_monitor_activity_log_alert(resource_group: 'example', name: 'AlertName') do + its('operations') { should include 'Microsoft.Authorization/policyAssignments/write' } +end +``` +### Test the Scope of an Activity Log Alert +```ruby +describe azure_monitor_activity_log_alert(resource_group: 'example', name: 'AlertName') do + its('scopes') { should include 'subscriptions/{SUBSCRIPTION_ID}' } +end +``` +## Matchers + +This InSpec audit resource has the following special matchers. For a full list of available matchers, please visit our [Universal Matchers page](https://docs.chef.io/inspec/matchers/). + +### enabled + +Test if a resource is enabled. If an activity log alert is not enabled, then none of its actions will be activated. +```ruby +describe azure_monitor_activity_log_alert(resource_group: 'example', name: 'AlertName') do + it { should be_enabled } +end +``` +### exists +```ruby +# If we expect a resource to always exist +describe azure_monitor_activity_log_alert(resource_group: 'example', name: 'AlertName') do + it { should exist } +end + +# If we expect a resource to never exist +describe azure_monitor_activity_log_alert(resource_group: 'example', name: 'AlertName') 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/docs/resources/azure_monitor_activity_log_alerts.md b/docs/resources/azure_monitor_activity_log_alerts.md new file mode 100644 index 000000000..e8be0e3f0 --- /dev/null +++ b/docs/resources/azure_monitor_activity_log_alerts.md @@ -0,0 +1,94 @@ +--- +title: About the azure_monitor_activity_log_alerts Resource +platform: azure +--- + +# azure_monitor_activity_log_alerts + +Use the `azure_monitor_activity_log_alerts` InSpec audit resource to test properties and configuration of multiple Activity Log Alerts. + +## 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_monitor_activity_log_alerts` resource block returns all Activity Log Alerts, either within a Resource Group (if provided), or within an entire Subscription. +```ruby +describe azure_monitor_activity_log_alerts do + it { should exist } +end +``` +or +```ruby +describe azure_monitor_activity_log_alerts(resource_group: 'my-rg') do + it { should exist } +end +``` +## Parameters + +- `resource_group` (Optional) + +## Properties + +|Property | Description | Filter Criteria<superscript>*</superscript> | +|-----------------|--------------------------------------------------------------------------------------|-----------------| +| ids | A list of the unique resource ids. | `id` | +| location | A list of locations for all the resources being interrogated. | `location` | +| names | A list of names of all the resources being interrogated. | `name` | +| tags | A list of `tag:value` pairs defined on the resources being interrogated. | `tags` | +| operations | A list of operations for all the resources being interrogated. | `operations` | +| resource_group | Azure resource group that the targeted resource resides in. | `resource_group` | + +<superscript>*</superscript> For information on how to use filter criteria on plural resources refer to [FilterTable usage](https://github.com/inspec/inspec/blob/master/docs/dev/filtertable-usage.md#a-where-method-you-can-call-with-hash-params-with-loose-matching). + +## Examples + +### Test that a Subscription Has the Named Activity Log Alert +```ruby +describe azure_monitor_activity_log_alerts do + its('names') { should include('ExampleLogAlert') } +end +``` +### Loop through All Resources with `resource_id` +```ruby +azure_monitor_activity_log_alerts.ids.each do |id| + describe azure_monitor_activity_log_alert(resource_id: id) do + it { should be_enabled } + end +end +``` +## Matchers + +This InSpec audit resource has the following special matchers. For a full list of available matchers, please visit our [Universal Matchers page](https://www.inspec.io/docs/reference/matchers/). + +### exists + +The control will pass if the filter returns at least one result. Use `should_not` if you expect zero matches. +```ruby +# If we expect 'ExampleGroup' Resource Group to have Activity Log Alerts +describe azure_monitor_activity_log_alerts(resource_group: 'ExampleGroup') do + it { should exist } +end + +# If we expect 'EmptyExampleGroup' Resource Group to not have Activity Log Alerts +describe azure_monitor_activity_log_alerts(resource_group: 'ExampleGroup') 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/docs/resources/azurerm_monitor_activity_log_alert.md b/docs/resources/azurerm_monitor_activity_log_alert.md index 75995e455..3ff1c2277 100644 --- a/docs/resources/azurerm_monitor_activity_log_alert.md +++ b/docs/resources/azurerm_monitor_activity_log_alert.md @@ -3,6 +3,8 @@ title: About the azurerm_monitor_activity_log_alert Resource platform: azure --- +> <b>WARNING</b> This resource will be deprecated in InSpec Azure Resource Pack version **2**. Please start using fully backward compatible [`azure_monitor_activity_log_alert`](azure_monitor_activity_log_alert.md) InSpec audit resource. + # azurerm\_monitor\_activity\_log\_alert Use the `azurerm_monitor_activity_log_alert` InSpec audit resource to test properties diff --git a/docs/resources/azurerm_monitor_activity_log_alerts.md b/docs/resources/azurerm_monitor_activity_log_alerts.md index f8f5c8467..5471208d4 100644 --- a/docs/resources/azurerm_monitor_activity_log_alerts.md +++ b/docs/resources/azurerm_monitor_activity_log_alerts.md @@ -3,6 +3,8 @@ title: About the azurerm_monitor_activity_log_alerts Resource platform: azure --- +> <b>WARNING</b> This resource will be deprecated in InSpec Azure Resource Pack version **2**. Please start using fully backward compatible [`azure_monitor_activity_log_alerts`](azure_monitor_activity_log_alerts.md) InSpec audit resource. + # azurerm\_monitor\_activity\_log\_alerts Use the `azurerm_monitor_activity_log_alerts` InSpec audit resource to verify that an diff --git a/libraries/azure_generic_resource.rb b/libraries/azure_generic_resource.rb index b2d62efe0..ab44e0c95 100644 --- a/libraries/azure_generic_resource.rb +++ b/libraries/azure_generic_resource.rb @@ -32,7 +32,7 @@ def initialize(opts = {}, static_resource = false) elsif static_resource && @opts.key?(:resource_id) # Ensure that the provided resource id is for the correct resource provider. raise ArgumentError, "Resource provider must be #{@opts[:resource_provider]}." \ - unless @opts[:resource_id].include?(@opts[:resource_provider]) + unless @opts[:resource_id].downcase.include?(@opts[:resource_provider].downcase) @opts.delete(:resource_provider) validate_parameters(required: %i(resource_id), allow: %i(resource_path resource_identifiers resource_provider)) else diff --git a/libraries/azure_monitor_activity_log_alert.rb b/libraries/azure_monitor_activity_log_alert.rb new file mode 100644 index 000000000..e37896f0e --- /dev/null +++ b/libraries/azure_monitor_activity_log_alert.rb @@ -0,0 +1,64 @@ +require 'azure_generic_resource' + +class AzureMonitorActivityLogAlert < AzureGenericResource + name 'azure_monitor_activity_log_alert' + desc 'Verifies settings for a Azure Monitor Activity Log Alert' + example <<-EXAMPLE + describe azure_monitor_activity_log_alert(resource_group: 'example', name: 'AlertName') do + it { should exist } + its('operations') { should include 'Microsoft.Authorization/policyAssignments/write' } + 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.Insights/activityLogAlerts', opts) + + # static_resource parameter must be true for setting the resource_provider in the backend. + super(opts, true) + end + + def conditions + return unless exists? + properties&.condition&.allOf + end + + def operations + return unless exists? + conditions&.select { |x| x.field == 'operationName' }&.collect(&:equals) + end + + def scopes + return unless exists? + properties&.scopes + end + + def enabled? + return unless exists? + properties&.enabled + end + + def to_s + super(AzureMonitorActivityLogAlert) + end +end + +# Provide the same functionality under the old resource name. +# This is for backward compatibility. +class AzurermMonitorActivityLogAlert < AzureMonitorActivityLogAlert + name 'azurerm_monitor_activity_log_alert' + desc 'Verifies settings for a Azure Monitor Activity Log Alert' + example <<-EXAMPLE + describe azurerm_monitor_activity_log_alert(resource_group: 'example', name: 'AlertName') do + it { should exist } + its('operations') { should include 'Microsoft.Authorization/policyAssignments/write' } + end + EXAMPLE + + def initialize(opts = {}) + Inspec::Log.warn Helpers.resource_deprecation_message(@__resource_name__, AzureMonitorActivityLogAlert.name) + super + end +end diff --git a/libraries/azure_monitor_activity_log_alerts.rb b/libraries/azure_monitor_activity_log_alerts.rb new file mode 100644 index 000000000..a6fadf860 --- /dev/null +++ b/libraries/azure_monitor_activity_log_alerts.rb @@ -0,0 +1,84 @@ +require 'azure_generic_resources' + +class AzureMonitorActivityLogAlerts < AzureGenericResources + name 'azure_monitor_activity_log_alerts' + desc 'Verifies settings for Azure Monitor Activity Log Alerts' + example <<-EXAMPLE + describe azure_monitor_activity_log_alerts do + its('names') { should include('example-log-alert') } + 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.Insights/activityLogAlerts', opts) + + # 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 inherited methods or making API calls. + return if failed_resource? + + # Define the column and field names for FilterTable. + # In most cases, the `column` should be the pluralized form of the `field`. + # @see https://github.com/inspec/inspec/blob/master/docs/dev/filtertable-usage.md + table_schema = [ + { column: :names, field: :name }, + { column: :ids, field: :id }, + { column: :tags, field: :tags }, + { column: :location, field: :location }, + { column: :resource_group, field: :resource_group }, + { column: :operations, field: :operations }, + ] + + # FilterTable is populated at the very end due to being an expensive operation. + AzureGenericResources.populate_filter_table(:table, table_schema) + end + + def to_s + super(AzureMonitorActivityLogAlerts) + end + + private + + def populate_table + # If @resources empty than @table should stay as an empty array as declared in superclass. + # This will ensure constructing resource and passing `should_not exist` test. + return [] if @resources.empty? + @resources.each do |resource| + operations = resource[:properties].dig(:condition, :allOf) + &.select { |alert| alert[:field] == 'operationName' }&.collect { |al| al[:equals] } + resource_group, _provider, _r_type = Helpers.res_group_provider_type_from_uri(resource[:id]) + @table << { + id: resource[:id], + name: resource[:name], + tags: resource[:tags], + location: resource[:location], + operations: operations, + resource_group: resource_group, + } + end + end +end + +# Provide the same functionality under the old resource name. +# This is for backward compatibility. +class AzurermMonitorActivityLogAlerts < AzureMonitorActivityLogAlerts + name 'azurerm_monitor_activity_log_alerts' + desc 'Verifies settings for Azure Monitor Activity Log Alerts' + example <<-EXAMPLE + describe azurerm_monitor_activity_log_alerts do + its('names') { should include('example-log-alert') } + end + EXAMPLE + + def initialize(opts = {}) + Inspec::Log.warn Helpers.resource_deprecation_message(@__resource_name__, AzureMonitorActivityLogAlerts.name) + super + end +end diff --git a/libraries/azurerm_monitor_activity_log_alert.rb b/libraries/azurerm_monitor_activity_log_alert.rb deleted file mode 100644 index 2ba47440f..000000000 --- a/libraries/azurerm_monitor_activity_log_alert.rb +++ /dev/null @@ -1,55 +0,0 @@ -# frozen_string_literal: true - -require 'azurerm_resource' - -class AzurermMonitorActivityLogAlert < AzurermSingularResource - name 'azurerm_monitor_activity_log_alert' - desc 'Verifies settings for a Azure Monitor Activity Log Alert' - example <<-EXAMPLE - describe azurerm_monitor_activity_log_alert(resource_group: 'example', name: 'AlertName') do - it { should exist } - its('operations') { should include 'Microsoft.Authorization/policyAssignments/write' } - end - EXAMPLE - - ATTRS = %i( - name - id - conditions - operations - scopes - ).freeze - - attr_reader(*ATTRS) - - def initialize(resource_group: nil, name: nil) - resp = management.activity_log_alert(resource_group, name) - return if has_error?(resp) - - @name = resp.name - @id = resp.id - @conditions = resp.properties.condition.allOf - @scopes = resp.properties.scopes - @enabled = resp.properties.enabled - @operations = collect_operations(@conditions) - @exists = true - end - - def enabled? - @enabled - end - - def to_s - "#{name} Activity Log Alert" - end - - private - - # Collect all Operation strings for the Activity Log Alert - # - # @param [Hash] 'allOf' conditions from response properties - # @return [Array] of operation strings - def collect_operations(conditions) - conditions.find_all { |x| x.field == 'operationName' }.collect(&:equals) - end -end diff --git a/libraries/azurerm_monitor_activity_log_alerts.rb b/libraries/azurerm_monitor_activity_log_alerts.rb deleted file mode 100644 index 21cd98e3d..000000000 --- a/libraries/azurerm_monitor_activity_log_alerts.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require 'azurerm_resource' - -class AzurermMonitorActivityLogAlerts < AzurermPluralResource - name 'azurerm_monitor_activity_log_alerts' - desc 'Verifies settings for Azure Monitor Activity Log Alerts' - example <<-EXAMPLE - describe azurerm_monitor_activity_log_alerts do - its('names') { should include('example-log-alert') } - end - EXAMPLE - - attr_reader :table - - FilterTable.create - .register_column(:names, field: 'name') - .register_column(:resource_group, field: 'resource_group') - .register_column(:location, field: 'location') - .register_column(:operations, field: 'operations') - .install_filter_methods_on_resource(self, :table) - - def initialize - resp = management.activity_log_alerts - return if has_error?(resp) - - @table = resp.collect(&with_resource_group) - .collect(&with_operations) - end - - include Azure::Deprecations::StringsInWhereClause - - def to_s - 'Activity Log Alerts' - end - - def with_resource_group - lambda do |group| - # Get resource group from ID string - name = id_to_h(group.id)[:resource_groups] - Azure::Response.create(group.members << :resource_group, group.values << name) - end - end - - def with_operations - lambda do |alert| - conditions = alert.properties.condition.allOf - operations = conditions.find_all { |x| x.field == 'operationName' }.collect(&:equals) - - Azure::Response.create(alert.members << :operations, alert.values << operations) - end - end -end diff --git a/test/unit/resources/azure_monitor_activity_log_alert_test.rb b/test/unit/resources/azure_monitor_activity_log_alert_test.rb new file mode 100644 index 000000000..b23fa6021 --- /dev/null +++ b/test/unit/resources/azure_monitor_activity_log_alert_test.rb @@ -0,0 +1,17 @@ +require_relative 'helper' +require 'azure_monitor_activity_log_alert' + +class AzureMonitorActivityLogAlertConstructorTest < Minitest::Test + def test_empty_param_not_ok + assert_raises(ArgumentError) { AzureMonitorActivityLogAlert.new } + end + + # resource_provider should not be allowed. + def test_resource_provider_not_ok + assert_raises(ArgumentError) { AzureMonitorActivityLogAlert.new(resource_provider: 'some_type') } + end + + def test_resource_group + assert_raises(ArgumentError) { AzureMonitorActivityLogAlert.new(name: 'my-name') } + end +end diff --git a/test/unit/resources/azure_monitor_activity_log_alerts_test.rb b/test/unit/resources/azure_monitor_activity_log_alerts_test.rb new file mode 100644 index 000000000..66773728c --- /dev/null +++ b/test/unit/resources/azure_monitor_activity_log_alerts_test.rb @@ -0,0 +1,25 @@ +require_relative 'helper' +require 'azure_monitor_activity_log_alerts' + +class AzureMonitorActivityLogAlertsConstructorTest < Minitest::Test + # resource_type should not be allowed. + def test_resource_type_not_ok + assert_raises(ArgumentError) { AzureMonitorActivityLogAlerts.new(resource_provider: 'some_type') } + end + + def tag_value_not_ok + assert_raises(ArgumentError) { AzureMonitorActivityLogAlerts.new(tag_value: 'some_tag_value') } + end + + def tag_name_not_ok + assert_raises(ArgumentError) { AzureMonitorActivityLogAlerts.new(tag_name: 'some_tag_name') } + end + + def test_resource_id_not_ok + assert_raises(ArgumentError) { AzureMonitorActivityLogAlerts.new(resource_id: 'some_id') } + end + + def test_name_not_ok + assert_raises(ArgumentError) { AzureMonitorActivityLogAlerts.new(name: 'some_name') } + end +end