---
title: "Chef"
description: "Comprehensive Chef reference guide covering installation, cookbooks, recipes, resources, knife commands, server management, and automation workflows for infrastructure configuration management."
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/cheatsheets/chef
---

# Chef

Chef is a powerful Infrastructure as Code platform for automating infrastructure configuration, deployment, and management. This cheatsheet provides comprehensive reference guides for Chef concepts, commands, patterns, and best practices to help you efficiently manage infrastructure with Chef Infra, Chef Server, and related tools.

## Getting Started

### Installation & Setup

Install Chef workstation and set up development environment

**Keywords:** install, setup, workstation, requirements, dependencies

#### Install Chef Workstation on Linux

```bash
wget https://packages.chef.io/files/stable/chef-workstation/23.10.1234/ubuntu/22.04/chef-workstation_23.10.1234-1_amd64.deb
sudo dpkg -i chef-workstation_23.10.1234-1_amd64.deb
```

#### Verify Chef Installation

```bash
chef --version
```

_exec_
```bash
Chef Workstation: 23.10.1234
Chef Infra Client: 18.3.0
Chef InSpec: 5.22.3
Chef Habitat: 1.6.521
Test Kitchen: 3.5.0
Cookstyle: 7.32.1
```

Displays all installed Chef tools and versions

- Chef client version 18+ recommended
- Test Kitchen included in workstation

### Chef Configuration

Configure knife and chef client settings

**Keywords:** config, knife.rb, client.rb, credentials

#### Create knife Configuration

```yaml
current_dir = File.dirname(__FILE__)
log_level                :info
log_location             STDOUT
node_name                'dev_user'
client_key               "#{current_dir}/dev_user.pem"
validation_client_name   'chef-validator'
validation_key           "#{current_dir}/chef-validator.pem"
chef_server_url          'https://chef.example.com/organizations/myorg'
cookbook_path            ["#{current_dir}/../cookbooks"]
```

Basic knife.rb configuration for chef server connectivity

- Store in ~/.chef/knife.rb
- PEM keys must have correct permissions (600)

#### Chef Client Configuration

```yaml
log_level :info
log_location "/var/log/chef-client.log"
chef_server_url "https://chef.example.com/organizations/myorg"
validation_client_name "chef-validator"
node_name Socket.gethostname
```

Chef-client.rb configuration file

- Located at /etc/chef/client.rb
- Loaded on each chef-client run

### Workstation Setup

Initialize and configure Chef development workspace

**Keywords:** workspace, directory, bootstrap, knife

#### Generate Chef Repo

```bash
chef generate repo my-chef-repo
cd my-chef-repo
```

_exec_
```bash
Generating Chef Infra repo my-chef-repo
- Creating workspace directory structure
- Creating default cookbooks directory
- Creating default roles directory
- Creating default environments directory
- Creating data_bags directory
```

Creates standard Chef repository structure

- Generates .gitignore and basic files
- Ready for git initialization

#### Generate Cookbook

```bash
chef generate cookbook cookbooks/apache2
```

_exec_
```bash
Generating cookbook apache2
- Creating cookbook directory structure
- Creating CHANGELOG.md
- Creating metadata.rb
- Creating README.md
- Creating spec/spec_helper.rb
- Creating .kitchen.yml
```

Creates new cookbook with standard structure

- Creates ChefSpec and InSpec test files
- Ready for Test Kitchen

## Basic Resources

### File Resource

Manage file creation, deletion, and modification

**Keywords:** file, create, delete, modify, rights, mode, owner

#### Create Simple File

```ruby
file '/etc/app/config.txt' do
  content 'application configuration'
  owner 'root'
  group 'root'
  mode '0644'
  action :create
end
```

#### Create File with Content Test

```ruby
file '/var/log/app.log' do
  content "#{Time.now} - Application Started\n"
  owner 'app_user'
  group 'app_group'
  mode '0666'
end
```

_exec_
```bash
Recipe: default
* file[/var/log/app.log] action create
- create new file /var/log/app.log
- update permissions to 0666
- update ownership to app_user:app_group
```

Creates file with specific ownership and permissions

- Mode as string with leading 0
- Modes are octal: 0644, 0755, etc.

#### Delete File

```ruby
file '/etc/old-config.conf' do
  action :delete
end
```

Removes file from system

- Safe to run multiple times (idempotent)
- Will not error if file doesn't exist

### Package Resource

Install, upgrade, and remove software packages

**Keywords:** package, install, remove, upgrade, version

#### Install Single Package

```ruby
package 'nginx' do
  action :install
end
```

#### Install Multiple Packages

```ruby
package ['curl', 'wget', 'git', 'htop'] do
  action :install
end
```

_exec_
```bash
Recipe: default
* package[curl] action install
- install version 7.68.0-1ubuntu1 of curl
* package[wget] action install
- install version 1.20.3-1 of wget
* package[git] action install
- install version 1:2.34.1-1ubuntu1 of git
```

Installs multiple packages in single resource

- Chef uses platform package manager
- Version specified as :install installs latest
- Use version attribute for specific version

#### Install Specific Version

```ruby
package 'nginx' do
  version '1.18.0-0ubuntu1'
  action :install
end
```

Install specific version of package

- Version must be available in package repository

### Service Resource

Manage system services startup and runtime

**Keywords:** service, start, stop, restart, enable, managable

#### Start and Enable Service

```ruby
service 'nginx' do
  supports status: true, restart: true, reload: true
  action [:enable, :start]
end
```

#### Restart Service on Configuration Change

```ruby
file '/etc/nginx/nginx.conf' do
  content node['nginx']['config']
  notifies :restart, 'service[nginx]', :immediately
end

service 'nginx' do
  supports status: true, restart: true
  action :nothing
end
```

_exec_
```bash
Recipe: default
* file[/etc/nginx/nginx.conf] action create
- update content in file /etc/nginx/nginx.conf
* service[nginx] action restart
- restart service nginx
```

File change triggers service restart with notifications

- :immediately processed before other resources
- :action :nothing prevents immediate service start

#### Stop and Disable Service

```ruby
service 'old-service' do
  action [:stop, :disable]
end
```

Stops running service and disables autostart

- Disable prevents service from starting on boot

## Recipes & Cookbooks

### Recipe Syntax

Write Chef recipes with proper Ruby syntax

**Keywords:** recipe, ruby, syntax, resources, action

#### Basic Recipe Structure

```ruby
# Recipe: default.rb
description 'Install and configure web server'

package 'apache2'

service 'apache2' do
  action [:enable, :start]
end

file '/var/www/html/index.html' do
  content '<h1>Welcome</h1>'
  mode '0644'
end
```

Complete recipe with multiple resources

- Recipes are Ruby files
- Comments start with
- Executed top to bottom

#### Conditional Execution

```ruby
package 'mysql-server' do
  action :install
  only_if { node['install_database'] == true }
end

execute 'initialize-db' do
  command 'mysql_install_db'
  not_if 'test -d /var/lib/mysql/mysql'
end
```

Only run resources based on conditions

- only_if: resource executes only if condition true
- not_if: resource executes only if condition false

### Cookbook Structure

Organize cookbooks with files, templates, attributes

**Keywords:** cookbook, structure, files, templates, attributes, metadata

#### Cookbook Directory Layout

```bash
cookbooks/apache2/
├── recipes/
│   ├── default.rb
│   ├── server.rb
│   └── ssl.rb
├── files/
│   ├── default/
│   │   └── httpd.conf
├── templates/
│   ├── default/
│   │   └── apache2.conf.erb
├── attributes/
│   ├── default.rb
│   └── server.rb
├── metadata.rb
├── README.md
└── .kitchen.yml
```

Standard cookbook directory structure

- recipes/: Chef recipes
- files/: Static files (binaries, configs)
- templates/: Templated files with variables (ERB)
- attributes/: Default attribute values

#### Cookbook Metadata

```ruby
name 'apache2'
maintainer 'Chef Community'
maintainer_email 'cookbooks@example.com'
description 'Installs and configures Apache web server'
version '14.0.0'
license 'Apache-2.0'
chef_version '>= 16.0'

depends 'openssl'
depends 'httpd'

supports 'ubuntu', '>= 18.04'
supports 'centos', '>= 7.0'
```

Cookbook metadata with dependencies

- version in semantic versioning
- depends: cookbook dependencies
- supports: compatible operating systems

### Recipe Patterns

Common recipe patterns and best practices

**Keywords:** pattern, recipe, idempotent, converge, guard

#### Idempotent File Installation

```ruby
remote_file '/opt/app/binary' do
  source 'https://downloads.example.com/app-1.0.tar.gz'
  checksum 'abcd1234ef567890'
  mode '0755'
  action :create_if_missing
end
```

Downloads file only if not already present

- create_if_missing prevents re-download
- checksum validates file integrity
- Idempotent: safe to run multiple times

#### Recipe with Node Attributes

```ruby
package node['packages']['webserver']

service node['services']['webserver'] do
  action [:enable, :start]
end

node.override['app']['installed'] = true
```

Use node attributes in recipes

- node[] accesses attribute values
- node.override sets attribute value
- Attributes from multiple sources merged

## Knife Commands

### Node Management

List, bootstrap, and manage Chef nodes

**Keywords:** knife, node, bootstrap, list, show, delete

#### Bootstrap Node

```bash
knife bootstrap 192.168.1.100 -u ubuntu -i ~/.ssh/key.pem -N webserver01 --run-list 'recipe[apache2]' --sudo
```

_exec_
```bash
Connecting to 192.168.1.100
192.168.1.100 ➜ Installing Chef Infra Client 18.3.0...
192.168.1.100 ➜ Chef Infra Client successfully installed
192.168.1.100 ➜ Starting Chef Infra Client, version 18.3.0
192.168.1.100 ➜ Running handlers: [chef-client-finished]
192.168.1.100 ➜ Chef Infra Client finished, 8/8 resources updated in 25 seconds
```

Bootstrap installs Chef client and runs initial recipes

- Default runs once for initial setup
- -N sets node name in Chef Server
- --run-list specifies recipes to execute

#### List All Nodes

```bash
knife node list
```

_exec_
```bash
database01
webserver01
webserver02
loadbalancer01
```

Shows all nodes registered with Chef Server

- Requires knife.rb configuration
- Only shows registered nodes

#### Show Node Details

```bash
knife node show webserver01
```

_exec_
```bash
Node Name: webserver01
Environment: production
FQDN: webserver01.example.com
IP: 192.168.1.100
Run List: recipe[apache2], recipe[php]
Roles: [web_server]
Chef Version: 18.3.0
Platform: ubuntu 22.04 x86_64
Attributes:
  apache:
    port: 80
    workers: 4
```

Detailed information about specific node

- Shows attributes, run_list, roles
- Platform and Chef version visible

### Cookbook Operations

Manage and upload cookbooks

**Keywords:** cookbook, upload, delete, list, test, lint

#### Upload Cookbook

```bash
knife cookbook upload apache2 -o cookbooks/
```

_exec_
```bash
Uploading apache2 [14.0.0]
Uploaded 1 cookbook
apache2 [14.0.0]
```

Uploads cookbook to Chef Server

- -o specifies cookbook directory
- Version in metadata.rb required
- Creates version in Chef Server

#### Lint Cookbook

```bash
cookstyle cookbooks/apache2 --autocorrect
```

_exec_
```bash
Inspecting 8 files
6 files OK
2 files corrected
```

Lint checks and corrects cookbook code style

- cookstyle is Chef's linter
- --autocorrect fixes style issues
- Runs before uploading

#### Delete Cookbook

```bash
knife cookbook delete apache2 -v 14.0.0
```

Removes cookbook version from Chef Server

- -v specifies version to delete
- Requires confirmation

### Data Bags

Manage encrypted data and secrets

**Keywords:** data, bag, create, edit, secret, encryption

#### Create Data Bag

```bash
knife data bag create users
```

_exec_
```bash
Created data_bag[users]
```

Creates new data bag container

- Data bags store JSON data
- Can contain multiple items

#### Create Encrypted Data Bag Item

```bash
knife data bag create secrets db_password --secret-file ~/.chef/encrypted_data_bag_secret
```

_exec_
```bash
Created data_bag_item[secrets::db_password]
```

Creates encrypted data bag item

- Secret file required for encryption
- Stored encrypted on Chef Server

## Chef Server Management

### Chef Server Setup

Install and configure Chef Server

**Keywords:** server, install, setup, configuration, backend, frontend

#### Install Chef Server

```bash
wget https://packages.chef.io/files/stable/chef-server/15.5.1234/ubuntu/22.04/chef-server-core_15.5.1234-1_amd64.deb
sudo dpkg -i chef-server-core_15.5.1234-1_amd64.deb
sudo chef-server-ctl reconfigure
```

_exec_
```bash
Chef Server Starting...
* omnibus-ctl (default) -> install
* chef-server-postgresql (default) -> install
* opscode-erchef (default) -> install
* nginx (default) -> install
Chef Server configured and started successfully
```

Install and configure Chef Server

- Requires 4GB minimum RAM
- PostgreSQL backend required
- Takes time on first run

#### Create Organization

```bash
sudo chef-server-ctl org-create myorg "My Organization" --filename myorg-validator.pem
```

_exec_
```bash
Organization myorg created successfully
Validator key written to myorg-validator.pem
```

Create new organization on Chef Server

- Requires admin credentials
- Generates validator key for bootstrapping

### Chef Server Control Commands

Administrative commands for Chef Server

**Keywords:** chef-server-ctl, status, user, org, grant

#### Check Chef Server Status

```bash
sudo chef-server-ctl status
```

_exec_
```bash
run: chef-server-postgresql: (pid 1234) 5678s; run: log: (pid 1235) 98s
run: opscode-erchef: (pid 1236) 5600s; run: log: (pid 1237) 98s
run: nginx: (pid 1238) 5580s; run: log: (pid 1239) 98s
down: opscode-solr4: 10s, normally up; run: log: (pid 1240) 98s
```

Shows status of all Chef Server services

- All services should show run
- Verify connectivity issues with status

#### Create Admin User

```bash
sudo chef-server-ctl user-create admin Admin User admin@example.com --filename admin.pem
```

_exec_
```bash
User admin created successfully
Private key written to admin.pem
```

Creates new admin user

- Save private key securely
- Can grant org permissions

#### Grant User Permissions

```bash
sudo chef-server-ctl grant-server admin admin
```

Grants server admin permissions to user

- Must include org and user
- Requires restart after changes

## Advanced Resources

### Template Resource

Create files from ERB templates with variables

**Keywords:** template, erb, variable, dynamic, render

#### Simple ERB Template

```ruby
template '/etc/app/config.conf' do
  source 'config.conf.erb'
  owner 'app'
  group 'app'
  mode '0644'
  variables(
    app_name: 'MyApp',
    port: 8080,
    environment: node.chef_environment
  )
  notifies :restart, 'service[app]'
end
```

#### ERB Template File Content

```erb
# Configuration for <%= @app_name %>
server {
  listen <%= @port %>;
  server_name _;

  # Environment: <%= @environment %>
  <% if @environment == 'production' %>
    access_log /var/log/nginx/access.log combined;
  <% else %>
    access_log /var/log/nginx/dev-access.log;
  <% end %>
}
```

_exec_
```bash
# Configuration for MyApp
server {
  listen 8080;
  server_name _;

  # Environment: production
  access_log /var/log/nginx/access.log combined;
}
```

Template renders with variables substituted

- ERB syntax <%= %> for output
- <% %> for logic without output
- Variables passed to template via hash

#### Template with Lazy Attribute

```ruby
template '/etc/mysql/my.cnf' do
  source 'my.cnf.erb'
  variables lazy {
    {
      max_connections: node['mysql']['max_connections'],
      port: node['mysql']['port']
    }
  }
  action :create
end
```

Lazy attribute evaluates at convergence time

- Lazy {} defers evaluation
- Useful for dynamic attribute values
- Re-evaluates on each run

### Execute Resource

Run arbitrary commands on target system

**Keywords:** execute, command, shell, bash, guard, timeout

#### Simple Execute Command

```ruby
execute 'bash /usr/local/bin/install-app.sh' do
  not_if 'test -d /opt/app'
end
```

#### Execute with Guard Clause

```ruby
execute 'initialize-database' do
  command '/usr/bin/mysql_install_db'
  user 'mysql'
  group 'mysql'
  only_if { !::File.exist?('/var/lib/mysql/mysql') }
end
```

_exec_
```bash
Recipe: default
* execute[initialize-database] action run
- execute /usr/bin/mysql_install_db
```

Execute resource with guard prevents duplicate initialization

- only_if prevents execution if condition met
- Command must be idempotent

#### Execute with Timeout

```ruby
execute 'build-application' do
  command 'make build && make test'
  cwd '/opt/src'
  timeout 600
  user 'ubuntu'
end
```

Execute command with time limit

- Timeout in seconds
- cwd changes working directory
- Useful for long-running builds

### Ruby Block Resource

Execute Ruby code within recipes

**Keywords:** ruby_block, code, eval, notification

#### Ruby Block with Notification

```ruby
ruby_block 'create_application_user' do
  block do
    shell_out!('useradd -m -s /bin/bash appuser')
  end
  not_if { ::File.exist?('/home/appuser') }
end
```

#### Ruby Block to Set Attributes

```ruby
ruby_block 'generate-api-key' do
  block do
    key = SecureRandom.hex(32)
    node.override['app']['api_key'] = key
    Chef::Log.info("Generated API key: #{key}")
  end
  action :run
end
```

Ruby block generates API key and sets attribute

- Block contains arbitrary Ruby code
- shell_out! executes shell commands
- Can set node attributes

## Attributes & Variables

### Node Attributes

Set and manage node attributes

**Keywords:** attribute, default, override, automatic, attribute_file

#### Default Attributes File

```ruby
# attributes/default.rb
default['apache2']['port'] = 80
default['apache2']['workers'] = 4
default['apache2']['modules'] = %w(mod_rewrite mod_ssl)
default['apache2']['config_dir'] = '/etc/apache2'
```

#### Override Attributes

```ruby
# attributes/override.rb
override['apache2']['port'] = 8080
override['apache2']['workers'] = 8
```

Override attributes set higher priority

- default lowest priority
- override highest priority
- Node attributes in middle

#### Access Attributes in Recipe

```ruby
package 'apache2'

template '/etc/apache2/apache2.conf' do
  variables(
    port: node['apache2']['port'],
    workers: node['apache2']['workers'],
    modules: node['apache2']['modules']
  )
end
```

Access attributes using node[] syntax

- node['key'] accesses attribute value
- node['key'] = value sets attribute
- Attributes come from multiple sources

### Data Bags

Store shared data and secrets

**Keywords:** data, bag, secrets, json, encryption

#### Data Bag Item Structure

```json
{
  "id": "db_user",
  "username": "appdb",
  "password": "encrypted_password",
  "host": "database.example.com"
}
```

#### Access Data Bag in Recipe

```ruby
db_config = data_bag_item('database', 'db_user')

template '/etc/app/database.yml' do
  variables(
    host: db_config['host'],
    username: db_config['username'],
    password: db_config['password']
  )
end
```

Load and use data bag item in recipe

- data_bag_item(bag, item) loads data
- Encrypted items auto-decrypted
- Secret file required for decryption

### Environments

Create and manage environment configurations

**Keywords:** environment, production, staging, development, cookbook_versions

#### Environment Definition

```ruby
name 'production'
description 'Production environment'

cookbook_versions(
  'apache2' => '= 14.0.0',
  'mysql' => '= 8.4.0'
)

override_attributes(
  apache2: { port: 80 },
  mysql: { max_connections: 1000 }
)
```

Production environment with cookbook versions

- cookbook_versions locks recipe versions
- override_attributes apply to all nodes
- Located in environments/ directory

#### Access Environment in Recipe

```ruby
if node.chef_environment == 'production'
  Chef::Log.warn("Running in PRODUCTION")
  node.override['app']['debug'] = false
else
  node.override['app']['debug'] = true
end
```

Conditional logic based on environment

- node.chef_environment returns environment name
- Useful for environment-specific configs

## Roles & Environments

### Role Definition

Create and manage roles

**Keywords:** role, name, description, run_list, attributes

#### Web Server Role

```ruby
name 'web_server'
description 'Web server role for application'

run_list(
  'recipe[apache2]',
  'recipe[php]',
  'recipe[ssl]'
)

override_attributes(
  apache2: {
    port: 80,
    max_clients: 256
  }
)
```

Role with recipes and attributes

- run_list specifies recipes
- Roles can include other roles
- override_attributes apply when role assigned

#### Database Server Role

```ruby
name 'database_server'
description 'Primary database server'

run_list(
  'recipe[mysql::server]',
  'recipe[mysql::replication]'
)

override_attributes(
  mysql: {
    server_id: 1,
    max_connections: 500
  }
)
```

Database server role with MySQL recipes

- Dedicated role for database servers
- Includes replication configuration

### Role Management

Create and apply roles to nodes

**Keywords:** knife, role, create, upload, apply

#### Upload Role to Server

```bash
knife role from file roles/web_server.rb
```

#### Assign Role to Node

```bash
knife node run_list set webserver01 'role[web_server]'
```

_exec_
```bash
Set the run list to ['role[web_server]'] for node webserver01
```

Updates node's run list to include role

- Role must exist on Chef Server
- Run list can include multiple roles

#### Add Recipe to Existing Run List

```bash
knife node run_list add webserver01 'recipe[monitoring]'
```

_exec_
```bash
Run List: recipe[monitoring], role[web_server]
```

Appends recipe to node's run list

- New recipes added to end
- Can specify position with -a option

## Testing & Validation

### Test Kitchen

Local testing of cookbooks with Test Kitchen

**Keywords:** kitchen, test, local, converge, verify

#### Kitchen Configuration

```yaml
# .kitchen.yml
driver:
  name: vagrant
provisioner:
  name: chef-zero
platforms:
  - name: ubuntu-22.04
  - name: centos-8
suites:
  - name: default
    run_list:
      - recipe[apache2::default]
    attributes:
```

Test Kitchen configuration for two platforms

- Defines test platforms and driver
- run_list specifies recipes to test
- Can test multiple suites

#### Kitchen Test Lifecycle

```bash
kitchen test default-ubuntu-2204
```

_exec_
```bash
-----> Starting Test Kitchen
-----> Creating <default-ubuntu-2204>...
-----> Kitchen is finished. (15m4s)
-----> Creating <default-ubuntu-2204>...
  Instance created on 192.168.56.101
-----> Converging <default-ubuntu-2204>...
  Chef Infra Client, version 18.3.0
  Running handlers:
  Chef Infra Client finished, 7/7 resources updated
-----> Verifying <default-ubuntu-2204>...
-----> Kitchen is finished. (18m16s)
```

Complete kitchen test creates, converges, and destroys instance

- kitchen test is full cycle
- Creates VM, runs chef, runs tests, destroys
- Test output shows convergence

#### Kitchen Commands

```bash
kitchen list
kitchen create
kitchen converge
kitchen verify
kitchen destroy
```

Individual kitchen lifecycle steps

- list: shows test instances
- create: creates instances
- converge: runs chef client
- verify: runs InSpec tests
- destroy: removes instances

### ChefSpec Unit Testing

Unit test recipes with ChefSpec

**Keywords:** chefspec, unit, test, spec, rspec

#### ChefSpec Test File

```ruby
require 'spec_helper'

describe 'apache2::default' do
  let(:chef_run) do
    ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '22.04').converge(described_recipe)
  end

  it 'converges successfully' do
    expect { chef_run }.not_to raise_error
  end

  it 'installs apache2 package' do
    expect(chef_run).to install_package('apache2')
  end

  it 'enables and starts service' do
    expect(chef_run).to enable_service('apache2')
    expect(chef_run).to start_service('apache2')
  end
end
```

ChefSpec tests recipe behavior

- Uses RSpec framework
- Mock convergence without real systems
- Fast feedback on recipe changes

#### Run ChefSpec Tests

```bash
chef exec rspec
```

_exec_
```bash
apache2::default
  converges successfully
  installs apache2 package
  enables and starts service

Finished in 0.34 seconds
3 examples, 0 failures
```

ChefSpec test execution results

- Fast feedback on recipe changes
- Run before kitchen tests

### InSpec Testing

Integration testing with InSpec

**Keywords:** inspec, test, integration, verify, controls

#### InSpec Control

```ruby
control 'apache-package-1' do
  title 'Apache2 package is installed'
  desc 'Verify Apache web server package is installed'
  impact 1.0
  describe package('apache2') do
    it { should be_installed }
  end
end

control 'apache-service-1' do
  title 'Apache2 service is enabled'
  desc 'Verify Apache web server is enabled and active'
  impact 1.0
  describe service('apache2') do
    it { should be_installed }
    it { should be_enabled }
    it { should be_running }
  end
end
```

InSpec controls verify infrastructure state

- Controls test actual system state
- impact indicates severity (0-1.0)
- describe blocks contain assertions

#### Run InSpec Tests

```bash
inspec exec test/integration/default/default.rb
```

_exec_
```bash
Profile Summary: 2 successful controls, 0 control failures, 0 skipped
Test Summary: 5 successful, 0 failures, 0 skipped
```

InSpec test execution shows all controls pass

- Runs against real or test systems
- Verbose output shows each check
- Failures reported with details

## Cookbook Patterns & Workflows

### Common Patterns

Recommended patterns for cookbook development

**Keywords:** pattern, best_practice, reusable, modularity, DRY

#### Wrapper Cookbook Pattern

```ruby
# cookbooks/production-apache2/recipes/default.rb
include_recipe 'apache2::default'

node.override['apache2']['port'] = 443
node.override['apache2']['ssl_enabled'] = true

include_recipe 'apache2::ssl'
```

Wrapper cookbook includes and customizes library cookbook

- Separates library from production code
- Keeps library cookbooks generic
- Wrapper applies production config

#### Helper Methods in Libraries

```ruby
# cookbooks/apache2/libraries/helpers.rb
module Apache2Helper
  def self.apache_user
    node['apache2']['user']
  end

  def self.apache_group
    node['apache2']['group']
  end
end
```

#### Use Library Methods

```ruby
# recipe using helper
file '/etc/apache2/config' do
  owner Apache2Helper.apache_user
  group Apache2Helper.apache_group
end
```

Helpers reduce code duplication across recipes

- Libraries loaded before recipes
- Reusable logic in helper methods

### Development Workflow

Recommended cookbook development workflow

**Keywords:** workflow, develop, test, lint, upload

#### Full Development Workflow

```bash
# 1. Create cookbook
chef generate cookbook cookbooks/apache2

# 2. Edit recipes and tests
vi cookbooks/apache2/recipes/default.rb
vi cookbooks/apache2/test/integration/default/default_spec.rb

# 3. Lint code
cookstyle cookbooks/apache2 --autocorrect

# 4. Run unit tests
chef exec rspec cookbooks/apache2

# 5. Test on local VM
kitchen test

# 6. Upload to Chef Server
knife cookbook upload apache2 -o cookbooks/

# 7. Converge node
knife ssh 'role:web_server' 'sudo chef-client'
```

Complete workflow from development to production

- Iterative process with feedback loops
- Test before uploading to server
- Use version control for all changes

#### Converge Multiple Nodes

```bash
knife ssh 'role:web_server' 'sudo chef-client' -a ipaddress
```

_exec_
```bash
Starting Chef Infra Client, version 18.3.0
resolving cookbooks for run list: ["role[web_server]"]
Synchronizing Cookbooks:
  - apache2 (14.0.0)
  - php (8.0.0)
Running handlers:
Chef Infra Client finished, 8/8 resources updated in 32 seconds
```

Converge all nodes with web_server role

- knife ssh targets nodes by search query
- -a specifies attribute for connection
- Executes command on matching nodes

### Best Practices Summary

Final best practices checklist

**Keywords:** practice, checklist, design, security, maintenance

#### Recipe Best Practices

```ruby
# Good Recipe Practices

# 1. Use meaningful resource names
service 'webserver' do
  service_name 'apache2'  # Use if name differs
  action [:enable, :start]
end

# 2. Use guard clauses for idempotency
package 'nginx' do
  action :install
  not_if 'which nginx'
end

# 3. Use notifications for dependencies
file '/etc/nginx/nginx.conf' do
  content lazy { IO.read(template_file) }
  notifies :reload, 'service[nginx]'
end

# 4. Log important information
ruby_block 'app-initialized' do
  block { Chef::Log.info "Application initialized" }
end
```

Common recipe best practices

- Guard clauses ensure idempotency
- Notifications manage dependencies
- Logging aids debugging

**Best practices:**

- Always use guard clauses (only_if, not_if) for idempotency
- Use notifications instead of hardcoded dependencies
- Keep cookbooks small and focused on single responsibility
- Use attributes for configuration, not hardcoding values
- Test all changes with ChefSpec and Kitchen before production
- Use consistent naming for recipes and resources
- Document features in README.md and comments
- Use data bags for secrets, never hardcode passwords
- Version cookbook dependencies explicitly
- Run cookstyle for linting before upload

**Common errors:**

- **undefined**: undefined
- **undefined**: undefined
- **undefined**: undefined
- **undefined**: undefined
- **undefined**: undefined
- **undefined**: undefined
- **undefined**: undefined
- **undefined**: undefined
