require "resources/azure/azure_backend" module Inspec::Resources class AzureResourceGroup < AzureResourceBase name "azure_resource_group" desc ' InSpec Resource to get metadata about a specific Resource Group ' supports platform: "azure" attr_reader :name, :location, :id, :total, :counts, :mapping # Constructor to get the resource group itself and perform some analysis on the # resources that in the resource group. # # This analysis is defined by the the mapping hashtable which is used to define # the 'has_xxx?' methods (see AzureResourceGroup#create_has_methods) and return # the counts for each type # # @author Russell Seymour def initialize(opts) opts.key?(:name) ? opts[:group_name] = opts[:name] : false # Ensure that the opts only have the name of the resource group set opts.select! { |k, _v| k == :group_name } super(opts) # set the mapping for the Azure Resources @mapping = { nic: "Microsoft.Network/networkInterfaces", vm: "Microsoft.Compute/virtualMachines", extension: "Microsoft.Compute/virtualMachines/extensions", nsg: "Microsoft.Network/networkSecurityGroups", vnet: "Microsoft.Network/virtualNetworks", managed_disk: "Microsoft.Compute/disks", managed_disk_image: "Microsoft.Compute/images", sa: "Microsoft.Storage/storageAccounts", public_ip: "Microsoft.Network/publicIPAddresses", } # Get information about the resource group itself resource_group # Get information about the resources in the resource group resources # Call method to create the has_xxxx? methods create_has_methods # Call method to allow access to the tag values create_tag_methods end # Return the provisioning state of the resource group # # @author Russell Seymour def provisioning_state properties.provisioningState end # Analyze the fully qualified id of the resource group to return the subscription id # that this resource group is part of # # The format of the id is # /subscriptions//resourceGroups/ # # @author Russell Seymour def subscription_id id.split(%r{\/}).reject(&:empty?)[1] end # Method to parse the resources that have been returned # This allows the calculations of the amount of resources to be determined # # @author Russell Seymour # # @param [Hash] resource A hashtable representing the resource group def parse_resource(resource) # return a hash of information parsed = { "name" => resource.name, "type" => resource.type, } parsed end # This method catches the xxx_count calls that are made on the resource. # # The method that is called is stripped of '_count' and then compared with the # mappings table. If that type exists then the number of those items is returned. # However if that type is not in the Resource Group then the method will return # a NoMethodError exception # # @author Russell Seymour # # @param [Symbol] method_id The name of the method that was called def method_missing(method_id) # Determine the mapping_key based on the method_id mapping_key = method_id.to_s.chomp("_count").to_sym if mapping.key?(mapping_key) # based on the method id get the namespace, type_name = mapping[mapping_key].split(/\./) # check that the type_name is defined, if not return 0 if send(namespace).methods.include?(type_name.to_sym) # return the count for the method id send(namespace).send(type_name) else 0 end else msg = format("undefined method `%s` for %s", method_id, self.class) raise NoMethodError, msg end end private # For each of the mappings this method creates the has_xxx? method. This allows the use # of the following type of test # # it { should have_nics } # # For example, it will create a has_nics? method that returns a boolean to state of the # resource group has any nics at all. # # @author Russell Seymour # @private def create_has_methods return if failed_resource? # Create the has methods for each of the mappings # This is a quick test to show that the resource group has at least one of these things mapping.each do |name, type| # Determine the name of the method name method_name = format("has_%ss?", name) namespace, type_name = type.split(/\./) # use the namespace and the type_name to determine if the resource group has this type or not result = send(namespace).methods.include?(type_name.to_sym) ? true : false define_singleton_method method_name do result end end end end end