Learn Chef - Chef InSpec (Compliance)
Series/Learn Chef/Episode 11
Episode 11 of 23

Learn Chef - Chef InSpec (Compliance)

Learn how to write compliance profiles with Chef InSpec using controls and describe blocks, the package, service, file, and command resources, then run inspec exec to scan nodes, including the CIS- and STIG-style profile workflow and integration with Chef Automate.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Introduction

In episode 10 you already learned about Policyfiles and how Policyfile.lock.json guarantees full reproducibility for every release. You know how to define run lists and cookbooks declaratively, then push them to policy groups such as staging and production without mutating cookbooks on the server. Infrastructure can now be rebuilt identically, but nothing yet answers one important question: is the configuration that was released actually secure and compliant with the standards?

Running chef-client can ensure the desired state is achieved, but it does not guarantee that no drift occurs afterwards. A configuration file can be modified, a service can be stopped, or a port can be opened. To detect this drift, you need Chef InSpec, a code-based compliance testing engine.

This episode 11 covers how to write InSpec profiles and controls with describe blocks, the package, service, file, and command resources, running inspec exec to scan nodes, the compliance workflow with CIS- and STIG-style profiles, and integrating the results with Chef Automate.

Profiles and Controls

An InSpec profile is a collection of controls that describe the security conditions a system must satisfy. A control is a single test with a unique identity and description. The profile structure is generated with the command:

Initialize a new profile
inspec init profile baseline

This command creates the following structure:

Baseline profile structure
baseline/
├── README.md
├── attributes/          # atribut profil (opsional)
├── controls/
│   └── example.rb       # file kontrol utama
└── inspec.yml           # metadata profil

The inspec.yml file is the profile metadata: name, version, maintainer, and supported platforms.

Controls and Describe Blocks

Controls are written in Ruby inside controls/. Each control has a short description, tags for tracking, and one or more describe do blocks. InSpec executes the describe do blocks and reports the results as passing or failing controls.

controls/example.rb
control 'cis-1-1' do
  impact 0.7
  title 'Set sudoers file permissions'
  desc 'sudoers file harus dimiliki oleh root dan mode 0440'
  tag cci: 'CCI-000225'
  tag cis: '1.1'
 
  describe file('/etc/sudoers') do
    it { should exist }
    it { should be_owned_by 'root' }
    it { should be_grouped_into 'root' }
    it { should be_mode 0440 }
  end
end

The control above tests for the existence of the /etc/sudoers file, root ownership, and 0440 permissions. impact expresses the severity on a scale from 0 to 1, while tag links the control to external references such as the CIS Control Version.

Note

Every describe block may contain multiple it statements. InSpec evaluates them all and reports per statement. If one statement fails, the control still runs to completion so you get the full picture, rather than stopping at the first failure.

Core InSpec Resources

InSpec provides built-in resources for testing various aspects of a system. The four most frequently used:

describe file('/etc/ssh/sshd_config') do
  it { should be_file }
  it { should be_owned_by 'root' }
  it { should be_mode 0644 }
  its('content') { should match 'PermitRootLogin no' }
end

The command resource runs a command on the target system and tests its output, exit status, and stderr. This is most useful when there is no specific resource for the thing you want to test.

Tip

InSpec resources follow consistent naming rules. If you need another one, inspec resource list shows all available resources, and inspec help resource <name> explains the syntax. For example, the port, user, and group resources are very useful for network security audits.

Running Scans with inspec exec

To scan a system, use inspec exec with a profile and a target:

Local and remote scans
inspec exec baseline
inspec exec baseline -t ssh://deploy@203.0.113.30 -i ~/.ssh/id_ed25519

The first command scans the local machine, while the second connects to a remote machine over SSH using a private key. The scan output shows the number of passing, failing, and not-applicable controls, along with details for each failing control. To make the scan exit with a failing code whenever any control does not pass, use inspec exec baseline --fail-if 'failed > 0':

Scan with a failure threshold
inspec exec baseline --fail-if 'failed > 0' --reporter cli json:/tmp/scan.json

The --reporter cli json:/tmp/scan.json option writes the scan results in two formats at once: a summary to the terminal and full details in JSON. This JSON file is what gets sent to Chef Automate later.

Important

JSON output is the bridge between InSpec and other systems. CI pipelines, compliance dashboards, and Chef Automate all read this format. Always get into the habit of including a JSON reporter in every scan that will be integrated into a workflow.

Compliance Workflow with CIS and STIG

Production compliance profiles usually follow public standards such as CIS Benchmarks or STIG (Security Technical Implementation Guide), and Chef provides official profiles that can be used as a starting point:

Use a CIS profile from Supermarket
inspec supermarket profile cis-ubuntu-22.04
inspec exec cis-ubuntu-22.04 -t ssh://deploy@203.0.113.30

For internal needs, inherit a standard profile and then harden it with additional controls:

inspec.yml with depends
name: corporate-baseline
version: 0.1.0
depends:
  - name: cis-ubuntu-22.04
    url: https://supermarket.chef.io/profiles/cis-ubuntu-22.04

Inherited profiles are used through the include_controls tag in the main control, allowing you to add custom controls on top of the baseline standard. The recommended workflow: check compliance in staging after every policy release, and only promote to production if the staging scan passes all mandatory controls.

Warning

CIS and STIG profiles often contain controls that conflict with business needs. Manage exceptions explicitly through tag and profile attributes, rather than by deleting controls, so the audit trail stays complete and explainable.

Integration with Chef Automate

Chef Automate is the platform that displays InSpec scan results in a compliance dashboard. The integration flow: nodes use the chef_automate resource or scan-jobs agents that run inspec exec periodically, then send the results as JSON to Automate via the data collection endpoint. Automate then shows the compliance score per node, per profile, and per control as graphs, so node health, chef-client run status, and compliance results all appear on one screen.

To send scan results directly from an InSpec profile to Automate, run the scan with the automate reporter:

Send scan results to Automate
inspec exec baseline \
  --reporter automate \
  --inspec-opt automate-url https://automate.example.com \
  --inspec-opt token $AUTOMATE_TOKEN

Make sure the token you use is an API token with compliance scanning permissions. Once submitted, the Automate dashboard shows score trends over time, so drift that occurs after a release can be seen and acted upon.

Conclusion

In this episode 11 you learned how to write InSpec profiles and controls with describe blocks, use the package, service, file, and command resources to test various aspects of a system, and run scans with inspec exec both locally and remotely. You also understood the compliance workflow with CIS and STIG profiles and how to integrate scan results into Chef Automate.

Key takeaways:

  • An InSpec profile is a collection of controls, and a control is a single test with an identity and impact.
  • Describe blocks contain it statements that evaluate resources such as file, package, service, and command.
  • inspec exec scans local or remote targets and supports many reporter formats including JSON.
  • CIS and STIG profiles can be inherited and hardened to meet security baselines.
  • Chef Automate combines compliance scan results with chef-client run status in a single dashboard.

In the next episode, episode 12, we will discuss Chef Automate in full: the compliance dashboard and node visibility, node health, cookbook run status, the compliance pipeline for CI/CD, and the role of the data collection service in the Automate platform. See you there.

Learn Chef - Chef InSpec (Compliance) | Learn Chef