This role allows you to execute Inspec tests that are contained within your project when executing on Ansible Tower / AWX. It works with both Linux and Windows targets (other targets not supported / tested).
Note: this is intended to test live targets after having ran your playbook(s). It is not related to Kitchen or Molecule.
Create a folder within your role(s) named inspec-profile
(the name must be exact!). Then, follow the Inspec profile structure to create the contents of the profile.
You will most likely have the following basic structure -
inspec-profile
├── controls
│ ├── test_myfeature1.rb
│ └── test_myfeature2.rb
└── inspec.yml
Under the controls folder, the filenames should begin with test_
, as shown above.
The test_*.rb
file(s) in the controls folder will define the tests to run against your target machines. There are dozens of resources you can utilize to write your tests.
Here is a basic example that confirms port 80 is listening on the target machine:
describe port(80) do
it { should be_listening }
end
You can have multiple tests in each file, and you can have multiple files, if necessary, for cleaner organization.
You must create a task within your role(s) to execute the tests. Insert the following block at the bottom of your tasks/main.yml
file:
- tags: inspec
block:
- set_fact:
current_role_path: '{{ role_path }}'
- name: run inspec tests
include_role:
name: inspec
tasks_from: inspec-exec.yml
vars:
inspec_imported_role_path: '{{ current_role_path }}'
Inserting this block at the bottom of the main.yml
file ensures the tests run after your role has completed its tasks.
You may have several environments, & between them, servers may be listening on different ports, contain different configurations, etc. To ensure your tests have the right information, you can use attributes.
The Inspec role will handle converting your Ansible vars into Inspec attributes. To do this, set the inspec_vars_list variable and provide the list in the following format:
inspec_vars_list:
- { name: 'apache_root', value: '{{ apache_root }}' }
- { name: 'apache_listen_addr', value: '{{ apache_listen_addr }}'}
- { name: 'apache_listen_port', value: '{{ apache_listen_port }}' }
Name is the name of the attribute and Value is the value of the attribute. In most cases you will just pass the name and value of your existing Ansible vars.
Include the format above when executing your tests, as such:
- tags: inspec
block:
- set_fact:
current_role_path: '{{ role_path }}'
- name: run inspec tests
include_role:
name: inspec
tasks_from: inspec-exec.yml
vars:
inspec_imported_role_path: '{{ current_role_path }}'
inspec_vars_list:
- { name: 'apache_root', value: '{{ apache_root }}' }
- { name: 'apache_listen_addr', value: '{{ apache_listen_addr }}'}
- { name: 'apache_listen_port', value: '{{ apache_listen_port }}' }
You must write your tests in such a way that they'll utilize the attributes. For example, the following code tests that apache is listening on a particular port, as defined by an attribute:
apache_listen_port = attribute(
'apache_listen_port', default: 80, description: 'The port apache is listening on.'
)
describe port(apache_listen_port) do
it { should be_listening }
its('protocols') { should cmp 'tcp' }
end
The default value above, 80
, is used if no attribute was provided.