Surface Go 1 can't access fog host variables in FOS during postdownload scripts
-
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’sFor 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 thisIn 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 setaddomain='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
-
TL;DR
Some variables behaving weirdly in postdownload scripts on certain hardware.
-
Hi @JJ-Fullmer, I will take a dive into this when I have a bit more time soon.
-
@JJ-Fullmer Ok, lets get into this. First of all I have to say you have build a great tool chain around FOG. Amazing! Well done.
Sounds really strange from what you describe so far. I have never heard from environment and bash in general acting differently on Surface Go 1 or being hardware specific at all.
Could it be something you do even earlier in the process (bootup) that could be specific to the Surface Go 1?
From what you posted I do see environment variables I have never heard of before:
$domainJoinStr
,$unattend
and many more. So I guess you have a modifiedfog/service/ipxe/boot.php
(actuallybootmenu.class.php
) file, right?Let’s start by looking at the different kernel cmdline. Boot up a system not showing the issue, run
cat /proc/cmdline
and post output here. Then do the same thing in a debug session on the Surface Go 1. -
@Sebastian-Roth The variables you don’t recognize are part of post installation scripts, I haven’t modified any php classes.
I will see if I can get that output on monday, we’ve now deployed all of our surface go 1’s back to their users but I can probably nab one for a few minutes if I get in early enough.
I can also try a different ipxe boot file for good measure. I use ipxe.efi maybe snponly.efi will load things differently and yield different results
-
@Sebastian-Roth Just an FYI, I ran into this again on different hardware. An HP Z2 G4 Workstation did the same thing.
There may have been more to it though, still troubleshooting a bit. My failsafe in the postdownload script to have it still join the domain through the unattend file was injected into the file, but the computer did not join the domain through that method, there may have been a different reason for that though.Point being, there could be other hardware that weirdly doesn’t get all the variables. I sadly can’t super test on this hardware as I discovered it when imaging a production machine that I can’t take out of production for testing. But I can probably find a chance to get the fos level device info or something.