Ansible - agentless provisioning

March 30, 2016

Reading time ~6 minutes

Ansible is an automation tool that is recognized for be simple and powerful at the same time. From my experience, I can say this is mainly because of its scripting language: YAML, and its agentless architecture.

YAML and Ansible components

“YAML is a human friendly data serialization standard for all programming languages” (Source:

This means that is actually really easy to understand and start working with it. For example:

- hosts: webserver
    - package: apache
        state: latest

This “playbook” says that a webserver host have 1 task: install latest Apache package, using a package “module”.

Pretty simple eh?

To check how powerful Ansible can be, take a look on their Module Index:

To achieve reusability: These tasks can be grouped as “roles”, that are a compilation of tasks to execute a common goal. e.g: a Java role to install Java SDK on your node.

Those are the main components of Ansible: Playbooks, Modules, and Roles.

Agentless architecture

This means that you don’t need a “ansible-client” in your node to run tasks, you can have a master that says what you need to run on your nodes. This is an important feature compared to other tools where you need a “***-client” to make your node translate and run commands:

You don’t need a client but you need some packages. But this packages are ssh and python-related and they are very common:

Ansible also have a default “push” approach, where a master sends commands to your nodes. This is also different from other tools that are based on a “pull” approach, where the node asks for commands, although this is also possible with Ansible:

There is a final feature I would like to mention: Connection Type. By default Ansible relies on SSH to send commands to your nodes, but there are cases where SSH is not an option or you don’t need it: local commands, Windows, Docker.

In these cases, connection type option enables your playbook to run commands using WinRM in the case of Windows, or Docker execute commands on Docker containers, or just run local commands in your workstation.

Let’s check some code:

I have implemented a Ansible Role to install Java some time ago:

Just to explain what it does, let’s check the main task file:

  - debug:
      msg: "This Java Provider will be installed: "

  - include: install-.yml

  - include: set-java-home.yml

It will show a message, include a task depending on “java_provider” variable and finally set JAVA_HOME variable.

Also this role has a “tests” directory where you can add playbooks to test your role:

- name: test install openjdk jdk 8 on centos 7
  hosts: test01
    - role: java
      java_provider: openjdk
      java_version: 8
      java_type: jdk
- name: test install openjdk jre 8 on centos 7
  hosts: test02
    - role: java
      java_provider: openjdk
      java_version: 8
      java_type: jre
# more tests...

And I test this playbooks using Vagrant and VirtualBox:

Vagrant.configure(2) do |config|

  config.vm.provision "ansible" do |ansible|
    ansible.playbook = "test.yml"
    ansible.galaxy_role_file = "roles.yml"

  config.vm.define "test01" do |node| = "jeqo/ansible-centos7"

  config.vm.define "test02" do |node| = "jeqo/ansible-centos7"

  # more test nodes...

So, lets test that OpenJDK 8 is running OK in Centos:

vagrant up test01

PLAY [test install openjdk jdk 8 on centos 7] **********************************

TASK [setup] *******************************************************************
ok: [test01]

TASK [java : debug] ************************************************************
ok: [test01] => {
    "msg": "This Java Provider will be installed: openjdk"

TASK [java : include] **********************************************************
included: /home/jeqo/dev/jeqo/ansible-role-java/tests/roles/java/tasks/install-openjdk.yml for test01

TASK [java : set_fact] *********************************************************
skipping: [test01]

TASK [java : set_fact] *********************************************************
ok: [test01]

TASK [java : set_fact] *********************************************************
skipping: [test01]

TASK [java : set_fact] *********************************************************
ok: [test01]

TASK [java : install openjdk (debian)] *****************************************
skipping: [test01]

TASK [java : install openjdk (redhat)] *****************************************

But one thing I always want is to reuse this roles on Docker containers, without prepare a Container with SSH, that is recognized as an anti-pattern:

So, since Ansible 2.0 a Docker connection type is included OOTB, and I give it a try:

I added a playbook to create a container:

- hosts:
  connection: local
    - name: my container
        name: poccontainer
        image: centos
        command: sleep infinity
        state: started

Here I’m using “connection: local” to execute commands locally.

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
a5e49bd032be        centos              "sleep infinity"    About an hour ago   Up About an hour                        poccontainer

And once I have a container running, I can run this playbook:

- hosts: poccontainer
  connection: docker
    - package: name=sudo
    - command: "sed -i -e \"s/Defaults    requiretty.*/ #Defaults    requiretty/g\" /etc/sudoers"
    - role: java
      java_provider: openjdk
      java_type: jdk
      java_version: 8

Pre-tasks are required to install sudo package and configure tty. And then run role as usual:

$ ansible-playbook provisioning.yml -vvvv
Using /home/jeqo/dev/jeqo/poc-ansible-docker/ansible.cfg as config file
Loaded callback default of type stdout, v2.0
2 plays in provisioning.yml

PLAY ***************************************************************************

TASK [setup] *******************************************************************
<poccontainer> EXEC ['/usr/bin/docker', 'exec', '-i', u'poccontainer', '/bin/sh', '-c', '/bin/sh -c \'( umask 22 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1459355431.02-32251179247729 `" && echo "` echo $HOME/.ansible/tmp/ansible-tmp-1459355431.02-32251179247729 `" )\'']
<poccontainer> PUT /tmp/tmpNCOaxi TO /root/.ansible/tmp/ansible-tmp-1459355431.02-32251179247729/setup
<poccontainer> EXEC ['/usr/bin/docker', 'exec', '-i', u'poccontainer', '/bin/sh', '-c', u'/bin/sh -c \'LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /root/.ansible/tmp/ansible-tmp-1459355431.02-32251179247729/setup; rm -rf "/root/.ansible/tmp/ansible-tmp-1459355431.02-32251179247729/" > /dev/null 2>&1\'']
ok: [poccontainer]


  • This samples show how versatile Ansible is, using roles and connection type. But there are more platforms where Ansible can fit, as with AWS: and other Cloud platforms:

  • One question can be: Is this a replacement of Dockerfile? Maybe, depends on you. Dockerfile are very simple and only works with Docker. Dockerfile also has a nice feature to create an image each step, so you can distribute images easily. This is missing in Ansible, where you execute commands on a running Docker container. Also Ansible is missing commit and push tasks to put containers on Docker Hub, but you can replace it with local commands as here:

- hosts:
  connection: local
    - name: commit
      command: docker commit poccontainer

Although Ansible also have a module to run Dockerfiles:

Hope this helps you to get started with Ansible and Docker.

Jorge is part of the middleware team at Sysco and an Oracle ACE Associate . He has developed business solutions based on Oracle Fusion Middleware technologies since 2010. Also he is certified as SOA Architect and Implementation Specialist at BPM, SOA and WebLogic products.

Get “non XML” file on request based on a file name and send it via HTTP POST with OSB 12c

Requirement is fairly simple, there is a need to transfer a “non XML” file (in this specific case .pdf document) based on the file name w...… Continue reading