Files
MacAdmin/Jamf Pro/Scripts/Remove-LocalAdminRights.py
Zack T 9073cfa747 v1.1.0 = Fixed Python3 conversion issues
+ Fixed Python3 conversion issues...
+ Code improvements, etc.
2022-04-05 14:13:30 -07:00

191 lines
5.9 KiB
Python

#!/opt/ManagedFrameworks/Python.framework/Versions/Current/bin/python3
###################################################################################################
# Script Name: Remove-LocalAdminRights.py
# By: Zack Thompson / Created: 4/20/2020
# Version: 1.1.0 / Updated: 4/1/2022 / By: ZT
#
# Description: This script removes accounts from the local admin group; accounts that need to
# remain in the admin group can be specified in Jamf Pro script parameters
#
###################################################################################################
import os
import plistlib
import re
import shlex
import subprocess
import sys
def execute_process(command, input=None):
"""
A helper function for subprocess.
Args:
command (str): The command line level syntax that would be written in a shell script or
a terminal window.
input (str, optional): Any input that should be passed "interactively" to the process
being executed.
Returns:
dict: Results in a dictionary
"""
# Validate that command is not a string
if not isinstance(command, str):
raise TypeError("Command must be a str type")
# Format the command
command = shlex.split(command)
# Run the command
process = subprocess.Popen( command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
shell=False, universal_newlines=True )
if input:
(stdout, stderr) = process.communicate(input=input)
else:
(stdout, stderr) = process.communicate()
return {
"stdout": (stdout).strip(),
"stderr": (stderr).strip() if stderr != None else None,
"exitcode": process.returncode,
"success": True if process.returncode == 0 else False
}
def execute_dscl(option="-plist", datasource=".", command="-read", parameters=""):
"""Execute dscl and return the values
Args:
option (str, optional): The option to use. Defaults to "-plist".
datasource (str, optional): The node to query. Defaults to ".".
command (str, optional): The dscl command to run. Defaults to "-read".
parameters (str, optional): Parameters that will be passed to the command option. Defaults to "".
Returns:
dict: A dict of the results from dscl
"""
results = execute_process(f"/usr/bin/dscl {option} {datasource} {command} {parameters}")
# Verify command result
if not results['success']:
print("Failed to admin group membership!")
print(results['stderr'])
sys.exit(2)
return plistlib.loads(results['stdout'].encode())
def main():
print('\n***** Remove-LocalAdminRights process: START *****\n')
##################################################
# Define Variables
# List your environments jps servers and local admins here
prod_jps = "https://prod.jps.server.com:8443/"
prod_protected_admins = ["jma"]
dev_jps = "https://dev.jps.server.com:8443/"
dev_protected_admins = ["jmadev"]
exit_code=0
jamf_plist = "/Library/Preferences/com.jamfsoftware.jamf.plist"
##################################################
# Build protected_admins list
protected_admins = ["root"]
# Loop through the passed script parameters
for arg in sys.argv[4:]:
if len(arg) != 0:
if re.search(",", arg):
comma_args = arg.split(",", -1)
arg_list = [ account.strip() for account in comma_args ]
protected_admins.extend(arg_list)
else:
protected_admins.append(arg)
# Check the local environment and add the proper accounts
# Read the Jamf Plist to get the current environment
# Verify the file exists
if not os.path.exists(jamf_plist):
raise Exception("Missing: com.jamfsoftware.jamf.plist")
with open(jamf_plist, "rb") as plist:
jamf_plist_contents = plistlib.load(plist)
jss_url = jamf_plist_contents['jss_url']
# Determine which environment we're in
if jss_url == prod_jps:
protected_admins.extend(prod_protected_admins)
elif jss_url == dev_jps:
protected_admins.extend(dev_protected_admins)
else:
print("ERROR: Unknown Jamf Pro environment!")
sys.exit(1)
print('Protected Admins: {}\n\n'.format(protected_admins))
##################################################
# Bits staged...
# Get the admin group memembership
results_admin_group_members = execute_dscl(parameters="Groups/admin GroupMembership")
admin_group = results_admin_group_members.get('dsAttrTypeStandard:GroupMembership')
# For verbosity in the policy log
print("The following accounts are in the local admin group:")
for user in admin_group:
print(" - {}".format(user))
print("\n")
# Loop through the admin users
for user in admin_group:
# Check if the admin user is a protected_admin
if user not in protected_admins:
# Remove user from the admin group
results = execute_process("/usr/sbin/dseditgroup -o edit -d '{}' -t user admin".format(user))
# Check if the removal was successful
if not results['success']:
print("{} - FAILED to remove from admin group".format(user))
print(results['stderr'])
exit_code=6
else:
print("{} - removed from admin group".format(user))
print("\n")
# Get an updated list of the local admin group
results_updated_admin_group_members = execute_dscl(parameters="Groups/admin GroupMembership")
updated_admin_group = results_updated_admin_group_members.get('dsAttrTypeStandard:GroupMembership')
# For verbosity in the policy log
print("Updated local admin group:")
for user in updated_admin_group:
print(" - {}".format(user))
print("\n***** Remove-LocalAdminRights Process: COMPLETE *****")
sys.exit(exit_code)
if __name__ == "__main__":
main()