Boxstarter

Released Boxstarter 2.1: Install multiple Windows environments in one command and better pipeline output by Matt Wrock

multiboxToday I released Boxstarter v2.1.0 which includes support for piping multiple machines to the Install-BoxstarterPackage command and much improved options for receiving output on both the standard output pipeline as well as the host console. This release also adds a few bug fixes and some “Fit and Finish” details for remote installs.

These features were originally intended for v2.0, but the core 2.0 remoting integration proved to be much more involved than anticipated and stood well on their own. So in the interest of getting that release out the door sooner, I held off on these rather minor additions. I think this release does provide a better experience but it is the final prerequisite for the VM integration release coming next.

Multiple Machine Installs

While this is possible in v2.0 by simply looping over a list of computer names, connection URIs or PowerShell sessions, the first class support for installing packages on multiple machines does provide some improved efficiencies and an easier way of processing the results. Boxstarter can configure the local Windows PowerShell remoting settings for all machines at once instead of setting and rolling back on each machine.

The ComputerName, ConnectionURI and Session parameters of Install-BoxstarterPackage can now be passed in an array and can also be passed in the pipeline input.

$cred=Get-Credential=MyTargetMachine\myusername
Get-WebServerNames | 
  Install-BoxstarterPackage -PackagNamee WebPackage -Credential $cred –Force

 

Assuming Get-WebServerNames returns an array of server names, the above command will install the WebPackage package on each. Boxstarter will ensure that all of those servers are in the local machine’s list of trusted hosts and ensures that credentials can be delegated to them as well before establishing the remote session.

Receive installation results as separate objects on the pipeline

For each machine that Boxstarter runs the package, a PSObject is returned with metadata related to the results of the install.

result2

The properties included in the PSObject are:

  • ComputerName: The name of the computer where the package was installed
  • StartTime: The time that the installation began
  • FinishTime: The time that Boxstarter finished the installation
  • Completed: True or False indicating if Boxstarter was able to complete the installation without a terminating exception interrupting the install. Even if this value is True, it does not mean that all components installed in the package succeeded. Boxstarter will not terminate an installation if individual Chocolatey packages fail. Use the Errors property to discover errors that were raised throughout the installation.
  • Errors: An array of all errors encountered during the duration of the installation.

There are probably other properties that will be added to this object in future releases. Maybe a list of all chocolatey packages successfully installed.

Adjusting host output verbosity

A lot of the output that Boxstarter would previously output to the console is now sent to the verbose stream. In other words, by default, Boxstarter tries to limit output to things that are either of immediate interest or indicate that something that might take a while is taking place. Everything else is still available, but requires the use of the –Verbose parameter to be output to the console. However, all verbose content is always written to the Boxstarter.log file in case you want to refer to this information later.

If you would like to silence everything except the standard output stream (all verbose and write-host output), you can set $Boxstarter.SuppressLogging to $True.

> $Boxstarter.SuppressLogging=$True
> "win7","win2012" | Install-BoxstarterPackage -PackageName test-package -Credential $cred -Force

Errors       : {}
ComputerName : win7
Completed    : True
FinishTime   : 11/30/2013 9:41:58 PM
StartTime    : 11/30/2013 9:41:54 PM

Errors       : {}
ComputerName : win2012
Completed    : True
FinishTime   : 11/30/2013 9:42:11 PM
StartTime    : 11/30/2013 9:41:58 PM

Other fixes and improvements

In addition to these features added, there are a few additional items worthy of notice:

  • Don't touch UAC or AutoLogon registry keys on remote installs.
  • Improve Remote Reboot detection and suppressing reapeted connection creations while remote machine is installing windows updates.
  • Fix overriding organization update servers with the public Windows Update server.
  • Fix remoting configuration when client has assigned Trusted Hosts to a global wildcard.
  • Improve reliability of .net 4.5 installation as Chocolatey prerequisite particularly when running on a newly installed OS.

Up Next: Boxstarter Virtualization Module

Other than possible bug fix releases, I don’t plan on any releases between now and the first version of a new Boxstarter PowerShell module that focuses on VM provisioning. This module should provide support for Hyper-V, AWS, and Azure VMs. Given a VM name, Boxstarter will enable PowerShell remoting on the VM with no user intervention. It will eliminate the need to check and manually configure remoting on target machines. It will return a ConnectionURI that Install-BoxstarterPackage can use to install packages. At least for Hyper-V, this will include the ability to reset the VM to a specified checkpoint as well.

So stay tuned…

Released Boxstarter 2.0: Remote Windows Environment Installs, Packageless Scripts and a New Documentation Web Site! by Matt Wrock

amazon

A couple weeks ago, I released Boxstarter 2.0 with lots of new features, a couple of which I think are especially cool. Now that the documentation and new web site at Boxstarter.org are complete, I think a proper blog post is in order.

While I did bump the major version, this release is compatible with any 1.x.x Boxstarter script. The version number increase is intended to emphasize some significant features introduced in this release.

Above is a screenshot of installing a Minecraft server onto an AWS Machine on my local laptop powershell console.

Remote Installations

Perhaps the most significant feature introduced in this release is remote installations - the ability to point Boxstarter to any remote machine and supply a package that can be installed on that machine from the user’s local command line. This could be your other laptop or a Hyper-V, Azure or AWS VM.

This does require powershell remoting to be enabled on the remote machine. That’s uper simple to do. Just run this command as administrator on the remote machine.

Enable-PSRemoting –Force

You only have to do this once. However, if WMI ports are open on that machine, Boxstarter can enable remoting remotely. Remoting on the local machine where Boxstarter runs is completely configured, if necessary, by Boxstarter. You can install Boxstarter Packages (which are Chocolatey packages really) on any remote machine that meets Boxstarter’s prerequisites (at least Powershell 2 on Windows 7/2008R2 or higher) just like you would locally with this command:

$cred=Get-Credential=MyTargetMachine\myusername
Install-BoxstarterPackage -ComputerName MyTargetMachine -PackageName MyPackage -Credential $cred

Here is a screenshot of a remote install to a Windows 7 VM: result

Laying the groundwork for VM provisioning

Back in September I began work to automate VM provisioning using Boxstarter. This really ended up being more about learning than simply building features. Long story short: I ended up figuring out exactly how to do this but in the process realized it would be best to “decouple” the remote installation from the VM mechanics. So expect the VM stuff fairly soon where you may see a command like this:

Get-BoxstarterVM -VMName MyVM -SnapShotName myCheckpoint | `
Install-BoxstarterPackage -PackageName MyPackage -Credential $creds

Get-BoxstarterVM will enable remoting, restore the optional chekpoint and return a ConnectionURI to be piped to Install-BoxstarterPackage. There will be a different flavor for Hyper-V, Azure and AWS.

No Package needed, Install environments with a Gist!

Now you can use a text file or any URL that contains raw text (like a gist) in lieu of a Package name. If Boxstarter detects that a package name is resolvable to a local file or it begins with http(s)://, Boxstarter will auto generate a temporary package injecting this text as the ChocolateyInstall.ps1. For example:

gist3

Install-BoxstarterPackage -PackageName `
https://gist.github.com/mwrock/7382880/raw/f6525387b4b524b8eccef6ed4d5ec219c82c0ac7/gistfile1.txt

I’d like to credit and thank Eric Williams (@MotoWilliams) for coming up with this great idea. In many cases particularly with a Boxstarter install, there may not be a need to have a Nuget package published on a feed. It might be a one-off install or something intended for just yourself and maybe a few others. So having to pack and push a Chocolatey package is likely overkill.

This also works with the Boxstarter Web Launcher URLs.

http://boxstarter.org/package/nr/url?https://gist.github.com/mwrock/7382880/raw/f6525387b4b524b8eccef6ed4d5ec219c82c0ac7/gistfile1.txt

This doesn’t even require Boxstarter or Chocolatey to be installed. Run this from IE and a .net click-once app is downloaded and installed that will bootstrap a Boxstarter install of the gist script.

Other new Features:

  • Reboots now suspend Bitlocker if enabled, preventing the need to enter your bitlocker key before startig windows
  • Its compatible with the newly released Chocolatey version 0.9.8.23
  • If installing on a machine that does not require a password, Boxstarter will no longer prompt for one if you do not provide Credentials to Install-BoxstarterPackage or if you use the –NoPassword parameter of Invoke-ChocolateyBoxstarter or Boxstarter.bat.

Brand new Boxstarter.org with lots of Documentation and “How To”s

I want Boxstarter to be easy to use and help to be discoverable. So I spent my free time over a weekend composing a bunch of help pages capturing all of the key Boxstarter scenarios. This includes instructions for:

  • Installing Boxstarter
  • Creating Packages
  • Publishing Packages
  • Executing packages

I think I covered most of the permutations of these activities clearly. Please let me know if you find anything that is not clear or if there are gaps not covered.

Hope to be getting back to feature work soon and release the new Boxstarter Virtualization module.

Windows VM Provisioning Part 1: Inject a ‘startup on boot ‘ script into a VHD. by Matt Wrock

jokescript

I’m currently in the process of adding a Virtualization module to my Boxstarter project. This post is part of a three part series covering some of the technicalities involved. Although these posts will document how Boxstarter provisions a Windows VM, I think that there will be information covered to accommodate a wide range of scenarios whether you are interested in using Boxstarter or not.

The Boxstarter Scenario

Boxstarter is a set of Powershell modules (or you can simply invoke from a URL) that can deploy a single application or standup a complete environment from a script leveraging Chocolatey and Nuget packaging technologies. Its target scenario is to bring a Windows machine from bare OS to an environment that is fully patched and has everything you need to get stuff done. Yes, there are lots of cool solutions that can do this at enterprise scale but Boxstarter is designed to be light weight and very simple. It can perform this provisioning on a physical machine but what about VMs?

You can of course log on to a VM and use Boxstarter just as you would on any physical machine, but I want to eliminate the need to have to setup network settings and manually RDP or use a Hyper-V console to connect to the VM. Deploying a Boxstarter Install to a VM should be just as simple as any other environment.

How will Boxstarter provision a VM without the need to manually prepare it

Here is the basic flow:

  1. Inject a script into a VHD that will run under the local machine account with administrative privileges on boot. This script will add a firewall rule and edit the Local Account Token Filter Policy.
  2. Use PSEXEC to invoke another script from a user account that will enable powershell Remoting on the VM with credssp authentication so that your credentials in the remote session can be used to access other remote resources from the VM.
  3. Use powershell remoting to Invoke a Boxstarter package from the VM Host but that will run on the VM Guest.
  4. Wrap all of this up into a single, simple command. that can be extended to be VM Vendor agnostic but will work with Hyper-V and Windows Azure VMs out of the box.

Ideally, this could even be leveraged to create a Vagrant Provisioner.

This post will cover the zeroth point above. The use cases of plugging a startup script right into a VHD span well beyond Boxstarter and the means of doing it is not particularly difficult but it did take me a while to figure out how to get it done right to accommodate both simple workstation environments as well as Domain topologies.

Requirements on the Host and Guest

The Goal is that this should work on any “vanilla” bare OS guest install with access to the internet and no “special” networking configuration. No Firewall tweaking, no need to enable powershell remoting on the host or guest and no installation of software on the guest beyond the operating system. That said, the following are required:

  1. The VM guest must be able to access the internet unless your boxstarter package installs everything from a local source.
  2. The Host must be running at least Powershell v.3 and have the Hyper-V module available.
  3. The Guest must be running windows 7, 8, server 2008 R2 or server 2012.
  4. The VHD where the script is injected must contain the system volume of the VM (windows\system32).

The Script Script (the script that installs the script)

The script lives in the Boxstarter.VirtualMachine module and can be called like so:

$vhd=Get-VMHardDiskDrive -VMName "MyVMName"Add-VHDStartupScript $vhd.Path -FilesToCopy "c:\myFiles\file.ps1","..\MyOtherFiles" {    $here = Split-Path -Parent $MyInvocation.MyCommand.Path    . "$here\file.ps1"}
This will take the VHD used by the VM on the host named MyVMName, the file c:\myfiles\file.ps1 as well as all files in ..\MyOtherFiles will be copied to the VHD. Furthermore, the script block above will be stored in a file in the same directory as the copied files. A local Group Policy will be added to the Registry stored in the VHD that will call the above script when the VM next boots. To be clear, the script runs at boot time and not login time so that no separate login is necessary to kick things off. The script will run under the local machine account.

Validate and Mount the VHD

function Add-VHDStartupScript {[CmdletBinding()]param(    [Parameter(Position=0,Mandatory=$true)]    [ValidateScript({Test-Path $_})]    [ValidatePattern("\.(a)?vhd(x)?$")]    [string]$VHDPath,    [Parameter(Position=1,Mandatory=$true)]    [ScriptBlock]$Script,    [Parameter(Position=2,Mandatory=$false)]    [ValidateScript({ $_ | % {Test-Path $_} })]    [string[]]$FilesToCopy = @())if((Get-ItemProperty $VHDPath -Name IsReadOnly).IsReadOnly){    throw New-Object -TypeName InvalidOperationException `      -ArgumentList "The VHD is Read-Only"}    $volume=mount-vhd $VHDPath -Passthru | get-disk | Get-Partition | Get-Volumetry{    Get-PSDrive | Out-Null    $winVolume = $volume | ? {        Test-Path "$($_.DriveLetter):\windows\System32\config"    }    if($winVolume -eq $null){        throw New-Object -TypeName InvalidOperationException `          -ArgumentList "The VHD does not contain system volume"    }
In the beginning of the script we validate the user input and Mount the VHD. Pretty straight forward stuff.

Copy files and create startup script file

$TargetScriptDirectory = "Boxstarter.Startup"mkdir "$($winVolume.DriveLetter):\$targetScriptDirectory" -Force | out-nullNew-Item "$($winVolume.DriveLetter):\$targetScriptDirectory\startup.bat" -Type File `  -Value "@echo off`r`npowershell -ExecutionPolicy Bypass -NoProfile -File `"%~dp0startup.ps1`""`  -force | out-nullNew-Item "$($winVolume.DriveLetter):\$targetScriptDirectory\startup.ps1" -Type File `  -Value $script.ToString() -force | out-nullForEach($file in $FilesToCopy){    Copy-Item $file "$($winVolume.DriveLetter):\$targetScriptDirectory" -Force}

Here we copy the files provided in the FilesToCopy parameter and create a powershell file to hold the script in the script block and a batch file that will invoke the powershell file.

Load the registry hive in the VHD

reg load HKLM\VHDSYS "$($winVolume.DriveLetter):\windows\system32\config\software" | out-null

This takes the file in the VHD that contains HKLM\Software, which is where the computer startup group policies reside and Loads its keys into a new hive referencable from HKLM:\VHDSYS. Now we can query and modify the values in the registry as easily as we can any of our local registry information.

Add the Group Policy

Now that the VHD Registry is loaded, we need to add a Local Group Policy that will invoke our startup.bat file upon boot. This is a bit involved to account for various scenarios such as:

  • What if you already have different startup scripts
  • What if you have already have added a startup script and do not want to add a duplicate
  • What if you have one or more domain group policies

To try and keep things at least somewhat tidy, we will place this logic in a separate function:

function Get-RegFile {    $regFileTemplate = "$($boxstarter.BaseDir)\boxstarter.VirtualMachine\startupScript.reg"    $startupRegFile = "$env:Temp\startupScript.reg"    $policyKey = "HKLM:\VHDSYS\Microsoft\Windows\CurrentVersion\Group Policy"    $scriptNum=0    $localGPONum=0    if(Test-Path "$policyKey\Scripts\Startup\0\0"){        $localGPO = Get-ChildItem "$policyKey\Scripts\Startup" | ? {            (GCI -path $_.PSPath -Name DisplayName).DisplayName -eq "Local Group Policy"        }        if($localGPO -ne $null) {            $localGPONum = $localGPO.PSChildName            $localGPO=$null #free the key for GC so it can be unloaded        }        else{            Shift-OtherGPOs "$policyKey\Scripts\Startup"            Shift-OtherGPOs "$policyKey\State\Machine\Scripts\Startup"        }        if(test-path "$policyKey\Scripts\Startup\$localGPONum"){            $scriptDirs = Get-ChildItem "$policyKey\Scripts\Startup\$localGPONum"            $existingScriptDir = $scriptDirs | ? {                 (Get-ItemProperty -path $_.PSPath -Name Script).Script `                  -like "*\Boxstarter.Startup\startup.bat"            }            if($existingScriptDir -eq $null){                [int]$scriptNum = $scriptDirs[-1].PSChildName                $scriptNum += 1            }            else {                $scriptNum = $existingScriptDir.PSChildName                $existingScriptDir = $null #free the key for GC so it can be unloaded            }        }        $scriptDirs=$null    }    (Get-Content $regFileTemplate) | % {        $_ -Replace "\\0\\0", "\$localGPONum\$scriptNum"    } | Set-Content $startupRegFile -force    return $startupRegFile}

function Shift-OtherGPOs($parentPath){    Get-ChildItem $parentPath | Sort-Object -Descending | % {        [int]$num = $_.PSChildName        $oldName = $_.Name.Replace("HKEY_LOCAL_MACHINE","HKLM:")        [string]$newName = "$($num+1)"        try {Rename-Item -Path $oldName -NewName $newName} catch [System.InvalidCastException] {            #possible powershell bug when renaming reg keys that are numeric            #the key is copied but the old key remains            Remove-Item $oldName -Recurse -force        }    }}
A couple things to note here. The script Group Policies appear to be mirrored at both:
  • HKLM:\VHDSYS\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup
  • HKLM:\VHDSYS\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup
    I honestly do not know why or what the significance is of the different locations. These are just the keys I saw that were affected when I manually played with creating startup scripts inside GPEDIT.MSC.

The Script keys maintain the following subkey structure:

\Scripts\Startup\{policy node}\{script node}

The policy and script nodes are each simple integers starting at 0 and increment for each additional policy scope or script. So a machine with a Local policy containing 1 script and a domain policy containing 2 scripts would look like:

\Scripts\Startup\0\0 – Local policy script

\Scripts\Startup\1\0 – First domain Policy script

\Scripts\Startup\1\1 – Second domain Policy script

From what I could tell in my experimentation, the Local Policy always occupied position 0.

Here are some surprising and unintuitive findings to be aware of:

  • When you are done with the registry you will need to unload it just as we loaded it in order to free up the file. That is not surprising. What is surprising is that if you save any keys to a variable as you are navigating its values, you will need to dereference those variables. It was fun in VB6 and it is still fun today! To add icing to this cake, you also need to do the thing that you should really never do: call GC::Collect() before unloading. Yep, that’s right. Unless of coarse you like Access Exceptions.
  • There seems to be a bug in the powershell registry provider when renaming keys that have a numeric value. Doing so raises a invalid cast exception. It also goes ahead and creates the renamed key but it does not delete the old name. This is why I delete it inside of the catch block.

The call to the above Get-RegFile function and the surrounding import code looks like:

reg load HKLM\VHDSYS "$($winVolume.DriveLetter):\windows\system32\config\software" | out-null$startupRegFile = Get-RegFilereg import $startupRegFile 2>&1 | out-nullRemove-Item $startupRegFile -force
We use the reg command to import the temp file with the altered template and then dispose of the temp file.
 

The registry import template

I found it easiest to create a .reg file containing all of the registry modifications instead of using the powershell registry provider to individually modify the tree. I simply needed to determine the correct policy and script nodes and then inject those into the template. Here is the template:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\VHDSYS\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts]

[HKEY_LOCAL_MACHINE\VHDSYS\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Shutdown]

[HKEY_LOCAL_MACHINE\VHDSYS\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup]

[HKEY_LOCAL_MACHINE\VHDSYS\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup\0]"GPO-ID"="LocalGPO""SOM-ID"="Local""FileSysPath"="%SystemRoot%\\System32\\GroupPolicy\\Machine""DisplayName"="Local Group Policy""GPOName"="Local Group Policy""PSScriptOrder"=dword:00000001

[HKEY_LOCAL_MACHINE\VHDSYS\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup\0\0]"Script"="%SystemDrive%\\Boxstarter.Startup\\startup.bat""Parameters"="""ExecTime"=hex(b):00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00

[HKEY_LOCAL_MACHINE\VHDSYS\Microsoft\Windows\CurrentVersion\Group Policy\Scripts]

[HKEY_LOCAL_MACHINE\VHDSYS\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown]

[HKEY_LOCAL_MACHINE\VHDSYS\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup]

[HKEY_LOCAL_MACHINE\VHDSYS\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0]"GPO-ID"="LocalGPO""SOM-ID"="Local""FileSysPath"="%SystemRoot%\\System32\\GroupPolicy\\Machine""DisplayName"="Local Group Policy""GPOName"="Local Group Policy""PSScriptOrder"=dword:00000001

[HKEY_LOCAL_MACHINE\VHDSYS\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0\0]"Script"="%SystemDrive%\\Boxstarter.Startup\\startup.bat""Parameters"="""IsPowershell"=dword:00000000"ExecTime"=hex(b):00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00

Cleaning up

Finally, we unload the registry hive and dismount the VHD:

}finally{    [GC]::Collect()    reg unload HKLM\VHDSYS 2>&1 | out-null    Dismount-VHD $VHDPath}

Again as noted before, we have the unattractive GC Collect. If anyone discovers a way to avoid this please comment but I was not able to find any other way around this. Failing to call Collect results in an Access Exception when unloading the registry. This is only the case if you have used the powershell registry provider to navigate the loaded hive. Also as noted before, if you have referenced any keys in a variable, you must deallocate those variables before the call to GC::Collect().

Reboot a VM attached to the VHD and see the script run

That’s it. Now you can reboot a VM attached to this VHD and its script will execute under the local machine account’s credentials with administrative privileges. Since the VHD format is supported by almost all of the major Virtualization vendors, you should be able to leverage this script on most virtualization platforms.

Next: Enable Powershell Remoting

The next post will explore how to use this script to enable powershell remoting on a VM guest. It is not enough to simply have the script enable remoting since remoting must be enabled by a user account. I’ll show you the approach I found that works to set that up.

Setup a new machine with just a URL and Chocolatey package by Matt Wrock

When I first started working on Boxstarter, a set of scripts that can stand up a complete, customized windows environment in a completely unattended manner, one of my key objectives was to be able to spin up a new environment with as few bits on hand as possible. Until now, Boxstarter achieved this by allowing you to create a portable Chocolatey package on a thumb drive or network share. I have never really been satisfied with that. It always seems when the time comes and I want to repave, that thumb drive or share is not around or in an up to date state. What I really want is just to type something brief and rememberable that would bring down all the bits I need to perform the setup (at least solving the “not around” part). This is now a reality.

Introducing The Boxstarter Click-Once Web Launcher

You can use any browser that supports Click Once applications. This is limited to IE unless you add extensions to Chrome or Firefox. You can also use either the Command Line or a Powershell console, both using the same command as long as IE is your default browser. The fact that this method is rather IE-centric is not much of a drawback since every Windows machine has IE as its default browser in the base install which is the key Boxstarter scenario.

launchAll you need to remember is the Boxstarter domain and the Chocolatey package that drives your machine setup. In the above example, the package is called…well…example. Boxstarter probes both the public Chocolatey.org gallery and the Myget.org community boxstarter feed for a package ID named Example.

This first installs a Boxstarter Click-Once Bootstrapper.

install

 

 

 

 

 

 

Be aware that on Windows 8, the Smart Screen filter will supply a somewhat frightening message stating that you have downloaded software that is not yet trusted.

SmartScreen

 

 

 

 

Even though the Boxstarter Web Launcher is signed with an Authoritative certificate, it has not “gained enough reputation” to be considered safe. In theory, this should eventually go away. In the meantime, you need to click the “More info” link to get this dialog:

runanyway

 

 

 

 

Click the “Run Anyway” link to proceed. Next, unless you have UAC disabled, you need to give Boxstarter permissions to run with administrative privileges. Boxstarter needs these privileges in order to run all of the application installs, reboot and perform all actions you want it to to tweak your windows environment.

Elevate

 

 

 

 

 

Next the normal Boxstarter session begins and prompts you for your windows account password:

console

 

 

 

 

 

Boxstarter stores the password you enter in a special place in windows designed specifically for this purpose. The password is stored in encrypted form and is never sent over the network or stored in plain text. This allows you to be automatically logged on whenever Boxstarter needs to reboot your system throughout the system installation.

If you want to install a package without the risk of reboot and without this prompt, you can invoke boxstarter using this URL:

HTTP://Boxstarter.org/package/nr/example

Its all in the “/nr/”. This might be handy if you know no reboot is needed and perhaps you want to use Boxstarter because you know Chocolatey is not installed.

What happens next?

Boxstarter now installs Chocolatey and (if you are on windows 7 or server 2008 R2) .net4.0 if these are not already installed. It then proceeds to run the package you specified in the original URL. This is just a normal Chocolatey package. Boxstarter can run any Chocolatey package. What’s different when running the package in Boxstarter is the following:

  • Boxstarter shuts down the windows update service and if installed, the SCCM client. These can often interfere with software installations.
  • Boxstarter intercepts all Chocolatey install calls. If there is a pending reboot, boxstarter invokes a system reboot and ensures that it will be reinvoked first thing on startup.
  • Boxstarter examines all Chocolatey failures and if these are standard MSI Pending reboot failures, it will like above, restart your system and try again after restarting.
  • Boxstarter runs the entire installation as admin to avoid prompts for permissions elevation or errors complaining about insufficient permissions.
  • Boxstarter imports the Boxstarter.Winconfig powershell module making all of its CmdLets available that provide various helpers for customizing windows settings (enabling remote desktop, customizing windows explorer defaults, icon sizes, etc.). The most important feature in this module is the ability to run all critical windows updates. A routine requirement when setting up a new system.

What might a Chocolatey package look like for a Boxstarter install?

Here is an example:

Update-ExecutionPolicy Unrestricted Move-LibraryDirectory "Personal""$env:UserProfile\skydrive\documents" Set-ExplorerOptions -showHidenFilesFoldersDrives -showProtectedOSFiles -showFileExtensions Enable-RemoteDesktop cinstm VisualStudioExpress2012Web cinstm fiddler cinstm mssqlserver2012express cinstm git-credential-winstore cinstm console-devel cinstm skydrive cinstm poshgit cinst Microsoft-Hyper-V-All -source windowsFeatures cinst IIS-WebServerRole -source windowsfeatures cinst TelnetClient -source windowsFeatures Install-ChocolateyPinnedTaskBarItem "$env:programfiles\console\console.exe" copy-item (Join-Path (Get-PackageRoot($MyInvocation)) 'console.xml') -Force $env:appdata\console\console.xml Install-ChocolateyVsixPackage xunit http://visualstudiogallery.msdn.microsoft.com/463c5987-f82b-46c8-a97e-b1cde42b9099/file/66837/1/xunit.runner.visualstudio.vsix Install-WindowsUpdate -AcceptEula

This is a relatively simple setup. Here is what will happen:

  • The user’s powershell execution policy will be permanently set to unrestricted.
  • The MyDocuments folder is moved to Skydrive (I always do this and it is incredibly convenient since my docs (including things like powershell profile) are synced between machines and makes the risk of a machine failure less ominous).
  • The Windows explorer settings are set to a developer sane level.
  • Remote Desktop is enabled.
  • Several typical developer applications are installed including Visual Studio and Sql server. When installed together these two often make for a “reboot perfect storm.” All of their dependent packages will be installed as well like GIT for poshgit and the .net 4.5 framework for Visual Studio.
  • Hyper-V, IIS and the Telnet client are installed
  • I “pin” Console, my favorite command line console, to the task bar and copy its configuration file with my preferred settings that I embedded in the chocolatey package to the directory where it expects to find it.
  • I install the XUnit test runner Visual Studio extension.
  • Lastly I install all critical windows updates.

Chances are this will include at least one or two reboots in windows 8 and several more in Windows 7. Boxstarter will reboot when needed and then simply rerun the package after restart. Chocolatey helps us from installing packages more than once. On subsequent runs it knows if we installed something previously and will just skip to the next install. This does mean that any custom script we add, and we can add any powershell we want, must be repeatable.

I thought Powershell 4’s Desired State Configuration solves this

Desired state configuration is the flagship feature shipping with Powershell 4 on Windows 8.1 and Server 2012 R2. It is no less than very cool and will indeed change the way we do machine configuration in Windows at last. It allows you to create declarative configuration files hat describe exactly how you want a machine to look and then tells one or more machines in your enterprise, look like this and do whatever it takes to make that happen.

The gaps that Boxstarter tries to fill here are:

  • Backward compatibility back to windows 7 and server 2008 R2.
  • Support for client systems. DSC is currently strongly targeting server infrastructure
  • Tooling. Eventually, I want people to be able to use Boxstarter who have no knowledge of powershell or any scripting for that matter.

What's next for Boxstarter?

I’ve been on a bit of a oss hiatus lately with my day job keeping me quite busy during non daylight hours, but I have had some time lately to do some polishing and also make some headway into another goal I have for Boxstarter: Easily deploying a Boxstarter install to a new VM. If you are familiar with Vagrant, a great VM environment setup platform for VirtualBox, VMWare and AWS then you can picture where I am going with this. I’d like to bring the same (or atleast very similar) capabilities to Hyper-V and windows Azure VMs.

This week I wrote a test script to help me automate testing Boxstarter packages and Boxstarter itself. Its just part of my PSake build file now:

task Test-VM -requiredVariables "VmName","package"{ $vm= Get-VM $VmName Restore-VMSnapshot $vm-Name $vm.ParentSnapshotName -Confirm:$false Start-VM $VmName $creds= Get-Credential -Message "$vmName credentials"-UserName "$env:UserDomain\$env:username" $me=$env:computername $remoteDir=$baseDir.replace(':','$') $encryptedPass= convertfrom-securestring -securestring $creds.password $modPath="\\$me\$remoteDir\Boxstarter.Chocolatey\Boxstarter.Chocolatey.psd1" $script= { Import-Module $args[0] Invoke-ChocolateyBoxstarter $args[1]-Password $args[2] } Write-Host "Waiting for $vmName to start..." do {Start-Sleep -milliseconds 100} until ((Get-VMIntegrationService $vm | ?{$_.name -eq"Heartbeat"}).PrimaryStatusDescription -eq"OK") Write-Host "Importing Module at $modPath" Invoke-Command -ComputerName $vmName-Credential $creds-Authentication Credssp -ScriptBlock $script-Argumentlist $modPath,$package,$creds.Password }

This takes a given VM and reverts it to its parent snapshot and then uses Powershell remoting to run the install. Take a look at this gist I wrote with a function to test pure Chocolatey packages. It includes some comments on how to setup the remoting.

I’d like to improve on this by cloning a VM from an existing VHD, then run the Boxstarter install on the VM taking over the mess of setting up the remoting and VM networking. We’ll see how that shapes up.

Feedback

If you have the chance to try Boxstarter out, I’m very interested to hear how he experience is. What is confusing, what does not work or what you would really like to see added as a feature. Please feel free to utilize the Codeplex Discusion and Issues features to voice these. You can also find complete documentation on the Boxstarter.Codplex.com documentation wiki.

Easily Script Machine Reinstalls with Boxstarter by Matt Wrock

boxLogoAlmost a year ago now I started this small project, Boxstarter.  The project has sustained many interruptions as I have had busy spurts at work and involvements with other projects. It has gone through some serious refactorings moving from a scrappy script to a larger codebase organized into a few PowerShell modules and a suite of unit tests. A lot of its original code I moved over to Chocolatey and I plan to continue to contribute to Chocolatey where it makes sense. Chocolatey Rocks! So overall, I have probably rewritten this thing twice and now I feel it is far from finished but represents a nice base that makes “Box Starting” a much simpler and repeatable process.

Repaving Sucks

I think that pretty much says it. The idea of creating Boxstarter came to me when I had a SSD die and had to do a dreaded repaving of my machine shortly followed by installing several Windows 8 revisions from Consumer Preview to RTM. At the time, I was in the middle of a project where I was using powershell to automate a 50 page deployment document. So I knew if I can automate installing AppFabric, network shares, multiple web apps and other infrastructure settings, surely I can script the build of my own PC.

Then I found Chocolatey

So as I was looking in to how I could setup a fully functioning dev environment on one box to be just as  I left it on another, I inevitably discovered Chocolatey. Chocolatey is built on top of Nuget but instead of maintaining library packages for you dev project, it manages machine wide software package installations. This is good for several reasons:

  • Its plain simple to install apps that can be tedious to install on your own. Instead of hunting around the internet for the download page, forgetting to uncheck the animae tool bar download and waiting three minutes to click the next button, just type CINST <your app> and be done with it. Next time its time for a mega Visual Studio install session, save yourself and use CINST VisualStudioExpress2012Web.
  • Now lets say you have a bunch of apps you installed with Chocolatey and want to just update everything. Simply type CUP ALL.
  • The very best thing of all: create a “meta-package” or package.config and now you can install all of your apps in one go. Chocolatey just iterates the list and installs everything one by one along with all of their dependencies.

If you have not heard of or have not used Chocolatey, do yourself a favor and install it now.

What is Boxstarter? Chocolatey Tailored Specifically for Fresh Machine Installs

Chocolatey is awesome, but having done a TON of experimentation with automating new machine setups of all sorts of flavors, OSs and complexity, I have learned that setting up an environment can be much more than simply running a chain of installers.

Let me quickly list the benefits of Boxstarter and then I’ll dive into a few highlights:

  • Ensures the entire install session runs as administrator. This avoids occasional prompts to elevate your shell and limits it to just one at the beginning assuming you are not already running as admin.
  • Shuts down the Windows Update Service and Configuration Manager if installed during the install session. These can often interfere with installations causing installs to fail because either an update is blocking the install you are trying to run or they install patches that require a reboot before other software can be installed.
  • Can discover if there is a pending reboot and will reboot the machine and restart the install if asked to reboot. If written correctly, the install will pretty much start from where it left off. Further, Boxstarter can automatically log you in so you don’t have to stick around.
  • Boxstarter handles the initial installation of Chocolatey and if you are on a fresh win7 or server 2008R2, it will install .net 4.0 first which is a Chocolatey prerequisite.
  • Provides a bunch of helper functions for tweaking various windows settings.
  • Automates installation of critical windows updates.
  • Makes it easy to setup a local Boxstarter repo on your network so any connected machine can kickoff a install with one command.
  • Provides helper functions making it easy to create your own Boxstarter package.

The Boxstarter Killer Feature: Handling Reboots

I used to spend hours tweaking my install scripts, playing with ordering and various tricks to avoid having to reboot. There finally came a point when I realized this was pointless. Win8/Server2012 are a lot more reboot resistant but are still prone to them. Things get worse here when you are installing patches and more complicated apps like Visual Studio an/or SQL Server. I have realized that Reboots happen and can be unpredictable so the only thing to do is be able to deal with them.

The challenges are making sure the install scripts picks up right after restart, ensuring that the script does not spark a UAC prompt and block the setup, have it securely store your credentials so that it automatically logs back on after reboot but turns off auto logins after the script completes.

Boxstarter does all of these things. As a Boxstarter package author, you simply need to compose your packages to be repeatable. This means you should be able to run it again and again without error or data loss and ideally it should skip any setup processes that have already been run.

What is a Boxstarter Package?

Its just a Chocolatey package, but its intent is usually to either install a fresh environment or to lay down a complicated install chain that is highly prone to needing one or even several reboots. You can store them locally, on Chocolatey or on Myget or anywhere else you configure Boxstarter to look.

Show me the Code

First. Install Boxstarter.  The easiest way to do this is to install Boxstarter.Chocolatey from Chocolatey or download the zip from the CodePlex site and run the setup.bat. This installs all dependent modules and puts them in your user module path.

Next create a package, build it and deploy your repository to b consumed from anywhere in your network or even a thumb drive. Like this:

#After extracting Boxstarter.1.0.0.zip on MYCOMPUTER
.\setup.bat
Import-Module $env:appdata\boxstarter\Boxstarter.Chocolatey\Boxstarter.Chocolatey.psd1
#Create minimal nuspec and chocolateyInstall
New-BoxstarterPackage MyPackage
#Edit Install script to customize your environment
Notepad Join-Path $Boxstarter.LocalRepo "tools\ChocolateyInstall.ps1"
#Pack nupkg
Invoke-BoxstarterBuild MyPackage

#share Repo
Set-BoxstarterShare
#Or Copy to thumb drive G
Copy-Item $Boxstarter.BaseDir G:\ -Recurse

#Logon to your bare Windows install
\\MYCOMPUTER\Boxstarter\Boxstarter Mypackage

#Enter password when prompted and come back later to find all your apps installed

 

Now lets look at what an install package might look like

Install-WindowsUpdate -AcceptEula
Update-ExecutionPolicy Unrestricted
Move-LibraryDirectory "Personal" "$env:UserProfile\skydrive\documents"
Set-ExplorerOptions -showHidenFilesFoldersDrives -showProtectedOSFiles -showFileExtensions
Set-TaskbarSmall
Enable-RemoteDesktop

cinstm VisualStudioExpress2012Web
cinstm fiddler
cinstm mssqlserver2012express
cinstm git-credential-winstore
cinstm console-devel
cinstm skydrive
cinstm poshgit
cinstm windbg

cinst Microsoft-Hyper-V-All -source windowsFeatures
cinst IIS-WebServerRole -source windowsfeatures
cinst IIS-HttpCompressionDynamic -source windowsfeatures
cinst IIS-ManagementScriptingTools -source windowsfeatures
cinst IIS-WindowsAuthentication -source windowsfeatures
cinst TelnetClient -source windowsFeatures

Install-ChocolateyPinnedTaskBarItem "$env:windir\system32\mstsc.exe"
Install-ChocolateyPinnedTaskBarItem "$env:programfiles\console\console.exe"

copy-item (Join-Path (Get-PackageRoot($MyInvocation)) 'console.xml') -Force $env:appdata\console\console.xml

Install-ChocolateyVsixPackage xunit http://visualstudiogallery.msdn.microsoft.com/463c5987-f82b-46c8-a97e-b1cde42b9099/file/66837/1/xunit.runner.visualstudio.vsix
Install-ChocolateyVsixPackage autowrocktestable http://visualstudiogallery.msdn.microsoft.com/ea3a37c9-1c76-4628-803e-b10a109e7943/file/73131/1/AutoWrockTestable.vsix

 

Whats going on here?

Boxstarter installs critical updates, sets your powershell execution policy to unrestricted, makes windows explorer usable, installs some great apps, installs some of your favorite windows features, moves your Documents library to skydrive (I love this for the truly portable desktop), installs your favorite VS extensions and sets up things like pinned items and task bar size preference.

A lot of this functionality comes to you compliments of Chocolatey and others are specific to Boxstarter.

What’s Next?

As I see it, this is just the absolute base functionality so far. There is so much more to be added to make an installation process truly seamless. Here are some features I plan to begin soon:

  • Create a Boxstarter package automatically based on software already installed and windows features turned on to mimic these on another machine.
  • While Boxstarter can be installed and run either on bare metal or a VM, I want to make Boxstarter the powershell/hyper-v equivalent of Vagrant. Making the deployment of scripted VMs simple.
  • Add a one-click installer making a an easy one liner install command possible remotely.

There is a lot of Boxstarter functionality I have not covered here. I plan to blog fairly regularly providing brief posts describing various ways Boxstarter can augment your automation. If you want to learn more now. Checkout the Boxstarter Codeplex site which has complete documentation of all features and commands.