ansible

Updated: April 30, 2024

Ansible is a configuration tool, that can setup and administer multiple machines on a network.

Note: Ansible only needs to be installed on a controller node.


Table of Contents


SSH KEYS

For ansible to work ssh access is required for the machines you want to config and admin.

How to make a key


INSTALL

For ansible to work it needs to be installed on a control machine. This is the only machine that will have anisible related software on it. As ansible is used on other machines they will not have any software on them.

Ubuntu
sudo apt install ansible
Mac
brew install ansible

For any others check here


Create User

Create an ansible user for carrying out ansible playbooks. This user helps to distinguish if it was ansible that did something in the logs from all other users – even root.

useradd -m ansible                # create ansible user with home directory
usermod -s /bin/bash ansible      # set users shell
su - ansible                      # switch to ansible user

Edit visudo and search for with /wheel First set editor for visudo

EDITOR=vim  visudo
## Samthing without a password
# %wheel    ALL=(ALL)     NOPASSWD: ALL
ansible     ALL=(ALL)     NOPASSWD: ALL

CONFIG

Ansible will install a basic configuration in /etc/ansible but you should copy the config to another location for working directory so that multiple platforms can be managed without effecting the others:

Configs will override any config location above them in this list.

nocows = 0
forks = 7
inventory = /home/ansible/inventory
remote_user = ansible
remote_port = 2222
private_key_file = ~/.ssh/key-pair.pem
transport = smart #paramiko

VAULT

Ansible Vault is used to keep sensitive data safe.

# encrypt data in a file
ansible-vault encrypt private.yml

# to change the data that is already encrypted or view it
ansible-vault edit private.yml

# for use in play book
ansible-playbook playbook.yml --ask-vault-pass

INVENTORY FILES

Also known as hosts files. They work much the same as cfg for precedence.


ADHOC Commands

Adhoc commands are not so much setup, as more just poking around checking on things. Testing things to work.

# check that all hosts can be reached
ansible -m ping all

# check hostname of every server
ansible -a 'hostname' all

# find how much disk space
ansible -a 'df -h' all

# check time and date
ansible -a "date" multi

# dry run any achoc or playbook command.
--check

All the commands above will be executed as user. To execute as root use the -e option. Will work if root has no password. To be able to use a password use -K

# add a user to the servers
ansible -b -K -m user -a 'name=testuser' all

# check if user was created on machine login to one of them
getent passwd | egrep testuser

# here is why ansible is great, lets check them all at once!
ansible -m shell -a 'getent passwd | egrep testuser' all

# remove testuser from the servers
ansible -b -K -m user -a 'name=testuser state=absent' all

COMMANDS

# show all variable of target host
ansible <IP or hostname> -m setup

-a    # use adhoc command
-f    # tell ansible how many forks to use
-m    # use a module
-b    # become root
-K    # ask for password

--limit # limit an adhoc command to a single server



Includes

Includes are other ansible playbooks that usually are a specific role. They can be used in the same level as the playbook including them or can be from some other place. This will require a bit more of the path if it is somewhere else and not at the playbooks level. They are run in the order they are placed. You can put them at beginning or end of a task or block. Or you can place them at start or end of the file.

tasks:
  - include: selinux.yml
  - block:
      - name: some task
        

- include: yml on some other level to run last

Galaxy

Community released roles.

ansible-galaxy collection list                # lists collections already installed

Roles

ansible-galaxy init mariadb

Inside the roles folder we need to make 2 directories for a main.yml to live.

cd roles
mkdir -p basic/tasks
cd !:2

Create the required yml vim main.yml

- name: "Installing VIM"  # optional: prints our during process so good to use them
  apt: pkg=vim state=present

If installing many items we can list them in short form.

- name: "Installing additional software"
  apt: pkg={{ item }} state=present
  with_items:
  - dnsutils
  - git
  - vim
  - ntp
  - at
  - tree
  - lvm2

PLAYBOOK

Playbooks determine which role should be applied to which target machine. Playbooks should be created in the working directory.

vim playbook.yml

- hosts: all
  become: true		# become root
  roles:
  - basic			# roles to use

Because we selected to become root when we run the playbook we will use -K again to ask for password.

ansible-playbook -K playbook.yml

Delegate

---
- name: shutdown delegage
  hosts: worker1
  become: yes
  become_user: megacron

  tasks:
    - name: restarting machine...
      shell: sleep 2 && shutdown -r now "rastarting to apply changes"
      async: 1
      poll: 0
      ignore_errors: true

    - name: waiting for server to come back...
      wait_for: host={{inventory_hostname}} state=started delay=30 timeout=300
      become: no
      delegate_to: 127.0.0.1
- name: shutdown delegage
  hosts: worker1
  become: yes
  become_user: megacron

  tasks:
    - name: gathering local facts
      setup:
      delegate_to: 127.0.0.1
      delegate_facts: true

COPYING FILES

- name: "Adding bashrc"
  copy: src=../files/bash.bashrc dest=/etc/bash.bashrc owner=root group=root mode=0644

# src is where to find the file on the control machine relative to main.yml

For this to work we have to place a copy of the .bashrc file by making a folder to place the file.

mkdir roles/basic/files

While we did add our bashrc the old file is still there. To remove this safely we use shell with the creates command, the means ansible will check if the file is there but wont execute command if it exists.

- name: "Adding bashrc"
  copy: src=../files/bash.bashrc dest=/etc/bash.bashrc owner=root group=root mode=0644

- name: "Removing megacron's bashrc..."
  shell: creates=/home/megacron/.bashrc_backup mv /home/megacron/.bashrc /home/megacron/.bashrc_backup

- name: "Removing roots's bashrc..."
  shell: creates=/root/.bashrc_backup mv /root/.bashrc /root/.bashrc_backup

MAKING WEBSERVER WITH REVERSE PROXY

mkdir -p roles/webserver/tasks
vim roles/webserver/tasks/main.yml

main.yml

- name: "Installing Webserver Software"
  apt: name={{ item }}
  with_items:
  - apache2

- name: "Enabling Proxy Module"
  apache2-module: name=proxy_http

- name: "Adding Proxy Configuration"
  template: src=../files/proxy.conf dest=/etc/apache2/conf-available owner=root group=root mode=0644
  notify:
  - enable-proxy-config		# use by handler

We can add configuration files related to the webserver.

mkdir -p roles/webserver/files
vun roles/webserver/files/proxy.conf

proxy.conf

ProxyPass / http://{{hostvars['appserver']['ansible_eth0']['ipv4']['address']}}:8000/
ProxyPassReverse / http://{{hostvars['appserver']['ansible_eth0']['ipv4']['address']}}:8000/

HANDLERS

Handlers are best used when you do not want to restart a service every time but only when a change has been made requiring a reboot of the service. These also should be placed in their corresponding folder.

mkdir roles/webserver/handlers
vim roles/webserver/handlers/main.yml

main.yml

- name: enable-proxy-config		# must match name given in notify list
  shell: a2enconf proxy.conf
  notify:
  - restart-apache		# handler notifying another handler.

- name: restart-apache
  service: name=apache2 state=restarted

Update the playbook so role matches correct machine

- hosts: all
  become: true		# become root
  roles:
  - basic			# roles to use

- hosts: webserver
  become: true
  roles:
  - webserver

Now we can move to setup the appserver now that the proxy is up.

mkdir -p roles/appserver/{taks,handlers,files}
vim roles/appserver/tasks/main.yml

main.yml

- name: "Installing Required Software"
  apt: pkg={{ item }} state=present
  with_items:
  - gunicorn		# small server for small python applications
  - supervisor		# python management tool to run apps like gunicorn
  - python-mysqldb
  - python-falcon

- name: "Making sure supervisor is enabled and started..."
  service: name=supervisor state=started enabled=yes
  # makes sure it is started now, enabled so starts after reboots

- name: "Creating the base folder of the application"
  file: path=/opt/testapp state=directory owner=nobody group=nogroup mode=0755

- name: "Copying the application..."
  copy: src=../files/testapp.py dest=/opt/testapp/testapp.py owner=nobody group=nogroup mode=0755
  notify:		# create notify to restart app if any changes are made
  - restart-app

- name: "Copying the supervisor config file..."
  template: src=../files/testapp.conf dest=/etc/supervisor/conf.d/testapp.conf owner=nobody group=nogroup mode=0644
  notify:
  - reread-config
  - restart-app

- name: "Adding IP of dbserver to /etc/hosts..."
  lineinfile: name=/etc/hosts line=" hostvars['dbserver']['ansible_eth0']['ipv4']['address'] }} dbserver"

SECRETS (PASSWORDS)

vim roles/appserver/files/testapp.conf

testapp.conf

[program:testapp]
command=/usr/bin/gunicorn --bind 0.0.0.0:8000 testapp:app
directory=/opt/testapp
user=nobody
autostart=true
autorestart=true
redirect_stderr=true
environment=databasepassword="{{ lookup('password', 'credentials/dbpassword.txt') }}"  # by creating this file we can control the passoword, lookup is used with ansible actions and templates.

create the handlers that were mentioned before (notify)

vim roles/appserver/handlers/main.yml

main.yml

- name: reread-config
  shell: supervisorctl reread

- name: restart-app
  supervisorctl: name=testapp state=restarted

Add role to ansible playbook

- hosts: all
  become: true		# become root
  roles:
  - basic			# roles to use

- hosts: webserver
  become: true
  roles:
  - webserver

- hosts: appserver
  become: true
  roles:
  - appserver

SETUP DATABASE

Again create standard file structure for ansible

mkdir -p roles/dbserver/{tasks,handlers,files}
vim roles/dbserver/tasks/main.yml

main.yml

- name: "Installing database packages..."
  apt: pkg={{ item }} state=present
  with_items:
  - mysql-server
  - python-mysqldb

- name: "Adding database backup cronjob"
  cron: hour=10 minute=22 user=root job="/usr/bin/mysqldump --all-databases > /root/dbbackup.dump" name="mySQL Backup"
# keep database from being overwritten by changes
- name: "Adding database..."
  mysql_db: name=ansibletutorial state=present
  notify:
  - import-db

- name: "Copying database dump..."
  copy: src=../files/dbdump.sql dest=/var/tmp/dbdump.sql owner=root group=root mode=0600

- name: "Granting Access"
  mysql_user: name=ansibletutorial host=% priv=ansibletutorial.*:ALL password={{ lookup('password', 'credentials/dbpassword.txt') }}

- name: "Allowing remote access"
  copy: src=../files/n-mysqld-bind.cnf dest=/etc/mysql/mysql.conf.d/n-mysqld-bind.cnf owner=root group=root mode=0644
  notify:
  - restart-mysql

Now we have to create the files we listed in the tasks.

vim roles/dbserver/files/n-mysqld-bind.cnf

n-mysqld-bind.cnf

[mysqld]
bind-address = 0.0.0.0
cp /var/tmp/dbdump.sql roles/dbserver/files/
vim roles/dbserver/handlers/main.yml	# next create the handlers

main.yml

- name: import-db
  mysql_db: name=ansibletutorial state=import target=/var/tmp/dbdump.sql

- name: restart-mysql
  service: name=mysql state=restarted

Finally add the new role to the playbook

- hosts: all
  become: true		# become root
  roles:
  - basic			# roles to use

- hosts: webserver
  become: true
  roles:
  - webserver

- hosts: appserver
  become: true
  roles:
  - appserver

- hosts: dbserver
  become: true
  roles:
  - dbserver

WUT I DUN DID #TODO

sudo apt update && sudo apt upgrade -y
sudo apt nfs-common git zsh rpi-eeprom
sudo mkdir -p nfs/euryale
sudo mount -t nfs -O proto=tcp,port=2049,rw,all_squash,anonuid=1001,anongid=1001 192.168.1.87:/volume1/tftp/pi-pxe/euryale /nfs/euryale -vvv
sudo rsync -xa --progress --exclude /nfs / /nfs/euryale
mkdir bootmnt
sudo mount -t nfs -O proto=tcp,port=2049,rw,all_squash,anonuid=1001,anongid=1001 192.168.1.87:/volume1/tftp/bootmnt /nfs/bootmnt -vvv
vcgencmd otp_dump | grep 28: | sed s/.*://g
sudo mkdir -p bootmnt/686f3d0c
sudo cp -r /boot/* /nfs/bootmnt/686f3d0c/
sudo vi /nfs/euryale/etc/fstab
sudo vi /nfs/pi-tftpboot/686f3d0c/cmdline.txt