Agentless Provisioning with Chef
One of the main points of criticism about Chef I heard over the last few years has been the need to have an agent deployed at remote machines. Sometimes that is not desired, sometimes it is not even possible.
Due to this, configuring remote machines has become the stronghold of other tools - but a new feature of Chef changes the landscape fundamentally.
The so-called Chef Target Mode arrived with Chef 15.0.293 and, while not having been documented properly up to now, has been overshadowed by the EULA changes which broke quite some systems over the net. But that’s another story.
Target Mode (RFC112) has been made possible by several changes in the Chef backend, notably including the Train library which is a spin-off from Chef’s sister project InSpec. Over there, it has evolved from a mere SSH/WinRM transport layer into a multipurpose tool by itself, including different plugin types which makes it massively extensible. As Chef is allowing a single node to remotely manage multiple systems, this means that mechanics for a Credentials File (RFC099) were needed as well.
Only a few resources are supported as of August 2019, namely:
This list will grow over time, but rather slowly that rapidly as many resources will need updated implementations or even separate execution paths to support remote devices. Still, it is easily possible to write your own custom resources for Target Mode to move ahead.
The credentials file
First off, we need to configure the credentials used to access our remote node. As you can use SSH, WinRM or more exotic protocols (only limited by what the Train universe has to offer), there are multiple ways of setting up the necessary login data for your devices. You will find, that the options in your credentials file are the exact same as the ones for the corresponding Train plugin.
Credential files are looked for in two locations basically: your user-specific
~/.chef/credentials file or a system-wide
/etc/chef/credentials one. Guard it well with the appropriate access rights or that might turn out bad
Simple SSH connection with user and password:
['192.168.240.123'] host = '192.168.240.123' user = 'root' password = 'Passw0rd'
Note that the hostname has to be quoted to make this work. If something is not right, Chef will notify you about that with some warnings on start.
More secure is to do SSH with a keyfile:
['192.168.240.123'] host = '192.168.240.123' user = 'root' key_files = '.chef/ssh/my.key'
This will also poll your local ssh-agent automatically, if you have it running.
['192.168.240.123'] host = '192.168.240.123' user = 'root' password = 'Passw0rd' bastion_host = 'jumpserver.example.com' bastion_user = 'jdoe' verify_host_key = true
You might have devices which you can only reach via a jump host/bastion server or whatever you want to call it. Train already has that covered.
The SSH connection options can be found in the Train SSH source code on Github
A Simple Cookbook
How to write a cookbook is not part of this post, that should be covered in the Chef basics. If you do not know how a cookbook really works, contact us and either book a seat in our regular Chef Foundations trainings in Hannover or get in touch to get some more individual workshop.
For target mode, we can only use resources which are target mode-ready, so let’s keep this simple:
execute 'apt update && apt upgrade --yes'
This is the most basic example to use and certainly not the highlight of this post - that’s for Chef to remotely invoke a package upgrade.
If you want to use other resources to preprocess some templates and then remotely invoke them, I will show you a quick and dirty way of doing that until Chef implements some sort of
process_locally do ... end syntax in a separate post.
This one is pretty easy if you have SSH access configured to the remote machine and you can even find it in the Chef help:
chef-client -z -t 192.168.240.123 -r 'recipe[mycookbook::default]'`
So we are just invoking Chef in local target mode, without any server connections. If you set up everything correctly, you should see a regular Chef run, but with a remote target.
Keep in mind, that remote runs are slower due to latency and as Train starts a simple platform detection first, this will add a bit of lag as well.
When I first played around with Target Mode, I suspected I could just put some
protocol=winrm into the Credentials file or supply a URL like
-t winrm://somehost. Turns out that is not (yet) the case. If you want to specify a protocol different from SSH, you seem to need configuration for that in your Chef config file (
/etc/chef/client.rb are popular places):
target.protocol = 'winrm'