1

I was trying to create some basic inspec tests to validate a set of HTTP URLs. The way I started is like this -

control 'http-url-checks' do
  impact 1.0
  title 'http-url-checks'
  desc '
   Specify the URLs which need to be up and working.
  '
  tag 'http-url-checks'

  describe http('http://example.com') do
    its('status') { should eq 200 }
    its('body') { should match /abc/ }
    its('headers.name') { should eq 'header' }
  end

  describe http('http://example.net') do
    its('status') { should eq 200 }
    its('body') { should match /abc/ }
    its('headers.name') { should eq 'header' }
  end
end

We notice that the URLs are hard-coded in the controls and isn't a lot of fun. I'd like to move them to some 'attributes' file of some sort and loop through them in the control file.

My attempt was to use the 'files' folder structure inside the profile.I created a file - httpurls.yml and had the following content in it -

- url: http://example.com
- url: http://example.net

..and in my control file, I had the construct -

  my_urls = yaml(content: inspec.profile.file('httpurls.yml')).params

  my_urls.each do |s|
    describe http(s['url']) do
      its('status') { should eq 200 }
    end
  end

However, when I execute the compliance profile, I get an error - 'httpurls.yml not found' (not sure about the exact error message though though). The following is the folder structure I had for my compliance profile.

enter image description here

What I am doing wrong?

Is there a better way to achieve what I am trying to do?

james.garriss
  • 12,959
  • 7
  • 83
  • 96
mmukhe
  • 668
  • 9
  • 22
  • Going to untag `chef` as this does not appear related to Chef as a project. Given there aren't a ton of standalone InSpec users, I would highly recommend signing up for Chef community slack and dropping this question in the `#inspec` channel there too. – coderanger Mar 01 '18 at 04:43
  • Sure. Trying it out there as well! – mmukhe Mar 01 '18 at 06:57
  • "*not sure about the exact error message though though*" ー so please make sure before you post on SO and include the relevant message in the question. ・ There's nothing wrong in the code you posted in the question. – techraf Mar 13 '18 at 06:02
  • Actually, there is, @techraf, because he's hard-coded values that he wants to be variables instead. Making this change would make his InSpec profile more flexible and reusable. Perhaps it's not "wrong," but it's also fair to say that it's not quite "right." – james.garriss Feb 04 '21 at 13:34

2 Answers2

2

The secret is to use profile attributes, as defined near the bottom of this page:

https://www.inspec.io/docs/reference/profiles/

First, create a profile attributes YML file. I name mine profile-attribute.yml.

Second, put your array of values in the YML file, like so:

urls:
  - http://example.com
  - http://example.net

Third, create an attribute at the top of your InSpec tests:

my_urls = attribute('urls', description: 'The URLs that I am validating.')

Fourth, use your attribute in your InSpec test:

my_urls.each do |s|
  describe http(s['url']) do
    its('status') { should eq 200 }
  end
end

Finally, when you call your InSpec test, point to your YML file using --attrs:

inspec exec mytest.rb --reporter=cli --attrs profile-attribute.yml
james.garriss
  • 12,959
  • 7
  • 83
  • 96
0

There is another way to do this using files (instead of the profile attributes and the --attrs flag). You can use JSON or YAML.

First, create the JSON and/or YAML file and put them in the files directory. A simple example of the JSON file might look like this:

{
    "urls": ["https://www.google.com", "https://www.apple.com"]
}

And a simple example of the YAML file might look like this:

urls:
- https://www.google.com
- https://www.apple.com

Second, include code at the top of your InSpec file to read and parse the JSON and/or YAML, like so:

jsoncontent = inspec.profile.file("tmp.json")
jsonparams = JSON.parse(jsoncontent)
jsonurls = jsonparams['urls']

yamlcontent = inspec.profile.file("tmp.yaml")
yamlparams = YAML.load(yamlcontent)
yamlurls = yamlparams['urls']

Third, use the variables in your InSpec tests, like so:

jsonurls.each do |jsonurl|
  describe http(jsonurl) do
    puts "json url is " + jsonurl
    its('status') { should eq 200 }
  end
end

yamlurls.each do |yamlurl|
  describe http(yamlurl) do
    puts "yaml url is " + yamlurl
    its('status') { should eq 200 }
  end
end

(NOTE: the puts line is for debugging.)

The result is what you would expect:

json url is https://www.google.com
json url is https://www.apple.com
yaml url is https://www.google.com
yaml url is https://www.apple.com

Profile: InSpec Profile (inspec-file-test)
Version: 0.1.0
Target:  local://

  http GET on https://www.google.com
     ✔  status should eq 200
  http GET on https://www.apple.com
     ✔  status should eq 200
  http GET on https://www.google.com
     ✔  status should eq 200
  http GET on https://www.apple.com
     ✔  status should eq 200
james.garriss
  • 12,959
  • 7
  • 83
  • 96