The following items are new for Chef Infra Client 12.5 and/or are changes from previous versions. The short version: - **New way to build custom resources** The process for extending the collection of resources that are built into Chef has been simplified. It is defined only in the `/resources` directory using a simplified syntax that easily leverages the built-in collection of resources. (All of the ways you used to build custom resources still work.) - **"resource attributes" are now known as "resource properties"** In previous releases of Chef, resource properties are referred to as attributes, but this is confusing for users because nodes also have attributes. Starting with chef-client 12.5 release---and retroactively updated for all previous releases of the documentation---"resource attributes" are now referred to as "resource properties" and the word "attribute" now refers specifically to "node attributes". - **ps_credential helper to embed usernames and passwords** Use the `ps_credential` helper on Microsoft Windows to create a `PSCredential` object---security credentials, such as a user name or password---that can be used in the **dsc_script** resource. - **New Handler DSL** A new DSL exists to make it easier to use events that occur during Chef Infra Client run from recipes. The `on` method is easily associated with events. The action Chef Infra Client takes as a result of that event (when it occurs) is up to you. - **The -j / --json-attributes supports policy revisions and environments** The JSON file used by the `--json-attributes` option for Chef Infra Client may now contain the policy name and policy group associated with a policy revision or may contain the name of the environment to which the node is associated. - **verify property now uses path, not file** The `verify` property, used by file-based resources such as **remote_file** and **file**, runs user-defined correctness checks against the proposed new file before making the change. For versions of Chef Infra Client prior to 12.5, the name of the temporary file was stored as `file`; starting with chef-client 12.5, use `path`. This change is documented as a warning across all versions in any topic in which the `version` attribute is documented. - **depth property added to deploy resource** The `depth` property allows the depth of a git repository to be truncated to the specified number of versions. - **The knife ssl check subcommand supports SNI** Support for Server Name Indication (SNI) is added to the `knife ssl check` subcommand. - **Chef Policy group and name can now be part of the node object** Chef policy is a beta feature of Chef Infra Client that will eventually replace roles, environments or manually specifying the run_list. Policy group and name can now be stored as part of the node object rather than in the client.rb file. A recent version of the Chef server, such as 12.2.0 or higher, is needed to fully utilize this feature. ## Custom Resources A custom resource: - Is a simple extension of Chef that adds your own resources - Is implemented and shipped as part of a cookbook - Follows easy, repeatable syntax patterns - Effectively leverages resources that are built into Chef and/or custom Ruby code - Is reusable in the same way as resources that are built into Chef For example, Chef includes built-in resources to manage files, packages, templates, and services, but it does not include a resource that manages websites.

Note

See our documentation on [custom resources](https://docs.chef.io/custom_resources/) for more information about custom resources, including a scenario that shows how to build a `website` resource.
### Syntax A custom resource is defined as a Ruby file and is located in a cookbook's `/resources` directory. This file - Declares the properties of the custom resource - Loads current state of properties, if the resource already exists - Defines each action the custom resource may take The syntax for a custom resource is. For example: ```ruby property :property_name, RubyType, default: 'value' load_current_value do # some Ruby for loading the current state of the resource end action :action_name do # a mix of built-in Chef resources and Ruby end action :another_action_name do # a mix of built-in Chef resources and Ruby end ``` where the first action listed is the default action.

Warning

Do not use existing keywords from Chef Infra Client resource system in a custom resource, like "name". For example, `property :property_name` in the following invalid syntax: `property :name, String, default: 'thename'`.
This example `site` utilizes Chef's built in `file`, `service` and `package` resources, and includes `:create` and `:delete` actions. Since it uses built in Chef resources, besides defining the property and actions, the code is very similar to that of a recipe. ```ruby property :homepage, String, default: '

Hello world!

' action :create do package 'httpd' service 'httpd' do action [:enable, :start] end file '/var/www/html/index.html' do content homepage end end action :delete do package 'httpd' do action :delete end end ``` where - `homepage` is a property that sets the default HTML for the `index.html` file with a default value of `'

Hello world!

'` - the `action` block uses the built-in collection of resources to tell Chef Infra Client how to install Apache, start the service, and then create the contents of the file located at `/var/www/html/index.html` - `action :create` is the default resource, because it is listed first; `action :delete` must be called specifically (because it is not the default resource) Once built, the custom resource may be used in a recipe just like any of the resources that are built into Chef. The resource gets its name from the cookbook and from the file name in the `/resources` directory, with an underscore (`_`) separating them. For example, a cookbook named `exampleco` with a custom resource named `site.rb` is used in a recipe like this: ```ruby exampleco_site 'httpd' do homepage '

Welcome to the Example Co. website!

' end ``` and to delete the exampleco website, do the following: ```ruby exampleco_site 'httpd' do action :delete end ``` ## Custom Resource DSL Use the Custom Resource DSL to define property behaviors within custom resources, such as: - Loading the value of a specific property - Comparing the current property value against a desired property value - Telling Chef Infra Client when and how to make changes ### action_class Use the `action_class` block to make methods available to the actions in the custom resource. Modules with helper methods created as files in the cookbook library directory may be included. New action methods may also be defined directly in the `action_class` block. Code in the `action_class` block has access to the new_resource properties. Assume a helper module has been created in the cookbook `libraries/helper.rb` file. ```ruby module Sample module Helper def helper_method # code end end end ``` Methods may be made available to the custom resource actions by using an `action_class` block. ```ruby property file, String action :delete do helper_method FileUtils.rm(new_resource.file) if file_ex end action_class do def file_exist ::File.exist?(new_resource.file) end def file_ex ::File.exist?(new_resource.file) end require 'fileutils' include Sample::Helper end ``` ### converge_if_changed Use the `converge_if_changed` method inside an `action` block in a custom resource to compare the desired property values against the current property values (as loaded by the `load_current_value` method). Use the `converge_if_changed` method to ensure that updates only occur when property values on the system are not the desired property values and to otherwise prevent a resource from being converged. To use the `converge_if_changed` method, wrap it around the part of a recipe or custom resource that should only be converged when the current state is not the desired state: ```ruby action :some_action do converge_if_changed do # some property end end ``` For example, a custom resource defines two properties (`content` and `path`) and a single action (`:create`). Use the `load_current_value` method to load the property value to be compared, and then use the `converge_if_changed` method to tell Chef Infra Client what to do if that value is not the desired value: ```ruby property :content, String property :path, String, name_property: true load_current_value do if ::File.exist?(new_resource.path) content IO.read(new_resource.path) end end action :create do converge_if_changed do IO.write(new_resource.path, new_resource.content) end end ``` When the file does not exist, the `IO.write(path, content)` code is executed and Chef Infra Client output will print something similar to: ```bash Recipe: recipe_name::block * resource_name[blah] action create - update my_file[blah] - set content to "hola mundo" (was "hello world") ``` **Multiple Properties** The `converge_if_changed` method may be used multiple times. The following example shows how to use the `converge_if_changed` method to compare the multiple desired property values against the current property values (as loaded by the `load_current_value` method). ```ruby property :path, String, name_property: true property :content, String property :mode, String load_current_value do if ::File.exist?(new_resource.path) content IO.read(new_resource.path) mode ::File.stat(new_resource.path).mode end end action :create do converge_if_changed :content do IO.write(new_resource.path, new_resource.content) end converge_if_changed :mode do ::File.chmod(new_resource.mode, new_resource.path) end end ``` where - `load_current_value` loads the property values for both `content` and `mode` - A `converge_if_changed` block tests only `content` - A `converge_if_changed` block tests only `mode` Chef Infra Client will only update the property values that require updates and will not make changes when the property values are already in the desired state ### default_action The default action in a custom resource is, by default, the first action listed in the custom resource. For example, action `aaaaa` is the default resource: ```ruby property :property_name, RubyType, default: 'value' ... action :aaaaa do # the first action listed in the custom resource end action :bbbbb do # the second action listed in the custom resource end ``` The `default_action` method may also be used to specify the default action. For example: ```ruby property :property_name, RubyType, default: 'value' default_action :aaaaa action :aaaaa do # the first action listed in the custom resource end action :bbbbb do # the second action listed in the custom resource end ``` defines action `aaaaa` as the default action. If `default_action :bbbbb` is specified, then action `bbbbb` is the default action. Use this method for clarity in custom resources, if deliberately stating the default resource is desired, or to specify a default action that is not listed first in the custom resource. ### load_current_value Use the `load_current_value` method to load the specified property values from the node, and then use those values when the resource is converged. This method may take a block argument. Use the `load_current_value` method to guard against property values being replaced. For example: ```ruby load_current_value do if ::File.exist?('/var/www/html/index.html') homepage IO.read('/var/www/html/index.html') end if ::File.exist?('/var/www/html/404.html') page_not_found IO.read('/var/www/html/404.html') end end ``` This ensures the values for `homepage` and `page_not_found` are not changed to the default values when Chef Infra Client configures the node. ### new_resource.property Custom resources are designed to use core resources that are built into Chef. In some cases, it may be necessary to specify a property in the custom resource that is the same as a property in a core resource, for the purpose of overriding that property when used with the custom resource. For example: ```ruby resource_name :node_execute property :command, String, name_property: true property :version, String Useful properties from the `execute` resource property :cwd, String property :environment, Hash, default: {} property :user, [String, Integer] property :sensitive, [true, false], default: false prefix = '/opt/languages/node' load_current_value do current_value_does_not_exist! if node.run_state['nodejs'].nil? version node.run_state['nodejs'][:version] end action :run do execute 'execute-node' do cwd cwd environment environment user user sensitive sensitive # gsub replaces 10+ spaces at the beginning of the line with nothing command <<-CODE.gsub(/^ {10}/, '') #{prefix}/#{new_resource.version}/#{command} CODE end end ``` where the `property :cwd`, `property :environment`, `property :user`, and `property :sensitive` are identical to properties in the **execute** resource, embedded as part of the `action :run` action. Because both the custom properties and the **execute** properties are identical, this will result in an error message similar to: ```ruby # ArgumentError wrong number of arguments (0 for 1) ``` To prevent this behavior, use `new_resource.` to tell Chef Infra Client to process the properties from the core resource instead of the properties in the custom resource. For example: ```ruby resource_name :node_execute property :command, String, name_property: true property :version, String Useful properties from the `execute` resource property :cwd, String property :environment, Hash, default: {} property :user, [String, Integer] property :sensitive, [true, false], default: false prefix = '/opt/languages/node' load_current_value do current_value_does_not_exist! if node.run_state['nodejs'].nil? version node.run_state['nodejs'][:version] end action :run do execute 'execute-node' do cwd new_resource.cwd environment new_resource.environment user new_resource.user sensitive new_resource.sensitive # gsub replaces 10+ spaces at the beginning of the line with nothing command <<-CODE.gsub(/^ {10}/, '') #{prefix}/#{new_resource.version}/#{new_resource.command} CODE end end ``` where `cwd new_resource.cwd`, `environment new_resource.environment`, `user new_resource.user`, and `sensitive new_resource.sensitive` correctly use the properties of the **execute** resource and not the identically-named override properties of the custom resource. ### property Use the `property` method to define properties for the custom resource. The syntax is: ```ruby property :property_name, ruby_type, default: 'value', parameter: 'value' ``` where - `:property_name` is the name of the property - `ruby_type` is the optional Ruby type or array of types, such as `String`, `Integer`, `true`, or `false` - `default: 'value'` is the optional default value loaded into the resource - `parameter: 'value'` optional parameters For example, the following properties define `username` and `password` properties with no default values specified: ```ruby property :username, String property :password, String ``` **ruby_type** The property ruby_type is a positional parameter. Use to ensure a property value is of a particular ruby class, such as `true`, `false`, `nil`, `String`, `Array`, `Hash`, `Integer`, `Symbol`. Use an array of ruby classes to allow a value to be of more than one type. For example: ```ruby property :aaaa, String ``` ```ruby property :bbbb, Integer ``` ```ruby property :cccc, Hash ``` ```ruby property :dddd, [true, false] ``` ```ruby property :eeee, [String, nil] ``` ```ruby property :ffff, [Class, String, Symbol] ``` ```ruby property :gggg, [Array, Hash] ``` **validators** A validation parameter is used to add zero (or more) validation parameters to a property.
Parameter Description

:callbacks

Use to define a collection of unique keys and values (a ruby hash) for which the key is the error message and the value is a lambda to validate the parameter. For example:

callbacks: {
             'should be a valid non-system port' => lambda {
               |p| p > 1024 && p < 65535
             }
           }

:default

Use to specify the default value for a property. For example:

default: 'a_string_value'
default: 123456789
default: []
default: ()
default: {}

:equal_to

Use to match a value with ==. Use an array of values to match any of those values with ==. For example:

equal_to: [true, false]
equal_to: ['php', 'perl']

:regex

Use to match a value to a regular expression. For example:

regex: [ /^([a-z]|[A-Z]|[0-9]|_|-)+$/, /^\d+$/ ]

:required

Indicates that a property is required. For example:

required: true

:respond_to

Use to ensure that a value has a given method. This can be a single method name or an array of method names. For example:

respond_to: valid_encoding?
Some examples of combining validation parameters: ```ruby property :spool_name, String, regex: /$\w+/ ``` ```ruby property :enabled, equal_to: [true, false, 'true', 'false'], default: true ``` **desired_state** Add `desired_state:` to set the desired state property for a resource. This value may be `true` or `false`, and all properties default to true. - When `true`, the state of the property is determined by the state of the system - When `false`, the value of the property impacts how the resource executes, but it is not determined by the state of the system. For example, if you were to write a resource to create volumes on a cloud provider you would need define properties such as `volume_name`, `volume_size`, and `volume_region`. The state of these properties would determine if your resource needed to converge or not. For the resource to function you would also need to define properties such as `cloud_login` and `cloud_password`. These are necessary properties for interacting with the cloud provider, but their state has no impact on decision to converge the resource or not, so you would set `desired_state` to `false` for these properties. ```ruby property :volume_name, String property :volume_size, Integer property :volume_region, String property :cloud_login, String, desired_state: false property :cloud_password, String, desired_state: false ``` **identity** Add `identity:` to set a resource to a particular set of properties. This value may be `true` or `false`. - When `true`, data for that property is returned as part of the resource data set and may be available to external applications, such as reporting - When `false`, no data for that property is returned. If no properties are marked `true`, the property that defaults to the `name` of the resource is marked `true`. For example, the following properties define `username` and `password` properties with no default values specified, but with `identity` set to `true` for the user name: ```ruby property :username, String, identity: true property :password, String ``` **Block Arguments** Any properties that are marked `identity: true` or `desired_state: false` will be available from `load_current_value`. If access to other properties of a resource is needed, use a block argument that contains all of the properties of the requested resource. For example: ```ruby resource_name :file load_current_value do |new_resource| puts "The user typed content = #{new_resource.content} in the resource" end ``` ### property_is_set? Use the `property_is_set?` method to check if the value for a property is set. The syntax is: ```ruby property_is_set?(:property_name) ``` The `property_is_set?` method will return `true` if the property is set. For example, the following custom resource creates and/or updates user properties, but not their password. The `property_is_set?` method checks if the user has specified a password and then tells Chef Infra Client what to do if the password is not identical: ```ruby action :create do converge_if_changed do shell_out!("rabbitmqctl create_or_update_user #{username} --prop1 #{prop1} ... ") end if property_is_set?(:password) if shell_out("rabbitmqctl authenticate_user #{username} #{password}").error? converge_by "Updating password for user #{username} ..." do shell_out!("rabbitmqctl update_user #{username} --password #{password}") end end end end ``` ### provides Use the `provides` method to associate a custom resource with the Recipe DSL on different operating systems. When multiple custom resources use the same DSL, specificity rules are applied to determine the priority, from highest to lowest: 1. provides :resource_name, platform_version: '0.1.2' 2. provides :resource_name, platform: 'platform_name' 3. provides :resource_name, platform_family: 'platform_family' 4. provides :resource_name, os: 'operating_system' 5. provides :resource_name For example: ```ruby provides :my_custom_resource, platform: 'redhat' do |node| node['platform_version'].to_i >= 7 end provides :my_custom_resource, platform: 'redhat' provides :my_custom_resource, platform_family: 'rhel' provides :my_custom_resource, os: 'linux' provides :my_custom_resource ``` This allows you to use multiple custom resources files that provide the same resource to the user, but for different operating systems or operation system versions. With this you can eliminate the need for platform or platform version logic within your resources. **override** Chef will warn you if the Chef Infra Language is provided by another custom resource or built-in resource. For example: ```ruby class X < Chef::Resource provides :file end class Y < Chef::Resource provides :file end ``` This will emit a warning that `Y` is overriding `X`. To disable this warning, use `override: true`: ```ruby class X < Chef::Resource provides :file end class Y < Chef::Resource provides :file, override: true end ``` ### reset_property Use the `reset_property` method to clear the value for a property as if it had never been set, and then use the default value. For example, to clear the value for a property named `password`: ```ruby reset_property(:password) ``` ## Definition vs. Resource The following examples show: 1. A definition 2. The same definition rewritten as a custom resource 3. The same definition, rewritten again to use a [common resource property](/resource_common/) ### As a Definition The following definition processes unique hostnames and ports, passed on as parameters: ```ruby define :host_porter, :port => 4000, :hostname => nil do params[:hostname] ||= params[:name] directory '/etc/#{params[:hostname]}' do recursive true end file '/etc/#{params[:hostname]}/#{params[:port]}' do content 'some content' end end ``` ### As a Resource The definition is improved by rewriting it as a custom resource: ```ruby property :port, Integer, default: 4000 property :hostname, String, name_property: true action :create do directory "/etc/#{hostname}" do recursive true end file "/etc/#{hostname}/#{port}" do content 'some content' end end ``` Once built, the custom resource may be used in a recipe just like the any of the resources that are built into Chef. The resource gets its name from the cookbook and from the file name in the `/resources` directory, with an underscore (`_`) separating them. For example, a cookbook named `host` with a custom resource in the `/resources` directory named `porter.rb`. Use it in a recipe like this: ```ruby host_porter node['hostname'] do port 4000 end ``` or: ```ruby host_porter 'www1' do port 4001 end ``` ### Common Properties Unlike definitions, custom resources are able to use [common resource properties](/resource_common/). For example, `only_if`: ```ruby host_porter 'www1' do port 4001 only_if '{ node['hostname'] == 'foo.bar.com' }' end ``` ## ps_credential Helper Use the `ps_credential` helper to embed a `PSCredential` object--- [a set of security credentials, such as a user name or password](https://technet.microsoft.com/en-us/magazine/ff714574.aspx) ---within a script, which allows that script to be run using security credentials. For example, assuming the `CertificateID` is configured in the local configuration manager, the `SeaPower1@3` object is created and embedded within the `seapower-user` script: ```ruby dsc_script 'seapower-user' do code <<-EOH User AlbertAtom { UserName = 'AlbertAtom' Password = #{ps_credential('SeaPower1@3')} } EOH configuration_data <<-EOH @{ AllNodes = @( @{ NodeName = "localhost"; CertificateID = 'A8D1234559F349F7EF19104678908F701D4167' } ) } EOH end ``` ## Handler DSL Use the Handler DSL to attach a callback to an event. If the event occurs during Chef Infra Client run, the associated callback is executed. For example: - Sending email if a chef-client run fails - Sending a notification to chat application if an audit run fails - Aggregating statistics about resources updated during a chef-client runs to StatsD ### on Method Use the `on` method to associate an event type with a callback. The callback defines what steps are taken if the event occurs during Chef Client run and is defined using arbitrary Ruby code. The syntax is as follows: ```ruby Chef.event_handler do on :event_type do # some Ruby end end ``` where - `Chef.event_handler` declares a block of code within a recipe that is processed when the named event occurs during a chef-client run - `on` defines the block of code that will tell Chef Infra Client how to handle the event - `:event_type` is a valid exception event type, such as `:run_start`, `:run_failed`, `:converge_failed`, `:resource_failed`, or `:recipe_not_found` For example: ```bash Chef.event_handler do on :converge_start do puts "Ohai! I have started a converge." end end ``` ### Example: Send Email Use the `on` method to create an event handler that sends email when Chef Infra Client run fails. This will require: - A way to tell Chef Infra Client how to send email - An event handler that describes what to do when the `:run_failed` event is triggered - A way to trigger the exception and test the behavior of the event handler

Note

See our documentation on the Handler DSL for more information about using event handlers in recipes.
**Define How Email is Sent** Use a library to define the code that sends email when a chef-client run fails. Name the file `helper.rb` and add it to a cookbook's `/libraries` directory: ```ruby require 'net/smtp' module HandlerSendEmail class Helper def send_email_on_run_failure(node_name) message = "From: Chef \n" message << "To: Grant \n" message << "Subject: Chef run failed\n" message << "Date: #{Time.now.rfc2822}\n\n" message << "Chef run failed on #{node_name}\n" Net::SMTP.start('localhost', 25) do |smtp| smtp.send_message message, 'chef@chef.io', 'grantmc@chef.io' end end end end ``` **Add the Handler** Invoke the library helper in a recipe: ```ruby Chef.event_handler do on :run_failed do HandlerSendEmail::Helper.new.send_email_on_run_failure( Chef.run_context.node.name ) end end ``` - Use `Chef.event_handler` to define the event handler - Use the `on` method to specify the event type Within the `on` block, tell Chef Infra Client how to handle the event when it's triggered. **Test the Handler** Use the following code block to trigger the exception and have Chef Client send email to the specified email address: ```ruby ruby_block 'fail the run' do block do fail 'deliberately fail the run' end end ``` ## New Resource Properties The following property is new for the **deploy** resource:
Property Description

depth

Ruby Type: Integer

The depth of a git repository, truncated to the specified number of revisions.

## Specify Policy Revision Use the following command to specify a policy revision: ```bash chef client -j JSON ``` where the JSON file is similar to: ```javascript { "policy_name": "appserver", "policy_group": "staging" } ``` Or use the following settings to specify a policy revision in the client.rb file:
Setting Description
policy_group The name of a policy group that exists on the Chef server.
policy_name The name of a policy, as identified by the name setting in a Policyfile.rb file.
## New Configuration Settings The following settings are new for the client.rb file and enable the use of policy files:
Setting Description
named_run_list The run-list associated with a policy file.
policy_group The name of a policy group that exists on the Chef server. (See "Specify Policy Revision" in this readme for more information.)
policy_name The name of a policy, as identified by the name setting in a Policyfile.rb file. (See "Specify Policy Revision" in this readme for more information.)
## chef-client Options The following options are new or updated for Chef Infra Client executable and enable the use of policy files: `-n NAME`, `--named-run-list NAME` : The run-list associated with a policy file. `-j PATH`, `--json-attributes PATH` : This option now supports using a JSON file to associate a policy revision. Use this option to use policy files by specifying a JSON file that contains the following settings:
Setting Description
policy_group The name of a policy group that exists on the Chef server.
policy_name The name of a policy, as identified by the name setting in a Policyfile.rb file.
For example: ```javascript { "policy_name": "appserver", "policy_group": "staging" } ``` This option also supports using a JSON file to associate an environment: Use this option to set the `chef_environment` value for a node.

Note

Any environment specified for `chef_environment` by a JSON file will take precedence over an environment specified by the `--environment` option when both options are part of the same command.
For example, run the following: ```bash chef-client -j /path/to/file.json ``` where `/path/to/file.json` is similar to: ```javascript { "chef_environment": "pre-production" } ``` This will set the environment for the node to `pre-production`.