@HorizonG Short answer to both, yes.
There’s a bit of work to do to make it work but you can.
The first thing to know to help in full is what phase of sysprep you captured at?
If you captured right after the generalize phase (best practice) and specialize is what starts you can indeed update the unattend file dynamically with computer name, domain, ou, etc.
You can only effect the phases that haven’t happened yet. So you can add things to the specialize and oobe phases. Specialize does things before windows fully loads, it’s essentially a winpe environment, and oobe is the full windows where you can have a setupcomplete run. I have it kick off a series of powershell scripts (essentially).
Windows also moves the unattend file around across the phases, when I update the unattend file in a post install script I just update it in all these places. i.e. in the context of fog having mounted 😄 at /ntfs
"/ntfs/Windows/System32/Sysprep/Unattend.xml" "/ntfs/Windows/Panther/unattend.xml" "/ntfs/Windows/Panther/Unattend.xml"
I also have one at C:\Unattend.xml you’ll see in my example below.
Also note that it’s case sensitive, which is why I have 2 in the same spot as I’ve seen it both ways.
I don’t have time to dig into too much detail right now but here’s an example of injecting some stuff into the unattend files. I also included my bit where I can just patch in an updated Unattend.xml file, though this wouldn’t scale for every host I just use it for another option before recapturing a whole image to test an unattend change.
One very important bit for this to work as it does in the example is I have this bit in my specialize phase, which I replace with computername and AD info, replace NETBIOSDOMAINNAME with your short domain name that you use for this format logon string domain\username
<component name="Microsoft-Windows-UnattendedJoin" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Identification>
<JoinWorkgroup>NETBIOSDOMAINNAME</JoinWorkgroup>
</Identification>
</component>
I also have <ComputerName></ComputerName> in the specialize phase under my "Microsoft-Windows-Shell-Setup" component i.e. the end of this has that. I took out my company info from this example, you don’t need all of this the same, just a contextual example. The product key is the GVLK for windows 10/11 publicly available.
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DesktopOptimization>
<GoToDesktopOnSignIn>true</GoToDesktopOnSignIn>
<ShowWindowsStoreAppsOnTaskbar>true</ShowWindowsStoreAppsOnTaskbar>
</DesktopOptimization>
<BluetoothTaskbarIconEnabled>true</BluetoothTaskbarIconEnabled>
<ConvertibleSlateModePromptPreference>1</ConvertibleSlateModePromptPreference>
<CopyProfile>false</CopyProfile>
<DisableAutoDaylightTimeSet>false</DisableAutoDaylightTimeSet>
<EnableStartMenu>true</EnableStartMenu>
<OEMName>Company Name</OEMName>
<RegisteredOrganization>Company Name</RegisteredOrganization>
<ShowPowerButtonOnStartScreen>true</ShowPowerButtonOnStartScreen>
<RegisteredOwner>Company Name</RegisteredOwner>
<SignInMode>2</SignInMode>
<TimeZone>Mountain Standard Time</TimeZone>
<OEMInformation>
<SupportURL>http://helpme.company.tld</SupportURL>
<Logo>C:\img\company-logo.bmp</Logo>
<SupportPhone>555-5555</SupportPhone>
<SupportProvider>String that shows up in sys info</SupportProvider>
<Manufacturer>string that shows up in sys info</Manufacturer>
</OEMInformation>
<Themes>
<BrandIcon>C:\img\company-logo.png</BrandIcon>
<ThemeName>Company Theme</ThemeName>
<DesktopBackground>%WINDIR%\web\Wallpaper\some-injected-background.jpg</DesktopBackground>
<WindowColor>Automatic</WindowColor>
<DefaultThemesOff>false</DefaultThemesOff>
</Themes>
<DoNotCleanTaskBar>true</DoNotCleanTaskBar>
<AutoLogon>
<Password>
<Value>supersecretencryptedpassword</Value>
<PlainText>false</PlainText>
</Password>
<Enabled>true</Enabled>
<Username>Administrator</Username>
<LogonCount>99</LogonCount>
</AutoLogon>
<ProductKey>NPPR9-FWDCX-D2C8J-H872K-2YT43</ProductKey>
<ComputerName></ComputerName>
</component>
The fog post download examples. I also do something with the device form setting but I tried to just take that out for this example. Device form is mildly helpful for configuring the tablet vs desktop user experience if you have a mix of such devices.
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
#as a failsafe, reload the funcs.sh from fog
. /usr/share/fog/lib/funcs.sh
dots "Preparing Sysprep File at $unattend"
#update unattend files if an Unattend.xml file is present to replace current file
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
if [[ $adon=="1" ]]; then
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\nComputer Name: ${hostname}\nJoining Domain: ${addomain}\nWill be in OU: ${adou}\n"
echo -en "\n\nSetting Dynamic Unattend fields - \n\nComputer Name: ${hostname}\nJoining Domain: ${addomain}\nWill be in OU: ${adou}\n" >> $updateUnattendLog
sed -i \
-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
debugPause
sed -i \
-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
debugPause
sed -i \
-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