I'm fairly new to JAMF, but wrote a Python script that I want to call using a Self-Service Policy.
The simple point of the script is that it allows a user to enter in a description of a problem they are having with the computer, optionally add a screenshot, then email that information (along with their username/computer name/serial number) to an email address/ticketing system.
The script works perfectly when executed locally. When executed through a JAMF policy on the same computer, it does not appear to run and I get a Completed status in JAMF with the following error:
Executing Policy Request IT Support…
[Step 1 of 2]
Mounting Production (Master) to /Volumes/CasperShare…
[Step 2 of 2]
Running script EmailSupportTicket.py…
Script exit code: 127
Script result: /bin/sh: /Library/Application Support/JAMF/tmp/EmailSupportTicket.py: No such file or directory
-----
Script follows:
#!/usr/bin/python
import smtplib, email, sys, time, os
from Tkinter import
from ctypes import
from ctypes import util
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email import Encoders
iokit = cdll.LoadLibrary(util.find_library('IOKit'))
cf = cdll.LoadLibrary(util.find_library('CoreFoundation'))
cf.CFStringCreateWithCString.argtypes = [c_void_p, c_char_p, c_int32]
cf.CFStringCreateWithCString.restype = c_void_p
cf.CFStringGetCStringPtr.argtypes = [c_void_p, c_uint32]
cf.CFStringGetCStringPtr.restype = c_char_p
kCFAllocatorDefault = c_void_p.in_dll(cf, "kCFAllocatorDefault")
kCFStringEncodingMacRoman = 0
kIOMasterPortDefault = c_void_p.in_dll(iokit, "kIOMasterPortDefault")
kIOPlatformSerialNumberKey = "IOPlatformSerialNumber".encode("mac_roman")
iokit.IOServiceMatching.restype = c_void_p
iokit.IOServiceGetMatchingService.argtypes = [c_void_p, c_void_p]
iokit.IOServiceGetMatchingService.restype = c_void_p
iokit.IORegistryEntryCreateCFProperty.argtypes = [c_void_p, c_void_p, c_void_p, c_uint32]
iokit.IORegistryEntryCreateCFProperty.restype = c_void_p
iokit.IOObjectRelease.argtypes = [c_void_p]
SERIAL = None
def getSerial():
global SERIAL
if SERIAL is None:
platformExpert = iokit.IOServiceGetMatchingService(kIOMasterPortDefault,
iokit.IOServiceMatching("IOPlatformExpertDevice"))
if platformExpert:
key = cf.CFStringCreateWithCString(kCFAllocatorDefault, kIOPlatformSerialNumberKey, kCFStringEncodingMacRoman)
serialNumberAsCFString =
iokit.IORegistryEntryCreateCFProperty(platformExpert,
key,
kCFAllocatorDefault, 0);
if serialNumberAsCFString:
SERIAL = cf.CFStringGetCStringPtr(serialNumberAsCFString, 0)
iokit.IOObjectRelease(platformExpert)
return SERIAL
# set the variables
sender = str(sys.argv[3]) + "<removed - actual script has our domain information>"
# identify the correct changegear queue
receiver = str(sys.argv[4])
subj = "Service Request from " + str(sys.argv[3])
smtpHost = "<removed for security reasons - the actual script has this server information>"
port = 587
# locate serial number
serial_number = getSerial();
# bodyText initial information
bodyText = "
Username: " + str(sys.argv[3]) + "
Computer Name: " + str(sys.argv[2]) + "
Serial Number: " + serial_number + "
"
# setup the message header
timegmt = time.gmtime(time.time( ))
fmt = '%a, %d %b %Y %H:%M:%S GMT'
datestr = time.strftime(fmt, timegmt)
msg = MIMEMultipart()
msg['From'] = sender
msg['To'] = receiver
msg['Date'] = datestr
msg['Subject'] = subj
# request bodyText
root = Tk()
screenshot_yes = IntVar()
def sendrequest(): msg.attach( MIMEText(bodyText + T.get('1.0', 'end')) ) root.destroy() if screenshot_yes.get() == 1: os.system("screencapture /tmp/screen.png") part = MIMEBase('application', "octet-stream") part.set_payload( open("/tmp/screen.png","rb").read() ) Encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="screenshot.png"') msg.attach(part) server = smtplib.SMTP(smtpHost) failed = server.sendmail(sender, receiver, msg.as_string()) server.quit()
# return the status if failed: mailsent = Tk() mailsent.title("Error") label = Message(mailsent, text="Unable to send. You must be on campus or connected to the VPN to use this function.", width=600) label.pack() FB = Button(mailsent, text="OK", command=mailsent.destroy) FB.pack(side=RIGHT) mailsent.mainloop() else: mailsent = Tk() mailsent.title("Success") if screenshot_yes.get() == 1: label = Message(mailsent, text="Your request for assistance has been sent with a screenshot of your desktop.", width=300) else: label = Message(mailsent, text="Your request for assistance has been sent.", width=300) label.pack() FB = Button(mailsent, text="OK", command=mailsent.destroy) FB.pack(side=RIGHT) mailsent.mainloop()
root.title("Request IT Assistance")
S = Scrollbar(root)
L = Message(root, text="Please describe the problem you are having below, then click the Send Email button.
To include a screenshot of your current desktop, select the checkbox below.", width=800)
L.pack(side=TOP)
T = Text(root, height=20, width=80)
T.focus()
S.pack(side=RIGHT, fill=Y)
T.pack(side=TOP, fill=Y)
S.config(command=T.yview)
T.config(yscrollcommand=S.set)
b2 = Button(root, text="Send Email to " + receiver, command=sendrequest)
b2.pack(side=RIGHT)
b1 = Button(root, text="Cancel", command=root.destroy)
b1.pack(side=RIGHT)
screenshot_yes.set(0)
c = Checkbutton(root, text="Send Screenshot", variable=screenshot_yes)
c.pack(side=LEFT)
root.mainloop()