diff --git a/docs/resources/azure_generic_resource.md b/docs/resources/azure_generic_resource.md index a4a1b9712..1cab898bd 100644 --- a/docs/resources/azure_generic_resource.md +++ b/docs/resources/azure_generic_resource.md @@ -36,6 +36,7 @@ The following parameters can be passed for targeting a specific Azure resource. | resource_group | Azure resource group that the targeted resource has been created in. `MyResourceGroup` | | name | Name of the Azure resource to test. `MyVM` | | resource_provider | Azure resource provider of the resource to be tested. `Microsoft.Compute/virtualMachines` | +| resource_path | Relative path to the resource if it is defined on another resource. Resource path of a subnet in a virtual network would be: `{virtualNetworkName}/subnets`. | | resource_id | Unique id of Azure resource to be tested. `/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/virtualMachines/{vmName}` | | tag_name* | Tag name defined on the Azure resource. `name` | | tag_value | Tag value of the tag defined with the `tag_name`. `external_linux` | @@ -48,6 +49,7 @@ Either one of the parameter sets can be provided for a valid query: - `resource_group` and `name` - `name` - `resource_group`, `resource_provider` and `name` +- `resource_group`, `resource_provider`, `resource_path` and `name` - `tag_name` and `tag_value` Different parameter combinations can be tried. If it is not supported either the InSpec resource or the Azure Rest API will raise an error. @@ -101,6 +103,13 @@ describe azure_generic_resource(resource_group: 'my_vms', name: 'my_linux_vm') d its('tags') { should include('name') } # regardless of the value end ``` +### Test Properties of a Virtual Machine Resides in an Azure Dev Test Lab +```ruby +describe azure_generic_resource(resource_provider: 'Microsoft.DevTestLab/labs', resource_path: '{labName}/virtualmachines', resource_group: 'my_group', name: 'my_VM') do + its('properties.userName') { should cmp 'admin' } + its('properties.allowClaim') { should cmp false } +end +``` For more examples, please see the [integration tests](/test/integration/verify/controls/azure_generic_resource.rb). ## Matchers diff --git a/libraries/azure_backend.rb b/libraries/azure_backend.rb index 3ab4c984c..4855cb73b 100644 --- a/libraries/azure_backend.rb +++ b/libraries/azure_backend.rb @@ -433,13 +433,6 @@ def catch_failed_resource_queries resource_fail(message) end - # Track the status of the resource at InSpec Azure resource pack level. - # - # @return [TrueClass, FalseClass] Whether the resource is failed or not. - def failed_resource? - @failed_resource ||= false - end - # Ensure required parameters have been set to perform backend operations. # # Some resources may require several parameters to be set, in which case use `required`. diff --git a/libraries/azure_generic_resource.rb b/libraries/azure_generic_resource.rb index fb15a47f2..b2d62efe0 100644 --- a/libraries/azure_generic_resource.rb +++ b/libraries/azure_generic_resource.rb @@ -45,7 +45,13 @@ def initialize(opts = {}, static_resource = false) # # If there are static resource specific validations they can be passed here: # required parameters via `opts[:required_parameters]` - validate_parameters(require_any_of: %i(resource_group name tag_name tag_value resource_id resource_provider)) + validate_parameters(require_any_of: %i(resource_group + resource_path + name + tag_name + tag_value + resource_id + resource_provider)) end @display_name = @opts.slice(:resource_group, :resource_provider, :name, :tag_name, :tag_value, :resource_id) .values.join(' ') @@ -131,4 +137,11 @@ def resource_group res_group, _provider, _res_type = Helpers.res_group_provider_type_from_uri(id) res_group end + + # Track the status of the resource at InSpec Azure resource pack level. + # + # @return [TrueClass, FalseClass] Whether the resource is failed or not. + def failed_resource? + @failed_resource ||= false + end end diff --git a/libraries/azure_generic_resources.rb b/libraries/azure_generic_resources.rb index 529e812fe..c3f6bb3bb 100644 --- a/libraries/azure_generic_resources.rb +++ b/libraries/azure_generic_resources.rb @@ -96,6 +96,14 @@ def to_s(class_name = nil) end end + # Return the InSpec level resource failure. + # This is a diversion from singular resources + # since an empty response from API should not be considered as failure. + # FilterTable will respond properly when it is an empty response. + def failed_resource? + resource_failed? + end + def api_version_used_for_query @api_response[:api_version_used_for_query] if @api_response end @@ -110,8 +118,6 @@ def api_version_used_for_query_state # @param table_scheme [Array] [{column: :blahs, field: :blah}, {..}] def self.populate_filter_table(raw_data, table_scheme) filter_table = FilterTable.create - # puts "Table scheme in pop fil met #{table_scheme}" - # puts "Raw data: #{raw_data}" table_scheme.each do |col_field| filter_table.register_column(col_field[:column], field: col_field[:field]) end diff --git a/libraries/azure_graph_generic_resource.rb b/libraries/azure_graph_generic_resource.rb index 563fb13f1..1ae689377 100644 --- a/libraries/azure_graph_generic_resource.rb +++ b/libraries/azure_graph_generic_resource.rb @@ -76,4 +76,11 @@ def to_s(class_name = nil) "#{class_name.name.split('_').map(&:capitalize).join(' ')} #{api_info}: #{@display_name}" end end + + # Track the status of the resource at InSpec Azure resource pack level. + # + # @return [TrueClass, FalseClass] Whether the resource is failed or not. + def failed_resource? + @failed_resource ||= false + end end diff --git a/libraries/azure_graph_generic_resources.rb b/libraries/azure_graph_generic_resources.rb index 006a9a884..35fb4f041 100644 --- a/libraries/azure_graph_generic_resources.rb +++ b/libraries/azure_graph_generic_resources.rb @@ -126,4 +126,12 @@ def self.populate_filter_table(raw_data, table_scheme) end filter_table.install_filter_methods_on_resource(self, raw_data) end + + # Return the InSpec level resource failure. + # This is a diversion from singular resources + # since an empty response from API should not be considered as failure. + # FilterTable will respond properly when it is an empty response. + def failed_resource? + resource_failed? + end end diff --git a/libraries/azure_virtual_machines.rb b/libraries/azure_virtual_machines.rb index d3b086234..07dc0d0ec 100644 --- a/libraries/azure_virtual_machines.rb +++ b/libraries/azure_virtual_machines.rb @@ -83,7 +83,7 @@ def to_s 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? + return [] if @resources.empty? @resources.each do |resource| os_profile = resource[:properties][:osProfile] platform = \ diff --git a/libraries/backend/azure_connection.rb b/libraries/backend/azure_connection.rb index 319d06a1f..88f01b6f7 100644 --- a/libraries/backend/azure_connection.rb +++ b/libraries/backend/azure_connection.rb @@ -170,6 +170,9 @@ def fail_api_query(resp, message = nil) raise UnsuccessfulAPIQuery::ResourceNotFound, error_message end end + if resource_not_found_codes.include?(body[:httpStatusCode]) + raise UnsuccessfulAPIQuery::ResourceNotFound, message + end raise UnsuccessfulAPIQuery::UnexpectedHTTPResponse, message end end diff --git a/terraform/azure.tf b/terraform/azure.tf index f8eb720e2..93fd312b1 100644 --- a/terraform/azure.tf +++ b/terraform/azure.tf @@ -862,7 +862,7 @@ resource "azurerm_eventhub_authorization_rule" "auth_rule_inspectesteh" { } resource "azurerm_iothub" "iothub" { - name = "inspectest-iothub" + name = "inspectest-iothub-${random_string.event_hub.result}" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location sku {