Installing Multiple Packages on Multiple OS with Ansible

ansibleansible-playbook

I have a host with 2 servers: one has CentOS and the other Ubuntu installed.

I decided to install apache, nginx and php-fpm on both servers and wrote 3 playbooks.

  1. ubuntu.yml (/home/ansible/playbook):
---
- name: Ubuntu Playbook
  hosts: ubun
  become: true
  vars:
   - packages:
     - nginx
     - apache2
     - php-fpm 
  tasks:
   - name: Update apt package
   apt:
   name: "*"
   state: latest
   update_cache: yes
   - name: Install Packages
     apt:
       pkg: "{{ packages }}"
       state: latest
       update_cache: yes
   - name: Apache Service Start
     service:
       name: nginx
       state: restarted
       enabled: yes
  1. centos.yml (/home/ansible/playbook):
---
- name: CentOS Playbook
  hosts: cent
  become: true
  vars:
   packages:
   - epel-release
   - httpd
   - nginx
   - php-fpm
  tasks:
   - name: Update yum package
     yum:
       name: "*"
       state: latest
       update_cache: yes
   - name: Install Packages
     yum:
       name: "{{ packages }}"
       state: latest
       update_cache: yes
   - name: Apache Service Start
     service:
       name: nginx
       state: restarted
       enabled: yes
  1. base.yml (/home/ansible/playbook):
---
- name: Base Playbook
  hosts: aws
  become: true
  tasks:
    - name: Performing Tasks for CentOS
      when: ansible_facts['distribution'] == 'CentOS'
      include_tasks: centos.yml
    - name: Performing Tasks for Ubuntu
      when: ansible_facts['distribution'] == 'Ubuntu'
      include_tasks: ubuntu.yml

My 3 Ansible groups are:

  • [aws] which contains both the server

  • [cent] which contains CentOS server

  • [ubun] which contains Ubuntu server

I tried dry running centos.yml and ubuntu.yml separately and it worked, but when I try dry running base.yml the following error is raised:

    FAILED! => {"reason": "unexpected parameter type in action: <class 'ansible.parsing.yaml.objects.AnsibleSequence'>\n\nThe error appears to be in '/home/ansible/playbook/centos.yml': line 2, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n---\n- name: CentOS Playbook\n  ^ here\n"}

    FAILED! => {"reason": "unexpected parameter type in action: <class 'ansible.parsing.yaml.objects.AnsibleSequence'>\n\nThe error appears to be in '/home/ansible/playbook/ubuntu.yml': line 2, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n---\n- name: Ubuntu Playbook\n  ^ here\n"}

I already tried replacing import_tasks with include_tasks but I get the same error.

Best Answer

My solution is to merge these into one play, as they do the same thing.

---
- name: php-fpm play
  hosts: aws
  become: true
  vars:
  - repo:
      Debian:
      - apt # already installed, but need something here
      RedHat:
      - epel-release
  - packages:
      Debian:
      - apache2
      #- nginx # cannot have 2 listening on port 80
      - php-fpm
      RedHat:
      - httpd
      #- nginx
      - php-fpm
  - services:
      Debian: apache2
      RedHat: httpd

  tasks:
     # TODO move update * task to different play

   - name: Install repo
     # Seperate package transaction for EPEL
     # so it is available in the next task
     package:
       name: "{{ repo[ansible_os_family] }}"

   - name: Install Web Server Packages
     # Keyed by OS family fact to also support RHEL and Debian
     package:
       name: "{{ packages[ansible_os_family] }}"
       state: latest

   - name: Web Service Start
     service:
       name: "{{ services[ansible_os_family] }}"
       state: restarted
       enabled: yes

Can't have multiple servers listening on ports 80 and 443. I commented out nginx, as a task was mislabeled "Apache Service Start". If you want one of these proxying for the other or something, you will need to deploy a config file to change the ports.

Used the package: action which delegates to actual package manager. Enables a package install task to run on different OSes. Can't do update_cache that way, but yum doesn't need it when adding repos like apt does.

Vars structure is dicts of OS family specific values. This enables the package and service names to be indexed by facts. OS family, so this also works on RHEL and Debian in addition to CentOS and Ubuntu.

Indentation was wrong. Modules parameters need to be indented a level below task level directives like name:.

Can't include_tasks an entire play, that only works with import_playbook. include_tasks is easier when you have roles, which have a tasks directory for such files. (Play level tasks are a thing, but I am in favor of roles doing everything.)


There still is more work to make this useful.

When you need to use template to install configuration, EL puts configs in /etc/httpd/, while Debian is in /etc/apache2/.

Plenty of web server roles are open sourced, to look at if you want ideas. Check Galaxy.

Consider moving your tasks to roles, enables reuse.