Often in desktop deployment, many environmental conditions are assumed and taken for granted. This can result in a great deal of head scratching when those elements are suddenly taken away. One of the biggest assumed conditions is the presence of your network when building the desktop. Although for most large companies the always-connected environment is a given and the idea of building a desktop disconnected is unheard of, there are plenty of industries where reliable domain connectivity may not be the norm.
The focus of this article, however, isn't about how to develop a stand-alone, disconnected Windows 7 deployment. Many solutions for that scenario already exist, such as manual installations, scripted installations, thick image capture/deployment, and more robust tools like the Microsoft Deployment Toolkit. Instead, I want to focus on two of the challenges that come along with Windows 7 and offline building: automating a network join and subsequently logging onto your new computer with a domain account when your domain connectivity is limited to only VPN.
If you do not have a domain controller available to you at the time of building, your fresh install or syspreped Windows image obviously can't join. Windows 7 brings the additional security challenges of exactly when a VPN connection can be started. Although it is not impossible to configure a custom service that starts a VPN session using the integrated Windows VPN client, it can be time consuming to automate the inclusion of additional Resource Kit tools (not necessarily those specifically for Windows 7), the dynamic creation of a VPN connection script, the installation of the service that performs the connection on a delayed start, and the final automation to remove that service after your build process has completed. If that sounds complication, it is, and it might be more engineering than your deployment requires (you may also have an environment where the native VPN client is insufficient and a third party VPN client is required).
Automating the join of new computer can be as easy as following these steps during your automated build:
- Ensure the local administrator's account is enabled.
Can be as accomplished by adding a command line of cmd /c net user Administrator /active:yes as a RunSynchronousCommand under the 4 specialize | xxx_Microsoft-Windows-Deployment_neutral section in your unattend.xml file
- Ensure the local administrator's account is set to auto-logon at least twice.
Can be accomplished by adding the AutoLogon from the xxx_Microsoft-Windows-Shell-Setup_neutral component under the 4 specialize or the 7 oobeSystem section of your unattend.xml file
- Include an automated process that kicks off a VPN connection on user logon.
Can be accomplished by adding a script to the %ProgramData%\Microsoft\Windows\Start Menu\Startup folder, which will ensure that whichever account logs on, the process will run for once the desktop loads.
- Once connected, have that automated process join the domain with a specific admin account then reboot.
Can be accomplished in just a few script lines, as shown to the below in the VBScript snippet.
strComputer & "\root\cimv2:Win32_ComputerSystem.Name='" & _
strComputer & “'”)
ReturnValue = objComputer.JoinDomainOrWorkGroup(strDomain, _
strPassword, strDomain & "\" & strUser, strOU, _
JOIN_DOMAIN + ACCT_CREATE + DOMAIN_JOIN_IF_JOINED)
After the join, your computer is now part of the domain, but you're still disconnected and unable to log on with a domain account. Because you've enabled the auto-logon of the local administrator's account, you're able to get back into Windows. Thus resolves the first issue of easily joining post build and via VPN. The next issue, however, can be a little more tedious to automate: logging on with a domain account. If you were to set this up manually, the answer would be to launch the VPN connection while under the local administrator's credentials, then use Window's Switch Users feature to log on as a domain account while the VPN connection is still active: but the goal is to automate this process.
There is a nifty trick you can do with Windows: inadvertently load a user profile, even if the user has never formally logged on to the unit. You can try this at home by holding the shift key down and right-clicking any application icon, then select Run As Different User (or drop to the command prompt and use the RunAs command). By default, Windows will load the specified user's profile prior to launching the application. If that user does not have a profile on the local computer, Windows creates one. Because loading the profile can be a time-consuming event, many of the launch processes in Windows include an optional switch or setting that bypasses this and simply authenticates the user against either the local computer or the domain and then runs the process with the limitations that come with no profile. But getting back to the default behavior of loading a full profile, this action can be used to our advantage when needing to automate the caching of a domain profile for subsequent off-network logons.
Condensed to its simplest steps, the process works like this:
- Log onto Windows as the local administrator.
- Connect to the domain via VPN
- Launch, then close a process as the intended domain user.
- Reboot Windows
- Log on as the domain user offline.
This approach provides your automation the ability to cache as many domain profiles as you like, all at once, and in a relatively short period of time. It also provides you as the developer with a few challenges:
- Launching a process as an intended domain user does require you to know their password. There is no way around this. You have to know, and store, this sensitive information. If you're building for a new user, this is not such an issue as they can change their password on their first true logon. Caching a large quantity of profiles can be problematic unless you manage user passwords and assign them without the user having the ability to change it, or customize your build environment to prompt your for the username and password (one or more) at build time. If you only need to cache service account profiles, those passwords rarely change and thus you do not need to worry about updating your automation frequently, although out-of-the-box automation can mean passwords are stored in clear-text and thus a security concern so developing a method by which the passwords are encrypted/decrypted during the process may be needed.
- There is no native command-line utility (such as RunAs) that allows you to pass a password on the command-line. This means that your automation either needs to include programmatic methods for launching a program, or you need to find an alternative utility that does allow for passwords on the command-line.
- Although less of a challenge than a reminder, whatever process is launched needs to self-close. Picking a benign process is key, as that process doesn't need to do anything at all other than launch and exit; the profile is cached prior to the process starting. A good process to start is CMD.EXE /C ECHO as that launches, does nothing, then closes.
Lets revisit number two from above. Although earlier I mentioned that the default behavior of any alternative context launched process was for Windows to load the profile, when you do this programmatically, many methods don't, resulting in no cached profile. Your script or custom application should be tested to make sure the process spawning routine does. For example, the below PowerShell snippet utilizes the Start-Process cmdlet, with alternative credentials, and forcing the profile to load:
$SecurePassword = ConvertTo-SecureString '[Password]' -AsPlaintext -Force
$UserCredentials = New-Object System.Management.Automation.PSCredential `
Start-Process -FilePath 'CMD.EXE' -ArgumentList '/C ECHO' `
-Credential $UserCredentials -LoadUserProfile
There can also be an interesting pitfall when programmatically launching the application with alternative credentials when you are logged on as the local administrator and the domain account you're attempting to use has not been previously accessed: a cryptic error along the lines of "Only part of a ReadProcessMemory or WriteProcessMemory request was completed." This seems to have cropped up somewhere in Vista and never was resolved, but does have an easy workaround. Think of the caching process as introducing the domain account to Windows for the first time. If a stranger showed up at your door and attempted to move in without any previous information, you might be a little skeptical as well. An easy way to avoid this potential error is to give Windows a little information about the account first, then launch the caching process: Add the user to a local group prior to spawning the process, then remove the user when you're done. I personally like to add the user temporarily to the Administrator's group. This process is fairly straightforward as demonstrated in this snippet of VB.NET:
Nothing, Nothing, AuthenticationTypes.Secure)
deAdminGroup.Invoke("Add", "WinNT://" & strDomain & "/" & strUserName.ToString)
The process is then 1) Add the user to the local administrator's group, 2) run a process as that user to cache the profile, and 3) remove the user from the local administrator's group. Once you have completed this process, your build process can reboot, log on offline as a domain account, and perform any finishing actions such as disabling the local administrators account, setting UAC levels, etc.
Although not as common, a decentralized environment or small business has the same deployment needs as large companies. The challenges in developing a desktop deployment system can be as great or greater when you have to factor in the possibility of building a desktop in a hotel room with little more than a tethered smart phone for internet connectivity. A bit more work up front, but the ability to automate the entire process can greatly simplify the effort and make for a professional end result.