CFEngine bootstrap with Ansible

February 3, 2022

CFEngine and Ansible are two complementary infrastructure management tools. Findings from our analysis show that they can be combined and used side by side with joint forces to handle all areas in the best possible way. Part of infrastructure management is hosts deployment, either when building a brand new infrastructure or when growing one by adding new hosts. This is something Ansible truly excels in as it makes it very easy to run a sequence of steps on all hosts to initialize (deploy) them and it only requires SSH access to the hosts and Python installed on them. 1

Many other aspects of infrastructure management that follow the initial deployment are much better handled by CFEngine which, however, requires all hosts to have a CFEngine package installed and to be bootstrapped to a CFEngine hub (policy server). So a logical thing to do is to install and bootstrap CFEngine as part of the initialization (deployment) of the hosts.

Installing and bootstrapping CFEngine

Installing and bootstrapping CFEngine as part of the host deployment Ansible playbook is of course possible with standard Ansible tasks. Nevertheless, it's not as easy as it may seem at the first glance. CFEngine supports many platforms (AIX, HP-UX, Solaris, Windows and a number of GNU/Linux distributions), but it is not platform-independent. Each platform needs a different package (with different binaries), even though in case of GNU/Linux distributions it is a matter of optimal performance (and various CPU architectures). Also, there are separate packages for CFEngine hubs (policy servers) and CFEngine clients (servers managed by CFEngine, sometimes also called agents).

So installing and bootstrapping CFEngine requires quite a bit of logic which is, of course, possible to express in an Ansible playbook using facts, conditions, variables, etc. However, it's generally not an easy thing to do and requiring every user to solve it for themselves is wrong.

CFEngine Ansible module

Ansible playbooks (and role definitions) are sequences of tasks. Each task is of some type handled either by the Ansible core or by one of the Ansible modules. To make things as easy as possible for users, we wanted to implement a cfengine task type and so we created the CFEngine Ansible module, which is part of the CFEngine Ansible collection. With the collection installed on the system on which ansible-playbook (or ansible) is run, installing and bootstrapping hosts on the deployed machines is as easy as adding the following task to the deploy playbook:

- name: Install and bootstrap CFEngine
  cfengine.cfengine.cfengine:
    policy_server: hub.example.com
    version: 3.18.1

Wondering why the triple cfengine? Because, from right to left, it's the cfengine module (task type), in the cfengine collection in the cfengine namespace. The rest is straightforward – policy_server determines the CFEngine policy server (hub) the hosts should be bootstrapped to and version (optional) determines the version of CFEngine to be installed on the hosts. If the policy_server is the host name or IP address of the host the task is run on, it becomes a CFEngine hub (policy server), otherwise it becomes a CFEngine client.

Demo

Let's see the CFEngine Ansible module in action! First, let's create some demo infrastructure using the cf-remote tool:

[vpodzime@vpodzime-laptop ~]$ cf-remote spawn --platform centos-7-x64 --count 1 --role hub --name c7-hub
Waiting for VMs to get IP addresses..............DONE
Details about the spawned VMs can be found in /home/vpodzime/.cfengine/cf-remote/cloud_state.json
[vpodzime@vpodzime-laptop ~]$ cf-remote spawn --platform rhel-8-x64 --count 2 --role clients --name r8-clients
Spawning VMs.....DONE
Waiting for VMs to get IP addresses.............DONE
Details about the spawned VMs can be found in /home/vpodzime/.cfengine/cf-remote/cloud_state.json
[vpodzime@vpodzime-laptop ~]$ cf-remote spawn --platform ubuntu-18-04-x64 --count 2 --role clients --name u18-clients
Spawning VMs.....DONE
Waiting for VMs to get IP addresses.........................DONE
Details about the spawned VMs can be found in /home/vpodzime/.cfengine/cf-remote/cloud_state.json
[vpodzime@vpodzime-laptop ~]$ cf-remote spawn --platform debian-10-x64 --count 2 --role clients --name d10-clients
Spawning VMs.....DONE
Waiting for VMs to get IP addresses...........DONE
Details about the spawned VMs can be found in /home/vpodzime/.cfengine/cf-remote/cloud_state.json

Now we can use the new feature of cf-remote – the --ansible-inventory option that can be used to generate Ansible inventory data for the spawned hosts:

[vpodzime@vpodzime-laptop ~]$ cf-remote show --ansible-inventory
[c7-hub]
vpc7-hub-centos-7-x64hub0 ansible_host=54.229.177.67 ansible_user=centos ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"

[r8-clients]
vpr8-clients-rhel-8-x64client0 ansible_host=34.244.251.174 ansible_user=ec2-user ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
vpr8-clients-rhel-8-x64client1 ansible_host=54.246.161.133 ansible_user=ec2-user ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"

[u18-clients]
vpu18-clients-ubuntu-18-04-x64client0 ansible_host=54.229.140.112 ansible_user=ubuntu ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
vpu18-clients-ubuntu-18-04-x64client1 ansible_host=34.247.55.222 ansible_user=ubuntu ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"

[d10-clients]
vpd10-clients-debian-10-x64client0 ansible_host=34.243.247.62 ansible_user=admin ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
vpd10-clients-debian-10-x64client1 ansible_host=52.50.38.227 ansible_user=admin ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"

[all]
vpc7-hub-centos-7-x64hub0 ansible_host=54.229.177.67 ansible_user=centos ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
vpr8-clients-rhel-8-x64client0 ansible_host=34.244.251.174 ansible_user=ec2-user ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
vpr8-clients-rhel-8-x64client1 ansible_host=54.246.161.133 ansible_user=ec2-user ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
vpu18-clients-ubuntu-18-04-x64client0 ansible_host=54.229.140.112 ansible_user=ubuntu ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
vpu18-clients-ubuntu-18-04-x64client1 ansible_host=34.247.55.222 ansible_user=ubuntu ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
vpd10-clients-debian-10-x64client0 ansible_host=34.243.247.62 ansible_user=admin ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
vpd10-clients-debian-10-x64client1 ansible_host=52.50.38.227 ansible_user=admin ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"

[hub]
vpc7-hub-centos-7-x64hub0 ansible_host=54.229.177.67 ansible_user=centos ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"

[clients]
vpr8-clients-rhel-8-x64client0 ansible_host=34.244.251.174 ansible_user=ec2-user ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
vpr8-clients-rhel-8-x64client1 ansible_host=54.246.161.133 ansible_user=ec2-user ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
vpu18-clients-ubuntu-18-04-x64client0 ansible_host=54.229.140.112 ansible_user=ubuntu ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
vpu18-clients-ubuntu-18-04-x64client1 ansible_host=34.247.55.222 ansible_user=ubuntu ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
vpd10-clients-debian-10-x64client0 ansible_host=34.243.247.62 ansible_user=admin ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
vpd10-clients-debian-10-x64client1 ansible_host=52.50.38.227 ansible_user=admin ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"

As we can see, there are groups of hosts based on the names given to cf-remote spawn followed by the special groups all, hub and clients with the respective hosts in them. We can store the output in a file so that we can feed ansible-playbook with it:

[vpodzime@vpodzime-laptop ~]$ cf-remote show --ansible-inventory > /tmp/inventory

With the Ansible inventory (not to be confused with CFEngine inventory) ready, we can now run an Ansible playbook with a cfengine.cfengine.cfengine task to see what it does. Here is an example of such playbook:

---
- name: Deploy new hosts in the infrastructure
  hosts: all
  become: true
  tasks:
  - name: Install and bootstrap CFEngine
    cfengine.cfengine.cfengine:
      policy_server: 172.31.23.2
      version: 3.18.1

where the IP address of the policy server is the private IP address of the hub host (because it is spawned in AWS). First, we want to run the playbook on the policy server itself because that's required for other hosts to be able to bootstrap to it:

[vpodzime@vpodzime-laptop ~]$ ansible-playbook --inventory /tmp/inventory --limit hub ~/tmp/deploy.yml
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
ERROR! couldn't resolve module/action 'cfengine.cfengine.cfengine'. This often indicates a misspelling, missing collection, or incorrect module path.

The error appears to be in '/home/vpodzime/tmp/deploy.yml': line 6, column 5, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

  tasks:
  - name: Install and bootstrap CFEngine
    ^ here

Wait, what happened?! Well, as the error suggests, it's likely one of the mentioned issues. And since we didn't install the CFEngine Ansible collection, the second common cause, missing collection is the one. So let's install the collection:

[vpodzime@vpodzime-laptop ~]$ ansible-galaxy collection install https://gitlab.com/Northern.tech/CFEngine/cfengine-ansible-collection/uploads/71362febba022da55db9db748d7d399f/cfengine-cfengine-1.0.0.tar.gz
Process install dependency map
Starting collection install process
Installing 'cfengine.cfengine:1.0.0' to '/home/vpodzime/.ansible/collections/ansible_collections/cfengine/cfengine'

As you can see, we are installing the collection directly from the GitLab project where it lives. That is because the collection is not yet available in Ansible Galaxy.2

OK, let's give it another try with the collection installed! And this time, let's also use the --verbose option so that we get some extra bits of information.

[vpodzime@vpodzime-laptop ~]$ ansible-playbook --verbose --inventory /tmp/inventory --limit hub ~/tmp/deploy.yml
Using /etc/ansible/ansible.cfg as config file
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

PLAY [Deploy new hosts in the infrastructure] *****************************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************************************************
ok: [vpc7-hub-centos-7-x64hub0]

TASK [Install and bootstrap CFEngine] *************************************************************************************************************************************************************
changed: [vpc7-hub-centos-7-x64hub0] => {"cfengine_package": "https://cfengine-package-repos.s3.amazonaws.com/enterprise/Enterprise-3.18.1/hub/redhat_7_x86_64/cfengine-nova-hub-3.18.1-1.el7.x86_64.rpm", "cfengine_policy_server": "172.31.23.2", "cfengine_role": "hub", "changed": true, "state": "CFEngine installed and bootstrapped to 172.31.23.2"}

PLAY RECAP ****************************************************************************************************************************************************************************************
vpc7-hub-centos-7-x64hub0  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

As we can see, the Install and bootstrap CFEngine task has the status changed and thanks to the --verbose option we can also see extra details – the package it installed was the cfengine-nova-hub package for RHEL/CentOS 7 in version 3.18.1, the host is a CFEngine hub and its policy server is 172.31.23.2, thus the host itself. So exactly what we wanted and expected.

Ansible tasks are idempotent – if the state they describe is already the state of the host, they are supposed to do nothing. So it should be safe to run the playbook on the hub host again, right? Well, let's try it:

[vpodzime@vpodzime-laptop ~]$ ansible-playbook --verbose --inventory /tmp/inventory --limit hub ~/tmp/deploy.yml
Using /etc/ansible/ansible.cfg as config file
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

PLAY [Deploy new hosts in the infrastructure] *****************************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************************************************
ok: [vpc7-hub-centos-7-x64hub0]

TASK [Install and bootstrap CFEngine] *************************************************************************************************************************************************************
ok: [vpc7-hub-centos-7-x64hub0] => {"cfengine_policy_server": "172.31.23.2", "cfengine_role": "hub", "changed": false, "state": "CFEngine installed and bootstrapped to 172.31.23.2"}

PLAY RECAP ****************************************************************************************************************************************************************************************
vpc7-hub-centos-7-x64hub0  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

This time the state of the task is ok so nothing was changed. And again, thanks to the --verbose option, we can also see the extra details and we can check they are the same as when the task actually installed CFEngine and bootstrapped the host.

Now we can deploy the remaining hosts, which are in the clients group:

[vpodzime@vpodzime-laptop ~]$ ansible-playbook --verbose --inventory /tmp/inventory --limit clients ~/tmp/deploy.yml
Using /etc/ansible/ansible.cfg as config file
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

PLAY [Deploy new hosts in the infrastructure] *****************************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************************************************
[WARNING]: Platform linux on host vpd10-clients-debian-10-x64client0 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could
change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
ok: [vpd10-clients-debian-10-x64client0]
ok: [vpr8-clients-rhel-8-x64client1]
ok: [vpr8-clients-rhel-8-x64client0]
ok: [vpu18-clients-ubuntu-18-04-x64client1]
ok: [vpu18-clients-ubuntu-18-04-x64client0]
[WARNING]: Platform linux on host vpd10-clients-debian-10-x64client1 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could
change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
ok: [vpd10-clients-debian-10-x64client1]

TASK [Install and bootstrap CFEngine] *************************************************************************************************************************************************************
changed: [vpd10-clients-debian-10-x64client0] => {"cfengine_package": "https://cfengine-package-repos.s3.amazonaws.com/enterprise/Enterprise-3.18.1/agent/agent_debian10_x86_64/cfengine-nova_3.18.1-1.debian10_amd64.deb", "cfengine_policy_server": "172.31.23.2", "cfengine_role": "agent", "changed": true, "state": "CFEngine installed and bootstrapped to 172.31.23.2"}
changed: [vpu18-clients-ubuntu-18-04-x64client1] => {"cfengine_package": "https://cfengine-package-repos.s3.amazonaws.com/enterprise/Enterprise-3.18.1/agent/agent_ubuntu18_x86_64/cfengine-nova_3.18.1-1.ubuntu18_amd64.deb", "cfengine_policy_server": "172.31.23.2", "cfengine_role": "agent", "changed": true, "state": "CFEngine installed and bootstrapped to 172.31.23.2"}
changed: [vpd10-clients-debian-10-x64client1] => {"cfengine_package": "https://cfengine-package-repos.s3.amazonaws.com/enterprise/Enterprise-3.18.1/agent/agent_debian10_x86_64/cfengine-nova_3.18.1-1.debian10_amd64.deb", "cfengine_policy_server": "172.31.23.2", "cfengine_role": "agent", "changed": true, "state": "CFEngine installed and bootstrapped to 172.31.23.2"}
changed: [vpr8-clients-rhel-8-x64client1] => {"cfengine_package": "https://cfengine-package-repos.s3.amazonaws.com/enterprise/Enterprise-3.18.1/agent/agent_rhel8_x86_64/cfengine-nova-3.18.1-1.el8.x86_64.rpm", "cfengine_policy_server": "172.31.23.2", "cfengine_role": "agent", "changed": true, "state": "CFEngine installed and bootstrapped to 172.31.23.2"}
changed: [vpr8-clients-rhel-8-x64client0] => {"cfengine_package": "https://cfengine-package-repos.s3.amazonaws.com/enterprise/Enterprise-3.18.1/agent/agent_rhel8_x86_64/cfengine-nova-3.18.1-1.el8.x86_64.rpm", "cfengine_policy_server": "172.31.23.2", "cfengine_role": "agent", "changed": true, "state": "CFEngine installed and bootstrapped to 172.31.23.2"}
changed: [vpu18-clients-ubuntu-18-04-x64client0] => {"cfengine_package": "https://cfengine-package-repos.s3.amazonaws.com/enterprise/Enterprise-3.18.1/agent/agent_ubuntu18_x86_64/cfengine-nova_3.18.1-1.ubuntu18_amd64.deb", "cfengine_policy_server": "172.31.23.2", "cfengine_role": "agent", "changed": true, "state": "CFEngine installed and bootstrapped to 172.31.23.2"}

PLAY RECAP ****************************************************************************************************************************************************************************************
vpd10-clients-debian-10-x64client0 : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpd10-clients-debian-10-x64client1 : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpr8-clients-rhel-8-x64client0 : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpr8-clients-rhel-8-x64client1 : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpu18-clients-ubuntu-18-04-x64client0 : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpu18-clients-ubuntu-18-04-x64client1 : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

We can see that the task made changes on all the hosts, that it installed the respective packages for Debian 10, RHEL 8 and Ubuntu 18.04 and that all the hosts were bootstrapped to the given policy server so they became CFEngine agents (clients).

One last remaining thing is to just run the playbook again on all hosts because that is what should be happening regularly to make sure the hosts are in the desired state. This time we drop the --verbose option to make the output less noisy:

[vpodzime@vpodzime-laptop ~]$ ansible-playbook --inventory /tmp/inventory ~/tmp/deploy.yml
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

PLAY [Deploy new hosts in the infrastructure] *****************************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************************************************
ok: [vpu18-clients-ubuntu-18-04-x64client0]
ok: [vpu18-clients-ubuntu-18-04-x64client1]
ok: [vpr8-clients-rhel-8-x64client1]
ok: [vpr8-clients-rhel-8-x64client0]
ok: [vpc7-hub-centos-7-x64hub0]
[WARNING]: Platform linux on host vpd10-clients-debian-10-x64client0 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could
change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
ok: [vpd10-clients-debian-10-x64client0]
[WARNING]: Platform linux on host vpd10-clients-debian-10-x64client1 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could
change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
ok: [vpd10-clients-debian-10-x64client1]

TASK [Install and bootstrap CFEngine] *************************************************************************************************************************************************************
ok: [vpc7-hub-centos-7-x64hub0]
ok: [vpu18-clients-ubuntu-18-04-x64client0]
ok: [vpu18-clients-ubuntu-18-04-x64client1]
ok: [vpr8-clients-rhel-8-x64client1]
ok: [vpr8-clients-rhel-8-x64client0]
ok: [vpd10-clients-debian-10-x64client1]
ok: [vpd10-clients-debian-10-x64client0]

PLAY RECAP ****************************************************************************************************************************************************************************************
vpc7-hub-centos-7-x64hub0  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpd10-clients-debian-10-x64client0 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpd10-clients-debian-10-x64client1 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpr8-clients-rhel-8-x64client0 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpr8-clients-rhel-8-x64client1 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpu18-clients-ubuntu-18-04-x64client0 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpu18-clients-ubuntu-18-04-x64client1 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

As we can see, the Install and bootstrap CFEngine task just reports that all hosts are ok, i.e. in the desired state. And what happens if we add new hosts to the infrastructure?

[vpodzime@vpodzime-laptop ~]$ cf-remote spawn --platform centos-7-x64 --count 2 --role clients --name c7-clients
Spawning VMs.....DONE
Waiting for VMs to get IP addresses..........DONE
Details about the spawned VMs can be found in /home/vpodzime/.cfengine/cf-remote/cloud_state.json
[vpodzime@vpodzime-laptop ~]$ cf-remote show --ansible-inventory > /tmp/inventory
[vpodzime@vpodzime-laptop ~]$ ansible-playbook --inventory /tmp/inventory ~/tmp/deploy.yml
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

PLAY [Deploy new hosts in the infrastructure] *****************************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************************************************
ok: [vpr8-clients-rhel-8-x64client1]
ok: [vpu18-clients-ubuntu-18-04-x64client1]
ok: [vpu18-clients-ubuntu-18-04-x64client0]
ok: [vpr8-clients-rhel-8-x64client0]
ok: [vpc7-hub-centos-7-x64hub0]
[WARNING]: Platform linux on host vpd10-clients-debian-10-x64client0 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could
change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
ok: [vpd10-clients-debian-10-x64client0]
[WARNING]: Platform linux on host vpd10-clients-debian-10-x64client1 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could
change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
ok: [vpd10-clients-debian-10-x64client1]
ok: [vpc7-clients-centos-7-x64client0]
ok: [vpc7-clients-centos-7-x64client1]

TASK [Install and bootstrap CFEngine] *************************************************************************************************************************************************************
ok: [vpc7-hub-centos-7-x64hub0]
ok: [vpu18-clients-ubuntu-18-04-x64client1]
ok: [vpu18-clients-ubuntu-18-04-x64client0]
ok: [vpr8-clients-rhel-8-x64client1]
ok: [vpr8-clients-rhel-8-x64client0]
ok: [vpd10-clients-debian-10-x64client0]
ok: [vpd10-clients-debian-10-x64client1]
changed: [vpc7-clients-centos-7-x64client0]
changed: [vpc7-clients-centos-7-x64client1]

PLAY RECAP ****************************************************************************************************************************************************************************************
vpc7-clients-centos-7-x64client0 : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpc7-clients-centos-7-x64client1 : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpc7-hub-centos-7-x64hub0  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpd10-clients-debian-10-x64client0 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpd10-clients-debian-10-x64client1 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpr8-clients-rhel-8-x64client0 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpr8-clients-rhel-8-x64client1 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpu18-clients-ubuntu-18-04-x64client0 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
vpu18-clients-ubuntu-18-04-x64client1 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

As expected, the task makes no change on the already deployed hosts and it does make changes on the new hosts. With --verbose we would see that it indeed installed the CentoOS 7 CFEngine package only this time the cfengine-nova package, so the client package not the hub package, and that it bootstrapped the hosts to the CFEngine hub (policy server).

Conclusions

In this blog post we presented the CFEngine Ansible module that allows CFEngine to be installed and bootstrapped on the hosts using Ansible in a very easy way. We believe that using Ansible when deploying hosts in an infrastructure is a great and smart choice. And we also belive that CFEngine can handle many following aspects of infrastructure management once the hosts are deployed much better. So providing an easy mechanism to ensure CFEngine can start doing its job when Ansible is done deploying hosts is a logical step.

The module provides the basic required functionality and works well on all GNU/Linux distributions supported by CFEngine. But it can surely be extended and improved, especially for environments that cannot rely on private network mechanisms handling the trust aspects. The module is of course open-source and we welcome all contributions and issue reports.


  1. At least theoretically. In practice, many of the Ansible modules require certain tools all libraries to be installed on the hosts. ↩︎

  2. Last time we tried to add it there, the Sign up didn't work properly and when it somehow worked, we were not really happy about the Login with GitHub integration which required access to quite a bit of information about us at GitHub↩︎