Category: Algemeen

Build a Windows lab with VirtualBox, Packer and Vagrant, adding sysprep (part 2)

In my former article I elaborated on how to create a lab with Windows servers quickly using Vagrant, Packer and Virtualbox. What I did not realize at that time is that the box I created had 2 issues:

  • The box is not sysprepped. The sysprep is mandatory if you want to create a Domain Controller and are adding boxes to the domain. You'll end up with 2 boxes with the same SID.
  • The MAC address is cloned for every Vagrant machine that is based on the box. This becomes a problem if you want to place the hosts in a bridged network.


Then I went and Googled (after trying in vain myself) and found this repository: Someone has already done it.

Step 1:
Here is a link to the json file:
Copy this file to the packer-windows folder and save it as 'windows_2012_r2_sysprep.json'

Step 2
Make sure you copy the Autounattend_sysprep.xml file from \packer-community-templates\answer_files\2012_r2 to the \packer-windows\answer_files\2012_r2 folder.

Now you can run packer build -only virtualbox-iso windows_2012_r2_sysprep.json. And grab a coffee.

MAC address

The MAC address can be set in the Vagrant file for each machine. First you need to know what the name is of the network interface for the bridge.


Then you can insert the name of the nic in the Vagrantfile like this (see the marked lines):

Vagrant.require_version ">= 1.6.2"

$root_provision_script = <<'ROOT_PROVISION_SCRIPT'
& $env:windir\system32\tzutil /s "W. Europe Standard Time"


Vagrant.configure("2") do |config|
    config.vm.define "dsc01" = "windows_2012_r2"
    config.vm.hostname = "dsc01" 
    config.vm.provider :virtualbox do |v, override|
        v.gui = true
end :forwarded_port, guest: 3389, host: 3391, id: "rdp", auto_correct: true :forwarded_port, guest: 22, host: 2223, id: "ssh", auto_correct: true "public_network", :bridge => 'Qualcomm Atheros AR8151 PCI-E Gigabit Ethernet Controller (NDIS 6.30)', :mac => "5CA1AB1E0001"
        config.vm.provision "shell", inline: $root_provision_script

So there it is.

How to use Powershell without Google

Here are a few tips to use Powershell without using Google.

I know my way around Powershell quite OK. But I Googled a lot and when in a hurry I copied and pasted a lot. So I never took the time to be really in depth. Now is the time! And there is a way to write Powershell without Google! You need to know some very basics and how to study cmndlets, methods and properties.

Let's go!

Suppose you want to check if there are any PST's on a harddrive and let's pretend you know nothing, just like John Snow.

Find out what cmdlets are available with Get-Command

Obviously we need to recurse directories to see if there are any files with a .pst extension. So let's see if there's a cmdlet (a function) with 'dir' in it.

PS C:\Users\Jacqueline> get-command '*dir*'

CommandType     Name                         
-----------     ----
Alias           chdir -> Set-Location
Alias           dir -> Get-ChildItem
Alias           rmdir -> Remove-Item

Get-ChildItem looks like the cmdlet we need. Let's see how it works with the help files.

Get the help files

The problem with command line interfaces: you can't 'guess' which command to use and what the parameters are. So you will need to read the help files. And you will want to update them. Unfortunately, the help files are in c:\windows\system32, so you need to run the command as an Administrator. You can only update-help once a day unless you use the -Force parameter. So open a console as an Admin and run:

PS C:\> update-help -UICulture en-US -force

Needless to say an Internet connection is required. What if you don't have one?


Saving help to an alternate location

In that case you can save the help files on an alternate location or on a netwerk share and then update-help.

PS C:\> save-help -DestinationPath C:\powershell\help2 -force -UICulture en-US

and then (as an Administrator):

PS C:\> Update-Help -SourcePath C:\powershell\help2\ -force -UICulture en-US

Now you can use the help files.

Using the help

PS C:\> Help Get-ChildItem

Will display all there is to know about Get-Childitem. Like parameters and what kind of parameters it accepts (string, arrays and so on). If you scroll down the help you get to see the remarks:

    To see the examples, type: "get-help Get-ChildItem -examples".
    For more information, type: "get-help Get-ChildItem -detailed".
    For technical information, type: "get-help Get-ChildItem -full".
    For online help, type: "get-help Get-ChildItem -online"

The -examples are very convenient if you want to have a quick solution.

So now we can play a bit with Get-ChildItem. Let's discover its syntax:

    Get-ChildItem [[-Path] ] [[-Filter] ] [-Exclude ] [-Force] [-Include ] [-Name] [-Recurse] [-UseTransaction
    []] []

    Get-ChildItem [[-Filter] ] [-Exclude ] [-Force] [-Include ] [-Name] [-Recurse] -LiteralPath  [-UseTransacti
    on []] []

    Get-ChildItem [-Attributes ] [-Directory] [-File] [-Force] [-Hidden] [-ReadOnly] [-System] [-UseTransaction] [

Here we see it accepts a -Path parameter which is an array because there are brackets: String[]. So we can input multiple search locations by creating an array of locations. Let's see how we can define an array in Powershell.

get-help array

And you will see you get very valuable information about how to create an array. I could create an array like this:

$search = @($env:HOMEPATH,"c:\temp")

Notice the quotes around c:\temp because we're dealing with strings.
The $env:HOMEPATH is already a variable which returns a string.

We can test the array like follows:

PS C:\Users\Jacqueline> $search = @($env:HOMEPATH\Dropbox,"c:\temp")
PS C:\Users\Jacqueline> $search

Now we can do a search ilke this:

get-childitem -path $search -Recurse -Include "*.pst" 

I don't want to look at all those red error messages, so let's suppress them:

get-childitem -path $search -Recurse -Include "*.pst" -ErrorAction silentlycontinue

And now for real:

PS C:\Temp> get-childitem -path $search -Recurse -Include "*.pst" -ErrorAction SilentlyContinue

    Directory: C:\Users\Jacqueline\Dropbox\work

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----         5-4-2013     18:33      211305472 jacqueline.pst

    Directory: C:\temp

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        26-7-2015     13:12        5242880 archive.pst
-a----        26-7-2015     13:12        5242880 old.pst

Let's put the result in a variable, like so:

$pst = Get-ChildItem -Path "c:\" -Include "*.pst" -Recurse -ErrorAction SilentlyContinue

Investigating $pst with Get-Member

Like Get-Command and Get-Help, Get-Member is a really import cmdlet you should know about. With Get-Member we can investigate which properties and methods are available. How can I actually write a script or type a command-line command without having to memorize every object model found on MSDN?

Once you connect to an object you can pipe that object to Get-Member; in turn, Get-Member will enumerate the properties and methods of that object.

PS C:\Temp> $pst | Get-Member

   TypeName: System.IO.FileInfo

Name                      MemberType     Definition
----                      ----------     ----------
Mode                      CodeProperty   System.String Mode{get=Mode;}
AppendText                Method         System.IO.StreamWriter AppendText()
CopyTo                    Method         System.IO.FileInfo CopyTo(string destFileName), System.IO.FileInfo... Create                    Method         System.IO.FileStream Create()
CreateObjRef              Method         System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
CreateText                Method         System.IO.StreamWriter CreateText()
Decrypt                   Method         void Decrypt()
Delete                    Method         void Delete()
Encrypt                   Method         void Encrypt()
Equals                    Method         bool Equals(System.Object obj)


Scrolling down the list you will notice a Method GetType. Let's run that:

PS C:\Temp> $pst.GetType()

IsPublic IsSerial Name          BaseType
-------- -------- ----          --------
True     True     Object[]      System.Array

So $pst is an Array (we already knew that..) but what is in the array?

PS C:\Temp> $pst | ForEach-Object { write-host $_.GetType()}

So, we've got an array full of FileInfo objects. Each objects has a set of methods and properties, which we can query by using Get-Member.

Copying and renaming the PST's to another location

Let's copy the PST's to another location and rename then so some admin can import the PST into a mailbox.

Just copying is not that hard:

$pst | Copy-Item -Destination C:\Temp\pst-share

But if I want to rename the file as well I have to be a bit more 'developerish':

foreach ($f in $pst) {
    $name = $env:USERNAME + '-' + $f.Name
    Copy-Item $f.FullName -Destination "C:\temp\pst-share\$name"

Let's debate on this script tomorrow.

The Daily Tooltip from the Button Factory

Want tooltips every week? Then like our facebook page and receive a daily tooltip from the Buttonfactory!



Happy birthday to us! 5th anniversary

itslet5jrHappy birthday to us...happy birthday tooo us!!! This month we celebrate the fifth anniversary of the Buttonfactory.

So we made a very special 5th anniversary facebook page so you can send us your birthday wishes:)

Or you can ask us a question about a topic you would like to see us cover and in January we will  pick the best questions!


ASP.NET Web API, Angularjs and MongoDb part 5

This is the sequel to this.

You can find the finished application here:


When I was creating this I was really confused about 'the double MVC' setup. The one that comes with ASP.NET and the one that comes with Angularjs.

It felt like what I was doing was bloathed and wrong.

I decided to start again from scratch, but this time with Node as the backend.