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.ymlcookbooks/cookbook_name/recipes/default.yamlcookbooks/cookbook_name/recipes/default.json
Named recipes:
cookbooks/cookbook_name/recipes/web.ymlcookbooks/cookbook_name/recipes/database.yamlcookbooks/cookbook_name/recipes/app.json
Root-level recipe alias (acts as the default recipe):
cookbooks/cookbook_name/recipe.ymlcookbooks/cookbook_name/recipe.yamlcookbooks/cookbook_name/recipe.json
Note
Creating more than one recipe with the same filename but different file extensions isn’t supported. For example,default.yamlanddefault.yml.Define your recipe with the top-level
resourceskey 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
packageresource uses theinstallaction and theversionproperty to install Nginx 1.18.0. - the
serviceresource uses theenableandstartactions 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