Some of you may have already used expect in linux. It's a nifty little tool. A tool that allows you to automate command line interfaces that require interactive prompts. It's simple, let's look at an example of expect in action.

#!/usr/bin/expect -f

# Change your own password automatically
spawn passwd
expect "Enter new UNIX password: "
send "mynewpassword\r"
expect "Retype new UNIX password: "
send "mynewpassword\r"
expect eof

Notice the shebang line #!/usr/bin/expect -f. Expect is an interactive shell. If you just type expect from the command line you will be placed in a shell that gives you commands such as spawn, send, and expect.

Let's look at what expect is doing in the code snippet above.


We tell expect to spawn a child process of passwd

spawn passwd

Next, we tell expect to wait for some stout from the child process where the text says Enter New UNIX password

expect "Enter new UNIX password: "

Then we tell expect to send a password with a carriage return, the \r idiom.

send "mynewpassword\r"

Finally, we expect the termination of the child process.

expect eof


So, how do we implement this in Chef? It's more simple than you think.

bash "modify_root_password" do
    user "root"
    code <<-EOF
    /usr/bin/expect -c 'spawn passwd
    expect "Enter new UNIX password: "
    send "#{user_root['password']}\r"
    expect "Retype new UNIX password: "
    send "#{user_root['password']}\r"
    expect eof'
    EOF
end

You're done! It's similar to the code with a few exceptions:

  • It's wrapped in Chefs bash resource and using the attribute code to tell it what to execute. The user_root hash comes from an encrypted data bag.

  • We use the '/usr/bin/expect -c' notation because as far as i know chef doesnt implement an expect interpreter under the script resource.


Here is another example creating a tarsnap keyfile:

bash "create_tarsnap_private_key" do
    not_if do
        File.exists?("#{node[:tarsnap][:private_key]}")
    end
    user "root"
    cwd "/tmp"
    code <<-EOF
    /usr/bin/expect -c 'spawn tarsnap-keygen --keyfile #{node[:tarsnap][:private_key]} --machine #{node[:fqdn]}  --user #{tarsnap_user['username']}
    expect "Enter tarsnap account password: "
    send "#{tarsnap_user['password']}\r"
    expect eof'
    EOF
end

I have made a cookbook that automates the tarsnap install process for you.