Chef Infra JSON and YAML recipes
JSON and YAML recipes let you define Chef Infra resources using a no-code syntax instead of Ruby. This feature makes Chef Infra recipes more accessible to users who prefer declarative YAML or JSON syntax over Ruby code.
YAML and JSON recipes simplify defining Chef resources for basic use cases. While they have significant limitations compared to Ruby recipes, they’re valuable for teams that prefer YAML syntax and don’t need advanced Chef DSL features. For complex scenarios involving dynamic logic, node attributes, or resource relationships, use Ruby recipes.
For most production environments, use a hybrid approach: YAML or JSON recipes for simple static configurations and Ruby recipes for complex logic. This approach balances simplicity and functionality.
For information about Ruby recipes, see the Ruby recipe documentation.
Support
We introduced YAML recipes in Chef Infra Client 16.0. We added support for YAML recipes with the .yml
file extension in Infra Client 17.2.29. We added support for JSON recipes in Chef Infra Client 18.8.
Create a JSON or YAML recipe
To create a JSON or YAML recipe, follow these steps:
Create a JSON or YAML file for your recipe in the same locations as Ruby recipes:
Standard recipe location:
cookbooks/cookbook_name/recipes/default.yml
cookbooks/cookbook_name/recipes/default.yaml
cookbooks/cookbook_name/recipes/default.json
Named recipes:
cookbooks/cookbook_name/recipes/web.yml
cookbooks/cookbook_name/recipes/database.yaml
cookbooks/cookbook_name/recipes/app.json
Root-level recipe alias (acts as the default recipe):
cookbooks/cookbook_name/recipe.yml
cookbooks/cookbook_name/recipe.yaml
cookbooks/cookbook_name/recipe.json
Note
Creating more than one recipe with the same filename but different file extensions isn’t supported. For example,default.yaml
anddefault.yml
.Define your recipe with the top-level
resources
key containing an array of items where each item has the following:type
: The Chef resource type (string)name
: The resource name/identifier (string)- resource-specific actions and properties as key-value pairs
For example:
resources: - type: "package" name: "nginx" action: "install" version: "1.18.0" - type: "service" name: "nginx" action: ["enable", "start"]
{ "resources": [ { "type": "package", "name": "nginx", "action": "install", "version": "1.18.0" }, { "type": "service", "name": "nginx", "action": [ "enable", "start" ] } ] }
In this example:
- the
package
resource uses theinstall
action and theversion
property to install Nginx 1.18.0. - the
service
resource uses theenable
andstart
actions to enable and start Nginx.
Examples
Basic file management
Use the directory
resource to create the /opt/app_name
directory and apply owner and group permissions to the directory. Use the file
resource to create the /opt/app_name/config.txt
file, add text to the file, and apply file owner and group permissions to the file.
---
resources:
- type: "directory"
name: "/opt/app_name"
owner: "app_name"
group: "app_name"
mode: "0755"
recursive: true
- type: "file"
name: "/opt/app_name/config.txt"
content: "This is a configuration file"
owner: "app_name"
group: "app_name"
mode: "0644"
{
"resources": [
{
"type": "directory",
"name": "/opt/app_name",
"owner": "app_name",
"group": "app_name",
"mode": "0755",
"recursive": true
},
{
"type": "file",
"name": "/opt/app_name/config.txt",
"content": "This is a configuration file",
"owner": "app_name",
"group": "app_name",
"mode": "0644"
}
]
}
Package and service management
Use the package
resource to install Nginx and curl. Then use the service
resource to enable and start Nginx.
---
resources:
- type: "package"
name: "nginx"
action: "install"
- type: "package"
name: "curl"
action: "install"
- type: "service"
name: "nginx"
action: ["enable", "start"]
{
"resources": [
{
"type": "package",
"name": "nginx",
"action": "install"
},
{
"type": "package",
"name": "curl",
"action": "install"
},
{
"type": "service",
"name": "nginx",
"action": [
"enable",
"start"
]
}
]
}
User management
Use the group
resource to create a group called “developers” and the user
resource to create a user, give them properties, and assign them to the developers group.
---
resources:
- type: "group"
name: "developers"
gid: 3000
- type: "user"
name: "alice"
uid: 2001
gid: 3000
home: "/home/alice"
shell: "/bin/bash"
action: "create"
{
"resources": [
{
"type": "group",
"name": "developers",
"gid": 3000
},
{
"type": "user",
"name": "alice",
"uid": 2001,
"gid": 3000,
"home": "/home/alice",
"shell": "/bin/bash",
"action": "create"
}
]
}
Template with static variables
Use the template
resource create the /etc/app_name/config.yml
file using the config.yml.erb
template.
---
resources:
- type: "template"
name: "/etc/app_name/config.yml"
source: "config.yml.erb"
owner: "root"
group: "root"
mode: "0644"
{
"resources": [
{
"type": "template",
"name": "/etc/app_name/config.yml",
"source": "config.yml.erb",
"owner": "root",
"group": "root",
"mode": "0644"
}
]
}
Guards
Some common resource functionality is also supported, as long as the value of a property can be expressed as one of the four primitive types (string, integer, boolean, array). That means it’s possible to use only_if
or not_if
guards as long as they shell out to Bash or PowerShell and aren’t passed a Ruby block.
For example, this is supported:
resources:
- type: "directory"
name: "/var/www/html"
only_if: "which apache2"
{
"resources": [
{
"type": "directory",
"name": "/var/www/html",
"only_if": "which apache2"
}
]
}
Ruby blocks aren’t supported:
# Can't be expressed in YAML - Ruby blocks not supported
resources:
- type: "directory"
name: "/var/www/html"
only_if: "{ ::File.exist?('/usr/sbin/apache2') }"
Convert a YAML recipe to Ruby
Use the knife yaml convert
command to convert YAML recipes to Ruby:
knife yaml convert recipes/default.yml recipes/default.rb
Converting from Ruby to YAML or JSON isn’t supported due to their limitations.
YAML and JSON recipe limitations
Chef Infra YAML and JSON recipes have the following limitations.
No Ruby code blocks
YAML and JSON recipes can’t include Ruby code blocks, which limits their functionality compared to Ruby recipes:
# Can't be expressed in YAML - Ruby blocks not supported
template "/etc/nginx/nginx.conf" do
source "nginx.conf.erb"
variables({
worker_processes: node['cpu']['total']
})
notifies :restart, "service[nginx]", :delayed
only_if { node['platform'] == 'ubuntu' }
end
No conditional logic
YAML and JSON recipes can’t include conditional logic like if
, unless
, only_if
, or not_if
with Ruby expressions:
# Can't include complex conditionals
resources:
- type: "package"
name: "nginx"
# Can't do: only_if { node['platform'] == 'ubuntu' }
No node attribute access
YAML and JSON recipes can’t directly access node attributes or perform Ruby evaluations:
# Can't access node attributes dynamically
resources:
- type: "user"
name: "webapp"
# Can't do: home "/home/#{node['webapp']['user']}"
home: "/home/webapp" # Must be static
No resource notifications
YAML and JSON recipes can’t express complex resource relationships and notifications:
# Can't express notifications between resources
resources:
- type: "template"
name: "/etc/nginx/nginx.conf"
source: "nginx.conf.erb"
# Can't do: notifies :restart, "service[nginx]", :delayed
No include or require functionality
YAML and JSON recipes can’t include other recipes or libraries:
# Can't include other recipes
# include_recipe "cookbook::other_recipe"
Troubleshooting
Missing resources
key
Chef Infra Client returns this error if a recipe is missing the top-level resources
hash.
ArgumentError: YAML recipe 'recipes/default.yml' must contain a top-level 'resources' hash (YAML sequence), i.e. 'resources:'
Single document limitation
YAML recipes support only one YAML document in each file. Multiple documents separated by ---
aren’t allowed:
---
resources:
- type: "file"
name: "/tmp/file1.txt"
---
resources:
- type: "file"
name: "/tmp/file2.txt"
Chef Infra Client returns the following error with multiple documents in one file:
ArgumentError: YAML recipe 'recipes/default.yml' contains multiple documents, only one is supported
Ambiguous file extensions
Chef Infra Client returns this error if two recipes have the same filename with different file extensions. For example, default.yaml
and default.yml
.
Chef::Exceptions::AmbiguousYAMLFile: Found both default.yml and default.yaml in cookbook, update the cookbook to remove one