class Jamf::Script

A Script in the JSS.

As of Casper 9.4, the script contents as stored in the database are accessible via the API

According to Jamf as of early 2021, it has been some years now since its been possible to store script contents on a dist. point - they are all always in the database.

Use the {#run} method to run the script on the local machine.

@see Jamf::APIObject

Constants

CATEGORY_DATA_TYPE

How is the category stored in the API data?

CATEGORY_SUBSET

Where is the Category in the API JSON?

DEFAULT_PRIORITY

which is default?

DIST_POINT_SCRIPTS_FOLDER

The script storage folder on the distribution point, if used

OBJECT_HISTORY_OBJECT_TYPE

the object type for this object in the object history table. See {APIObject#add_object_history_entry}

PARAMETER_KEYS

The keys used in the @parameters Hash

PRIORITIES

Priority to use for running the script in relation to other actions during imaging

RSRC_BASE

The base for REST resources of this class

RSRC_LIST_KEY

the hash key used for the JSON list output of all objects in the JSS

RSRC_OBJECT_KEY

The hash key used for the JSON object output. It’s also used in various error messages

Attributes

code[R]

@return {String] the actual code for this script, if it’s stored in the database.

contents[R]

@return {String] the actual code for this script, if it’s stored in the database.

filename[R]

@return [String] the file name of the script, if stored in a dist. point

info[R]

@return [String] the info field for this script

notes[R]

@return [String] the notes field for this script

os_requirements[R]

@return [Array<String>] the OS versions this can be installed onto. For all minor versions, the format is 10.5.x

oses[R]

@return [Array<String>] the OS versions this can be installed onto. For all minor versions, the format is 10.5.x

parameter_descriptions[R]

@return [Hash] descriptions of parameters 4-11. Parameters 1-3 are predefined as target drive, computer name, and username

parameter_labels[R]

@return [Hash] descriptions of parameters 4-11. Parameters 1-3 are predefined as target drive, computer name, and username

parameters[R]

@return [Hash] descriptions of parameters 4-11. Parameters 1-3 are predefined as target drive, computer name, and username

priority[R]

@return [String] either ‘Before’ or ‘After’ or “At Reboot”.

script_contents[R]

@return {String] the actual code for this script, if it’s stored in the database.

script_contents_encoded[R]

@return [String] the code for this script, Base64-encoded

Public Class Methods

new(**args) click to toggle source
Calls superclass method Jamf::APIObject::new
    # File lib/jamf/api/classic/api_objects/script.rb
136 def initialize(**args)
137   super
138 
139   @filename = @init_data[:filename] || @name
140   @info = @init_data[:info]
141   @notes = @init_data[:notes]
142   @os_requirements = @init_data[:os_requirements] ? JSS.to_s_and_a(@init_data[:os_requirements])[:arrayform] : []
143   @parameters = @init_data[:parameters] ? @init_data[:parameters] : {}
144   @priority = @init_data[:priority] || DEFAULT_PRIORITY
145   @script_contents = @init_data[:script_contents]
146   @script_contents_encoded = @init_data[:script_contents_encoded]
147   if @script_contents && @script_contents_encoded.to_s.empty?
148     @script_contents_encoded = Base64.encode64 @script_contents
149   end
150 end

Public Instance Methods

code=(new_val)
Alias for: script_contents=
contents=(new_val)
Alias for: script_contents=
filename=(new_val) click to toggle source

Change the script filename

Setting it to nil will make it match the script name

@param new_val the new filename

@return [void]

@note This method does NOT change the filename on the distribution point

if that's where you store your scripts.
    # File lib/jamf/api/classic/api_objects/script.rb
163 def filename=(new_val)
164   new_val = nil if new_val == ''
165   new_val = @name unless new_val
166 
167   return nil if new_val == @filename
168 
169   @filename = new_val
170   @need_to_update = true
171 end
info=(new_val) click to toggle source

Change the info field

@param new_val the new info

@return [void]

    # File lib/jamf/api/classic/api_objects/script.rb
257 def info=(new_val)
258   return nil if new_val == @info
259   # line breaks should be \r
260   new_val = new_val.to_s.tr("\n", "\r")
261   @info = new_val
262   @need_to_update = true
263 end
name=(new_val) click to toggle source

Change the script’s display name

If the filename is the same as the name, the filename will be changed also

@param new_val the new display name

@return [void]

    # File lib/jamf/api/classic/api_objects/script.rb
181 def name=(new_val)
182   new_val = new_val.to_s
183   return if new_val == @name
184 
185   raise Jamf::MissingDataError, "Name can't be empty" if new_val.empty?
186   raise Jamf::AlreadyExistsError, "A script already exists with the name '#{new_val}'" if Jamf::Script.all_names.include? new_val
187 
188   # if the filename matches the name, change that too.
189   @filename = new_val if @filename == @name
190   @name = new_val
191 
192   # if our REST resource is based on the name, update that too
193   @rest_rsrc = "#{RSRC_BASE}/name/#{CGI.escape @name.to_s}" if @rest_rsrc.include? '/name/'
194   @need_to_update = true
195 end
notes=(new_val) click to toggle source

Change the notes field

@param new_val the new notes

@return [void]

    # File lib/jamf/api/classic/api_objects/script.rb
271 def notes=(new_val)
272   return nil if new_val == @notes
273   # line breaks should be \r
274   new_val = new_val.to_s.tr("\n", "\r")
275   @notes = new_val
276   @need_to_update = true
277 end
os_requirements=(new_val) click to toggle source

Change the os_requirements

Minumum OS’s can be specified as a string using the notation “>=10.6.7” See the {JSS.expand_min_os} method for details.

@param new_val[String, Array<String>] the new os requirements as a comma-separted String or an Array of Strings

@return [void]

@example String value

myscript.os_requirements "10.5, 10.5.3, 10.6.x"

@example Array value

ok_oses = ['10.5', '10.5.3', '10.6.x']
myscript.os_requirements ok_oses

@example Minimum OS

myscript.os_requirements ">=10.7.5"
    # File lib/jamf/api/classic/api_objects/script.rb
216 def os_requirements=(new_val)
217   # nil should be an empty array
218   new_val = [] if new_val.to_s.empty?
219 
220   # if any value starts with >=, expand it
221   case new_val
222   when String
223     new_val = JSS.expand_min_os(new_val) if new_val =~ /^>=/
224   when Array
225     new_val.map! { |a| a =~ /^>=/ ? JSS.expand_min_os(a) : a }
226     new_val.flatten!
227     new_val.uniq!
228   else
229     raise Jamf::InvalidDataError, 'os_requirements must be a String or an Array of strings'
230   end # case
231 
232   # get the array version
233   @os_requirements = JSS.to_s_and_a(new_val)[:arrayform]
234   @need_to_update = true
235 end
Also aliased as: oses=
oses=(new_val)
Alias for: os_requirements=
parameters=(new_val) click to toggle source

Replace all the script parameters at once.

This will replace the entire set with the hash provided.

@param new_val the Hash keys must exist in {PARAMETER_KEYS}

@return [void]

    # File lib/jamf/api/classic/api_objects/script.rb
287 def parameters=(new_val)
288   return nil if new_val == @parameters
289   new_val = {} if new_val.nil? || (new_val == '')
290 
291   # check the values
292   raise Jamf::InvalidDataError, ':parameters must be a Hash with keys :parameter4 thru :parameter11' unless \
293     new_val.is_a?(Hash) && ((new_val.keys & PARAMETER_KEYS) == new_val.keys)
294   new_val.each do |_k, v|
295     raise Jamf::InvalidDataError, ':parameter values must be strings or nil' unless v.nil? || v.is_a?(String)
296   end
297 
298   @parameters = new_val
299   @need_to_update = true
300 end
priority=(new_val) click to toggle source

Change the priority of this script

@param new_val the new priority, which must be one of {PRIORITIES}

@return [void]

    # File lib/jamf/api/classic/api_objects/script.rb
243 def priority=(new_val)
244   return nil if new_val == @priority
245   new_val = DEFAULT_PRIORITY if new_val.nil? || (new_val == '')
246   raise Jamf::InvalidDataError, ":priority must be one of: #{PRIORITIES.join ', '}" unless PRIORITIES.include? new_val
247   @priority = new_val
248   @need_to_update = true
249 end
run(**opts) click to toggle source

Run this script on the current machine.

If the script code is available in the {#script_contents} attribute, then that code is saved to a tmp file, and executed. The tmp file is deleted immediately after running

After the script runs, this method returns a two-item Array.

  • the first item is an Integer, the exit status of the script itself (0 means success)

  • the second item is a String, the output (stdout + stderr) of the script.

The exit status of the jamf binary process will be available as a Process::Status object in $? immediately after running.

@param opts the options for running the script

@option opts :target the ‘target drive’, passed to the script as the first commandline option.

Defaults to '/'

@option opts :computer_name the name of the computer, passed to the script as the second commandline

option. Defaults to the name of the current machine

@option opts :username the username to be passed to the script as the third commandline option.

Defaults to the current console user.

@option opts :p4..:p11 the values to be passed as the 4th - 11th commandline params

Script params 1, 2, & 3 are the target:, computer_name: and username: params

@option opts :show_output should the output (stdout + stderr) be copied to

stdout in realtime, as well as returned?

@return [Array<(Integer,String)>] the exit status and stdout+stderr of the script

    # File lib/jamf/api/classic/api_objects/script.rb
388 def run(**opts)
389   raise Jamf::MissingDataError, 'script_contents does not start with #!' unless @script_contents.to_s.start_with? '#!'
390 
391   opts[:target] ||= '/'
392   opts[:computer_name] ||= Jamf::Client.run_jamf('getComputerName')[/>(.*?)</, 1]
393   opts[:username] ||= Jamf::Client.console_user
394 
395   params = [opts[:target], opts[:computer_name], opts[:username]]
396   params << opts[:p4]
397   params << opts[:p5]
398   params << opts[:p6]
399   params << opts[:p7]
400   params << opts[:p8]
401   params << opts[:p9]
402   params << opts[:p10]
403   params << opts[:p11]
404 
405   # everything must be a string
406   params.map! &:to_s
407 
408   # remove nils
409   params.compact!
410 
411   # remove empty strings
412   params.delete_if &:empty?
413 
414   return_value = []
415 
416   # Save and run the script from a private temp dir
417   # which will be deleted when finished
418   require 'tmpdir'
419   Dir.mktmpdir do |dir|
420     executable = Pathname.new "#{dir}/#{@name}"
421     executable.jss_touch
422     executable.chmod 0o700
423     executable.jss_save @script_contents
424 
425     cmd = [executable.to_s]
426     cmd += params
427 
428     stdout_and_stderr_str, status = Open3.capture2e(*cmd)
429 
430     return_value << status.exitstatus
431     return_value << stdout_and_stderr_str
432   end # Dir.mktmpdirs
433 
434   return_value
435 end
script_contents=(new_val) click to toggle source

Change the executable code of this script.

If the arg is a Pathname instance, or a String starting with “/” Then the arg is assumed to be a file from which to read the code.

Otherwise it should be a String with the code itself, and it must start with ‘#!“

After doing this, use {#create} or {#update} to write it to the database or use {#upload_master_file} to save it to the master dist. point.

@param new_val the new script contents or a path to a file containing it.

@return [void]

    # File lib/jamf/api/classic/api_objects/script.rb
335 def script_contents=(new_val)
336   new_code = case new_val
337              when String
338                if new_val.start_with? '/'
339                  Pathname.new(new_val).read
340                else
341                  new_val
342                end # if
343              when Pathname
344                new_val.read
345              else
346                raise Jamf::InvalidDataError, 'New code must be a String (path or code) or Pathname instance'
347              end # case
348 
349   raise Jamf::InvalidDataError, "Script contents must start with '#!'" unless new_code.start_with? '#!'
350 
351   @script_contents = new_code
352   @script_contents_encoded = Base64.encode64 @script_contents
353   @need_to_update = true
354 end
Also aliased as: code=, contents=
set_parameter(param_num, new_val) click to toggle source

Change one of the stored parameters

@param param_num which param are we setting? must be 4..11

@param new_val the new value for the parameter

@return [void]

    # File lib/jamf/api/classic/api_objects/script.rb
310 def set_parameter(param_num, new_val)
311   raise Jamf::NoSuchItemError, 'Parameter numbers must be from 4-11' unless (4..11).cover? param_num
312   pkey = "parameter#{param_num}".to_sym
313   raise Jamf::InvalidDataError, 'parameter values must be strings or nil' unless new_val.nil? || new_val.is_a?(String)
314   return nil if new_val == @parameters[pkey]
315   @parameters[pkey] = new_val
316   @need_to_update = true
317 end
set_parameter_description(param_num, new_val)
Alias for: set_parameter
set_parameter_label(param_num, new_val)
Alias for: set_parameter

Private Instance Methods

rest_xml() click to toggle source

Return the xml for creating or updating this script in the JSS

    # File lib/jamf/api/classic/api_objects/script.rb
452 def rest_xml
453   doc = REXML::Document.new
454   scpt = doc.add_element 'script'
455 
456   scpt.add_element('filename').text = @filename
457   scpt.add_element('id').text = @id
458   scpt.add_element('info').text = @info
459   scpt.add_element('name').text = @name
460   scpt.add_element('notes').text = @notes
461   scpt.add_element('os_requirements').text = JSS.to_s_and_a(@os_requirements)[:stringform]
462   scpt.add_element('priority').text = @priority
463   add_category_to_xml(doc)
464 
465   if @parameters.empty?
466     scpt.add_element('parameters').text = nil
467   else
468     pars = scpt.add_element('parameters')
469     PARAMETER_KEYS.each { |p| pars.add_element(p.to_s).text = @parameters[p] }
470   end
471 
472   scpt.add_element('script_contents_encoded').text = script_contents_encoded
473 
474   doc.to_s
475 end