Part 1 in this series can be found here.

Introduction

Those readers who followed on from Part 1 in this series will know that I’m working on a side project that requires testing against a CIFS/SMB storage server and that the only hardware I have access to is my normal, everyday home workstation, lest I get banished to the sofa when it’s time to sleep.

A quick summary of my other goals are:

  • Find a solution that only consumes resources on my workstation when I’m using it. I must be able to consistently create and destroy my infrastructure on-demand.
  • I should be able to easily reuse as much of the solution as possible when I need other types of server instance in future.

This series is a walkthrough of utilizing a great stack of industry-leading automation tools to meet the goals listed above. These technologies are Vagrant, Hyper-V and Chef, all with a heavy dose of PowerShell. Together they allow me to use my resources efficiently, deploy quickly and with a consistency of configuration that you just can’t get with manually handled test infrastructure.

Part 1 covered Vagrant Providers, Hyper-V, the creation of a Windows Server 2016 base image and the integrations between all of them that give us an automated, repeatable deployment of a vanilla Windows Server. This post is going to cover Vagrant Provisioners, Chef Cookbooks and how to use declarative system configuration code to quickly and efficiently transform our vanilla server instance into the type of server I need for my dev testing.

I’m going to assume you’re comfortable with Vagrant boxes and providers in this post. If not, Part 1 is well worth working through. If you want to work through this post as a standalone piece you should hopefully be able to get a compatible Windows Server box from Atlas, install Chef Client on it and change the credentials used in the Vagrantfile. But on the whole this post is going to assume you’ve done everything in Part 1, so bear that in mind if you haven’t.

Requirements

Tool Version used in this post
Vagrant 1.9.2
Chef Development Kit  1.2.22

Vagrant Provisioners

I briefly introduced provisioners in Part 1 as being the mechanism that takes over after the provider has created a VM from your base image, its job is to apply some configuration that makes your server instance useful for some purpose. There’s a clear separation of concerns in Vagrant between instance creation (provider) and instance configuration (provisioner). This is a good thing because by not fixing my current use case to the base image I make it more reusable in other contexts. I don’t have to go through the time-costly process of installing Windows Server again if next week I want an IIS Web Server instead, I only have to change what I want the provisioner to do. The box is the same, the work the provider does is the same.

The shell provisioner

The Vagrant provisioner that most people get introduced to first is the shell provisioner. The most common way to use it is to specify it in your Vagrantfile and either pass it a line of code to execute:

Vagrant.configure("2") do |config|
  config.vm.provision "shell",
    inline: "Write-Host Hello, World!"
end

Or pass it a reference to a script relative to your Vagrant directory:

Vagrant.configure("2") do |config|
  config.vm.provision "shell", path: "Script.ps1"
end

Scripting is not the way to manage infrastructure

So now’s a good time to come out and say it: I actually don’t recommend scripting (in the traditional sense) for configuring infrastructure. The only thing I recommend less is configuring the infrastructure by hand.

Now that might sound a bit of an oxymoron to you. An automation blog advising you not to write scripts does make it sound like I’m having an existential crisis but don’t call the men in white coats yet, I did caveat my statement with “in the traditional sense”. By that, I mean a script written in the imperative style, i.e. a list of PowerShell or bash commands telling the computer precisely the steps it should take to achieve an outcome.

The problem with imperative scripts is that they’re only useful down the very narrow path they were written for. I could write a script to turn vanilla Windows Server into the file server I need, feed it into the shell provisioner and I would have what I set out to achieve. However, that script won’t help me much when the file server has been alive for a while and it stops working because some service or other has quietly terminated. That’s why you can’t really manage infrastructure successfully with traditional scripting, scripts are not adaptive enough to deal with the range of issues that successful infrastructure automation would need to deal with.

So for that reason, we’re not going to be using Vagrant’s shell provisioner.

If not scripting, then what?

A specification.

Imagine that you have your own personal sysadmin that could configure any server instance you require. All you need to do is write them a specification for that system and you can leave the implementation details to them. They never forget the requirements in the spec and regularly double-check all systems that they have built for you to ensure that they still meet your specification, without being asked. They’re several orders of magnitude quicker than the average sysadmin and they never complain about being called to action at 3 am either. That dream is what using configuration management software like Chef or Puppet provides. These systems read in a specification for a system, written in a domain-specific language (DSL) that describes the intended state of a system, then their interpreters modify the system with idempotent resources/packages to ensure that the system complies with the desired state.

The main difference between an imperative script and one of these specifications is that they are declarative. They say only what you want not how to get there. If the system is not in the desired state, these systems will configure it so that it complies with the specification. If the system already complies with the specification, they will leave it alone. The declarative nature of these DSLs makes them much more useful when your target machine has drifted away from a known state in an unpredictable way. For this reason, we’re going to use Vagrant’s Chef Solo provisioner and a Chef Cookbook to configure our Vagrant VM instead.

Cooking up a Storage Server

Installing the ChefDK

To install the Chef Development Kit on your host machine, open PowerShell as Administrator and execute:

# Download ChefDK. You can browse the Chef website for the most appropriate URI for you.
# Although this one has "2012r2" in it, this is a URI for Windows 10.
Invoke-WebRequest -UseBasicParsing -Uri https://packages.chef.io/files/stable/chefdk/1.2.22/windows/2012r2/chefdk-1.2.22-1-x86.msi -OutFile chefdk-x86.msi

# Install the MSI. Verbose logging is output to msiexec_log.log if you need to troubleshoot.
$result = Start-Process msiexec.exe -Wait -PassThru -ArgumentList "/qn /L*v msiexec_log.log /i $(Get-Location)\chefdk-x86.msi"

# Check that msiexec returned 0 (success)
$result.ExitCode

# After a successful install, if you open a new PowerShell window you will find that
# 'C:\opscode\chefdk\bin\' has been added to your path. Let's add it manually here so
# that we can keep using this shell window.
$env:Path = "$env:Path;C:\opscode\chefdk\bin\"

# Make sure everything is okay. All components should pass verification.
chef verify

Generating a Chef Cookbook

Cookbooks are the main unit of configuration in the Chef ecosystem. They can be thought of as similar to software packages, they’re a self-contained structure containing code, properties, dependency references, templates, test code, etc. for the purpose of applying some sort of configuration or policy to a machine under Chef’s control. In this case “code” means that declarative DSL I was talking about earlier, which is actually written in Ruby.

Don’t be alarmed if you’ve come this far and are worried that you don’t know Ruby very well, the DSL has a very consistent grammar and you don’t really need to know the dark corners of Ruby to understand or gain value from it. The Vagrantfile used by Vagrant is written in Ruby as well, so this isn’t entirely new territory for us.

To create a new cookbook:

# Make a new directory called 'cookbooks' in Vagrant directory. This is the default
# location for Chef cookbooks in Vagrant.
New-Item -Name cookbooks -ItemType Directory
Set-Location -Path .\cookbooks\
# chef generate cookbook {name}
chef generate cookbook windows_file_server

You will see output like:

Generating cookbook windows_file_server
- Ensuring correct cookbook file content
- Committing cookbook files to git
- Ensuring delivery configuration
- Ensuring correct delivery build cookbook content
- Adding delivery configuration to feature branch
- Adding build cookbook to feature branch
- Merging delivery content feature branch to master

Your cookbook is ready. Type `cd windows_file_server` to enter it.

There are several commands you can run to get started locally developing and testing your cookbook.
Type `delivery local --help` to see a full list.

Why not start by writing a test? Tests for the default recipe are stored at:

test/smoke/default/default_test.rb

If you'd prefer to dive right in, the default recipe can be found at:

recipes/default.rb

We have a new directory in our cookbooks folder called windows_file_server. Let’s take a look at the file structure.

windows_file_server
│   .gitignore
│   .kitchen.yml
│   Berksfile
│   chefignore
│   metadata.rb
│   README.md
│
├───.delivery
│   │   config.json
│   │   project.toml
│   │
│   └───build_cookbook
│       │   .kitchen.yml
│       │   Berksfile
│       │   chefignore
│       │   LICENSE
│       │   metadata.rb
│       │   README.md
│       │
│       ├───data_bags
│       │   └───keys
│       │           delivery_builder_keys.json
│       │
│       ├───recipes
│       │       default.rb
│       │       deploy.rb
│       │       functional.rb
│       │       lint.rb
│       │       provision.rb
│       │       publish.rb
│       │       quality.rb
│       │       security.rb
│       │       smoke.rb
│       │       syntax.rb
│       │       unit.rb
│       │
│       ├───secrets
│       │       fakey-mcfakerton
│       │
│       └───test
│           └───fixtures
│               └───cookbooks
│                   └───test
│                       │   metadata.rb
│                       │
│                       └───recipes
│                               default.rb
│
├───recipes
│       default.rb
│
├───spec
│   │   spec_helper.rb
│   │
│   └───unit
│       └───recipes
│               default_spec.rb
│
└───test
    └───smoke
        └───default
                default_test.rb

A couple of interesting artifacts are:

  • .kitchen.yml – The configuration for testing this cookbook in Test Kitchen, a test harness for cookbooks that can validate them across a variety of virtualization platforms.
  • BerksfileBerkshelf is a dependency manager for cookbooks and Berksfile is where its configuration lives. The default Berksfile contains little more than a reference to https://supermarket.chef.io, which is the public repository for Chef cookbooks. Berkshelf will use this and any other configuration you add to resolve any dependencies you have on other cookbooks. metadata is a reference to the cookbook’s metadata file.
  • metadata.rb – Where cookbook metadata is stored. We will mainly use it for declaring dependencies for Berkshelf to manage.
  • .delivery\* – This directory contains artifacts for setting up Continuous Delivery of the cookbook with Chef Automate.
  • recipes\default.rb – The default recipe for the cookbook. The difference between a recipe and a cookbook is that a recipe is purely a unit of the Chef DSL and a cookbook is one or more recipes with a known structure, attributes, templates, dependencies, etc. When a cookbook is run without scoping to the recipe level, the default recipe is used.
  • spec\* – ChefSpec is a BDD (behavior-driven development) testing framework for cookbooks. It’s an extension of RSpec, the de-facto BDD framework of Ruby. This directory contains an example of a ChefSpec test.
  • test\* – InSpec is a framework for compliance testing and auditing of Chef-managed nodes at runtime. This directory contains an example InSpec test for the default recipe.

Test Kitchen, Chef Automate, ChefSpec and InSpec are outside the scope of this blog post, but it’s worth broadly knowing what everything in the generated cookbook is for, even if we won’t be touching many of them to get the cookbook working with Vagrant.

Writing a recipe

Let’s construct the recipe that will transform our Vagrant VM into a file server when we vagrant up. Open recipes\default.rb in your favorite text editor and edit it so that it matches the following recipe:

#
# Cookbook:: windows_file_server
# Recipe:: default
#
# Copyright:: 2017, The Authors, All Rights Reserved.

windows_feature 'File-Services' do
  action :install
  all true
  install_method :windows_feature_powershell
end

directory 'C:\SmbShare' do
  action :create
end

windows_share 'SmbShare' do
  action :create
  path 'C:\SmbShare'
end

This is our first time looking at a Chef recipe, so let’s talk the basics. Each of the three blocks of configuration code here is a reference to a Chef Resource. They each represent a policy that describes the desired state of something under that resource’s control. The resource types used are windows_feature, directory and windows_share. Let’s tackle them one-by-one.

windows_feature 'File-Services' do
  action :install
  all true
  install_method :windows_feature_powershell
end

The windows_feature resource type predictably maps over Windows Features, i.e. the bundles of functionality you can install with the Install-WindowsFeature cmdlet in PowerShell. File-Services is the name of the resource (Windows Feature) we want to configure, installing it on our Windows Server instance makes it a CIFS/SMB storage server, which is the goal of this exercise. Together the resource type and name refer to a specific configurable entity that is the resource.

Inside the do block, we have Actions and Properties. Each usage of a resource can take one action, these are usually verbs and represent the operation you want to be performed on the resource. Here, we want to ensure that the File-Services feature is installed, so our action is :install. The property all declares whether we want any dependencies of the feature to be installed or not. The property install_method allows us to choose an installation method for the feature, as there is more than one way to install a feature in Windows. We’ve set this to PowerShell by specifying :windows_feature_powershell.

In each of these blocks, there is only ever one resource type, name and action (the default action is used if action is not present), everything else is a property or a value. Every resource has a docs page that has a full reference of actions, properties and values you can use. windows_feature and windows_share are part of the windows cookbook, you can find the docs page here.

directory 'C:\SmbShare' do
  action :create
end

The next block should look a little more approachable. The directory resource is built into Chef and is fully documented here. The resource this block configures is a directory at the path C:\SmbShare and the action is :create – so we want to ensure it exists.

windows_share 'SmbShare' do
  action :create
  path 'C:\SmbShare'
end

Finally, the windows_share resource turns our directory into a CIFS/SMB share.

Dependencies and vendoring

The windows_feature and windows_share resources are from the windows cookbook, which does not come bundled with the ChefDK. This isn’t a problem, we just need to declare it as a dependency. Open up metadata.rb and append the following line:

depends 'windows'

It’s as simple as that.

How does Chef know where to acquire the windows cookbook? Remember that Berkshelf is the dependency manager for cookbooks and our Berksfile already has a reference to the public cookbook repository, Chef Supermarket. Berkshelf will resolve our dependencies with what it can find in Chef Supermarket or any other repositories we declare in our Berksfile.

Now we need to acquire our dependencies and package everything up in a way that is consumable by Vagrant’s Chef Solo provisioner. This is as easy as making sure they are available in a known subdirectory of the Vagrant directory. To do that, we’re going to use Berkshelf’s vendor command to package everything up:

# Assuming your current working directory is the 'windows_file_server' cookbook
# directory, this will package 'windows_file_server' and 'windows' into a new
# 'vendor' subdirectory under your Vagrant directory.
berks vendor ..\..\vendor

Running with the Chef Solo provisioner

Now let’s edit our Vagrantfile to configure the Chef Solo provisioner. Open Vagrantfile in your text editor and append the following:

config.vm.provision "chef_solo" do |chef|
  chef.add_recipe "windows_file_server"
  chef.cookbooks_path = "vendor"
end

This is fairly straight-forward. config.vm.provision sets our provisioner, chef.add_recipe tells the provisioner which of our cookbooks to run and chef.cookbooks_path tells the provisioner to acquire them from the vendor subdirectory.

Exit back to PowerShell and vagrant up, you should see the following:

Bringing machine 'default' up with 'hyperv' provider...
==> default: Verifying Hyper-V is enabled...
==> default: Importing a Hyper-V instance
    default: Cloning virtual hard drive...
    default: Creating and registering the VM...
    default: Successfully imported a VM with name: WindowsServer2016_1
==> default: Auto-generating node name for Chef...
==> default: Starting the machine...
==> default: Waiting for the machine to report its IP address...
    default: Timeout: 120 seconds
    default: IP: fe80::a957:b655:520a:f178
==> default: Waiting for machine to boot. This may take a few minutes...
    default: WinRM address: 192.168.0.41:5985
    default: WinRM username: Administrator
    default: WinRM execution_time_limit: PT2H
    default: WinRM transport: negotiate
==> default: Machine booted and ready!
==> default: Preparing SMB shared folders...
    default: You will be asked for the username and password to use for the SMB
    default: folders shortly. Please use the proper username/password of your
    default: Windows account.
    default:
    default: Username: Kirk
    default: Password (will be hidden):
==> default: Mounting SMB shared folders...
    default: C:/Users/Kirk/Projects/WindowsServer2016/vendor => C:/vagrant-chef/129d4b105c1e6d988cf5d1013b0938f5/cookbooks
==> default: Running provisioner: chef_solo...
==> default: Detected Chef (latest) is already installed
==> default: Generating chef JSON and uploading...
==> default: Running chef-solo...
==> default: [2017-03-28T21:41:23+01:00] INFO: Started chef-zero at chefzero://localhost:8889 with repository at C:/vagrant-chef/129d4b105c1e6d988cf5d1013b0938f5
==> default:   One version per cookbook
==> default: Starting Chef Client, version 12.19.36
==> default: [2017-03-28T21:41:23+01:00] INFO: *** Chef 12.19.36 ***
==> default: [2017-03-28T21:41:23+01:00] INFO: Platform: x64-mingw32
==> default: [2017-03-28T21:41:23+01:00] INFO: Chef-client pid: 2936
==> default: [2017-03-28T21:41:34+01:00] INFO: HTTP Request Returned 404 Not Found: Object not found: chefzero://localhost:8889/nodes/vagrant-ea6c69d1
==> default: [2017-03-28T21:41:34+01:00] INFO: Setting the run_list to ["recipe[windows_file_server]"] from CLI options
==> default: [2017-03-28T21:41:34+01:00] INFO: Run List is 
] ==> default: [2017-03-28T21:41:34+01:00] INFO: Run List expands to [windows_file_server] ==> default: [2017-03-28T21:41:34+01:00] INFO: Starting Chef Run for vagrant-ea6c69d1 ==> default: [2017-03-28T21:41:34+01:00] INFO: Running start handlers ==> default: [2017-03-28T21:41:34+01:00] INFO: Start handlers complete. ==> default: [2017-03-28T21:41:34+01:00] INFO: HTTP Request Returned 404 Not Found: Object not found: ==> default: resolving cookbooks for run list: ["windows_file_server"] ==> default: [2017-03-28T21:41:34+01:00] INFO: Loading cookbooks [windows_file_server@0.1.0, windows@3.0.2, ohai@5.0.2] ==> default: Synchronizing Cookbooks: ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows_file_server/recipes/default.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows_file_server/README.md in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/auto_run.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/certificate.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/certificate_binding.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/feature.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/feature_dism.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/feature_powershell.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows_file_server/chefignore in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows_file_server/metadata.json in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/feature_servermanagercmd.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/font.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/http_acl.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/pagefile.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/path.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/printer_port.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/printer.rb in the cache. ==> default: - windows_file_server (0.1.0) ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/shortcut.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/share.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/zipfile.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/recipes/default.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/resources/task.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/libraries/powershell_helper.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/libraries/matchers.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/libraries/version_helper.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/libraries/registry_helper.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/libraries/windows_helper.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/libraries/windows_privileged.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/libraries/version.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/libraries/wmi_helper.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/attributes/default.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/.foodcritic in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/CONTRIBUTING.md in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/MAINTAINERS.md in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/ohai/resources/hint.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/files/dism_features.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/CHANGELOG.md in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/metadata.json in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/ohai/resources/plugin.rb in the cache. ==> default: [2017-03-28T21:41:34+01:00] INFO: Storing updated cookbooks/windows/README.md in the cache. ==> default: [2017-03-28T21:41:35+01:00] INFO: Storing updated cookbooks/ohai/recipes/default.rb in the cache. ==> default: [2017-03-28T21:41:35+01:00] INFO: Storing updated cookbooks/ohai/libraries/matchers.rb in the cache. ==> default: [2017-03-28T21:41:35+01:00] INFO: Storing updated cookbooks/ohai/.foodcritic in the cache. ==> default: [2017-03-28T21:41:35+01:00] INFO: Storing updated cookbooks/ohai/CHANGELOG.md in the cache. ==> default: [2017-03-28T21:41:35+01:00] INFO: Storing updated cookbooks/ohai/CONTRIBUTING.md in the cache. ==> default: [2017-03-28T21:41:35+01:00] INFO: Storing updated cookbooks/ohai/MAINTAINERS.md in the cache. ==> default: - windows (3.0.2)[2017-03-28T21:41:35+01:00] INFO: Storing updated cookbooks/ohai/metadata.json in the cache. ==> default: [2017-03-28T21:41:35+01:00] INFO: Storing updated cookbooks/ohai/README.md in the cache. ==> default: - ohai (5.0.2) ==> default: Installing Cookbook Gems: ==> default: Compiling Cookbooks... ==> default: Converging 3 resources ==> default: Recipe: windows_file_server::default ==> default: * windows_feature[File-Services] action install ==> default: * windows_feature_powershell[File-Services] action install ==> default: Success Restart Needed Exit Code Feature Result ==> default: ------- -------------- --------- -------------- ==> default: True No Success {File and iSCSI Services, BranchCache for ... ==> default: - install Windows feature File-Services ==> default: * directory[C:\SmbShare] action create[2017-03-28T21:42:14+01:00] INFO: directory[C:\SmbShare] created directory C:\SmbShare ==> default: - create new directory C:\SmbShare ==> default: * windows_share[SmbShare] action create[2017-03-28T21:42:14+01:00] WARN: Failed to retrieve any security information about the share. ==> default: - Creating share SmbShare ==> default: [2017-03-28T21:42:15+01:00] INFO: Chef Run complete in 40.873137 seconds ==> default: Running handlers: ==> default: [2017-03-28T21:42:15+01:00] INFO: Running report handlers ==> default: Running handlers complete ==> default: [2017-03-28T21:42:15+01:00] INFO: Report handlers complete ==> default: Chef Client finished, 4/4 resources updated in 52 seconds

You’ll notice this time that Vagrant asks you for some credentials that it can use to mount the vendor directory on the endpoint. Just provide some when prompted, it’s much better than hard-coding your secure account credentials in the Vagrantfile!

Chef transformed our newly created Windows Server 2016 image into a file server in 52 seconds. That’s pretty good going!

Closing thoughts

I hope you can see that the Chef DSL is a very powerful and approachable way of configuring DevTest infrastructure. You could even acquire some cookbooks used to configure your production environments and have an even more realistic test environment.

The simple recipe I put together for my file server doesn’t quite do everything I need it to for my side project yet, I need to test share access with domain credentials and access permission. Next, I need to go and create a new cookbook to configure a Domain Controller, then I can apply both cookbooks to my server instance with Vagrant. Even though they will be applied to the same VM, it’s better to use multiple cookbooks for this (rather than one that does both) as I might want to reuse them in a multi-machine environment with a separate Domain Controller in the future. The great thing is that I can combine my general purpose base image with any combination of cookbooks I like and that’s a great position for maximizing reuse of these artifacts in future projects.

About the Author Kirk MacPhee

An experienced software developer and technical lead, specializing in automation technologies and their application.

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s