So we’re using a postdownload script to inject ad join information into an unattend file.
This works great except on first generation microsoft surface go’s
For some reason they are missing the host specific variables during imaging
i.e., if I start a debug imaging task on a VM and run $
then tab to view all defined variables I see something like this
In a debug session on a surface go 1, I get only the build-int all caps system variables.
If in the same debug session I manually load the funcs.sh
source /usr/share/fog/lib/funcs.sh
[Tue Jan 31 root@fogclient /images/postdownloadscripts]# $
$BASH $EUID $PAGER $UID $isdebug
$BASHOPTS $GROUPS $PATH $USER $ismajordebug
$BASHPID $HISTCMD $PIGZ_COMP $_ $loglevel
$BASH_ALIASES $HISTFILE $PIPESTATUS $addomain $mac
$BASH_ARGC $HISTFILESIZE $PPID $adon $netbiosdomain
$BASH_ARGV $HISTSIZE $PS1 $adou $osid
$BASH_ARGV0 $HOME $PS2 $adpass $ramdisk_size
$BASH_CMDS $HOSTNAME $PS4 $aduser $root
$BASH_COMMAND $HOSTTYPE $PWD $chkdsk $rootfstype
$BASH_LINENO $IFS $RANDOM $consoleblank $storage
$BASH_SOURCE $LINENO $REG_LOCAL_MACHINE_7 $domainJoinStr $storageip
$BASH_SUBSHELL $LINES $REG_LOCAL_MACHINE_XP $ftp $type
$BASH_VERSINFO $LOGNAME $SECONDS $hostearly $unattend
$BASH_VERSION $MACHTYPE $SHELL $hostname $unattends
$COLUMNS $MAIL $SHELLOPTS $img $var
$COMP_WORDBREAKS $MAILCHECK $SHLVL $imgFormat $web
$DIRSTACK $OLDPWD $SSH_CLIENT $imgPartitionType
$EDITOR $OPTERR $SSH_CONNECTION $imgType
$EPOCHREALTIME $OPTIND $SSH_TTY $imgid
$EPOCHSECONDS $OSTYPE $TERM $initrd
I can then see the variables however, $addomain
is null when it should not be. The other ad related variables are populated.
But then if I manually set addomain='mydomain.com'
or try putting that into my postdownload script, it still gets lost while running the postdownload script, which only occurs on this hardware.
If I load the funcs.sh, set the addomain variable manually, and then paste in just the sed commands I have for updating the unattend file, it works as it should. But this requires a debug session and a lot of manual effort.
so if I run
. /usr/share/fog/lib/funcs.sh
#I typically do this in a loop of all standard unattend paths. like this
#unattends=("/ntfs/Unattend.xml" "/ntfs/Windows/System32/Sysprep/Unattend.xml" "/ntfs/Windows/Panther/unattend.xml" "/ntfs/Windows/Panther/Unattend.xml")
#but for testing purposes, (and since pasting longer snippets into a ssh bash shell can be finicky) just doing one path
unattend="/ntfs/Windows/System32/Sysprep/Unattend.xml"
cp $unattend $unattend.old
domainJoinStr="<JoinDomain></JoinDomain>\n\t\t<MachineObjectOU></MachineObjectOU>\n\t\t<Credentials>\n\t\t\t<Domain></Domain>\n\t\t\t<Password></Password>\n\t\t\t<Username></Username>\n\t\t</Credentials>"
echo -en "\n\nInjecting Unattend Join fields into unattend for Dynamic update....\n"
echo -en "\n\nInjecting Unattend Join fields into unattend for Dynamic update....\n" >> $updateUnattendLog
# get the value of the workgroup to set as the netbios domain for the domain login
netbiosdomain=`sed -n '/JoinWorkgroup/{s/.*<JoinWorkgroup>//;s/<\/JoinWorkgroup.*//;p;}' $unattend`
#replace the workgroup join string with the domain tags to be updated
sed -i -e "s|<JoinWorkgroup>${netbiosdomain}</JoinWorkgroup>|${domainJoinStr}|g" $unattend >/dev/null 2>&1
echo -en "\n\nSetting Dynamic Unattend fields - \n\nDeviceForm: ${DeviceForm}\nComputer Name: ${hostname}\nJoining Domain: ${addomain}\nWill be in OU: ${adou}\n"
echo -en "\n\nSetting Dynamic Unattend fields - \n\nDeviceForm: ${DeviceForm}\nComputer Name: ${hostname}\nJoining Domain: ${addomain}\nWill be in OU: ${adou}\n" >> $updateUnattendLog
sed -i \
-e "s|<DeviceForm>3</DeviceForm>|<DeviceForm>${DeviceForm}</DeviceForm>|g" \
-e "s|<ComputerName></ComputerName>|<ComputerName>${hostname}</ComputerName>|g" \
-e "s|<Name>\*</Name>|<Name>${hostname}</Name>|g" \
-e "s|<Password></Password>|<Password>${adpass}</Password>|g" \
-e "s|<Username></Username>|<Username>${aduser}</Username>|g" \
-e "s|<Domain></Domain>|<Domain>${netbiosdomain}</Domain>|g" \
-e "s|<MachineObjectOU></MachineObjectOU>|<MachineObjectOU>${adou}</MachineObjectOU>|g" \
-e "s|<JoinDomain></JoinDomain>|<JoinDomain>${addomain}</JoinDomain>|g" $unattend >/dev/null 2>&1
echo -en "\n\nRemoving Workgroup join section and backup unattend as adding domain join was a success...\n"
echo -en "\n\nRemoving Workgroup join section and backup unattend as adding domain join was a success...\n" >> $updateUnattendLog
rm -f $unattend.old
sed -i "/<JoinWorkgroup>/d" $unattend >/dev/null 2>&1
sed -i "/<MachinePassword>/d" $unattend >/dev/null 2>&1
This works, but running the same from a postdownload script loses the funcs.sh functions and some of the ad related variables. Luckily the aduser and adpass variables work so I can hardcode in a failsafe of my domain and a default ou, but putting a machine in the wrong ou then requires additional failsafes to be sure it gets the correct policies, which I do also have for my provisioning process, but things run much smoother when it joins the correct ou from the get go.
Any thoughts on this weird one?
Also, here is how the script is called within postdownload scripts.
fog.postdownload is called
#!/bin/sh
## This file serves as a starting point to call your custom postimaging scripts.
## <SCRIPTNAME> should be changed to the script you're planning to use.
## Syntax of post download scripts are
#. ${postdownpath}<SCRIPTNAME>
bash ${postdownpath}fog.custominstall
fog.custominstall calls my driver injector script (which is working fine for this) and then my unattend editor
#!/bin/bash
. /usr/share/fog/lib/funcs.sh
[[ -z $postdownpath ]] && postdownpath="/images/postdownloadscripts/"
echo $osid
case $osid in
5|6|7|9)
clear
[[ ! -d /ntfs ]] && mkdir -p /ntfs
getHardDisk
if [[ -z $hd ]]; then
handleError "Could not find hdd to use"
fi
getPartitions $hd
for part in $parts; do
umount /ntfs >/dev/null 2>&1
fsTypeSetting "$part"
case $fstype in
ntfs)
dots "Testing partition $part"
ntfs-3g -o force,rw $part /ntfs
ntfsstatus="$?"
if [[ ! $ntfsstatus -eq 0 ]]; then
echo "Skipped"
continue
fi
if [[ ! -d /ntfs/windows && ! -d /ntfs/Windows && ! -d /ntfs/WINDOWS ]]; then
echo "Not found"
umount /ntfs >/dev/null 2>&1
continue
fi
echo "Success"
break
;;
*)
echo " * Partition $part not NTFS filesystem"
;;
esac
done
if [[ ! $ntfsstatus -eq 0 ]]; then
echo "Failed"
debugPause
handleError "Failed to mount $part ($0)\n Args: $*"
fi
echo "Done"
debugPause
. ${postdownpath}fog.copydrivers
. ${postdownpath}fog.updateunattend
debugPause
umount /ntfs
;;
*)
echo "Non-Windows Deployment"
debugPause
return
;;
esac
Then this is the contents of fog.updateunattend with edits for information sensitivity
#!/bin/bash
# Value Device form Description
# 0 Unknown Use this value if your device does not fit into any of the other device form factors.
# 1 Phone A typical smartphone combines cellular connectivity, a touch screen, rechargeable power source, and other components into a single chassis.
# 2 Tablet A device with an integrated screen that's less than 18". It combines a touch screen, rechargeable power source, and other components into a single chassis with an optional attachable keyboard.
# 3 Desktop A desktop PC form factor traditional comes in an upright tower or small desktop chassis and does not have an integrated screen.
# 4 Notebook A notebook is a portable clamshell device with an attached keyboard that cannot be removed.
# 5 Convertible A convertible device is an evolution of the traditional notebook where the keyboard can be swiveled, rotated or flipped, but not completely removed. It is a blend between a traditional notebook and tablet, also called a 2-in-1.
# 6 Detachable A detachable device is an evolution of the traditional notebook where the keyboard can be completely removed. It is a blend between a traditional notebook and tablet, also called a 2-in-1.
# 7 All-in-One An All-in-One device is an evolution of the traditional desktop with an attached display.
# 8 Stick PC A device that turns your TV into a Windows computer. Plug the stick into the HDMI slot on the TV and connect a USB or Bluetooth keyboard or mouse.
# 9 Puck A small-size PC that users can use to plug in a monitor and keyboard.
# A Surface Hub Microsoft Surface Hub
# B Head-mounted display A holographic computer that is completely untethered - no wires, phones, or connection to a PC needed.
# C Industry handheld A device screen less than 7” diagonal designed for industrial solutions. May or may not have a cellular stack.
# D Industry tablet A device with an integrated screen greater than 7” diagonal and no attached keyboard designed for industrial solutions as opposed to consumer personal computer. May or may not have a cellular stack.
# E Banking A machine at a bank branch or another location that enables customers to perform basic banking activities including withdrawing money and checking one's bank balance.
# F Building automation A controller for industrial environments that can include the scheduling and automatic operation of certain systems such as conferencing, heating and air conditioning, and lighting.
# 10 Digital signage A computer or playback device that's connected to a large digital screen and displays video or multimedia content for informational or advertising purposes.
# 11 Gaming A device that's used for playing a game. It can be mechanical, electronic, or electromechanical equipment.
# 12 Home automation A controller that can include the scheduling and automatic operation of certain systems including heating and air conditioning, security, and lighting.
# 13 Industrial automation Computers that are used to automate manufacturing systems such as controlling an assembly line where each station is occupied by industrial robots.
# 14 Kiosk An unattended structure that can include a keyboard and touch screen and provides a user interface to display interactive information and allow users to get more information.
# 15 Maker board A low-cost and compact development board that's used for prototyping any number IoT-related things.
# 16 Medical Devices built specifically to provide medical staff with information about the health and well-being of a patient.
# 17 Networking A device or software that determines where messages, packets, and other signals will go next.
# 18 Point of Service An electronic cash register or self-service checkout.
# 19 Printing A printer, copy machine, or a combination of both.
# 1A Thin client A device that connects to a server to perform computing tasks as opposed to running apps locally.
# 1B Toy A device used solely for enjoyment or entertainment.
# 1C Vending A machine that dispenses items in exchange for payment in the form of coin, currency, or credit/debit card.
# 1D Industry other A device that doesn't fit into any of the previous categories.
. /usr/share/fog/lib/funcs.sh
ceol=`tput el`;
make=`dmidecode -s system-manufacturer`;
make="${make%.*}";
updateUnattendLog="/ntfs/logs/updateUnattend.log"
touch $updateUnattendLog >/dev/null 2>&1
echo -en "\n\n Start of unattend injection log \n\n" > $updateUnattendLog;
if [[ "${make}" == "Hewlett-Packard" ]]; then make="hp"; fi
if [[ "${make}" == "HP" ]]; then make="hp"; fi
if [[ "${make}" == "Hp" ]]; then make="hp"; fi
if [[ "${make}" == "VMware, Inc" ]]; then make="VMware"; fi
dots "Identifying hardware model, make is ${make}"
case $make in
[Ll][Ee][Nn][Oo][Vv][Oo])
model=$(dmidecode -s system-version)
;;
*[Ii][Nn][Tt][Ee][Ll]* | *[Aa][Ss][Uu][Ss]*)
# For the Intel NUC and intel mobo pick up the system type from the
# baseboard product name
model=$(dmidecode -s baseboard-product-name)
;;
*)
# Technically, we can remove the Dell entry above as it is the same as this [default]
model=$(dmidecode -s system-product-name)
;;
esac
if [[ -z $model ]]; then
# if the model isn't identified then just stick with desktop
echo -en "\n\nUnable to identify the hardware for manufacturer ${make} assuming desktop form\n\n";
echo -en "\n\nUnable to identify the hardware for manufacturer ${make} assuming desktop form\n\n" >> $updateUnattendLog;
DeviceForm=3;
else
if [[ "${model}" == "Surface Go" ]]; then
echo -en "\n\nSurface Go will also match other generations of Surface Go, adding a 1\n\n"
echo -en "\n\nSurface Go will also match other generations of Surface Go, adding a 1\n\n" >> $updateUnattendLog;
model="Surface Go 1";
fi
echo -en "\n\n${model} Identified, determining device form\n\n";
case $model in
*[Aa][Ii][Oo]* | *[Ee]lite[Oo]ne* )
echo -en "\n\n${model} matches a known all-in-one form string, setting device form to all in one\n\n";
echo -en "\n\n${model} matches a known all-in-one form string, setting device form to all in one\n\n" >> $updateUnattendLog;
DeviceForm=7
;;
*Surface* | *Switch* | *SA5-271* | *SW512* | *SW312* )
echo -en "\n\n${model} matches a known detachable form string, setting device form to detachble\n\n";
echo -en "\n\n${model} matches a known detachable form string, setting device form to detachble\n\n" >> $updateUnattendLog;
DeviceForm=6
;;
*Convertible* | *Yoga* )
echo -en "\n\n${model} matches a known convertible form string, setting device form to convertible\n\n";
echo -en "\n\n${model} matches a known convertible form string, setting device form to convertible\n\n" >> $updateUnattendLog;
DeviceForm=5
;;
*ProBook* | *Zbook* | *ThinkPad* | *ThinkBook* | *Sattelite* | *IdeaPad* )
echo -en "\n\n${model} matches a known notebook form string, setting device form to notebook\n\n";
echo -en "\n\n${model} matches a known notebook form string, setting device form to notebook\n\n" >> $updateUnattendLog
DeviceForm=4
;;
*[Pp]ro[Dd]esk* | *[Ee]lite[Dd]esk* | *DM* | *Mini* | *Desktop* | *VMware* | *Twr* | *Tower* | *CMT* | *USDT* | *MT* | *ProArt* | *SFF*)
echo -en "\n\n${model} matches a known desktop form string, setting device form to desktop\n\n";
echo -en "\n\n${model} matches a known desktop form string, setting device form to desktop\n\n" >> $updateUnattendLog
DeviceForm=3
;;
* )
echo -en "\n\n${model} doesn't match any known deviceForm strings, assuming desktop device form\n\n";
echo -en "\n\n${model} doesn't match any known deviceForm strings, assuming desktop device form\n\n" >> $updateUnattendLog
DeviceForm=3
;;
esac
echo -en "\n\nDevice form code is ${DeviceForm}\n\n"
echo -en "\n\nDevice form code is ${DeviceForm}\n\n" >> $updateUnattendLog
fi
unattends=("/ntfs/Unattend.xml" "/ntfs/Windows/System32/Sysprep/Unattend.xml" "/ntfs/Windows/Panther/unattend.xml" "/ntfs/Windows/Panther/Unattend.xml")
for unattend in ${unattends[@]}; do
[[ ! -f $unattend ]] && break
. /usr/share/fog/lib/funcs.sh
dots "Preparing Sysprep File at $unattend"
#update unattend files if an Unattend.xml file is present
if [[ -f "/images/drivers/Unattend.xml" ]]; then
echo -en "\n\nUnattend.xml patch file detected, updating the Unattend.xml file baseline\n\n";
echo -en "\n\nUnattend.xml patch file detected, updating the Unattend.xml file baseline\n\n" >> $updateUnattendLog
rsync -aqzz "/images/drivers/Unattend.xml" $unattend;
else
echo -en "\n\nNo Unattend.xml patch file detected, skipping update of unattend.xml file baseline and just updating contents\n\n";
echo -en "\n\nNo Unattend.xml patch file detected, skipping update of unattend.xml file baseline and just updating contents\n\n" >> $updateUnattendLog
fi
#echo "File update Done"
debugPause
# [[ -z $addomain ]] && continue
#if there is a domain to join, then join it in the unattend, otherwise just set computername
if [[ $adon=="1" ]]; then
dots "Set PC to join the domain with correct name and OU"
if [[ -z $addomain ]]; then
#set the default domain to join if missing
addomain='mydomain.com'
fi
if [[ -z $adou ]]; then
#set the default ou if missing
adou='OU=default,DC=mydomain,DC=com'
fi
#make a backup of the unattend before editing
cp $unattend $unattend.old
domainJoinStr="<JoinDomain></JoinDomain>\n\t\t<MachineObjectOU></MachineObjectOU>\n\t\t<Credentials>\n\t\t\t<Domain></Domain>\n\t\t\t<Password></Password>\n\t\t\t<Username></Username>\n\t\t</Credentials>"
echo -en "\n\nInjecting Unattend Join fields into unattend for Dynamic update....\n"
echo -en "\n\nInjecting Unattend Join fields into unattend for Dynamic update....\n" >> $updateUnattendLog
# get the value of the workgroup to set as the netbios domain for the domain login
netbiosdomain=`sed -n '/JoinWorkgroup/{s/.*<JoinWorkgroup>//;s/<\/JoinWorkgroup.*//;p;}' $unattend`
#replace the workgroup join string with the domain tags to be updated
sed -i -e "s|<JoinWorkgroup>${netbiosdomain}</JoinWorkgroup>|${domainJoinStr}|g" $unattend >/dev/null 2>&1
echo -en "\n\nSetting Dynamic Unattend fields - \n\nDeviceForm: ${DeviceForm}\nComputer Name: ${hostname}\nJoining Domain: ${addomain}\nWill be in OU: ${adou}\n"
echo -en "\n\nSetting Dynamic Unattend fields - \n\nDeviceForm: ${DeviceForm}\nComputer Name: ${hostname}\nJoining Domain: ${addomain}\nWill be in OU: ${adou}\n" >> $updateUnattendLog
sed -i \
-e "s|<DeviceForm>3</DeviceForm>|<DeviceForm>${DeviceForm}</DeviceForm>|g" \
-e "s|<ComputerName></ComputerName>|<ComputerName>${hostname}</ComputerName>|g" \
-e "s|<Name>\*</Name>|<Name>${hostname}</Name>|g" \
-e "s|<Password></Password>|<Password>${adpass}</Password>|g" \
-e "s|<Username></Username>|<Username>${aduser}</Username>|g" \
-e "s|<Domain></Domain>|<Domain>${netbiosdomain}</Domain>|g" \
-e "s|<MachineObjectOU></MachineObjectOU>|<MachineObjectOU>${adou}</MachineObjectOU>|g" \
-e "s|<JoinDomain></JoinDomain>|<JoinDomain>${addomain}</JoinDomain>|g" $unattend >/dev/null 2>&1
if [[ ! $? -eq 0 ]]; then
echo -en "\n\nFailed to update user, pass, ou, and domain setter, set just computername and deviceform instead and using simplified unattend file\n"
echo -en "\n\nFailed to update user, pass, ou, and domain setter, set just computername and deviceform instead and using simplified unattend file\n" >> $updateUnattendLog
echo -en "\n\Restoring unattend file from before domain join attempt\n"
echo -en "\n\Restoring unattend file from before domain join attempt\n" >> $updateUnattendLog
mv $unattend.old $unattend -f
echo -en "\n\nSetting Dynamic Unattend fields - \n\nDeviceForm: ${DeviceForm}\nComputer Name: ${hostname}"
echo -en "\n\nSetting Dynamic Unattend fields - \n\nDeviceForm: ${DeviceForm}\nComputer Name: ${hostname}" >> $updateUnattendLog
#rsync -aqzz "/images/drivers/workgroup-unattend.xml" $unattend;
debugPause
sed -i \
-e "s|<DeviceForm>3</DeviceForm>|<DeviceForm>${DeviceForm}</DeviceForm>|g" \
-e "s|<ComputerName></ComputerName>|<ComputerName>${hostname}</ComputerName>|g" \
-e "s|<Name>\*</Name>|<Name>${hostname}</Name>|g" $unattend >/dev/null 2>&1
if [[ ! $? -eq 0 ]]; then
echo -en "\nFailed again after using failsafe unattend\n"
echo -en "\nFailed again after using failsafe unattend\n" >> $updateUnattendLog
debugPause
handleError "Failed to update user, pass, ou, and domain setter and then failed the failsafe with no domain"
fi
else
echo -en "\n\nRemoving Workgroup join section and backup unattend as adding domain join was a success...\n"
echo -en "\n\nRemoving Workgroup join section and backup unattend as adding domain join was a success...\n" >> $updateUnattendLog
rm -f $unattend.old
sed -i "/<JoinWorkgroup>/d" $unattend >/dev/null 2>&1
sed -i "/<MachinePassword>/d" $unattend >/dev/null 2>&1
if [[ ! $? -eq 0 ]]; then
echo "Failed"
debugPause
handleError "Failed to remove the Workgroup setter"
fi
fi
echo -en "\n\nDone updating $unattend\n"
echo -en "\n\nDone updating $unattend\n" >> $updateUnattendLog
debugPause
else
echo -en "\n\nNo domain to join variable present, just setting deviceform and computer name and using simplified unattend file\n"
echo -en "\n\nNo domain to join variable present, just setting deviceform and computer name and using simplified unattend file\n" >> $updateUnattendLog
echo -en "\n\nSetting Dynamic Unattend fields - \n\nDeviceForm: ${DeviceForm}\nComputer Name: ${hostname}"
echo -en "\n\nSetting Dynamic Unattend fields - \n\nDeviceForm: ${DeviceForm}\nComputer Name: ${hostname}" >> $updateUnattendLog
# rsync -aqzz "/images/drivers/workgroup-unattend.xml" $unattend;
debugPause
sed -i \
-e "s|<DeviceForm>3</DeviceForm>|<DeviceForm>${DeviceForm}</DeviceForm>|g" \
-e "s|<ComputerName></ComputerName>|<ComputerName>${hostname}</ComputerName>|g" \
-e "s|<Name>\*</Name>|<Name>${hostname}</Name>|g" $unattend >/dev/null 2>&1
if [[ ! $? -eq 0 ]]; then
echo "Failed"
debugPause
handleError "Failed to set workgroup join fields"
fi
fi
done