Automated Setup of a Windows Environment Using Boxstarter and PowerShell

I have numerous Windows devices that I am responsible for setting up and maintaining. I have 4 personal Windows devices including my laptop, my wife’s laptop, my children’s computer, and my home desktop which also serves as our family media server. I then have 2 work Windows devices that I am responsible for a desktop and a laptop. My goal is to be able to walk-up to any of my Windows devices re-stall the Windows operating system, kick off a setup script, and have the machine back in a fully functioning state with all of the apps and files available and ready to use. I formed this stance over the years for multiple reasons:

  • Be able to wipe a device yearly to clean up the apps, extensions, and OS updates that accumulate over time.
  • Make sure that all personal files are being synced to the cloud to protect against the event that a device fails or is lost.
  • Limit the length of time a virus or malware can exist on a device.
  • Make sure the device is running the latest versions of applications and the Windows OS.
  • Make sure the device is configured properly for anti-virus, firewall, updates, and backups.

All of these items can be addressed without performing a wipe and load of the OS. But I have found that I can knock all of these out at one time by wiping the system. I get the additional benefit that if one of my devices does become infected with a virus I don’t have to struggle removing it. That process alone can take days as I wrote about here. I also don’t have to wonder afterwards whether I did completely remove the virus or if a piece of it is still lurking around in my system.

What Are the Steps

The setup and configuration of a device has many different steps and can differ from device to device. My wife’s laptop doesn’t need media server software or development tools such as Visual Studio. And it certainly doesn’t need to be joined to the domain at my company like my work devices do. So we need to create different scripts for the different roles the devices are serving. We want to be able to layer the scripts so that we can have a base script that performs the tasks that are common among all of the devices. This may include configuring Windows Update, installing Chrome and Microsoft Office, and configuring security such as Windows Controlled Folder Access.

Here is a list of tasks to consider:

  • Install Windows Features
    • IIS
    • Windows Subsystem for Linux (WSL)
    • Telnet
  • Configure Windows
    • Customize Windows Explorer
    • Customize Desktop and Taskbar
    • Setup User Accounts
    • Setup Windows Update
  • Install Applications
  • Configure Backup

The Tools

On Windows devices PowerShell is the primary scripting language and what we will be writing our scripts in. To install applications Windows doesn’t have a built-in solution as nice as apt-get on Linux. We will use a third-party tool called Chocolatey. It combines the power of nuget and PowerShell to enable the installation of applications from the command line. The last piece of the process that will bring all of it together is Boxstarter. It provides orchestration, reboot resiliency, and a number of commands and tools to simplify the process.

Boxstarter

To get started install Boxstarter. You can download the Boxstarter module installer from the web site or you can use Chocolatey to install the Boxstarter. Alternatively, you may invoke the module installer over the web using Powershell.

If you are running Powershellv3 or higher:

. { iwr -useb http://boxstarter.org/bootstrapper.ps1 } | iex; get-boxstarter -Force

If you are running powershell v2:

iex ((New-Object System.Net.WebClient).DownloadString('http://boxstarter.org/bootstrapper.ps1')); get-boxstarter -Force

This will install Chocolatey if necessary and then install the necessary Boxstarter modules.

The next step is to compose our script. Here is the Boxstarter-Base.txt script I use as the first step of all of my other scripts.

Set-ExplorerOptions -showHidenFilesFoldersDrives -showFileExtensions

# Enable Windows Controlled Folder Access
Set-MpPreference -EnableControlledFolderAccess Enabled

cinst -y 7zip.install --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y vlc  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y visualstudiocode --params '"/NoDesktopIcon /NoQuicklaunchIcon"'
cinst -y cmdermini  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y foxitreader  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y googlechrome  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y xyplorer  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y ditto  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"

Install-WindowsUpdate -AcceptEula

The script configures Explorer options, enables Windows Controlled Folder Access, installs applications, and then runs Windows Update. The script includes –cacheLocation because Chocolately had a bug at the time of this article that would recursively created nested folders for each application install.

Here is an example of a more complex script that I use to setup my development devices.

Update-ExecutionPolicy Unrestricted
Set-ExplorerOptions -showHidenFilesFoldersDrives -showProtectedOSFiles -showFileExtensions
cinst Microsoft-Hyper-V-All -source windowsFeatures
cinst Microsoft-Windows-Subsystem-Linux -source windowsFeatures
cinst TelnetClient -source windowsFeatures

# Enable Windows Controlled Folder Access
Set-MpPreference -EnableControlledFolderAccess Enabled

#cinst IIS-WebServerRole -source windowsfeatures
#cinst IIS-HttpCompressionDynamic -source windowsfeatures

cinst -y 7zip.install --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y autohotkey.portable  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y beyondcompare  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y curl  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y fiddler4  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y git.install  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y nodejs.install  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y notepadplusplus.install  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y visualstudiocode --params '"/NoDesktopIcon /NoQuicklaunchIcon"'  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y nuget.commandline  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y sysinternals  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y vlc  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y cmdermini  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y Wget  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y baretail  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y foxitreader  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y putty.install  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y fscapture  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y linqpad  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y googlechrome  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y xyplorer  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y docker-for-windows  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y postman  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y nimbletext  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y dotpeek  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
cinst -y ditto  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"
#cinst -Y listary  --cacheLocation "$env:userprofile\AppData\Local\Temp\chocolatey"

# Install applications not available in Chocolatey
(New-Object System.Net.WebClient).DownloadFile("https://download.teamviewer.com/download/TeamViewerPortable.zip","$Env:TEMP\TeamViewerPortable.zip");(Expand-Archive "$Env:TEMP\TeamViewerPortable.zip" -DestinationPath "$Env:USERPROFILE\AppData\Local\TeamViewerPortable\" -Force);
(New-Object System.Net.WebClient).DownloadFile("https://typora.io/windows/typora-setup-x64.exe","$Env:TEMP\typora-setup-x64.exe");cmd /c '%TEMP%\typora-setup-x64.exe /SILENT'
# Include this if you want Xamarin installed  --add Microsoft.VisualStudio.Workload.NetCrossPlat;includeOptional
(New-Object System.Net.WebClient).DownloadFile("https://aka.ms/vs/15/release/vs_enterprise.exe","$Env:TEMP\vs_enterprise.exe");cmd /c  'start /wait %TEMP%\vs_enterprise.exe --wait --passive --add Microsoft.VisualStudio.Workload.Azure --add Microsoft.VisualStudio.Workload.Data;includeOptional --add Microsoft.VisualStudio.Workload.NetCoreTools --add Microsoft.VisualStudio.Workload.NetWeb;includeOptional --add Microsoft.VisualStudio.Workload.Node --includeRecommended' 

# Visual Studio Code Extensions
code --install-extension ms-vscode.csharp
code --install-extension formulahendry.code-runner
code --install-extension streetsidesoftware.code-spell-checker
code --install-extension msjsdiag.debugger-for-chrome
code --install-extension PeterJausovec.vscode-docker
code --install-extension dbaeumer.vscode-eslint
code --install-extension abusaidm.html-snippets
code --install-extension eg2.vscode-npm-script
code --install-extension ms-vscode.powershell
code --install-extension Ionide.ionide-fsharp
code --install-extension Shan.code-settings-sync
code --install-extension Ionide.ionide-fake
code --install-extension Ionide.ionide-paket
code --install-extension esbenp.prettier-vscode
code --install-extension eamodio.gitlens
code --install-extension robertohuertasm.vscode-icons

Install-WindowsUpdate -AcceptEula

This script installs a number of Windows features including Hyper-V and Windows Subsystem for Linux, updates the PowerShell execution policy so that unsigned scripts can run, installs many applications from Chocolatey, installs several applications not available from Chocolatey, and installs Visual Studio Code extensions.

Once you have your script we need to save it. You have lots of options here. Boxstarter supports:

  • Saving as a package on any Nuget feed.
  • Saving a package to a Network share or any local media such as a thumb drive.
  • Saving to a single text file or any text based HTTP resource (like a Github Gist)

If it is a script you don’t have secrets in such as an application license key or account credentials then a Gist is a good place to put your script. But if you do have secret information then don’t use a Gist as even a secret Gist is visible to anyone with the URL. Security through obscurity is the same as no security at all.

Once you have saved the Gist, to get the URL we need click the “Raw” link. Gist

Now let’s run our script by invoking the the Install-BoxstarterPackage command pointing to the gist created above:

Install-BoxstarterPackage -PackgeName https://gist.githubusercontent.com/JoshuaCode/5cad04e705f31be41456e7632b3022cd/raw/7e1cb054c855c5bf725a9e85b13b59e8de59aa17/Boxstarter-Base.txt -DisableReboots

While you can use the -PackageName argument to point to any public Chocolatey package, you can also point to any URL that resolves to a plain text script like the gist we created. Note that we use the -DisableReboots argument to suppress automatic reboots.

Summary

You have now seen how easy it is to create your script, install Boxstarter, and run your script. The next step is to begin creating your own script. Remember that you have the full power of PowerShell available to you so be creative. Some additional ideas from my company device script include:

  • Join the domain

  • Merges registry settings

regedit /s RegistrySettings.reg
  • Add a nuget repository to Visual Studio
nuget sources Add -Name AcmeCompany -Source http://tfs.acmecompany.com:8080/tfs/CollectionName/_packaging/AcmeCompany/nuget/v3/index.json
  • Add a private extension gallery to Visual Studio which I detailed in this article

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s