# # Copyright:: Copyright (c) Chef Software Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require_relative "../resource" require_relative "../dist" class Chef class Resource class ChefClientScheduledTask < Chef::Resource unified_mode true provides :chef_client_scheduled_task description "Use the **chef_client_scheduled_task** resource to setup the #{Chef::Dist::PRODUCT} to run as a Windows scheduled task. This resource will also create the specified log directory if it doesn't already exist." introduced "16.0" examples <<~DOC **Setup #{Chef::Dist::PRODUCT} to run using the default 30 minute cadence**: ```ruby chef_client_scheduled_task "Run #{Chef::Dist::PRODUCT} as a scheduled task" ``` **Run #{Chef::Dist::PRODUCT} on system start**: ```ruby chef_client_scheduled_task '#{Chef::Dist::PRODUCT} on start' do frequency 'onstart' end ``` **Run #{Chef::Dist::PRODUCT} with extra options passed to the client**: ```ruby chef_client_scheduled_task "Run an override recipe" do daemon_options ["--override-runlist mycorp_base::default"] end ``` **Run #{Chef::Dist::PRODUCT} daily at 01:00 am, specifying a named run-list**: ```ruby chef_client_scheduled_task "Run chef-client named run-list daily" do frequency 'daily' start_time '01:00' daemon_options ['-n audit_only'] end ``` DOC resource_name :chef_client_scheduled_task property :task_name, String, description: "The name of the scheduled task to create.", default: Chef::Dist::CLIENT property :user, String, description: "The name of the user that #{Chef::Dist::PRODUCT} runs as.", default: "System", sensitive: true property :password, String, sensitive: true, description: "The password for the user that #{Chef::Dist::PRODUCT} runs as." property :frequency, String, description: "Frequency with which to run the task.", default: "minute", equal_to: %w{minute hourly daily monthly once on_logon onstart on_idle} property :frequency_modifier, [Integer, String], coerce: proc { |x| Integer(x) }, callbacks: { "should be a positive number" => proc { |v| v > 0 } }, description: "Numeric value to go with the scheduled task frequency", default: lazy { frequency == "minute" ? 30 : 1 }, default_description: "30 if frequency is 'minute', 1 otherwise" property :accept_chef_license, [true, false], description: "Accept the Chef Online Master License and Services Agreement. See ", default: false property :start_date, String, description: "The start date for the task in m:d:Y format (ex: 12/17/2020).", regex: [%r{^[0-1][0-9]\/[0-3][0-9]\/\d{4}$}] property :start_time, String, description: "The start time for the task in HH:mm format (ex: 14:00). If the frequency is minute default start time will be Time.now plus the frequency_modifier number of minutes.", regex: [/^\d{2}:\d{2}$/] property :splay, [Integer, String], coerce: proc { |x| Integer(x) }, callbacks: { "should be a positive number" => proc { |v| v > 0 } }, description: "A random number of seconds between 0 and X to add to interval so that all #{Chef::Dist::CLIENT} commands don't execute at the same time.", default: 300 property :run_on_battery, [true, false], description: "Run the #{Chef::Dist::PRODUCT} task when the system is on batteries.", default: true property :config_directory, String, description: "The path of the config directory.", default: Chef::Dist::CONF_DIR property :log_directory, String, description: "The path of the directory to create the log file in.", default: lazy { |r| "#{r.config_directory}/log" }, default_description: "CONFIG_DIRECTORY/log" property :log_file_name, String, description: "The name of the log file to use.", default: "client.log" property :chef_binary_path, String, description: "The path to the #{Chef::Dist::CLIENT} binary.", default: "C:/#{Chef::Dist::LEGACY_CONF_DIR}/#{Chef::Dist::DIR_SUFFIX}/bin/#{Chef::Dist::CLIENT}" property :daemon_options, Array, description: "An array of options to pass to the #{Chef::Dist::CLIENT} command.", default: lazy { [] } action :add do # TODO: Replace this with a :create_if_missing action on directory when that exists unless Dir.exist?(new_resource.log_directory) directory new_resource.log_directory do inherits true recursive true action :create end end # According to https://docs.microsoft.com/en-us/windows/desktop/taskschd/schtasks, # the :once, :onstart, :onlogon, and :onidle schedules don't accept schedule modifiers windows_task new_resource.task_name do run_level :highest command full_command user new_resource.user password new_resource.password frequency new_resource.frequency.to_sym frequency_modifier new_resource.frequency_modifier if frequency_supports_frequency_modifier? start_time new_resource.start_time start_day new_resource.start_date unless new_resource.start_date.nil? random_delay new_resource.splay if frequency_supports_random_delay? disallow_start_if_on_batteries new_resource.splay unless new_resource.run_on_battery action %i{create enable} end end action :remove do windows_task new_resource.task_name do action :delete end end action_class do # # The full command to run in the scheduled task # # @return [String] # def full_command # Fetch path of cmd.exe through environment variable comspec cmd_path = ENV["COMSPEC"] "#{cmd_path} /c \'#{client_cmd}\'" end # Build command line to pass to cmd.exe # # @return [String] def client_cmd cmd = new_resource.chef_binary_path.dup cmd << " -L #{::File.join(new_resource.log_directory, new_resource.log_file_name)}" cmd << " -c #{::File.join(new_resource.config_directory, "client.rb")}" # Add custom options cmd << " #{new_resource.daemon_options.join(" ")}" if new_resource.daemon_options.any? cmd << " --chef-license accept" if new_resource.accept_chef_license cmd end # # not all frequencies in the windows_task resource support random_delay # # @return [boolean] # def frequency_supports_random_delay? %w{once minute hourly daily weekly monthly}.include?(new_resource.frequency) end # # not all frequencies in the windows_task resource support frequency_modifier # # @return [boolean] # def frequency_supports_frequency_modifier? # these are the only ones that don't !%w{once on_logon onstart on_idle}.include?(new_resource.frequency) end end end end end