Blog-S_Generic_-1

Chef ♥’s Windows

Here at Opscode we have the stated goal of creating “Infrastructure Automation for the Masses”. We won’t rest until Chef manages EVERYTHING! And everything includes a whole lot more than Linux servers – it also includes our brethren on the Windows platform. To that end, I wanted to give everyone a quick update on some killer new features we’ve added to Chef for managing Windows and mixed *nix/Windows environments. We’ll be covering:

  • Windows platform support in core Chef/Ohai Libraries
  • The knife-windows plugin for Chef’s CLI tool Knife which adds new subcommands for:
    • invoking remote commands via WinRM
    • bootstrapping Windows nodes via WinRM and SSH
  • The new PowerShell cookbook Opscode recently contributed to the community

Chef/Ohai Libraries

A majority of the resources that ship with Chef work out of the box on the Windows platform. These include: Service, Execute, User, Group, Mount, Gem Package, Remote File, Cookbook File, Ruby Block, Log. This primes the toolbox of the Windows cook with a powerful set of primitives to accomplish a majority of Windows-related configuration management tasks. If you have identified any missing resources be sure to open a feature request in the Chef ticketing system. The Chef 0.10 release also fixed a number of outstanding compatibility issues related running Chef on the Windows platform.

Likewise Ohai 0.6.4 currently has no issues profiling nodes on the Windows platform. It also fixed an annoying bug that required a modification to a nodes client.rb file.

knife-windows

My favorite new feature in the recent release of Chef 0.10 is the ability to extend Knife’s subcommand system with Knife Plugins. For details on how you create your own plugins please take a look at our prior blog post which outlined the creation of a simple plugin from start to finish. One of the knife plugins we shipped alongside Chef 0.10 is named knife-windows. This plugin adds additional functionality to Knife for configuring/interacting with nodes running Microsoft Windows.

Knife Plugins ship as Ruby gems so knife-windows is easily installable with the following one-liner:

[sourcecode lang=”bash” gutter=”false”]
$ gem install knife-windows
$ knife –help

[ … SNIP … ]

** BOOTSTRAP COMMANDS **
knife bootstrap FQDN (options)
knife bootstrap windows winrm FQDN (options)
knife bootstrap windows ssh FQDN (options)

[ … SNIP … ]

** WINRM COMMANDS **
knife winrm QUERY COMMAND (options)
[/sourcecode]

Since knife-windows is written in pure Ruby (just like Chef) you should be able to install it on any platform that Chef supports. This means you can manage your Windows nodes from the comfort of bash on your favorite Linux distro or better yet zsh on OS X!

Let’s take a quick look at the new subcommands knife-windows brings to the party.

Running Remote Commands via WinRM – knife winrm

One of our community’s favorite core Knife subcommands is knife ssh. This subcommand gives you the ability to invoke a command via the SSH protocol across all (or a subset) of the nodes in your Chef-managed infrastructure. If your Windows nodes are already running an SSH daemon like freeSSHd or WinSSHD you can leverage knife ssh as is. While remote management via SSH is certainly possible on Windows servers, it’s not the protocol blessed by Microsoft. Currently, Microsoft recommends using Windows Remote Management (WinRM) which is their implementation of the of SOAP-based WS-Management Protocol. WinRM allows you to remotely call native objects in Windows. Basically WinRM allows you to open a remote session on a Windows server and then invoke commands against that server.

knife winrm exposes the same power as knife ssh except all commands are invoked via the WinRM protocol! So what can you do with knife winrm? Pretty much anything you would normally expect to do with knife ssh. The knife winrm even uses the same search syntax as the knife ssh and knife search subcommands.

Since code speaks louder than works let’s take a look at a few example commands.

Firstly you might find the uptime of all your web servers using this command:

[sourcecode lang=”bash” gutter=”false”]
$ knife winrm "role:web" "net statistics server" \
-m -x Administrator -P ‘secret_password’
[/sourcecode]

command output:

[sourcecode lang=”bash” gutter=”false” collapse=”true”]
ec2-50-xx-xx-124.compute-1.amazonaws.com Server Statistics for \\
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Statistics since 5/18/2011 4:48:02 PM
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Sessions accepted 1
ec2-50-xx-xx-124.compute-1.amazonaws.com Sessions timed-out 0
ec2-50-xx-xx-124.compute-1.amazonaws.com Sessions errored-out 0
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Kilobytes sent 0
ec2-50-xx-xx-124.compute-1.amazonaws.com Kilobytes received 0
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Mean response time (msec) 0
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com System errors 0
ec2-50-xx-xx-124.compute-1.amazonaws.com Permission violations 0
ec2-50-xx-xx-124.compute-1.amazonaws.com Password violations 0
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Files accessed 0
ec2-50-xx-xx-124.compute-1.amazonaws.com Communication devices accessed 0
ec2-50-xx-xx-124.compute-1.amazonaws.com Print jobs spooled 0
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Times buffers exhausted
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Big buffers 0
ec2-50-xx-xx-124.compute-1.amazonaws.com Request buffers 0
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com The command completed successfully.
ec2-50-xx-xx-124.compute-1.amazonaws.com
[/sourcecode]

You could also invoke everyone’s favorite Windows command ipconfig against a single server (vs a search query in the prior example) using the following command:

[sourcecode lang=”bash” gutter=”false”]
$ knife winrm ‘ec2-50-xx-xx-124.compute-1.amazonaws.com’ ‘ipconfig’ \
-m -x Administrator -P ‘secret_password’
[/sourcecode]

command output:

[sourcecode lang=”bash” gutter=”false” collapse=”true”]
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Windows IP Configuration
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Ethernet adapter Local Area Connection:
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Connection-specific DNS Suffix . : ec2.internal
ec2-50-xx-xx-124.compute-1.amazonaws.com Link-local IPv6 Address . . . . . : fe80::54f4:b9d4:16a4:b931%11
ec2-50-xx-xx-124.compute-1.amazonaws.com IPv4 Address. . . . . . . . . . . : 10.108.14.167
ec2-50-xx-xx-124.compute-1.amazonaws.com Subnet Mask . . . . . . . . . . . : 255.255.254.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Default Gateway . . . . . . . . . : 10.108.14.1
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Tunnel adapter isatap.ec2.internal:
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Media State . . . . . . . . . . . : Media disconnected
ec2-50-xx-xx-124.compute-1.amazonaws.com Connection-specific DNS Suffix . : ec2.internal
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Tunnel adapter Local Area Connection* 9:
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Connection-specific DNS Suffix . :
ec2-50-xx-xx-124.compute-1.amazonaws.com IPv6 Address. . . . . . . . . . . : 2001:0:4137:9e76:41d:1efb:f593:f158
ec2-50-xx-xx-124.compute-1.amazonaws.com Link-local IPv6 Address . . . . . : fe80::41d:1efb:f593:f158%12
ec2-50-xx-xx-124.compute-1.amazonaws.com Default Gateway . . . . . . . . . : ::

[/sourcecode]

Or better yet force a Chef run:

[sourcecode lang=”bash” gutter=”false”]
$ knife winrm ec2-50-xx-xx-124.compute-1.amazonaws.com \
‘chef-client -c c:/chef/client.rb’ -m -x Administrator -P ‘secret_password’
[/sourcecode]

command output:

[sourcecode lang=”bash” gutter=”false” collapse=”true”]
ec2-50-xx-xx-124.compute-1.amazonaws.com [Fri, 20 May 2011 22:33:04 +0100] INFO: *** Chef 0.10.0 ***
ec2-50-xx-xx-124.compute-1.amazonaws.com [Fri, 20 May 2011 22:33:17 +0100] INFO: Run List is

]
ec2-50-xx-xx-124.compute-1.amazonaws.com [Fri, 20 May 2011 22:33:17 +0100] INFO: Run List expands to [powershell::default]
ec2-50-xx-xx-124.compute-1.amazonaws.com [Fri, 20 May 2011 22:33:17 +0100] INFO: Starting Chef Run for ip-0A6C0EA7.ec2.internal
ec2-50-xx-xx-124.compute-1.amazonaws.com [Fri, 20 May 2011 22:33:20 +0100] INFO: Loading cookbooks [powershell]
ec2-50-xx-xx-124.compute-1.amazonaws.com [Fri, 20 May 2011 22:33:21 +0100] INFO: PowerShell 2.0 is already enabled on this version of Windows: 6.1.7601

[ … SNIP … ]

ec2-50-xx-xx-124.compute-1.amazonaws.com [Fri, 20 May 2011 22:33:26 +0100] INFO: Chef Run complete in 8.439546 seconds
ec2-50-xx-xx-124.compute-1.amazonaws.com [Fri, 20 May 2011 22:33:26 +0100] INFO: Running report handlers
ec2-50-xx-xx-124.compute-1.amazonaws.com [Fri, 20 May 2011 22:33:26 +0100] INFO: Report handlers complete
[/sourcecode]

It even includes knife ssh ‘s slick interactive mode:

[sourcecode lang=”bash” gutter=”false”]
$ knife winrm ec2-50-xx-xx-124.compute-1.amazonaws.com interactive \
-m -x Administrator -P "secret_password"
[/sourcecode]

command output:

[sourcecode lang=”bash” gutter=”false” collapse=”true”]
Connected to ec2-50-xx-xx-124.compute-1.amazonaws.com

To run a command on a list of servers, do:
on SERVER1 SERVER2 SERVER3; COMMAND
Example: on latte foamy; echo foobar

To exit interactive mode, use ‘quit!’

knife-winrm> dir
ec2-50-xx-xx-124.compute-1.amazonaws.com Volume in drive C has no label.
ec2-50-xx-xx-124.compute-1.amazonaws.com Volume Serial Number is FC27-1871
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Directory of C:\Users\Administrator
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com 05/18/2011 06:35 PM <DIR> .
ec2-50-xx-xx-124.compute-1.amazonaws.com 05/18/2011 06:35 PM <DIR> ..
ec2-50-xx-xx-124.compute-1.amazonaws.com 05/18/2011 06:35 PM <DIR> .gem
ec2-50-xx-xx-124.compute-1.amazonaws.com 05/21/2011 03:27 AM 519 config.yml
ec2-50-xx-xx-124.compute-1.amazonaws.com 05/18/2011 06:30 PM <DIR> Contacts
ec2-50-xx-xx-124.compute-1.amazonaws.com 05/18/2011 06:30 PM <DIR> Desktop
ec2-50-xx-xx-124.compute-1.amazonaws.com 05/18/2011 06:30 PM <DIR> Documents
ec2-50-xx-xx-124.compute-1.amazonaws.com 05/18/2011 06:30 PM <DIR> Downloads
ec2-50-xx-xx-124.compute-1.amazonaws.com 05/18/2011 06:30 PM <DIR> Favorites
ec2-50-xx-xx-124.compute-1.amazonaws.com 05/18/2011 06:30 PM <DIR> Links
ec2-50-xx-xx-124.compute-1.amazonaws.com 05/18/2011 06:30 PM <DIR> Music
ec2-50-xx-xx-124.compute-1.amazonaws.com 05/18/2011 06:30 PM <DIR> Pictures
ec2-50-xx-xx-124.compute-1.amazonaws.com 05/18/2011 06:30 PM <DIR> Saved Games
ec2-50-xx-xx-124.compute-1.amazonaws.com 05/18/2011 06:30 PM <DIR> Searches
ec2-50-xx-xx-124.compute-1.amazonaws.com 05/18/2011 06:30 PM <DIR> Videos
ec2-50-xx-xx-124.compute-1.amazonaws.com 1 File(s) 519 bytes
ec2-50-xx-xx-124.compute-1.amazonaws.com 14 Dir(s) 19,469,873,152 bytes free
knife-winrm>
[/sourcecode]

Your Window’s nodes will need to have properly configured WinRM installation before you can start invoking remote commands against them with the knife-windows. Please check out knife-window’s README and the WinRM configuration guide for more information and examples.

Bootstrapping Windows Nodes – knife bootstrap windows *

The goal of a Chef bootstrap is to get Chef installed on the target system so it can run chef-client and register with a Chef Server (like the Opscode Platform). The main assumption is nothing more than a baseline OS installation exists. knife-windows ships with 2 new subcommands for bootstrapping Windows nodes via the WinRM and SSH protocols. Although the remote command protocols differ, both subcommands leverage the same bootstrap template and thus perform the same steps on the target node:

  • Installs Ruby 1.8.7 via the Ruby Installer for Windows which also includes the latest version of RubyGems
  • Installs the RubyInstaller Development Kit (DevKit). The RubyInstaller Development Kit (DevKit) is a MSYS/MinGW based toolkit than enables you to build many of the native C/C++ extensions available for Ruby.
  • Installs required Windows-related gems from RubyGems.org
  • Installs latest Chef version from RubyGems.org including Ohai and Chef. The Chef version is configurable using the —bootstrap-version option.
  • Writes the validation.pem per the local knife configuration.
  • Writes a default config file for Chef (C:\chef\client.rb) using values per the local knife configuration.
  • Creates a file containing the run list given on the command line.
  • Runs chef-client with that run list.

Here is a command that might be used to bootstrap a production webserver via WinRM:

[sourcecode lang=”bash” gutter=”false” collapse=”false”]
$ knife bootstrap windows winrm ec2-50-xx-xx-124.compute-1.amazonaws.com \
-r ‘role[webserver]’ -E production \
-x Administrator -P ‘super_secret_password’
[/sourcecode]

Output from this command would look something like this:

[sourcecode lang=”bash” gutter=”false” collapse=”true”]
Bootstrapping Chef on ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com "Rendering bootstrap.bat chunk 1"
ec2-50-xx-xx-124.compute-1.amazonaws.com "Rendering bootstrap.bat chunk 2"
ec2-50-xx-xx-124.compute-1.amazonaws.com "Rendering bootstrap.bat chunk 3"
ec2-50-xx-xx-124.compute-1.amazonaws.com "Rendering bootstrap.bat chunk 4"
ec2-50-xx-xx-124.compute-1.amazonaws.com "Rendering bootstrap.bat chunk 5"
ec2-50-xx-xx-124.compute-1.amazonaws.com "Rendering bootstrap.bat chunk 6"
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>mkdir C:\chef
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>(
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.url = WScript.Arguments.Named("url")
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.path = WScript.Arguments.Named("path")
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP.6.0")
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.Set wshShell = CreateObject( "WScript.Shell" )
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.Set objUserVariables = wshShell.Environment("USER")
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.’http proxy is optional
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.’attempt to read from HTTP_PROXY env var first
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.On Error Resume Next
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.If NOT (objUserVariables("HTTP_PROXY") = "") Then
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.objXMLHTTP.setProxy 2, objUserVariables("HTTP_PROXY")
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.’fall back to named arg
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.ElseIf NOT (WScript.Arguments.Named("proxy") = "") Then
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.objXMLHTTP.setProxy 2, WScript.Arguments.Named("proxy")
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.End If
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.On Error Goto 0
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.objXMLHTTP.open "GET", url, false
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.objXMLHTTP.send()
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.If objXMLHTTP.Status = 200 Then
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.Set objADOStream = CreateObject("ADODB.Stream")
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.objADOStream.Open
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.objADOStream.Type = 1
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.objADOStream.Write objXMLHTTP.ResponseBody
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.objADOStream.Position = 0
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.Set objFSO = Createobject("Scripting.FileSystemObject")
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.If objFSO.Fileexists(path) Then objFSO.DeleteFile path
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.Set objFSO = Nothing
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.objADOStream.SaveToFile path
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.objADOStream.Close
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.Set objADOStream = Nothing
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.End if
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.Set objXMLHTTP = Nothing
ec2-50-xx-xx-124.compute-1.amazonaws.com ) 1>C:\chef\wget.vbs
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>cscript /nologo C:\chef\wget.vbs /url:http://files.rubyforge.vm.bytemark.co.uk/rubyinstaller/rubyinstaller-1.8.7-p334.exe /path:C:\Users\ADMINI~1\AppData\Local\Temp\rubyinstaller.exe
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>C:\Users\ADMINI~1\AppData\Local\Temp\rubyinstaller.exe /verysilent /dir="C:\ruby" /tasks="assocfiles,modpath"
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>cscript /nologo C:\chef\wget.vbs /url:http://cloud.github.com/downloads/oneclick/rubyinstaller/DevKit-tdm-32-4.5.1-20101214-1400-sfx.exe /path:C:\Users\ADMINI~1\AppData\Local\Temp\rubydevkit.exe
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>mkdir C:\DevKit
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>copy C:\Users\ADMINI~1\AppData\Local\Temp\rubydevkit.exe C:\DevKit
ec2-50-xx-xx-124.compute-1.amazonaws.com 1 file(s) copied.
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>cmd.exe /C C:\DevKit\rubydevkit.exe -y
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>cmd.exe /C C:\ruby\bin\ruby c:/DevKit/dk.rb init
ec2-50-xx-xx-124.compute-1.amazonaws.com [INFO] found RubyInstaller v1.8.7 at C:/ruby
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com Initialization complete! Please review and modify the auto-generated
ec2-50-xx-xx-124.compute-1.amazonaws.com ‘config.yml’ file to ensure it contains the root directories to all
ec2-50-xx-xx-124.compute-1.amazonaws.com of the installed Rubies you want enhanced by the DevKit.
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>cmd.exe /C C:\ruby\bin\ruby c:/DevKit/dk.rb install
ec2-50-xx-xx-124.compute-1.amazonaws.com [INFO] Updating convenience notice gem override for ‘C:/ruby’
ec2-50-xx-xx-124.compute-1.amazonaws.com [INFO] Installing ‘C:/ruby/lib/ruby/site_ruby/devkit.rb’
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>SET PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\DevKit\mingw\libexec\gcc\mingw32\4.5.1;C:\DevKit\mingw\libexec\gcc\mingw32\4.5.1
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>SETX PATH "C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\DevKit\mingw\libexec\gcc\mingw32\4.5.1;C:\DevKit\mingw\libexec\gcc\mingw32\4.5.1"
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com SUCCESS: Specified value was saved.
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>cmd.exe /C C:\ruby\bin\gem install win32-open3 rdp-ruby-wmi windows-api windows-pr –no-rdoc –no-ri –verbose
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem win32-open3-0.3.2-x86-mingw32
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed win32-open3-0.3.2-x86-mingw32
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem rdp-ruby-wmi-0.3.1
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed rdp-ruby-wmi-0.3.1
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem win32-api-1.4.8-x86-mingw32
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem windows-api-0.4.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed win32-api-1.4.8-x86-mingw32
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed windows-api-0.4.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem windows-pr-1.2.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed windows-pr-1.2.0
ec2-50-xx-xx-124.compute-1.amazonaws.com 5 gems installed
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>cmd.exe /C C:\ruby\bin\gem install ohai –no-rdoc –no-ri –verbose
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem yajl-ruby-0.8.2
ec2-50-xx-xx-124.compute-1.amazonaws.com Temporarily enhancing PATH to include DevKit…
ec2-50-xx-xx-124.compute-1.amazonaws.com Building native extensions. This could take a while…
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem systemu-2.2.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem mixlib-cli-1.2.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem mixlib-config-1.1.2
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem mixlib-log-1.3.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem ohai-0.6.4
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed yajl-ruby-0.8.2
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed systemu-2.2.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed mixlib-cli-1.2.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed mixlib-config-1.1.2
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed mixlib-log-1.3.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed ohai-0.6.4
ec2-50-xx-xx-124.compute-1.amazonaws.com 6 gems installed
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>cmd.exe /C C:\ruby\bin\gem install chef –no-rdoc –no-ri –verbose –version 0.10.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem mixlib-authentication-1.1.4
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem mime-types-1.16
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem rest-client-1.6.1
ec2-50-xx-xx-124.compute-1.amazonaws.com Installin
ec2-50-xx-xx-124.compute-1.amazonaws.com g gem bunny-0.6.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem json-1.5.1
ec2-50-xx-xx-124.compute-1.amazonaws.com Building native extensions. This could take a while…
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem polyglot-0.3.1
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem treetop-1.4.9
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem net-ssh-2.1.4
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem net-ssh-gateway-1.1.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem net-ssh-multi-1.0.1
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem erubis-2.7.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem moneta-0.6.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem highline-1.6.2
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem uuidtools-2.1.2
ec2-50-xx-xx-124.compute-1.amazonaws.com Installing gem chef-0.10.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed mixlib-authentication-1.1.4
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed mime-types-1.16
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed rest-client-1.6.1
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed bunny-0.6.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed json-1.5.1
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed polyglot-0.3.1
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed treetop-1.4.9
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed net-ssh-2.1.4
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed net-ssh-gateway-1.1.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed net-ssh-multi-1.0.1
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed erubis-2.7.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed moneta-0.6.0
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed highline-1.6.2
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed uuidtools-2.1.2
ec2-50-xx-xx-124.compute-1.amazonaws.com Successfully installed chef-0.10.0
ec2-50-xx-xx-124.compute-1.amazonaws.com 15 gems installed
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>(
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.—–BEGIN RSA PRIVATE KEY—–
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.79QkDTFG43/J9d/AEtlOJazvomkUedEw5t76EUWlZSNwh/ij6y+NHSo=
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.—–END RSA PRIVATE KEY—–
ec2-50-xx-xx-124.compute-1.amazonaws.com ) 1>C:\chef\validation.pem
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>(
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.require ‘win32ole’
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.WIN32OLE.codepage = WIN32OLE::CP_UTF8
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.log_level :info
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.log_location STDOUT
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.chef_server_url "https://api.opscode.com/organizations/schisamo-dev"
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.validation_client_name "schisamo-dev-validator"
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.client_key "c:/chef/client.pem"
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.validation_key "c:/chef/validation.pem"
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.file_cache_path "c:/chef/cache"
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.file_backup_path "c:/chef/backup"
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.cache_options ({:path => "c:/chef/cache/checksums", :skip_expires => true})
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.
ec2-50-xx-xx-124.compute-1.amazonaws.com echo.# Using default node name (fqdn)
ec2-50-xx-xx-124.compute-1.amazonaws.com ) 1>C:\chef\client.rb
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>(echo.{"run_list":[]}) 1>C:\chef\first-boot.json
ec2-50-xx-xx-124.compute-1.amazonaws.com
ec2-50-xx-xx-124.compute-1.amazonaws.com C:\Users\Administrator>c:/ruby/bin/ruby c:/ruby/bin/chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json -E _default
ec2-50-xx-xx-124.compute-1.amazonaws.com [Sat, 21 May 2011 03:28:23 +0100] INFO: *** Chef 0.10.0 ***
ec2-50-xx-xx-124.compute-1.amazonaws.com [Sat, 21 May 2011 03:28:29 +0100] INFO: Client key c:/chef/client.pem is not present – registering
ec2-50-xx-xx-124.compute-1.amazonaws.com [Sat, 21 May 2011 03:28:44 +0100] INFO: HTTP Request Returned 404 Not Found: Cannot load node ip-0A6C0EA7.ec2.internal
ec2-50-xx-xx-124.compute-1.amazonaws.com [Sat, 21 May 2011 03:28:47 +0100] INFO: Setting the run_list to [] from JSON
ec2-50-xx-xx-124.compute-1.amazonaws.com [Sat, 21 May 2011 03:28:47 +0100] INFO: Run List is []
ec2-50-xx-xx-124.compute-1.amazonaws.com [Sat, 21 May 2011 03:28:47 +0100] INFO: Run List expands to []
ec2-50-xx-xx-124.compute-1.amazonaws.com [Sat, 21 May 2011 03:28:47 +0100] INFO: Starting Chef Run for ip-0A6C0EA7.ec2.internal
ec2-50-xx-xx-124.compute-1.amazonaws.com [Sat, 21 May 2011 03:28:48 +0100] INFO: Loading cookbooks []
ec2-50-xx-xx-124.compute-1.amazonaws.com [Sat, 21 May 2011 03:28:48 +0100] WARN: Node ip-0A6C0EA7.ec2.internal has an empty run list.
ec2-50-xx-xx-124.compute-1.amazonaws.com [Sat, 21 May 2011 03:28:52 +0100] INFO: Chef Run complete in 4.69557 seconds
ec2-50-xx-xx-124.compute-1.amazonaws.com [Sat, 21 May 2011 03:28:52 +0100] INFO: Running report handlers
ec2-50-xx-xx-124.compute-1.amazonaws.com [Sat, 21 May 2011 03:28:52 +0100] INFO: Report handlers complete
[/sourcecode]

Here’s the same command done via SSH:

[sourcecode lang=”bash” gutter=”false”]
$ knife bootstrap windows ssh ec2-50-xx-xx-124.compute-1.amazonaws.com \
-r ‘role[webserver]’ -x Administrator -i ~/.ssh/id_rsa -E production
[/sourcecode]

PowerShell Cookbook

If you talk to any sysadmin managing Windows servers at scale and the first tool he’ll mention to you is PowerShell. PowerShell is the gateway to doing most everything cool on modern Windows servers. We recently shipped a cookbook that makes installing, activating and configuring PowerShell 2.0 on most modern versions of Windows very easy. The cookbook also ships with a Chef resource/provider for executing scripts using the PowerShell interpreter. It works exactly like the existing bash provider that is used heavily for automation tasks on Unix platforms.

Here’s a few quick usage examples:

[sourcecode language=”ruby” gutter=”false”]
# write out to an interpolated path
powershell "write-to-interpolated-path" do
code <<-EOH
$stream = [System.IO.StreamWriter] "#{Chef::Config[:file_cache_path]}/powershell-test.txt"
$stream.WriteLine("In #{Chef::Config[:file_cache_path]}…word.")
$stream.close()
EOH
end

# use the change working directory attribute
powershell "cwd-then-write" do
cwd Chef::Config[:file_cache_path]
code <<-EOH
$stream = [System.IO.StreamWriter] "C:/powershell-test2.txt"
$pwd = pwd
$stream.WriteLine("This is the contents of: $pwd")
$dirs = dir
foreach ($dir in $dirs) {
$stream.WriteLine($dir.fullname)
}
$stream.close()
EOH
end

# pass an env var to script
powershell "read-env-var" do
cwd Chef::Config[:file_cache_path]
environment ({‘foo’ => ‘BAZ’})
code <<-EOH
$stream = [System.IO.StreamWriter] "./test-read-env-var.txt"
$stream.WriteLine("FOO is $foo")
$stream.close()
EOH
end
[/sourcecode]

Obviously these are pretty contrived examples, but they illustrate the power that is unlocked by the powershell script provider.

The Future

Go forth and automate some Windows servers! We here at Opscode are very excited about the new chapter that is unfolding as the Windows and Chef communities cross paths. We’ll be releasing even more cookbooks targeting the Windows platform and we’d love the community to begin writing/sharing some cookbooks that cover common Windows automation tasks (clustered SQL Server installations anyone). As always, feel free to ask for help on our mailing list and on our IRC channel (irc.freenode.net#chef).

Tags:

Seth Chisamore

Seth is a Principal Software Development Engineer at Chef, focusing on Chef's continuous delivery and software distribution systems. He lives in Marietta, GA with his family and likes to say y'all a lot.