Useful ansible stuff


inventory_hostname‘ contains the name of the current node being worked on…. (as in, what it is defined in your hosts file as) so if you want to skip a task for a single node –

- name: Restart amavis
  service: name=amavis state=restarted
  when: inventory_hostname != "boris"

(Don’t restart Amavis for boris,  do for all others).

You could also use :

  when: inventory_hostname not in groups['group_name']

if your aim was to (perhaps skip) a task for some nodes in the specified group.


Need to check whether you need to reboot for a kernel update?

  1. If /vmlinuz doesn’t resolve to the same kernel as we’re running
  2. Reboot
  3. Wait 45 seconds before carrying on…
- name: Check for reboot hint.
  shell: if [ $(readlink -f /vmlinuz) != /boot/vmlinuz-$(uname -r) ]; then echo 'reboot'; else echo 'no'; fi
  ignore_errors: true
  register: reboot_hint

- name: Rebooting ...
  command: shutdown -r now "Ansible kernel update applied"
  async: 0
  poll: 0
  ignore_errors: true
  when: kernelup|changed or reboot_hint.stdout.find("reboot") != -1
  register: rebooting

- name: Wait for thing to reboot...
  pause: seconds=45
  when: rebooting|changed

Fixing ~/.ssh/known_hosts

Often an ansible script may create a remote node – and often it’ll have the same IP/name as a previous entity. This confuses SSH — so after creating :

- name: Fix .ssh/known_hosts. (1)
  local_action: command  ssh-keygen -f "~/.ssh/known_hosts" -R hostname

If you’re using ec2, for instance, you could do something like :

- name: Fix .ssh/known_hosts.
  local_action: command  ssh-keygen -f "~/.ssh/known_hosts" -R {{ item.public_ip }} 
  with_items: ec2_info.instances

Where ec2_info is your registered variable from calling the ‘ec2’ module.

Debug/Dump a variable?

- name: What's in reboot_hint?
  debug: var=reboot_hint

which might output something like :

"reboot_hint": {
        "changed": true, 
        "cmd": "if [ $(readlink -f /vmlinuz) != /boot/vmlinuz-$(uname -r) ]; then echo 'reboot'; else echo 'no'; fi", 
        "delta": "0:00:00.024759", 
        "end": "2014-07-29 09:05:06.564505", 
        "invocation": {
            "module_args": "if [ $(readlink -f /vmlinuz) != /boot/vmlinuz-$(uname -r) ]; then echo 'reboot'; else echo 'no'; fi", 
            "module_name": "shell"
        "rc": 0, 
        "start": "2014-07-29 09:05:06.539746", 
        "stderr": "", 
        "stdout": "reboot", 
        "stdout_lines": [

Which leads on to —

Want to run a shell command do something with the output?

Registered variables have useful attributes like :

  • changed – set to boolean true if something happened (useful to tell when a task has done something on a remote machine).
  • stderr – contains stringy output from stderr
  • stdout – contains stringy output from stdout
  • stdout_lines – contains a list of lines (i.e. stdout split on \n).

(see above)

- name: Do something
  shell: /usr/bin/something | grep -c foo || true
  register: shell_output

So – we could :

- name: Catch some fish (there are at least 5)
  shell: /usr/bin/somethingelse 
  when: shell_output.stdout > "5"

Default values for a Variable, and host specific values.

Perhaps you’ll override a variable, or perhaps not … so you can do something like the following in a template :

max_allowed_packet = {{ mysql_max_allowed_packet|default('128M') }}

And for the annoying hosts that need a larger mysql_max_allowed_packet, just define it within the inventory hosts file like :

busy-web-server mysql_max_allowed_packet=256M

Chef: Roles and Environments

  1. Bootstrap another node with your chef-server.
    If you get the following error:
    Failed to read the private key /etc/chef/validation.pem: # In the same error logs you will see a line:
    Delete your validation key in order to use your user credentials instead
    Please check that you don’t have any validation.pem key in /etc/chef, if any, then delete it.
    You can verify by executing one of the following:
    knife client list
    knife node show chef-node2
  2. Create a new cookbook postgresql:
    chef generate cookbook cookbooks/postgresql
  3. Edit cookbooks/postgresql/recipes/default.rb
    package 'postgresql-server' do
            notifies :run, 'execute[postgresql-init]'
    execute 'postgresql-init' do
            command 'postgresql-setup initdb'
            action :nothing
    service 'postgresql' do
            action [:enable, :start]
    <span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>

    The notifies :run, ‘execute[postgresql-init]’ will send a run signal to the execute task and execute the init command only when the package is installed.
    Check it with foodcritic and then push it to the git repo.

  4. role1.png
  5. role2.png
  6. role3.png
  7. role4.png
  8. role5role6role7
  9. role8
  10. Login to your chef-manage UI and select your node, click on “Edit run list” and remove all the recipes associated with it.
  11. In your chef-workstation we will now create a role. Execute the following command:
    knife role create web
    You will get the following error:
    ERROR: RuntimeError: Please set EDITOR environment variable
    We will have to set a default editor. Here we’ll use vi.
    Execute the following commands:
    vi ~/.bashrc
    export EDITOR= $(which vi)
    source ~/.bashrc
    Then execute the create role command.
    This will openup the role file in vi editor with the following content:

      "name": "web",
      "description": "",
      "json_class": "Chef::Role",
      "default_attributes": {
      "override_attributes": {
      "chef_type": "role",
      "run_list": [
      "env_run_lists": {
    <span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>
  12. Add the following in the run list section:
    Save and close.
  13. You can check the run list for your role in your chef-manage UI in policies section in roles.
  14. You can also edit the existing role with command:
    knife role edit web
  15. You can add the role to the node with the following command:
    knife node run_list set chef-node2 “role[web]”
  16. You can check the newly added role with the following command:
    knife node show chef-node2
  17. Then we need to issue the chef-client command from the workstation for all the nodes having role “web”.
    knife ssh “role:web” “sudo chef-client” -x gslab -P
    If you get the following error:
    WARNING: Failed to connect to chef-node2.local — SocketError: getaddrinfo: Name or service not known.
    Then add the host chef-node2.local to /etc/hosts file. Then execute the knife ssh command again.
  18.  Then upload the postgresql cookbook:
    knife cookbook upload postgresql
  19. Create role database:
    knife create role database
  20. The add the role to a node:
    knife node run_list set chef-node “role[postgresql]”
  21. Then edit the postgresql default.rb recipe. Edit the line to match the following:
    notifies :run, ‘execute[postgresql-init]’, :immediately
    Then upload the cookbook: knife cookbook upload postgresql
  22. Then execute the following command:
    knife ssh “role:database” “sudo chef-client” -x gslab -P


  1. Edit the websites.rb recipe in the apache cookbook.
    Add the following after the file section in it:

    webnodes = search("node", "role:web")
    webnodes.each do |node|
            puts node
    <span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>


  2. Upload the cookbook:
    knife cookbook upload apache
  3. Then execute chef-client on chef-node2 you will see an output as below. In the output you can see the hostname added:
    Starting Chef Client, version 13.3.42
    resolving cookbooks for run list: [“apache”, “apache::websites”, “apache::motd”]
    Synchronizing Cookbooks:
    – apache (0.2.1)
    Installing Cookbook Gems:
    Compiling Cookbooks…
    Converging 4 resources
    Recipe: apache::default
    * yum_package[apache2] action install (up to date)
    * service[apache2] action start (up to date)
    * service[apache2] action enable (up to date)
    Recipe: apache::websites
    * file[default www] action create (up to date)
    Recipe: apache::motd
    * file[/etc/motd] action create (up to date)Running handlers:
    Running handlers complete
    Chef Client finished, 0/5 resources updated in 08 seconds


  1. env1
  2. Now edit the apache metadata.rb file and change the version to 1.0. Then edit the websites.rb, change the content to “Hello World v1.0”. then upload the apache cookbook.
  3. Again edit the apache metadata.rb file and change the version to 2.0. Then edit the websites.rb, change the content to “Hello World v2.0”. then upload the apache cookbook.
  4. Then login to the chef manage UI and then in the policy section -> Environment -> Create.
    Add the name as “staging” and some description. click on Next. Then in the constraints section select the name apache, Operator as “=”,  Version as 2.0.0.
    Click on Add then Click on Create Environment.
    Add the name as “production” and some description. click on Next. Then in the constraints section select the name apache, Operator as “=”,  Version as 1.0.0.
    Click on Add. Click on Create Environment.
  5. Then on the Nodes page click on chef-node Edit the environment to be Production. Save it.
  6. Then click on the chef-node2 and change the environment to be staging. Save it.
  7. Then execute chef-client on both nodes. You should see that the chef-node is executing the apache version 2.0.0 (Production) and chef-node2 is executing the apache version 1.0.0 (Staging).
  8. You can hit the IP address of both the servers in the browser and see that the webpage show different result.
  9. Thus we have separated the cookbooks and nodes as per environemt.

Jenkins – Publish Over SSH Plugin: How to copy directory


I’m trying to use Jenkins’ Publish Over SSH plugin to copy all files AND sub-directories of some given directory, but so far, I’ve only able to copy files and NOT directory.

I have a directory named foo in my workspace, and during the build, I want to copy everything in this directory to a remote server. I’ve tried this pattern foo/**, but it doesn’t copy all sub-directories.


For recursive copy of directory you should give


Jenkins: publish over ssh does not put files to remote server


I have strange issue on the latest Jenkins 1.634.
Publish over ssh writes to log that it puts correctly file but nothing appears on remote server.
e.g. I have logs
SSH: cd [var/www/data-fb-localtest]
SSH: put [asm.js]
SSH: put [asm.js.gz]
SSH: put []
SSH: put [hero_main.js]
SSH: cd [/home/dev]
SSH: cd [var/www/data-fb-localtest/]
SSH: put [achievements.exm]
SSH: put [ai.exm]
SSH: put [atlas0.atlas]
SSH: put [atlas0.rgbz]
but nothing appears in var/www/data-fb-localtest



I found the issue. I do not set root remote directory and in publish task use absolute path. But plugin does use not absolute path but path relative to my user’s home directory