Convert mobile accounts to local accounts (and make them Admins)

HariSeldon
New Contributor III

Hello!

 

At the school where I work we are planning to convert the mobile user accounts (connected to AD) to local user accounts, and make them admins.

I saw the fantastic script on rtrouton's GitHub. 

I tested it on a test machine and works like a charm, though the issue I have is that it asks for user input, and trying to push it via policy in Jamf, it simply waits.

 

So the question is: is there a way for me to achieve this via Jamf Pro, without the use of Jamf Connect (we currently don't have it)?

 

Thank you

2 ACCEPTED SOLUTIONS

MehdiYawari
New Contributor III

Also I am using the same script as our devices are no longer bound to AD and no AD account on the device, instead we are using Kerboros SSO Extension. I had to adjust the script in a way that it doesn't ask user input. As admin, I can decide if the converted account should habe an admin right or not. Well it work like a charm.

View solution in original post

HariSeldon
New Contributor III

No worries @efil4xiN, I figured :-)

Thank you @MehdiYawari & @Tribruin .

I actually figured it out, confirming also what @MehdiYawari: I removed the user inputs ("select" menus) and now it works perfectly also from Jamf!

View solution in original post

15 REPLIES 15

efil4xiN
Contributor II

As long as you have credentials for both accounts, it should be possible. I would start here with Rich's write up 

HariSeldon
New Contributor III

Thank you, though it's not exactly what I'm looking for.

When I test the script on a test machine, the scripts needs user input in order to run. If I run the script via Jamf (with a policy), I can't see any of the user input, hence the script doesn't run, it just waits.

Maybe I'm missing some detail?

Tribruin
Valued Contributor II

Since scripts run as Administrator when run from Jamf, you can't get user input via terminal. You would have to rework the script to prompt the user via another tool (such as AppleScript or, maybe, SwiftDialog). 

MehdiYawari
New Contributor III

Also I am using the same script as our devices are no longer bound to AD and no AD account on the device, instead we are using Kerboros SSO Extension. I had to adjust the script in a way that it doesn't ask user input. As admin, I can decide if the converted account should habe an admin right or not. Well it work like a charm.

efil4xiN
Contributor II

@HariSeldon  My apologies, I posted in the wrong chat

HariSeldon
New Contributor III

No worries @efil4xiN, I figured :-)

Thank you @MehdiYawari & @Tribruin .

I actually figured it out, confirming also what @MehdiYawari: I removed the user inputs ("select" menus) and now it works perfectly also from Jamf!

Im looking to do this for our exiting users through policy run script, by removing the input menus from the script will this just apply to the account running the script or all the mobile users it finds?

Malcolm
Contributor II

@HariSeldon 
can you share your version to skip the select options? I'm having trouble doing that, and I don't see why.

HariSeldon
New Contributor III

Hey Malcolm,

What I did that worked for my case was to:

  • remove the "select" menu's leaving only the core commands to do the job, like so:

HariSeldon_0-1701181383168.png

 

  • changing the "select" menu to a for loop - it's located in the central part of the script, that lets one select the username from variable "netname"

HariSeldon_1-1701181458558.jpeg

 

Thanks, I ended up re-engineering script, it will process all mobile accounts and also then repeat and process all accounts as admin accounts for local accounts.

 

 

 

#!/bin/bash

active_directory_users=()

# Get all users
users="$(/usr/bin/dscl . list /Users UniqueID | awk '$2 > 1000 {print $1}') FINISHED"

# Filter users with "Active Directory" in their account information
for user in $users; do
    # Check if "Active Directory" is present in the information
    if [[ "$(/usr/bin/dscl . -read /Users/$user AuthenticationAuthority)" == *"Active Directory"* ]]; then
        active_directory_users+=("$user")
    fi
done

echo "Active Directory users:"
echo "${active_directory_users[@]}"

#force unbind of AD
/usr/sbin/dsconfigad -remove -force -u none -p none

    # Deletes the Active Directory domain from the custom /Search
    # and /Search/Contacts paths
    
/usr/bin/dscl /Search/Contacts -delete . CSPSearchPath "$searchPath"
/usr/bin/dscl /Search -delete . CSPSearchPath "$searchPath"

    # Changes the /Search and /Search/Contacts path type from Custom to Automatic
    
/usr/bin/dscl /Search -change . SearchPolicy dsAttrTypeStandard:CSPSearchPath dsAttrTypeStandard:NSPSearchPath
/usr/bin/dscl /Search/Contacts -change . SearchPolicy dsAttrTypeStandard:CSPSearchPath dsAttrTypeStandard:NSPSearchPath


    # macOS 10.14.4 will remove the the actual ShadowHashData key immediately 
    # if the AuthenticationAuthority array value which references the ShadowHash
    # is removed from the AuthenticationAuthority array. To address this, the
    # existing AuthenticationAuthority array will be modified to remove the Kerberos
    # and LocalCachedUser user values.
    AuthenticationAuthority=$(/usr/bin/dscl -plist . -read /Users/$user AuthenticationAuthority)
    Kerberosv5=$(echo "${AuthenticationAuthority}" | xmllint --xpath 'string(//string[contains(text(),"Kerberosv5")])' -)
    LocalCachedUser=$(echo "${AuthenticationAuthority}" | xmllint --xpath 'string(//string[contains(text(),"LocalCachedUser")])' -)
    
    # Remove Kerberosv5 and LocalCachedUser
    
for user in "${active_directory_users[@]}"; do
  # Retrieve the AuthenticationAuthority attribute for the current user
  user_auth_authority=$(dscl . -plist /Users/$user AuthenticationAuthority)

  # Check if Kerberosv5 is present in the AuthenticationAuthority attribute
  if [[ ! -z "$(echo "$user_auth_authority" | xmllint --xpath 'string(//string[contains(text(),"Kerberosv5")])' -)" ]]; then
    # Remove the Kerberosv5 value from the AuthenticationAuthority attribute
    dscl . -plist /Users/$user AuthenticationAuthority "${Kerberosv5}"
  fi

  # Check if LocalCachedUser is present in the AuthenticationAuthority attribute
  if [[ ! -z "$(echo "$user_auth_authority" | xmllint --xpath 'string(//string[contains(text(),"LocalCachedUser")])' -)" ]]; then
    # Remove the LocalCachedUser value from the AuthenticationAuthority attribute
    dscl . -plist /Users/$user AuthenticationAuthority "${LocalCachedUser}"
  fi
done

for user in "${active_directory_users[@]}"; do
  # Remove the cached_groups attribute
  /usr/bin/dscl . -delete /Users/$user cached_groups

  # Remove the cached_auth_policy attribute
  /usr/bin/dscl . -delete /Users/$user cached_auth_policy

  # Remove the CopyTimestamp attribute
  /usr/bin/dscl . -delete /Users/$user CopyTimestamp

  # Remove the AltSecurityIdentities attribute
  /usr/bin/dscl . -delete /Users/$user AltSecurityIdentities

  # Remove the SMBPrimaryGroupSID attribute
  /usr/bin/dscl . -delete /Users/$user SMBPrimaryGroupSID

  # Remove the OriginalAuthenticationAuthority attribute
  /usr/bin/dscl . -delete /Users/$user OriginalAuthenticationAuthority

  # Remove the OriginalNodeName attribute
  /usr/bin/dscl . -delete /Users/$user OriginalNodeName

  # Remove the SMBSID attribute
  /usr/bin/dscl . -delete /Users/$user SMBSID

  # Remove the SMBScriptPath attribute
  /usr/bin/dscl . -delete /Users/$user SMBScriptPath

  # Remove the SMBPasswordLastSet attribute
  /usr/bin/dscl . -delete /Users/$user SMBPasswordLastSet

  # Remove the SMBGroupRID attribute
  /usr/bin/dscl . -delete /Users/$user SMBGroupRID

  # Remove the PrimaryNTDomain attribute
  /usr/bin/dscl . -delete /Users/$user PrimaryNTDomain

  # Remove the AppleMetaRecordName attribute
  /usr/bin/dscl . -delete /Users/$user AppleMetaRecordName

  # Remove the MCXSettings attribute
  /usr/bin/dscl . -delete /Users/$user MCXSettings

  # Remove the MCXFlags attribute
  /usr/bin/dscl . -delete /Users/$user MCXFlags
  
  /usr/bin/killall DirectoryService
  /usr/bin/killall opendirectoryd
  
  sleep 20
  
  			homedir=`/usr/bin/dscl . -read /Users/"$user" NFSHomeDirectory  | awk '{print $2}'`
			if [[ "$homedir" != "" ]]; then
			   /bin/echo "Home directory location: $homedir"
			   /bin/echo "Updating home folder permissions for the $netusername account"
			   /usr/sbin/chown -R "$user" "$homedir"		
			fi
  
/bin/echo "Adding $user to the staff group on this Mac."
/usr/sbin/dseditgroup -o edit -a "$user" -t user staff
  
			/bin/echo "Displaying user and group information for the $user account"
			/usr/bin/id $user
            
            /usr/sbin/dseditgroup -o edit -a "$user" -t user admin; /bin/echo "Admin rights given to this account"
  
done

# Collect all users again
allusers="$(/usr/bin/dscl . list /Users UniqueID | awk '$2 > 1000 {print $1}') FINISHED"

# Grant admin rights to all users
for alluser in $allusers; do
    /usr/sbin/dseditgroup -o edit -a "$alluser" -t user admin
    /bin/echo "Adding $alluser to the staff group on this Mac."
/usr/sbin/dseditgroup -o edit -a "$alluser" -t user staff
  
			/bin/echo "Displaying user and group information for the $user account"
			/usr/bin/id $alluser
            
            /usr/sbin/dseditgroup -o edit -a "$alluser" -t user admin; /bin/echo "Admin rights given to this account"
done

 

 

 

In the original, it seemingly does follow up checks which seem unnecessary for the task at hand. The netuser seems unnecessary as it had already determined that the user was an Active Directory one when extracting the users.

I might need to check mine above, as I want to remove the Active Directory status as the very last step, so to confirm all previous steps have completed.

I also want to see if there's a section to check if an account is disabled, as demobilising an account while it is disabled, is not going to have a very good outcome I suspect.

actually the script above is not quite right, so I will try your changes and see how I go

looked like it was working but the end user couldn't change their password.

HariSeldon
New Contributor III

I could find the my updated version of the script, here you go:

 

#!/bin/bash
# Modified 5/8/2022
Version=1.5
# Original source is from MigrateUserHomeToDomainAcct.sh
# Written by Patrick Gallagher 
#
# Guidance and inspiration from Lisa Davies:
# http://lisacherie.com/?p=239
#
# Modified by Rich Trouton
#
#
# Further modified by Lorenzo Fittarelli
#
# Version 1.0 - Migrates an Active Directory mobile account to a local account by the following process:

# 1. Detect if the Mac is bound to AD and offer to unbind the Mac from AD if desired
# 2. Display a list of the accounts with a UID greater than 1000
# 3. Remove the following attributes from the specified account:
# 
# cached_groups
# cached_auth_policy
# CopyTimestamp - This attribute is used by the OS to determine if the account is a mobile account
# SMBPrimaryGroupSID
# OriginalAuthenticationAuthority
# OriginalNodeName
# SMBSID
# SMBScriptPath
# SMBPasswordLastSet
# SMBGroupRID
# PrimaryNTDomain
# AppleMetaRecordName
# MCXSettings
# MCXFlags
#
# 4. Selectively modify the account's AuthenticationAuthority attribute to remove AD-specific attributes.
# 5. Restart the directory services process
# 6. Check to see if the conversion process succeeded by checking the OriginalNodeName attribute for the value "Active Directory"
# 7. If the conversion process succeeded, update the permissions on the account's home folder.
# 8. Prompt if admin rights should be granted for the specified account
#
# Version 1.1
#
# Changes:
# 
# 1. After conversion, the specified account is added to the staff group.  All local accounts on this Mac are members of the staff group,
#    but AD mobile accounts are not members of the staff group.
# 2. The "accounttype" variable is now checking the AuthenticationAuthority attribute instead of the OriginalNodeName attribute. 
#    The reason for Change 2's attributes change is that the AuthenticationAuthority attribute will exist following the conversion 
#    process while the OriginalNodeName attribute may not.
#
#
# Version 1.2
#
# Changes:
#
# Add RemoveAD function to handle the following tasks:
#
# 1. Force unbind the Mac from Active Directory
# 2. Deletes the Active Directory domain from the custom /Search and /Search/Contacts paths
# 3. Changes the /Search and /Search/Contacts path type from Custom to Automatic
# 
# Thanks to Rick Lemmon for the suggested changes to the AD unbind process.
#
# Version 1.3
#
# Changes:
#
# Fix to account password backup and restore process. Previous versions 
# of the script were adding extra quote marks to the account's plist 
# file located in /var/db/dslocal/nodes/Default/users/.
#
# Version 1.4
#
# Changes:
#
# macOS 10.14.4 will remove the the actual ShadowHashData key immediately 
# if the AuthenticationAuthority array value which references the ShadowHash
# is removed from the AuthenticationAuthority array. To address this, the
# existing AuthenticationAuthority array will be modified to remove the Kerberos
# and LocalCachedUser user values.
#
# Thanks to the anonymous reporter who provided the bug report and fix.
#
#
# Version 1.5
#
# Changes:
#
# Stripped the script of all the user inputs (Select menu) and prompts to user
# in order to facilitate an automatic execution via Jamf. 
# The main Select menu where the user account is being converted from Mobile to Local,
# has been changed into a for loop, also facilitating automatic execution.



clear

listUsers="$(/usr/bin/dscl . list /Users UniqueID | awk '$2 > 1000 {print $1}') FINISHED"
FullScriptName=`basename "$0"`
ShowVersion="$FullScriptName $Version"
check4AD=`/usr/bin/dscl localhost -list . | grep "Active Directory"`

# Save current IFS state

OLDIFS=$IFS

IFS='.' read osvers_major osvers_minor osvers_dot_version <<< "$(/usr/bin/sw_vers -productVersion)"

# restore IFS to previous state

IFS=$OLDIFS

/bin/echo "********* Running $FullScriptName Version $Version *********"

RunAsRoot()
{
        ##  Pass in the full path to the executable as $1
        if [[ "${USER}" != "root" ]] ; then
                /bin/echo
                /bin/echo "***  This application must be run as root.  Please authenticate below.  ***"
                /bin/echo
                sudo "${1}" && exit 0
        fi
}

RemoveAD(){

    # This function force-unbinds the Mac from the existing Active Directory domain
    # and updates the search path settings to remove references to Active Directory 

    searchPath=`/usr/bin/dscl /Search -read . CSPSearchPath | grep Active\ Directory | sed 's/^ //'`

    # Force unbind from Active Directory

    /usr/sbin/dsconfigad -remove -force -u none -p none
    
    # Deletes the Active Directory domain from the custom /Search
    # and /Search/Contacts paths
    
    /usr/bin/dscl /Search/Contacts -delete . CSPSearchPath "$searchPath"
    /usr/bin/dscl /Search -delete . CSPSearchPath "$searchPath"
    
    # Changes the /Search and /Search/Contacts path type from Custom to Automatic
    
    /usr/bin/dscl /Search -change . SearchPolicy dsAttrTypeStandard:CSPSearchPath dsAttrTypeStandard:NSPSearchPath
    /usr/bin/dscl /Search/Contacts -change . SearchPolicy dsAttrTypeStandard:CSPSearchPath dsAttrTypeStandard:NSPSearchPath
}

PasswordMigration(){

    # macOS 10.14.4 will remove the the actual ShadowHashData key immediately 
    # if the AuthenticationAuthority array value which references the ShadowHash
    # is removed from the AuthenticationAuthority array. To address this, the
    # existing AuthenticationAuthority array will be modified to remove the Kerberos
    # and LocalCachedUser user values.
 

    AuthenticationAuthority=$(/usr/bin/dscl -plist . -read /Users/$netname AuthenticationAuthority)
    Kerberosv5=$(echo "${AuthenticationAuthority}" | xmllint --xpath 'string(//string[contains(text(),"Kerberosv5")])' -)
    LocalCachedUser=$(echo "${AuthenticationAuthority}" | xmllint --xpath 'string(//string[contains(text(),"LocalCachedUser")])' -)
    
    # Remove Kerberosv5 and LocalCachedUser
    if [[ ! -z "${Kerberosv5}" ]]; then
        /usr/bin/dscl -plist . -delete /Users/$netname AuthenticationAuthority "${Kerberosv5}"
    fi
    
    if [[ ! -z "${LocalCachedUser}" ]]; then
        /usr/bin/dscl -plist . -delete /Users/$netname AuthenticationAuthority "${LocalCachedUser}"
    fi
}

RunAsRoot "${0}"

# Check for AD binding and offer to unbind if found. 
if [[ "${check4AD}" = "Active Directory" ]]; then
#		Removed the Select menu, requesting user input in order to remove AD binding. Left only the command to remove the AD binding
		RemoveAD; /bin/echo "AD binding has been removed.";
			
fi
	
	until [ "$user" == "FINISHED" ]; do
		
#		Changed the Select menu into a for loop, leaving all the commands inside unchanged. 
#		That way they get executed automatically without the need of user input
		for netname in $listUsers; do
			
			if [ "$netname" = "FINISHED" ]; then
				/bin/echo "Finished converting users to local accounts"
				exit 0
			fi
			
			accounttype=`/usr/bin/dscl . -read /Users/"$netname" AuthenticationAuthority | head -2 | awk -F'/' '{print $2}' | tr -d '\n'`
			
			if [[ "$accounttype" = "Active Directory" ]]; then
				mobileusercheck=`/usr/bin/dscl . -read /Users/"$netname" AuthenticationAuthority | head -2 | awk -F'/' '{print $1}' | tr -d '\n' | sed 's/^[^:]*: //' | sed s/\;/""/g`
				if [[ "$mobileusercheck" = "LocalCachedUser" ]]; then
					/usr/bin/printf "$netname has an AD mobile account.\nConverting to a local account with the same username and UID.\n"
				else
					/usr/bin/printf "The $netname account is not a AD mobile account\n"
					break
				fi
			else
				/usr/bin/printf "The $netname account is not a AD mobile account\n"
				break
			fi

			
			# Remove the account attributes that identify it as an Active Directory mobile account
			
			/usr/bin/dscl . -delete /users/$netname cached_groups
			/usr/bin/dscl . -delete /users/$netname cached_auth_policy
			/usr/bin/dscl . -delete /users/$netname CopyTimestamp
			/usr/bin/dscl . -delete /users/$netname AltSecurityIdentities
			/usr/bin/dscl . -delete /users/$netname SMBPrimaryGroupSID
			/usr/bin/dscl . -delete /users/$netname OriginalAuthenticationAuthority
			/usr/bin/dscl . -delete /users/$netname OriginalNodeName
			/usr/bin/dscl . -delete /users/$netname SMBSID
			/usr/bin/dscl . -delete /users/$netname SMBScriptPath
			/usr/bin/dscl . -delete /users/$netname SMBPasswordLastSet
			/usr/bin/dscl . -delete /users/$netname SMBGroupRID
			/usr/bin/dscl . -delete /users/$netname PrimaryNTDomain
			/usr/bin/dscl . -delete /users/$netname AppleMetaRecordName
			/usr/bin/dscl . -delete /users/$netname PrimaryNTDomain
			/usr/bin/dscl . -delete /users/$netname MCXSettings
			/usr/bin/dscl . -delete /users/$netname MCXFlags

			# Migrate password and remove AD-related attributes
           
			PasswordMigration

			# Refresh Directory Services
			if [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -lt 7 ) ]]; then
				/usr/bin/killall DirectoryService
			else
				/usr/bin/killall opendirectoryd
			fi
			
			sleep 20
			
			accounttype=`/usr/bin/dscl . -read /Users/"$netname" AuthenticationAuthority | head -2 | awk -F'/' '{print $2}' | tr -d '\n'`
			if [[ "$accounttype" = "Active Directory" ]]; then
			   /usr/bin/printf "Something went wrong with the conversion process.\nThe $netname account is still an AD mobile account.\n"
			   exit 1
			 else
			   /usr/bin/printf "Conversion process was successful.\nThe $netname account is now a local account.\n"
			fi
			
			homedir=`/usr/bin/dscl . -read /Users/"$netname" NFSHomeDirectory  | awk '{print $2}'`
			if [[ "$homedir" != "" ]]; then
			   /bin/echo "Home directory location: $homedir"
			   /bin/echo "Updating home folder permissions for the $netname account"
			   /usr/sbin/chown -R "$netname" "$homedir"		
			fi
			
			# Add user to the staff group on the Mac
			
			/bin/echo "Adding $netname to the staff group on this Mac."
			/usr/sbin/dseditgroup -o edit -a "$netname" -t user staff
			
			
			/bin/echo "Displaying user and group information for the $netname account"
			/usr/bin/id $netname
			
# 		Removed the two user inputs: 1) prompting the end-user to confirm whether the
#		local account should be given admin rights, and 2) the Select menu
#		to choose either yes or no. Left simply the command, that way 
# 		it gets executed automatically, without the need of user input.
			
		/usr/sbin/dseditgroup -o edit -a "$netname" -t user admin; /bin/echo "Admin rights given to this account"; 

	done
done

 

Malcolm
Contributor II

Thanks, it seems to collect non Active Directory Users, and loops on them in this section, post the script running.

				/usr/bin/printf "The $netname account is not a AD mobile account

I think I can potentially fix it though.

Malcolm
Contributor II

seemingly the until command was forcing it to recheck

I've changed

 

	until [ "$user" == "FINISHED" ]; do

 

 

 

for netname in $listUsers; do

 

Not sure why at this point re running the script was collecting non active directory users post a successful run.

just a few more tests to be done this change and I thin I can pull the trigger on its use, for our exit process, and also to help migrate staff over to Jamf connect

my best guess it is a cached variable from the first successful run.