This was my first experience with Ansible and I utilized the same manifests for the tasks, variables, and playbooks.
For a more comprehensive approach to using Ansible with Collections and Roles, check out my other blog post: “Ansible Playground”
Install Ansible on Server #
| Install Ansible | |
| sudo apt update | Update Package Manager | 
| sudo apt install software-properties-common -y | Install apt utilities | 
| sudo add-apt-repository --yes --update ppa:ansible/ansible | Add repository | 
| sudo apt install ansible -y | Install Ansible | 
Ansible inventory: 
sudo vi /etc/ansible/hosts
Should look like this:
[jklug]  # no "-" allowed
192.168.30.100
192.168.30.101
192.168.30.102
Verify Ansible inventory: 
ansible all --list-hosts
Before running Ansible commands on an host, it’s necessary to establish a SSH connection to the host:
| ssh-keygen -t rsa -b 4096 | Create SSH key | 
| cat id_rsa.pub | Add key to authorized_keys on Ansible host | 
| ssh ubuntu@192.168.30.100 | SSH into Ansible host | 
| exit | Exit SSH connection | 
Check connection to Ansible host: 
ansible all -m ping
ansible jklug -m ping
Should look like this:
18.184.196.36 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
Ansible Playbooks #
Run Ansbible Playbook #
| ansible-playbook playbookname.yml | Run playbook | 
| ansible-playbook playbookname.yml --list-hosts | List hosts affected by playbook | 
| ansible-playbook playbookname.yml --limit hostgroup | Run Playbook for specific hostgroup | 
| ansible-playbook playbookname.yml -u username -b true | Run Playbook for specific user | 
| ansible-playbook playbookname.yml -u username -b true --ask-pass | Prompt for pw | 
Ansible Playbooks: System #
Reboot Server #
---
- name: Ubuntu server reboot
  hosts: jklug
  become: true
  
  tasks:
  - name: Reboot
    reboot:
      reboot_timeout: 60
      msg: "Reboot initiated by Ansible"
Apt update & apt upgrade #
---
- name: Apt update, apt upgrade
  hosts: jklug
  become: true
  tasks:
    - name: Apt update
      apt:
        update_cache: yes
    - name: Apt upgrade
      apt:
        upgrade: yes
        autoremove: no
        autoclean: no
Create User #
The following playbook creates a new user (tested on Ubuntu) and adds the user the one or more groups:
---
 - hosts: all
   become: yes
   vars:
   - user: user1 # username
   - password: password  # password
   tasks:
    - name: Create User
      user: 
        name: "{{ user }}"
        password: "{{ password | password_hash('sha512') }}"  
    
    - name: Add user to group # Optional
      user:
        name: "{{ user }}"
        shell: /bin/bash
        groups: docker,sudo # Add User to groups
        append: yes
Delete User #
This playbook delets an user (tested on Ubuntu), the deletion of the users home directory is optional:
---
 - hosts: all
   become: yes
   vars:
   - user: user1 # username
   tasks:
    - name: Delete User
      user: 
        name: "{{ user }}"
        state: absent
        remove: no # Don't remove Home directory
Ansible Playbooks: Docker Compose #
Install Docker & Docker-Compose #
This playbook installs Docker and Docker Compose with apt (tested on ubuntu):
---
- name: Install Docker on Ubuntu
  hosts: all
  remote_user: ubuntu # Run Playbook as specific user
  become: true
  tasks:
    - name: Update apt
      apt:
        update_cache: yes
    - name: Install dependencies
      apt:
        name: ca-certificates,curl,gnupg,lsb-release
        state: present
    - name: Create Docker keyring directory
      file:
        path: /etc/apt/keyrings
        state: directory
    - name: Add Docker GPG key
      shell: curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    - name: Add Docker repository to apt sources
      shell: echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    - name: Update apt again
      apt:
        update_cache: yes
    - name: Install Docker
      apt:
        name: docker-ce,docker-ce-cli,containerd.io,docker-buildx-plugin,docker-compose-plugin
        state: present
    - name: Add ubuntu user to docker group
      user:
        name: ubuntu
        groups: docker
        append: yes
Start Docker-Compose file #
Run a Container from a docker-compose.yml file in a defined directory:
- name: Docker Compose up -d
  hosts: jklug
  remote_user: ubuntu # Run Playbook as specific user
  become: true
  become_user: ubuntu
  become_method: sudo # optional
  tasks:
    - name: Docker Compose up -d
      shell: |
        cd ~/Docker-Compose
        docker compose up -d        
Create and start Docker-Compose file #
This playbook creates a docker-compose.yml file for an apache webserver in a defined directory and starts the container:
---
- name: Create and start Docker Compose file
  hosts: jklug
  remote_user: ubuntu # Run Playbook as specific user
  become: true
  become_user: ubuntu
  become_method: sudo # optional
  gather_facts: no
  vars:
    directory_path: "~/docker-compose"
    file_name: "docker-compose.yml"
    config_contents: |
      version: '3'
      services:
        web:
          image: httpd:latest
          ports:
            - "8080:80"
          volumes:
            - ./html:/usr/local/apache2/htdocs/      
  tasks:
    - name: Create directory for docker-compose.yml
      file:
        path: "{{ directory_path }}"
        state: directory
        mode: '0755'
    - name: Create docker-compose.yml file
      file:
        path: "{{ directory_path }}/{{ file_name }}"
        state: touch
        mode: '0644'
    - name: Copy config into docker-compose.yml file
      lineinfile:
        path: "{{ directory_path }}/{{ file_name }}"
        line: "{{ config_contents }}"
    - name: Start Container
      shell: |
        cd {{ directory_path }}
        docker compose up -d        
Ansible Webserver #
Install and configure Nginx #
This playbook Installs Nginx, creates a custom configuration file in /etc/nginx/sites-available and links it to /etc/nginx/sites-enabled. It also deletes the default configuration file in the sites-enabled directory:
---
- name: Install and configure Nginx
  hosts: jklug  # Define hosts group from /etc/ansible/hosts
  remote_user: ubuntu # Run Playbook as specific user
  become: true  # sudo privileges
  tasks:
    - name: Apt update
      apt:
        update_cache: yes
    - name: Install Nginx
      apt:
        name: nginx     # Package Name
        state: present  # Insall
    - name: Create Nginx Config
      copy:
        content: |
          server {
              listen 80;
              listen [::]:80;
              server_name jklug.work;
              root /var/www/html;
              index index.html index.nginx-debian.html;
              location / {
                  try_files $uri $uri/ =404;
              }
          }          
        dest: /etc/nginx/sites-available/jklug.work
        owner: root
        group: root
        mode: 0644
    - name: Create symbolic link to sites-enabled
      file:
        src: /etc/nginx/sites-available/jklug.work
        dest: /etc/nginx/sites-enabled/
        state: link
    
    - name: Delete default config file
      file:
        path: /etc/nginx/sites-enabled/default
        state: absent
    - name: Restart Nginx
      service:
        name: nginx
        state: restarted
Ansible Playbook Parts #
List Directory Content #
---
- name: Run shell command
  hosts: all
  tasks:
    - name: List contents of /tmp directory
      shell: ls -la /tmp
      register: result  # Register shell output in result variable
      
    - name: Display output
      debug:
        var: result.stdout_lines # Display result variable on Ansible server
Ad hoc commands #
Files & Folder #
Create or delete file: 
ansible jklug -m ansible.builtin.file -a "dest=/tmp/file3 state=touch" 
ansible jklug -m ansible.builtin.file -a "dest=/tmp/file3 state=absent"
Create or delete folder: 
ansible jklug -m ansible.builtin.file -a "dest=/tmp/folder1 state=directory" 
ansible jklug -m ansible.builtin.file -a "dest=/tmp/folder1 state=absent"
Copy file “/tmp/file1” from server to “tmp” on host: 
ansible jklug -m ansible.builtin.copy -a "src=/tmp/file1 dest=/tmp/"
Copy file with permissions (“become” necessary for root permissions): 
ansible jklug -m ansible.builtin.copy -a "src=/tmp/file2 dest=/tmp/ mode=600 owner=root group=root" --become
System & Services #
Reboot host: 
ansible jklug -a "/sbin/reboot" --become
Install apt package, for example “nginx”:  
ansible jklug -m apt -a "name=nginx state=present" --become or 
ansible jklug -m ansible.builtin.apt -a "name=nginx state=present" --become 
Install apt package & run apt update: 
ansible jklug -m apt -a "name=nginx state=present update_cache=true"
Start, Stop & Restart Services: systemd 
ansible jklug -m systemd -a "name=nginx state=started" --become 
ansible jklug -m systemd -a "name=nginx state=stopped" --become 
ansible jklug -m systemd -a "name=nginx state=restarted" --become
Start, Stop & Restart Services: systemctl 
ansible jklug -m ansible.builtin.service -a "name=nginx state=started" --become 
ansible jklug -m ansible.builtin.service -a "name=nginx state=stopped" 
ansible jklug -m ansible.builtin.service -a "name=nginx state=restarted"