I never used Ansible but since a few weeks, I try to figure out what good Ansible could be in comparison with shell scrips–Which proves, at least in my case, that the haunting ad-campaigns they run are effective! After many unsuccessful attempts–which proves how their documentation fail at answering one of the most obvious question–I think I finally got it:

Now, let's watch the introduction video and go randomly as a potential new user through the introduction material to Ansible ans let's compare it to what a skilled shell programmer can produce right-off the shelf.

My conclusion is that over shell scripting, Ansible essentially offers 1. The possibility of checking that a system agrees with a desired state, 2. the ability to integrate with Ansible Tower, which is a paying system that seems to include monitoring abilities. In some important cases, like when implementing the immutable server pattern, the point 1 is probably not very useful, so the list, is rather thin.

My conclusion is that the benefits offered by Ansible over shell-scripting, as the documentation present them, could be sensible in a few handful of optimistic cases well covered by available modules but are small or even hypothetical in the general case. For a skilled shell-programmer probably, these benefits are most likely counter-balanced by other aspects of the trade-off.

But this maybe only proves how bad the introduction material is!


# The quick start video:

There is a [quick start video](http://docs.ansible.com/ansible/quickstart.html). It starts with a page claiming that… well these are not really claims, these are bullet lists, an artefact [commonly used to suspend critical judgement](http://cristal.inria.fr/~weis/info/haladjian.pdf) in presentations (since the logic is not shown, it cannot be criticised!)

### 1. Ansible is simple:

1.1 Human readable automation – Specifications are technical documents, how could

      name: upgrade all packages
      yum:
        name: '*'
        state: latest

be easier to read than the corresponding *yum* invocation found in a shell-script? Furthermore, anybody who had contact to AppleScript dies laughing when they read “human readable automation”.

1.2 No special coding skills required – What is coding if not writing formal specifications? They have conditionals, variables, so, how is it not coding? And why would I need something I cannot program, that would henceforth be inflexible? The statement is happily inaccurate!

1.3 Tasks executed in order – Well, maybe some [codegolf aficionados](http://codegolf.stackexchange.com) are aware of languages that execute tasks in disorder, but executing tasks in order hardly looks exceptional.

1.4 Get productive quickly – Skilled shell programmers are productive now. This counter-argument is just as serious as the initial argument.


### 2. Ansible is powerful

A popular salesman trick to sell artefacts is to fool people into believing they will acquire the “power” of these artefacts. The history of advertisement for cars or isotonic drinks should supply a convincing list of examples.

Here Ansible can do “app deployment” – but shell script surely do, “configuration management” but this is a mere statement of the purpose of the tool, not a feature, and “workflow orchestration” which looks a bit pretentious but no example goes beyond what [GNU Parallel](https://www.gnu.org/software/parallel/man.html) can do.


### 3. Ansible is agentless

To populate the column, they wrote in three different manners that this only needs *ssh,* which, as everybody knows is a *daemon* and has nothing to do with these *agents* pervading the world configuration management!


### The rest of the video

The rest of the video introduces inventories, which are static lists of resources (like servers) and demonstrates how to deploy Apache on three servers simultaneously.  This really does not match the way I work, where resources are highly dynamic and can be enumerated by command-line tooling provided by my cloud provider, and consumed by my shell functions using the pipe `|` operator.  Also, I do not deploy Apache on three servers simultaneously, rather, I build a master instance image that I then use to start 3 instances which are exact replicas one of the other. So the “orchestrating” part of the argumentation does not look very pertinent.


# Random documentation step 1: Integration with EC2

EC2 is the computing service from Amazon, interacting with it is supported by some [Ansible module](http://docs.ansible.com/ansible/guide_aws.html). (Other popular cloud computing providers are also provided):

    # demo_setup.yml
    
    - hosts: localhost
      connection: local
      gather_facts: False
    
      tasks:
    
        - name: Provision a set of instances
          ec2:
             key_name: my_key
             group: test
             instance_type: t2.micro
             image: "{{ ami_id }}"
             wait: true
             exact_count: 5
             count_tag:
                Name: Demo
             instance_tags:
                Name: Demo
          register: ec2

The corresponding shell-script would be essentially identical with YAML replaced by JSON:

    provision_a_set_of_instances()
    {
      aws --output=text ec2 run-instances --image-id …   
    }

or the JSON version

    provision_a_set_of_instances()
    {
      aws --output=text ec2 run-instances --cli-input-json "$(provision_a_set_of_instances__json)"  
    }
    
    provision_a_set_of_instances__json()
    {
      cat <<EOF
    {
        "ImageId": … 
    }
    EOF
    }

Both version are essentially identical, the bulk of the payload is the enumeration of the initialisation values in a YAML or JSON structures.


# Random documentation step 2: Continuous Delivery and Rolling Upgrades

The largest part of [this guide](http://docs.ansible.com/ansible/guide_rolling_upgrade.html) does not display any really interesting feature: it introduces variables (IIRC, shell scripts also have variables)!, and an Ansible module handles mysql, so that if instead of searching after “how do I create a mysql user with privileges on X Y” and end with something like

    # Create Application DB User
    mysql --host "${mysql_host}" --user "${mysql_user}" --password "${mysql_password}" "${mysql_table}" <<EOF
    GRANT ALL PRIVILEGES ON *.* TO 'root'@'%';
    EOF

you search after “how do I create a mysql user with privileges on X Y in *ansible*”  and end up with 

    - name: Create Application DB User
      mysql_user: name={{ dbuser }} password={{ upassword }}
                  priv=*.*:ALL host='%' state=present

The difference is still probably not very meaningful. On that page we also discover that Ansible has a template meta-Programming language

    {% for host in groups['monitoring'] %}
    -A INPUT -p tcp -s {{ hostvars[host].ansible_default_ipv4.address }} --dport 5666 -j ACCEPT
    {% endfor %}

When I see this, I happen to really be in my comfort zone. This kind of simple meta-programming for declarative languages is exactly the same theoretical paradigm as BSD Makefiles! Which I [happen to have programmed extensively](https://github.com/michipili/bsdowl) This excerpt shows us that the promise of working with YAML file is broken (so I cannot run my playbooks through a YAML parser, *e.g.*). It also shows us that Ansible must discuss the subtle art of evaluation order: we have to decide if variables are expanded at the “declarative part” of the language or at the “imperative” meta-part of the language. Here shell programming is simpler, there is no meta-programming, aside from *explicit* `eval` or external-script sourcing.  The hypothetical equivalent shell excerpt would be

    enumerate_group 'monitoring' | {
      while read host; do
        …
      done
    }

whose complexity in comparison to the Ansible variant is probably tolerable: it just uses the plain, regular, boring constructs from the language.


# Random documentation step 3: Testing strategies

Last, [we meet what turns out to be the first actually interesting feature](http://docs.ansible.com/ansible/test_strategies.html) of Ansible: “Ansible resources are models of desired-state. As such, it should not be necessary to test that services are started, packages are installed, or other such things. Ansible is the system that will ensure these things are declaratively true. Instead, assert these things in your playbooks.” Now it starts to be a bit interesting, but:

1. Aside from a handful of standard situations readily implemented by available modules, I will have to feed the bits implementing the test myself, which will quite probably involve some shell commands.

2. Checking for the conformity of installations might not be very relevant in the context where the immutable server pattern is implemented: where all systems running are typically spawned from a master image (instance image or docker image for instance) and never updated – they are replaced by new instead.


# Unaddressed concern: the maintainability

The introductory material [from Ansible](http://docs.ansible.com/ansible/) ignores the question of the maintainability.  With essentially no type system, shell-scripting has the maintainability ease of JavaScript, Lisp or Python: extensive refactorings can only be achieved successfully with the help of an extensive automated testsuite – or at least designs that allows easy interactive testing. That said, while shell scripting is the [lingua franca](https://en.wikipedia.org/wiki/Mediterranean_Lingua_Franca) from system configuration and maintenance, nearly each programming language has an interface to the shell. It is therefore totally feasible to leverage the maintainability advantage of advanced languages, by using them to glue together the various the bits of shell-configuration bits. For OCaml, I wrote [Rashell](https://github.com/michipili/rashell) that essentially provides a hand of common interaction patterns for subprocesses, which makes the translation of configuration scripts to OCaml essentially trivial.

On the side from Ansible, the very weak structure of playbooks and the presence of a meta-programming feature make the situation essentially as bad as it is for shell scripting, with the minus points that it is not obvious how to write unit tests for Ansible, and the argument of introducing ad-hoc a higher-level language cannot be mimiced.


# Idempotency of configuration steps

The documentation of Ansible draws the attention on the necessity of writing idempotent configuration steps. More precisely, configuration steps should be written so that the step sequence *a b a* can be simplified to *a b*, i.e. we do not need to repeat configuration step. This is a stronger condition than idempotency. Since Ansible allows playbooks to use arbitrary shell commands, Ansible itself is unable to guarantee that this stronger condition is respected. This only relies on the programmer's discipline.