Ansible Provisioning

· by Christoph Stoettner · Read in about 5 min · (992 words)

In the first two parts of this little devops series, I showed build templates with Packer and deploying virtual-machines with Terraform.

Now we want to install some more packages on our new servers.

tl;dr

I started using Ansible for deployments some years ago. For example I showed deploying IBM Connections at Social Connections 12 in Vienna.

For more details on the functionality, have a look at the post How Ansible Works

Ansible uses plain ssh and doesn’t need any additional server or client component.

You can run Ansible as a separate task in your deployment pipeline, or add it as post-provisioning task to Terraform. So it runs automatically when Terraform deploys your systems.

Windows

You can use Ansible to deploy Windows Servers too. More interesting is running Ansible from a Windows machine to deploy Linux and Windows servers.

For Windows you can use WinRM or SSH to connect for remote management. The SSH support is experimental AFAIK.

Ansible

Installation

Ansible is available for all major Linux distributions. Check the documentation for installation instructions.

Little project

Directory structure of a little Ansible example
.
├── group_vars
│   └── all
│       └── main.yml
├── inventory
├── roles
│   ├── common
│   │   └── tasks
│   │       └── main.yml
│   └── ldap
│       ├── defaults
│       │   └── main.yml
│       └── tasks
│           └── main.yml
├── site.yml
└── templates
    ├── base.ldif.j2
    ├── db.ldif.j2
    ├── ldap-config.sh.j2
    ├── monitor.ldif.j2
    └── user-ldap.ldif.j2

In the inventory file you define your servers and group them together.

inventory
[gpn19]
gpn19-server1
gpn19-server2

[ldap]
gpn19-server2

So gpn19-server1 is only in the group gpn19, server gnp19-server2 is part of gpn19 and ldap. There is always a group all with all servers of an inventory file.

The Ansible playbook itself is defined as an Yaml-file:

site.yml
---
- hosts: all
  roles:
    - common

- hosts: ldap
  roles:
    - role: ldap

So for all hosts the role common will run and on group ldap the role ldap.

To make the roles independent of the environment, we use some variables. So it’s enough to just change the variable and the deployment will use different domain and passwords.

group_vars/all/task.yml
---
ldap:
  domain: dc=devops,dc=example,dc=com
  passwordencrypted: "{SSHA}CdGAzVNlrqgLbKo6pebBxuDBBkxokkHm"
  passwordclear: "password"

To keep it simple, the password is defined here in the variable file. Have a look at Ansible Vault to keep your passwords encrypted.

Using template files

For the ldap deployment we need to change some files on the target server. These are already prepared in the directory templates and use Jinga2 template engine.

templates/ldapconfig.sh.j2
#!/usr/bin/env bash

cd /etc/openldap/slapd.d
ldapmodify -Y EXTERNAL  -H ldapi:/// -f db.ldif
ldapmodify -Y EXTERNAL  -H ldapi:/// -f monitor.ldif
sleep 5
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/cosine.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/nis.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/inetorgperson.ldif
sleep 5
ldapadd -x -w {{ ldap.passwordclear }} -D "cn=ldapadm,{{ ldap.domain }}" -f /etc/openldap/slapd.d/base.ldif   (1)
sleep 10
ldapadd -x -w {{ ldap.passwordclear }} -D "cn=ldapadm,{{ ldap.domain }}" -f /etc/openldap/slapd.d/user-ldap.ldif
1 We can use our variable in Jinga2 and these will be replaced during deployment
roles/common/tasks/main.yml
---
# Disable Firewall during Installation
- name: Disable Firewall  (1)
  service:
    name=firewalld
    state=stopped
    enabled=no

# Disabe SELinux, most IBM Software is not supported with it
- name: Disable SELinux
  selinux:
    state: disabled

# Increase limits.conf for IBM products
- name: Change limits.conf    (2)
  pam_limits:
    domain: root
    limit_type: '-'
    limit_item: nproc
    value: '16384'
- pam_limits:
    domain: root
    limit_type: '-'
    limit_item: nofile
    value: '65536'
- pam_limits:
    domain: root
    limit_type: '-'
    limit_item: stack
    value: '10240'

# Install Unzip
- name: Install unzip to support unarchive function of ansible, add xauth
  package:
    name={{ item }}       (3)
    state=latest
  with_items:
    - unzip
    - xauth
    - vim

# Configure SSH X11Forward
- name: Update SSH configuration to be more secure.
  lineinfile:
    dest: "/etc/ssh/sshd_config"
    regexp: "{{ item.regexp }}"
    line: "{{ item.line }}"
    state: present
  with_items:
    - regexp: "^X11Forwarding"
      line: "X11Forwarding yes"
    - regexp: "^X11UseLocalhost"
      line: "X11UseLocalhost no"
- name: Restart SSH
  service:
    name=sshd
    state=restarted
    enabled=yes
1 Stop Firewalld and set to disabled
2 Configure some limits in limits.conf
3 Install additional packages (independent of yum or apt)
roles/ldap/tasks/main.yml
---
- name: Install system packages for OpenLDAP
  package:
    name={{ item }}
    state=latest
  with_items:
    - openldap-servers
    - openldap-clients

- name: Enable Slapd service    (1)
  service:
    name=slapd
    state=restarted
    enabled=yes

- name: Initial ldap config, copy templates db.ldif   (2)
  template: src=db.ldif.j2 dest=/etc/openldap/slapd.d/db.ldif
  tags: parse

- name: Initial ldap config, copy templates monitor.ldif
  template: src=monitor.ldif.j2 dest=/etc/openldap/slapd.d/monitor.ldif
  tags: parse

- name: Create base.ldif
  template: src=base.ldif.j2 dest=/etc/openldap/slapd.d/base.ldif
  tags: parse

- name: Create user.ldif
  template: src=user-ldap.ldif.j2 dest=/etc/openldap/slapd.d/user-ldap.ldif
  tags: parse

- name: Copy sample db config
  copy:
    src: "/usr/share/openldap-servers/DB_CONFIG.example"
    dest: "/var/lib/ldap/DB_CONFIG"
    remote_src: yes
    directory_mode: yes
    owner: ldap
    group: ldap

- name: Create LDAP Config Script
  template:
    src: ldap-config.sh.j2
    dest: /tmp/ldap-config.sh
    mode: 0755
  tags: parse

- name: Configure LDAP and Import Users
  shell: "/tmp/ldap-config.sh"

- name: Remove config script
  file:
    path: /tmp/ldap-config.sh
    state: absent
1 Enable slapd service
2 Copy db.ldif.j2 from templates to dest

Run Ansible Playbook

To run this playbook with our inventory file, just run:

ansible-playbook -i inventory site.yml
Run Ansible Playbook

So now you’re prepared to build new servers and deploy software on it. Have fun!

Ansible Galaxy

On Ansible Galaxy you can share and download Ansible roles. So start new projects searching there, don’t start from scratch.

About Galaxy (orginal: https://galaxy.ansible.com/docs/)

Galaxy is a hub for finding and sharing Ansible content.

Use Galaxy to jump-start your automation project with great content from the Ansible community. Galaxy provides pre-packaged units of work known to Ansible as Roles, and new in Galaxy 3.2, Collections.

Roles can be dropped into Ansible PlayBooks and immediately put to work. You’ll find roles for provisioning infrastructure, deploying applications, and all of the tasks you do everyday. The new Collection format provides a comprehensive package of automation that may include multiple playbooks, roles, modules, and plugins.

Gulasch Programmiernacht

The content of this series was part of my talk at the GPN19. So you can watch a video of the talk too: