class Jamf::Script
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
@return {String] the actual code for this script, if it’s stored in the database.
@return {String] the actual code for this script, if it’s stored in the database.
@return [String] the file name of the script, if stored in a dist. point
@return [String] the info field for this script
@return [String] the notes field for this script
@return [Array<String>] the OS versions this can be installed onto. For all minor versions, the format is 10.5.x
@return [Array<String>] the OS versions this can be installed onto. For all minor versions, the format is 10.5.x
@return [Hash] descriptions of parameters 4-11. Parameters 1-3 are predefined as target drive, computer name, and username
@return [Hash] descriptions of parameters 4-11. Parameters 1-3 are predefined as target drive, computer name, and username
@return [Hash] descriptions of parameters 4-11. Parameters 1-3 are predefined as target drive, computer name, and username
@return [String] either ‘Before’ or ‘After’ or “At Reboot”.
@return {String] the actual code for this script, if it’s stored in the database.
@return [String] the code for this script, Base64-encoded
Public Class Methods
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
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
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
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
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
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
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
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 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
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
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
Private Instance Methods
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