ITGix Team
ITGix Team
Passionate DevOps & Cloud Engineers
Reading time: 5 mins.
Last Updated: 08.01.2024

Table of Contents

What is Ansible?

In addition to the article about the PeerVPN installation and configuration, I will now show you a more advanced and quite ‘modern’ way to provision several servers and get your VPN client up really fast. You’ve probably heard of Ansible already. Well, one of its use cases is exactly what we need here: Configuration Manager. Many of us have experienced The Headache when we need to install, configure and then administer a whole environment. Yes, to repeat the same steps on hundreds of servers, where you have different OS distributions, application versions, and all kinds of dependencies, and all of that certainly leads to some problems.

Learn about Ansible

Read our comprehensive beginner guide on Ansible and learn all the basics!

Well, Ansible is here to help you with all that stuff. You can choose, set and customize anything that is required for a specific environment and suit its needs. So, let us start with an introduction to Ansible, its structure, and its components.
In my opinion, there are two approaches when you first start with Ansible. The first one is to read the official introduction to Ansible, which explains a lot about its structure, and then start with a simple playbook which you then extend to a role. Or the second one, where you make use of the Ansible Galaxy, which has a lot of community-provided roles open for use. Well, not every role is that much scalable and flexible as you want so you can simply combine both approaches, take an already-built role, and expand its functionalities. If you learn that quickly and all of that is boring, you can start building your own Ansible modules.

Now, let’s have a look at my PeerVPN role.

The directory structure:

    PeerVPN/                       # this hierarchy represents a "role"
        tasks/                         #
            main.yml                 # <-- the main. yml file task file includes install.yml + configure.yml
            install.yml                #
            configure.yml          #
   # <-- files for use with the template resource

            peervpn.service.j2  # <---templates end in .j2, moved in a certain location
            peervpn.conf.j2       #
        defaults/                      #
            main.yml                 # <-- default lower priority variables for this


- include: install.yml
- include: configure.yml
- service: name=peervpn state=started

Tasks/install.yml (NOTE the blankspaces at the start of the line):

- name: install dependencies
    name: "{{ item }}"
    state: latest
    - gcc
    - openssl-devel
    - openssl 
    - python-urllib3
    - pyOpenSSL
  when: ansible_os_family == "RedHat"
- name: downloading PeerVPN
  unarchive: src= dest=/tmp/ remote_src=yes
- name: moving extracted files [binary + config]
  command: /usr/bin/mv /tmp/peervpn-0-044/peervpn /usr/sbin/peervpn
  when: ansible_os_family == "RedHat"

Tasks/configure.yml (NOTE the blankspaces at the start of the line):

- name: create configuration directory
    path: /etc/peervpn
    state: directory
- name: write the peervpn config file
  template: src=../templates/peervpn.conf.j2 dest=/etc/peervpn/peervpn.conf
- name: create peervpn service
  template: src=../templates/peervpn.service.j2 dest=/usr/lib/systemd/system/peervpn.service
  when: ansible_os_family == "RedHat"


Description={{ unit_description }}
{% if vpn_initpeers -%}
Requires={{ unit_requires }}
{% endif -%}
After={{ unit_after }}
ConditionPathExists={{ unit_condition_path }}
Type={{ service_type }}
ExecStart={{ service_exec }}
PIDFile={{ service_pidfile }}
WantedBy={{ install_wantedby }}


port {{ peervpn_port }}
networkname {{ peervpn_networkname }}
psk {{ peervpn_password }}
enabletunneling {{ peevpn_enabletunneling }}
interface {{ peervpn_interface }}
ifconfig4 {{ peervpn_ip }}
{% if vpn_initpeers -%}
initpeers {{ vpn_initpeers }}
{% endif -%} 


peervpn_port: 7000
peervpn_networkname: MyPeerVPN
peervpn_password: VqmWsJje18/qqIOEN2+pLHugDtAcN5So03J0TL9wIQomAkQwcNR23SzM+m7cuJyCeyXXuHDLoKbzVbk176eDVLPS8wLNoWSiwPF2yQn57Q4vjRz3qjI51nhE3yEyKmHZgotQga4Uz0vyvTGjJRxtPJqZ/igDN0YmI25nwhsYsyi34c6pIcqPCOHYlILndCUh8AYk3hGNPc0lSnkxW/sY+Uo+5BU0K6nB1LYcMlXA9Ij0deU+eyRTfoVggKnpXdl5FikrELbAOyoo71F0PEjL73k5fmyGmsoEE1f4yvgUXOKOzsPUaf567SP4Hm+h/EpqkWMR7JGc5jBDsOlY52LTp5XaZNn+l+VEInCsFQiKgTW3zvzSmUcrPB3GqQ1KXknwfHmtCJe4SRiRZLKtSeTiPdPvXC0HBMk+KK9TNk45dDUi3/ougCIwavMPEedH7Gh5fvG5iNXjZ24tAAOTle2oBHVgO2Wq7llh2VwKkrzyYATuchusVLbHa98D0YR2wA==
peevpn_enabletunneling: "yes"
peervpn_interface: pvpn0
vpn_initpeers: false
unit_description: PeerVPN
unit_condition_path: /etc/peervpn/peervpn.conf
service_type: simple
service_exec: /usr/sbin/peervpn {{ unit_condition_path }}
service_pidfile: /var/run/peervpn/

So, now we have the role. The only thing left is the playbook and the inventory file. In fact the inventory file can be skipped(you can parse the hosts as arguments when you run the command) or your target hosts can be placed in the default Ansible inventory file in /etc/ansible/hosts. Here is an example playbook, which is provisioning 2 hosts:


- name: Configuring and installing PeerVPN init peer
  hosts: VPN_init_peer 
  remote_user: root
    - PeerVPN
- name: Configuring and installing PeerVPN other peers
  hosts: Host_1
  remote_user: root
    vpn_initpeers: "{{ groups.VPN_init_peer[0] }} {{ peervpn_port }}"
    - PeerVPN

 Note that I am using an inventory file for keeping the hosts:


To provision more hosts you need to simply add [Host_<2/3/4..etc>] and an IP in your inventory file and add accordingly paragraph(s) to the playbook file, with different peervpn_ip and hosts.
To run the above example, you need to have your public keys distributed on the target hosts because Ansible works over SSH by default. Now simply execute the following command in the dir where your playbook is: 
$ ansible-playbook playbook.yml -i inventory.yml

Leave a Reply

Your email address will not be published. Required fields are marked *

More Posts

In the dynamic world of serverless computing, securing your AWS Lambda function is crucial. However, one often neglected area is the security of containerized applications in Amazon Elastic Container Registry...
Note: The following example demonstrates upgrading a Kubernetes cluster from version 1.23 to 1.24. Replace the version numbers according to your specific setup. To ensure a seamless upgrade, it’s crucial...
Get In Touch
ITGix provides you with expert consultancy and tailored DevOps services to accelerate your business growth.
Newsletter for
Tech Experts
Join 12,000+ business leaders and engineers who receive blogs, e-Books, and case studies on emerging technology.