Skip to main content

4. Exploring InSpec Resources

Aaron LippoldAbout 2 min

Resources

Now that you have learned about making and running InSpec profiles, let's dig deeper into the mechanics of InSpec by learning about resources.

Core Resources

As you saw in the Beginner class, when writing InSpec code, many core resources are available because they are included in the main InSpec code base.

Local Resources

Local resources are those that exist only in the profile in which they are developed. Local resources are put in the libraries directory:

$ tree examples/profile
examples/profile
...
β”œβ”€β”€ libraries
β”‚   └── custom_resource.rb

Note that the libraries directory is not created by default within a profile when we use inspec init. We need to create the directory ourselves.

Once you create and populate a custom resource Ruby file inside the libraries directory, it can be utilized inside your local profile just like the core resources.

6.1. Resource Overview

Resources may be added to profiles in the libraries folder:

$ tree examples/profile
examples/profile
...
β”œβ”€β”€ libraries
β”‚   └── gordon_config.rb

6.2. Resource Structure

The smallest possible InSpec resource takes this form:

class Tiny < Inspec.resource(1)
  name 'tiny'
end

This is easy to write, but not particularly useful for testing.

Resources are written as a regular Ruby class, which inherits from the base inspec.resource class. The number (1) specifies the version this resource plugin targets. As Chef InSpec evolves, this interface may change and may require a higher version.

In addition to the resource name, the following attributes can be configured:

  • name - Identifier of the resource (required)
  • desc - Description of the resource (optional)
  • example - Example usage of the resource (optional)
  • supports - (Chef InSpec 2.0+) Platform restrictions of the resource (optional)

The following methods are available to the resource:

  • inspec - Contains a registry of all other resources to interact with the operating system or target in general.
  • skip_resource - A resource may call this method to indicate that requirements aren’t met. All tests that use this resource will be marked as skipped.

The following example shows a full resource using attributes and methods to provide simple access to a configuration file:

class GordonConfig < Inspec.resource(1)
  name 'gordon_config'

  # Restrict to only run on the below platforms (if none were given, all OS's supported)
  supports platform_family: 'fedora'
  supports platform: 'centos', release: '6.9'
  # Supports `*` for wildcard matcher in the release
  supports platform: 'centos', release: '7.*'

  desc '
    Resource description ...
  '

  example '
    describe gordon_config do
      its("signal") { should eq "on" }
    end
  '

  # Load the configuration file on initialization
  def initialize(path = nil)
    @path = path || '/etc/gordon.conf'
    @params = SimpleConfig.new( read_content )
  end

  # Expose all parameters of the configuration file.
  def method_missing(name)
    @params[name]
  end

  private

  def read_content
    f = inspec.file(@path)
    # Test if the path exist and that it's a file
    if f.file?
      # Retrieve the file's contents
      f.content
    else
      # If the file doesn't exist, skip all tests that use gordon_config
      raise Inspec::Exceptions::ResourceSkipped, "Can't read config at #{@path}"
    end
  end
end

Let's break down each component of the resource.

class

The class is where the Ruby file is defined.

name

The name is how we will call upon this resource within our controls, in the example above that would be gordon_config.

supports

Supports are used to define or restrict the Ruby resource to work in specific ways, as shown in the example above that is used to restrict our class to specific platforms.

desc

A simple description of the purpose of this resource.

examples

A simple use case example. The example is usually a describe block using the resource, given as a multi-line comment.

initialize method

An initilize method is required if your resource needs to be able to accept a parameter when called in a test (e.g. file('this/path/is/a/parameter'))

functionality methods

These methods return data from the resource so that you can use it in tests.