Class: JSS::Script

Inherits:
APIObject show all
Includes:
Categorizable, Creatable, Updatable
Defined in:
lib/jss/api_object/script.rb,
lib/jss.rb

Overview

A Script in the JSS.

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

This class will save the script contents back to the database with the Creatable#create or Updatable#update methods

If your scripts are stored on the master distribution point instead of the database, you can use #upload_master_file to save it to the server, and #delete_master_file to delete it from the server.

Use the #run method to run the script on the local machine via the 'jamf runScript' command

See Also:

Constant Summary collapse

RSRC_BASE =

The base for REST resources of this class

'scripts'.freeze
RSRC_LIST_KEY =

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

:scripts
RSRC_OBJECT_KEY =

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

:script
VALID_DATA_KEYS =

these keys, as well as :id and :name, are present in valid API JSON data for this class

[:parameters, :filename, :os_requirements].freeze
DIST_POINT_SCRIPTS_FOLDER =

The script storage folder on the distribution point, if used

'Scripts'.freeze
PRIORITIES =

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

['Before', 'After', 'At Reboot'].freeze
DEFAULT_PRIORITY =

which is default?

'After'.freeze
PARAMETER_KEYS =

The keys used in the @parameters Hash

[:parameter4, :parameter5, :parameter6, :parameter7, :parameter8, :parameter9, :parameter10, :parameter11].freeze
OBJECT_HISTORY_OBJECT_TYPE =

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

91
CATEGORY_SUBSET =

Where is the Category in the API JSON?

:top
CATEGORY_DATA_TYPE =

How is the category stored in the API data?

String

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args = {}) ⇒ Script

Returns a new instance of Script



140
141
142
143
144
145
146
147
148
149
150
# File 'lib/jss/api_object/script.rb', line 140

def initialize(args = {})
  super

  @filename = @init_data[:filename] || @name
  @info = @init_data[:info]
  @notes = @init_data[:notes]
  @os_requirements = @init_data[:os_requirements] ? JSS.to_s_and_a(@init_data[:os_requirements])[:arrayform] : []
  @parameters = @init_data[:parameters] ? @init_data[:parameters] : {}
  @priority = @init_data[:priority] || DEFAULT_PRIORITY
  @script_contents = @init_data[:script_contents]
end

Instance Attribute Details

#categoryString (readonly)

Returns the category of this script, stored in the JSS as the id number from the categories table

Returns:

  • (String)

    the category of this script, stored in the JSS as the id number from the categories table



128
129
130
# File 'lib/jss/api_object/script.rb', line 128

def category
  @category
end

#filenameString

Returns the file name of the script, if stored in a dist. point

Returns:

  • (String)

    the file name of the script, if stored in a dist. point



113
114
115
# File 'lib/jss/api_object/script.rb', line 113

def filename
  @filename
end

#infoString

Returns the info field for this script

Returns:

  • (String)

    the info field for this script



122
123
124
# File 'lib/jss/api_object/script.rb', line 122

def info
  @info
end

#need_to_updateBoolean (readonly) Originally defined in module Updatable

Returns do we have unsaved changes?

Returns:

  • (Boolean)

    do we have unsaved changes?

#notesString

Returns the notes field for this script

Returns:

  • (String)

    the notes field for this script



125
126
127
# File 'lib/jss/api_object/script.rb', line 125

def notes
  @notes
end

#os_requirementsArray<String> Also known as: oses

Returns the OS versions this can be installed onto. For all minor versions, the format is 10.5.x

Returns:

  • (Array<String>)

    the OS versions this can be installed onto. For all minor versions, the format is 10.5.x



116
117
118
# File 'lib/jss/api_object/script.rb', line 116

def os_requirements
  @os_requirements
end

#parametersHash

Returns script parameters 4-11. Parameters 1-3 are predefined as target drive, computer name, and username

Returns:

  • (Hash)

    script parameters 4-11. Parameters 1-3 are predefined as target drive, computer name, and username



131
132
133
# File 'lib/jss/api_object/script.rb', line 131

def parameters
  @parameters
end

#priorityString

Returns either 'Before' or 'After' or “At Reboot”.

Returns:

  • (String)

    either 'Before' or 'After' or “At Reboot”.



119
120
121
# File 'lib/jss/api_object/script.rb', line 119

def priority
  @priority
end

#script_contentsString Also known as: code, contents

Returns the actual code for this script, if it's stored in the database.

Returns:

  • (String)

    the actual code for this script, if it's stored in the database.



134
135
136
# File 'lib/jss/api_object/script.rb', line 134

def script_contents
  @script_contents
end

Instance Method Details

#category=(new_cat) ⇒ void Originally defined in module Categorizable

This method returns an undefined value.

Change the category of this object. Any of the NON_CATEGORIES values will unset the category

Parameters:

  • new_cat (Integer, String)

    The new category

Raises:

#category_assigned?Boolean Also known as: categorized? Originally defined in module Categorizable

Does this object have a category assigned?

Returns:

  • (Boolean)

    Does this object have a category assigned?

#category_idInteger Originally defined in module Categorizable

The id of the category for this object.

Returns:

  • (Integer)

    The id of the category for this object.

#category_nameString Also known as: category Originally defined in module Categorizable

The name of the category for this object. For backward compatibility, this is aliased to just 'category'

Returns:

  • (String)

    The name of the category for this object.

#category_objectJSS::Category Originally defined in module Categorizable

The JSS::Category instance for this object's category

Returns:

  • (JSS::Category)

    The JSS::Category instance for this object's category

#clone(new_name, api: nil) ⇒ APIObject Originally defined in module Creatable

make a clone of this API object, with a new name. The class must be creatable

Parameters:

  • name (String)

    the name for the new object

  • api (JSS::APIConnection)

    the API in which to create the object Defaults to the API used to instantiate this object

Returns:

  • (APIObject)

    An uncreated clone of this APIObject with the given name

Raises:

#create(api: nil) ⇒ Integer Originally defined in module Creatable

Create a new object in the JSS.

Parameters:

  • api (JSS::APIConnection)

    the API in which to create the object Defaults to the API used to instantiate this object

Returns:

  • (Integer)

    the jss ID of the newly created object

Raises:

#delete_master_file(rw_pw, unmount = true) ⇒ Boolean

Delete the filename from the master distribution point, if it exists.

If you'll be uploading several files you can specify unmount as false, and do it manually when all are finished.

Parameters:

  • rw_pw (String)

    the password for the read/write account on the master Distribution Point

  • unmount (Boolean) (defaults to: true)

    whether or not ot unount the distribution point when finished.

Returns:

  • (Boolean)

    was the file deleted?



387
388
389
390
391
392
393
394
395
396
397
# File 'lib/jss/api_object/script.rb', line 387

def delete_master_file(rw_pw, unmount = true)
  file = JSS::DistributionPoint.master_distribution_point.mount(rw_pw, :rw) + "#{DIST_POINT_SCRIPTS_FOLDER}/#{@filename}"
  if file.exist?
    file.delete
    did_it = true
  else
    did_it = false
  end # if exists
  JSS::DistributionPoint.master_distribution_point.unmount if unmount
  did_it
end

#evaluate_new_category(new_cat) ⇒ Array<String, Integer> Originally defined in module Categorizable

Given a category name or id, return the name and id TODO: use APIObject.exist? and/or APIObject.valid_id

Parameters:

  • new_cat (String, Integer)

    The name or id of a possible category

Returns:

  • (Array<String, Integer>)

    The matching name and id, which may be nil.

#name=(new_val) ⇒ void

This method returns an undefined value.

Change the script's display name

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

Parameters:

  • new_val (String)

    the new display name

Raises:



181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/jss/api_object/script.rb', line 181

def name=(new_val)
  return nil if new_val == @name
  new_val = nil if new_val == ''
  raise JSS::MissingDataError, "Name can't be empty" unless new_val
  raise JSS::AlreadyExistsError, "A #{RSRC_OBJECT_KEY} already exists with the name '#{args[:name]}'" if JSS.send(LIST_METHOD).values.include?

  ### if the filename is the same, keep it the same
  @filename = new_val if @filename == @name
  @name = new_val

  ### if our REST resource is based on the name, update that too
  @rest_rsrc = "#{RSRC_BASE}/name/#{CGI.escape @name}" if @rest_rsrc.include? '/name/'
  @need_to_update = true
end

#run(opts = {}) ⇒ Array<(Integer,String)>

Run this script on the current machine using the “jamf runScript” command.

If the script code is available in the #script_contents attribute, then that code is saved to a tmp file, and executed. Otherwise, the script is assumed to be stored on the distribution point.

If the dist. point has http downloads enabled, then the URL is used as the path with the 'jamf runScript' command.

If http is not an option, the dist.point is mounted, and the script copied locally before running. In this case the options must include :ro_pw => 'somepass' to provide the read-only password for mounting the distribution point. If :unmount => true is provided, the dist. point will be unmounted immediately after copying the script locally. Otherwise it will remain mounted, in case there's further need of it.

Any local on-disk copies of the script are removed 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 jamf binary, which will include the script output.

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

NOTE In the WEB UI and API, the definable parameters are numbered 4-11, since 1, 2, & 3 are the target drive, computer name, and user name respectively. However, the jamf binary refers to them as p1-p8, and that's how they are expected as options to #run. So if :p1=> “new param” is given as an aption to #run, it will override any value that the API provided in @parameters

Parameters:

  • opts (Hash) (defaults to: {})

    the options for running the script

Options Hash (opts):

  • :target (String, Pathname)

    the 'target drive', passed to the script as the first commandline option. Defaults to '/'

  • :computer_name (String)

    the name of the computer, passed to the script as the second commandline option. Defaults to the name of the current machine

  • :username (String)

    the username to be passed to the script as the third commandline option.

  • :p1..:p8 (String)

    the values to be passed as the 4th - 11th commandline options, overriding those defined with the script in the JSS

  • :ro_pw (String)

    the read-only password for mounting the distribution point, if needed

  • :unmount (Boolean)

    should the dist. point be unmounted when finished, if we mounted it?

  • :verbose (Boolean)

    should the 'jamf runScript' command be verbose?

  • :show_output (Boolean)

    should the output (stdout + stderr) of 'jamf runScript' be copied to stdout in realtime, as well as returned?

Returns:

  • (Array<(Integer,String)>)

    the exit status of the script and stdout+stderr of 'jamf runScript'. The exit status of the jamf binary will be available in $? immediately after running.



453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
# File 'lib/jss/api_object/script.rb', line 453

def run(opts = {})
  opts[:target] ||= '/'
  opts[:p1] ||= @parameters[:parameter4]
  opts[:p2] ||= @parameters[:parameter5]
  opts[:p3] ||= @parameters[:parameter6]
  opts[:p4] ||= @parameters[:parameter7]
  opts[:p5] ||= @parameters[:parameter8]
  opts[:p6] ||= @parameters[:parameter9]
  opts[:p7] ||= @parameters[:parameter10]
  opts[:p8] ||= @parameters[:parameter11]

  dp_mount_pt = nil
  delete_exec = false

  begin

    # do we have the code already? if so, save it out and make it executable
    if @script_contents && !@script_contents.empty?

      script_path = JSS::Client::DOWNLOADS_FOLDER

      executable = script_path + @filename

      executable.jss_touch
      executable.chmod 0o700
      executable.jss_save @script_contents
      delete_exec = true

    # otherwise, get it from the dist. point
    else
      dist_point = JSS::DistributionPoint.my_distribution_point api: @api

      ### how do we access our dist. point?
      if dist_point.http_downloads_enabled
        script_path = dist_point.http_url + "/#{DIST_POINT_SCRIPTS_FOLDER}/"

      else
        dp_mount_pt = dist_point.mount opts[:ro_pw]

        script_path = (dp_mount_pt + DIST_POINT_SCRIPTS_FOLDER)

      end # if http enabled

    end # if @script_contents and (not @script_contents.empty?)

    # build the command as an array.
    command_arry = ['-script', @filename, '-path', script_path.to_s]

    command_arry << '-target'
    command_arry << opts[:target].to_s

    command_arry << '-computerName' if opts[:computer_name]
    command_arry << opts[:computer_name] if opts[:computer_name]

    command_arry << '-username' if opts[:username]
    command_arry << opts[:username] if opts[:username]

    command_arry << '-p1' if opts[:p1]
    command_arry << opts[:p1] if opts[:p1]

    command_arry << '-p2' if opts[:p2]
    command_arry << opts[:p2] if opts[:p2]

    command_arry << '-p3' if opts[:p3]
    command_arry << opts[:p3] if opts[:p3]

    command_arry << '-p4' if opts[:p4]
    command_arry << opts[:p4] if opts[:p4]

    command_arry << '-p5' if opts[:p5]
    command_arry << opts[:p5] if opts[:p5]

    command_arry << '-p6' if opts[:p6]
    command_arry << opts[:p6] if opts[:p6]

    command_arry << '-p7' if opts[:p7]
    command_arry << opts[:p7] if opts[:p7]

    command_arry << '-p8' if opts[:p8]
    command_arry << opts[:p8] if opts[:p8]

    command_arry << '-verbose' if opts[:verbose]

    command = command_arry.shelljoin

    jamf_output =  JSS::Client.run_jamf 'runScript', command, opts[:show_output]

    jamf_output =~ /^.*Script exit code: (\d+)(\D|$)/

    script_exitstatus = Regexp.last_match(1).to_i

  ensure
    executable.delete if delete_exec && executable.exist?
    dist_point.unmount if dp_mount_pt && dp_mount_pt.mountpoint? && opts[:unmount]
  end # begin/ensure

  [script_exitstatus, jamf_output]
end

#set_parameter(param_num, new_val) ⇒ void

This method returns an undefined value.

Change one of the stored parameters

Parameters:

  • param_num (Integer)

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

  • new_val (String)

    the new value for the parameter

Raises:



309
310
311
312
313
314
315
316
# File 'lib/jss/api_object/script.rb', line 309

def set_parameter(param_num, new_val)
  raise JSS::NoSuchItemError, 'Parameter numbers must be from 4-11' unless (4..11).cover? param_num
  pkey = "parameter#{param_num}".to_sym
  raise JSS::InvalidDataError, 'parameter values must be strings or nil' unless new_val.nil? || new_val.is_a?(String)
  return nil if new_val == @parameters[pkey]
  @parameters[pkey] = new_val
  @need_to_update = true
end

#unset_categoryvoid Originally defined in module Categorizable

This method returns an undefined value.

Set the category to nothing

#updateBoolean Originally defined in module Updatable

Save changes to the JSS

Returns:

  • (Boolean)

    success

Raises:

#upload_master_file(rw_pw, unmount = true) ⇒ void

This method returns an undefined value.

Save the @script_contents for this script to a file on the Master Distribution point.

If you'll be uploading several files you can specify unmount as false, and do it manually when all are finished.

use #script_contents= to set the script_contents from a String or Pathname

Parameters:

  • rw_pw (String)

    the password for the read/write account on the master Distribution Point

  • unmount (Boolean) (defaults to: true)

    whether or not ot unount the distribution point when finished.

Raises:



365
366
367
368
369
370
371
372
373
374
# File 'lib/jss/api_object/script.rb', line 365

def upload_master_file(rw_pw, unmount = true)
  raise JSS::MissingDataError, 'No code specified. Use #code= first.' if @script_contents.nil? || @script_contents.empty?

  mdp = JSS::DistributionPoint.master_distribution_point
  raise JSS::InvaldDatatError, 'Incorrect password for read-write access to master distribution point.' unless mdp.check_pw :rw, rw_pw

  destination = mdp.mount(rw_pw, :rw) + "#{DIST_POINT_SCRIPTS_FOLDER}/#{@filename}"
  destination.save @script_contents
  mdp.unmount if unmount
end