Page tree
Skip to end of metadata
Go to start of metadata

External links:

NOMAD Deployment scripts

  • Tech support uses an account with "staging user" rights to enter the notebook in the domain:


Add macOS to domain AND assign it a special name...
#!/bin/sh

#------------------------------------------------------------------------------
# Script for entering PC with MacOS X in Active Directory 
#------------------------------------------------------------------------------
#-------------------------LIBRARY IMPORT---------------------------------
readonly PackageId="00006"; #ID of packet DOMAIN-NAME
readonly ProgramName="AW_join_Mac_to_domain"; #App name
readonly isWriteLog=true; #False - do not log in journal
#isWriteSrvLog = true #Write log on server
readonly libLclLogName=""; #name of local journal file
readonly libDebug=false; # function load process
#------------------------MODULE IMPORT-----------------------------------
# no modules
#------------------------------------------------------------------------------
pathlogfile="/var/log";
if [ -n "$OSD" ]; then
	logfile="$OSD";
else
	logfile="$PackageId-$ProgramName.log";
fi;

TAG="`echo $ProgramName | tr [a-z] [A-Z]`";
echo "[$TAG] Configured" >> $pathlogfile/$logfile;
# Basic Windows AD options
#echo "Enter account name:";
#read domainadmin;
var="`dsconfigad --show`";
if [ ${#var} -gt 1 ]; then
	echo "ComputerName=`scutil --get ComputerName`" >> $pathlogfile/$logfile;
	echo "LocalHostName=`scutil --get LocalHostName`" >> $pathlogfile/$logfile;
	echo "HostName=`scutil --get HostName`" >> $pathlogfile/$logfile;
	echo "Dsconfigad=" >> $pathlogfile/$logfile;
	echo "`dsconfigad --show`" >> $pathlogfile/$logfile;
	for element in `ls -1 /Users/`; do
		echo "${element}" >> $pathlogfile/$logfile;
		if [ "${element}" != ".localized" ] && [ "${element}" != "Shared" ] && [ "${element}" != "Guest" ]; then
			echo "PC was entered in domain earlier." > "/Users/${element}/Desktop/Important_message.txt";
		fi;
	done;
	exit 0;
fi;


# Advanced AD options
alldomains="enable"; # 'enable' or 'disable' automatic multi-domain authentication
localhome="enable"; # 'enable' or 'disable' force home directory to local drive
protocol="smb"; # 'afp' or 'smb' change how home is mounted from server
mobile="enable"; # 'enable' or 'disable' mobile account support for offline logon
mobileconfirm="enable"; # 'enable' or 'disable' warn the user that a mobile acct will be created
useuncpath="enable"; # 'enable' or 'disable' use AD SMBHome attribute to determine the home dir
user_shell="/bin/bash"; # e.g., /bin/bash or "none"
preferred="10.34.240.19"; # nopreferred
admingroups=("Domain Admins" "Enterprise Admins" "Techsupport"); # These comma-separated AD groups may administer the machine (e.g. "" or "APPLE\mac admins")
packetsign="allow"; # allow | disable | require
packetencrypt="allow"; # allow | disable | require
passinterval="99"; # number of days
namespace="domain"; # forest | domain
### End of configuration

# -- Get Mask = TB
if [ -e "/companybin/TB.txt" ]; then
	TB="`cat /companybin/TB.txt`";
	if [ ${#TB} -lt 1 ]; then
		echo "Not get TB code, but get TB.txt" >> $pathlogfile/$logfile;
		exit 1;
	fi;
else
	echo "Not found file TB.txt" >> $pathlogfile/$logfile;
	exit 2;
fi;
MODELTYPE="`system_profiler SPHardwareDataType 2>/dev/null | grep \"Model\ Identifier\" | tr -d \"\ \" | awk -F\"\:\" '{print $2}'`";
echo "MODELTYPE=$MODELTYPE" >> $pathlogfile/$logfile;
case "`echo $MODELTYPE | tr [A-Z] [a-z]`" in
	*server*)
		
		#№TYPECOMPUTER="S";
		TYPECOMPUTER="M";
	;;
	*book*)
		#TYPECOMPUTER="N";
		TYPECOMPUTER="M";
	;;
	*)
		#TYPECOMPUTER="D";
		TYPECOMPUTER="M";
	;;
esac;
if [ ${#TYPECOMPUTER} -lt 1 ]; then
	echo "Not get TYPECOMPUTER" >> $pathlogfile/$logfile;
	exit 2;
fi;

# -- Look up Computer name in pre-arranged MacNames.plist file using the MAC address as the key
# -- computerid=$(defaults read /Library/Management/Macnames $MACaddress)

# -- Get loggedUserName
# -- loggedInUser=$(ls -l /dev/console | awk '{print $3}');

# -- If all else fails set temporary fallback name appending last 6 digits of MAC
# -- if [ "${#domainadmin}" -gt 0 ]; then
# --    computerid="${domainadmin:0:3}-WSN-${MACaddress:9:2}${MACaddress:12:2}${MACaddress:15:2}";
# --    echo "computerid=${computerid}";
# -- fi;

# -- sudo dsconfigad -f -r -u "${udn}" -p "${password}" >> $pathlogfile/$logfile 2>&1;

echo "[$TAG] Setting " >> $pathlogfile/$logfile;

# Get name from special name generator web-server
curl -s http://server.company.com/osd-ws/sccm.asmx/CheckFreeNamePCinAD_IP?Mask=${TB}-WS${TYPECOMPUTER}'&'Domain=${domain}'&'ipaddress= -o /tmp/pcname.txt;

for i in "`cat /tmp/pcname.txt | tr -d \"\\\"\\\n\"`"; do
	if [ "${#i}" -gt 15 ]; then
		simbol="`echo \"${i}\" | cut -c34`";
		if [ "$simbol" == ">" ]; then
			computerid="`echo \"${i}\" | cut -c70-84`";
		else
			computerid="`echo \"${i}\" | cut -c35-49`";
		fi;
	fi;
done;

echo "computerid=${computerid}"  >> $pathlogfile/$logfile;

if [ ${#computerid} -lt 1 ] and [ ${#computerid} -gt 16 ]; then
	echo "Not get name PC!!!" >> $pathlogfile/$logfile;
	exit 3;
fi;

# -- computerid="`scutil --get LocalHostName`";
echo "Computer LocalHostName get to: ${computerid}" >> $pathlogfile/$logfile;

# -- set the user visible computer name
scutil --set ComputerName "${computerid}" >> $pathlogfile/$logfile 2>&1;
verify="`scutil --get ComputerName`";
if [ "$verify" != "${computerid}" ]; then
	echo "Set ComputerName failed - aborting" >> $pathlogfile/$logfile;
	exit 4;
fi;

# -- set the Bonjour local host name
scutil --set LocalHostName "${computerid}" >> $pathlogfile/$logfile 2>&1;
verify="`scutil --get LocalHostName`";
if [ "$verify" != "${computerid}" ]; then 
	echo "Set LocalHostName failed - aborting" >> $pathlogfile/$logfile;
	exit 5;
fi;

# -- set the network Host name (i.e. the account for this computer in network directories)
scutil --set HostName "${computerid}" >> $pathlogfile/$logfile 2>&1;
verify="`scutil --get HostName`";
if [ "$verify" != "${computerid}" ]; then 
	echo "Set HostName failed - aborting" >> $pathlogfile/$logfile;
	exit 6;
fi;

echo "Computer names set to: ${computerid}" >> $pathlogfile/$logfile;

# -- disable the local kerberos key distribution center to avoid possible conflict with AD
# -- This was fixed in SIU 10.5.6 so this should not really be necessary...unless said fix is thwarted.
# -- dscl /Local/Default delete /Config/KerberosKDC

# -- Kickstart ARD. Ensure remote management is possible.
# -- /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/ki ckstart -configure -allowAccessFor -allUsers -privs -all;
# -- /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/ki ckstart -activate;

# -- Ensure that the LDAP service plugin is Active
# -- defaults write /Library/Preferences/DirectoryService/DirectoryService "LDAPv3" "Active" >> $pathlogfile/$logfile 2>&1;
# -- plutil -convert xml1 /Library/Preferences/DirectoryService/DirectoryService.plist >> $pathlogfile/$logfile 2>&1;
# -- Ensure that the AD service plugin is Active
# defaults write /Library/Preferences/DirectoryService/DirectoryService "Active Directory" "Active" >> $pathlogfile/$logfile 2>&1;
# plutil -convert xml1 /Library/Preferences/DirectoryService/DirectoryService.plist >> $pathlogfile/$logfile 2>&1;

# -- Bind to Windows Active Directory
# -- echo "dsconfigad -a \"${computerid}\" -domain \"${domain}\" -u \"${udn}\" -p \"${password}\" -ou \"${ou}\""  >> $pathlogfile/$logfile 2>&1;
dsconfigad -a "${computerid}" -domain "${domain}" -u "${udn}" -p "${password}" -ou "${ou}" >> $pathlogfile/$logfile 2>&1;

# -- echo "Return code from first Windows AD Bind was: $?" >> $pathlogfile/$logfile;

# -- Bind to AD again to make sure (a return code = 17 implies the first bind really worked)
# -- dsconfigad -a "${computerid}" -domain "${domain}" -u "${udn}" -p "${password}" -ou "${ou}" >> $pathlogfile/$logfile 2>&1;
# -- if [ "$?" -ne 17 ]; then
# -- 	echo "Return code from second AD bind was: $? - Aborting" >> $pathlogfile/$logfile;
# -- 	exit 1;
# -- fi;

# -- Configure advanced AD plugin options
if [ "$admingroups" == "" ]; then
	dsconfigad -nogroups >> $pathlogfile/$logfile 2>&1;
else
	for i in "${admingroups[@]}"; do
		if [ ${#group} -lt 1 ]; then
			group="${i}";
		else
			group="${group},${i}";
		fi;
	done;
	echo "dsconfigad -groups \"$group\"" >> $pathlogfile/$logfile 2>&1;
	dsconfigad -groups "$group" >> $pathlogfile/$logfile 2>&1;
fi;

# echo "dsconfigad -alldomains \"$alldomains\" -localhome \"$localhome\" -protocol \"$protocol\" -mobile \"$mobile\" -mobileconfirm \"$mobileconfirm\" -useuncpath \"$useuncpath\" -shell \"$user_shell\" \"$preferred\" -packetsign \"$packetsign\" -packetencrypt \"$packetencrypt\" -passinterval \"$passinterval\" -namespace \"$namespace\"" >> $pathlogfile/$logfile;

dsconfigad -alldomains "$alldomains" -localhome "$localhome" -protocol "$protocol" -mobile "$mobile" -mobileconfirm "$mobileconfirm" -useuncpath "$useuncpath" -shell "$user_shell" "$preferred" -packetsign "$packetsign" -packetencrypt "$packetencrypt" -passinterval "$passinterval" -namespace "$namespace" >> $pathlogfile/$logfile 2>&1;
if [ "$?" -ne 0 ]; then
	echo "Return code from dsconfigad advanced configuration was: $? - Aborting" >> $pathlogfile/$logfile;
	exit 1;
fi;

# -- Add the AD node to the search path
if [ "$alldomains" == "enable" ]; then
	csp="/Active Directory/All Domains";
else
	csp="/Active Directory/$domain";
fi;

# -- dscl -q localhost -merge /Search CSPSearchPath "$csp";
echo "Search Path updated for AD" >> $pathlogfile/$logfile;

# -- restart the service to ensure all config changes are picked up
killall DirectoryService >> $pathlogfile/$logfile 2>&1;

for element in `ls -1 /Users/`; do
	echo "${element}" >> $pathlogfile/$logfile;
	if [ "${element}" != ".localized" ] && [ "${element}" != "Shared" ] && [ "${element}" != "Guest" ]; then
		echo "PC entering Domain procedure finished." > "/Users/${element}/Desktop/Important_Message.txt";
	fi;
done;
echo "[$TAG] done" >> $pathlogfile/$logfile;

exit 0;


  • The script also provides macOS notebook a <special name> from a dedicated web-server 
  • Every 10 minutes with cron/Windows scheduler a Python script uses AirWatch API to look through all devices. It checks their names, and for all macbooks with correct name itassigns a tag = "MacCorp"


Example script, assigning tags to Supervized devices...
#!/usr/bin/env python

import logging
import os 
import requests
import smtplib
import sys
import json

###### variables 
consoleURL = 'https://airwatch-api-server.company.com'
b64EncodedAuth = 'YWlyd2F0Y2hhcGkuY29tcGFueS5jb21cdGVjaG5pY2FsLWFjY291bnQ6UGFzc3dvcmQxMjMxMjM=' #Base64Encoded: airwatchapi.company.com\technical-account:Password123123
tenantCode = 'ozmFrMmgSet3v+Kcxp2YW8wo6EMw7dQ8jvQGNejCWpk=' # received from AirWatch Console

header = {"Authorization": "Basic " + b64EncodedAuth, 
		 "aw-tenant-code": tenantCode,
		 "Accept": "application/json"}

request = '/api/mdm/devices/search'

data = ''

# find all devices 

awTest = requests.get(consoleURL + request, headers=header)
	
json_data = awTest.json()

#print json_data.values()

with open('data.txt', 'w') as f:
	json.dump(json_data, f)

print("******************")
print ("Number of devices: " + str(len(json_data["Devices"])))

print("******************\n")
#print json_data

# printing results 
for i in json_data["Devices"]:
	if i["Platform"] == "Apple" and i["IsSupervised"] == False and i["EnrollmentStatus"] == "Enrolled":
		idd = i["Id"]["Value"]
		#print (str(idd) + "  *  ID")
		data = {"BulkValues":{"Value":[idd]}}
		#print data

		requests.post(consoleURL + "/api/mdm/tags/10003/adddevices", json=data, headers = header)		
		print (i["EnrollmentStatus"])
		print (i["DeviceFriendlyName"])
		print (i["UserName"])
		print ('__________________')
		print ('device id ' + str(i["Id"].values()))
		print (i["Platform"])
		print ("supervise is " + str(i["IsSupervised"]))
		print ("---------\n\n")


  • All devices with MacCorp tag are auto-assigned to "MacCorp" Smart Group
  • An AirWatch Profile is created, with Assignment to "MacCorp" Smart Group, which installs Nomad.pkg package: the package is created using Apps & Books → Internal App → Upload, and then parsing the Nomad.pkg using VMware Assistant for Munki, and uploading the resulting Nomad.plist file in the Upload wizard
  • A post-install script is used, which initializes the Nomad package with another plist with domain and Kerberos parameters. The plist file must be placed in the folder, where Nomad will find it


Post-install script...
#!/bin/bash

# Script: AirWatch Console Package Module post-script - NoMAD installation

readonly ProgramName="NoMAD-install"; #program name
readonly isWriteLog=true; #False - logging ON/OFF
#isWriteSrvLog = true # write log to server
readonly libLclLogName=""; #log name
readonly libDebug=false; # function load process

pathlogfile="/var/log";
if [ ! -n "$OSD" ]; then
	logfile="$ProgramName.log";
else
	logfile="$OSD";
fi;

TAG="`echo $ProgramName | tr [a-z] [A-Z]`";
echo "[$TAG] Starting..." >> $pathlogfile/$logfile;
/bin/bash /tmp/AirWatch_NoMAD_install.sh >> $pathlogfile/$logfile;
/bin/rm /tmp/AirWatch_NoMAD_install.sh >> $pathlogfile/$logfile;
echo "[$TAG] Done." >> $pathlogfile/$logfile;

exit 0;
Nomad initialize script...
#!/bin/bash

# NoMAD Deploy script

readonly ProgramName="NoMAD-install"; #program name
readonly isWriteLog=true; #False - logging ON/OFF
#isWriteSrvLog = true # write log to server
readonly libLclLogName=""; #log name
readonly libDebug=false; # function load process

pathlogfile="/var/log";
if [ ! -n "$OSD" ]; then
	logfile="$ProgramName.log";
else
	logfile="$OSD";
fi;

TAG="`echo $ProgramName | tr [a-z] [A-Z]`";
echo "[$TAG] Install" >> $pathlogfile/$logfile;

PathPackage="/tmp";
NamePackage="NoMAD.pkg";
Package="$PathPackage/$NamePackage";
/usr/sbin/installer -pkg $Package -target / -verboseR >> $pathlogfile/$logfile 2>&1;
/bin/rm $Package >> $pathlogfile/$logfile 2>&1;
NamePackage="NoMAD-LaunchAgent.pkg";
Package="$PathPackage/$NamePackage";
/usr/sbin/installer -pkg $Package -target / -verboseR >> $pathlogfile/$logfile 2>&1;
/bin/rm $Package >> $pathlogfile/$logfile 2>&1;
NamePackage="com.trusourcelabs.NoMAD.plist";
Package="$PathPackage/$NamePackage";
cp $Package /Library/Preferences/ >> $pathlogfile/$logfile 2>&1;
/bin/rm $Package >> $pathlogfile/$logfile 2>&1;

echo "[$TAG] Done" >> $pathlogfile/$logfile;
exit 0;
com.trusourcelabs.NoMAD.plist - NOMAD Parameters plist...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ADDomain</key>
<string>DOMAIN.COMPANY.COM</string>
<key>ChangePasswordType</key>
<string>Kerberos</string>
<key>DontShowWelcome</key>
<string>1</string>
<key>KerberosRealm</key>
<string>DOMAIN.COMPANY.COM</string>
<key>LocalPasswordSync</key>
<string>1</string>
<key>PasswordExpireAlertTime</key>
<string>259200</string>
<key>RenewTickets</key>
<string>1</string>
<key>SecondsToRenew</key>
<string>7200</string>
<key>ShowHome</key>
<string>1</string>
<key>Template</key>
<string>User Auth</string>
<key>UseKeychain</key>
<string>1</string>
<key>Verbose</key>
<string>0</string>
<key>X509CA</key>
<string></string>
</dict>
</plist>


  • After deployment, a user logsin to macOS and sees Nomad icon in system tray. He can choose Synchronize option in menu, and his domain password will be synced with local user password
  • Now user will enter macOS with LOCAL username and DOMAIN password. If password changes in Active Directory, it will be synced with local macOS password.