This September, RaspberryPi foundation has sold ten (10) Millions of unit. This small computer is amazing, you can setup very quickly several cool IoT projects. Since 2012, I bought all versions (B 256Mo, B 512Mo, B+, 2B, 3B) of RaspberryPi. I am a big fan of this product and you can find into this blog some projects I’ve made on top of this little computer. RaspberryPi version 3 comes with a wifi builtin interface, and this wifi interface can be configured as a Wifi Hotspot. I use it every day at home or at office. Today, I will show you how to configure a Wifi Hotspot with Ansible.

Howto setup a Hotspot Wifi w/ Ansible into a RaspberryPi 3

Prerequisites

For this lab, you need the following software :

  • a RaspberryPi version 3
    • with Raspbian and a ssh connection for pi user.
  • Ansible installed into our system.

Clone git project

First of all, clone the git repo project.

git clone git@github.com:gautric/ansible-raspberrypi.git
cd ansible-raspberrypi

Ansible playbook

To install and configure our Wifi Hotspot, we gonna use a YAML file used by Ansible. This file is realy simple to understand. It’s a list of tasks, and each task will perform only one action like :

  • installing package
  • coping file
  • enabling service
  • restart device

By default Raspbian comes with pi user w/ correct sudo grant. In this playbook, Ansible uses pi user (remote_user: pi) and performs all commands with sudo (become: true).

Warning

As you can see wpa_passphrase and ssid must be defined. You will see how to do it with command line directly.

---
- name: Configure WIFI Hotspot for RaspberryPi 3
  hosts: raspberrypi
  remote_user: pi
  vars:
    wpa_passphrase: XXXXXXX
    interface: wlan0
    ssid: YYYYYYYY
    ip_server: 10.0.0.1
    netmask: 255.255.255.0
    ip_dhcp_low: 10.0.0.2
    ip_dhcp_high: 10.0.0.15

  tasks:
    - name: hostapd install
      apt: name=hostapd state=present force=yes
      become: true

    - name: dnsmasq install
      apt: name=dnsmasq state=present force=yes
      become: true

    - name: dnsmasq.conf
      template:
        src: template/dnsmasq.conf.j2
        dest: /etc/dnsmasq.conf
        backup: true
      become: true

    - name: hostapd.conf
      template:
        src: template/hostapd.conf.j2
        dest: /etc/hostapd/hostapd.conf
        backup: true
      become: true

    - name: hostapd
      template:
        src: template/hostapd.j2
        dest: /etc/default/hostapd
        backup: true
      become: true

    - name: rc.local
      template:
        src: template/rc.local.j2
        dest: /etc/rc.local
        backup: true
      become: true

    - name: dnsmasq service
      service: name=dnsmasq enabled=yes state=started
      become: true

    - name: hostapd service
      service: name=hostapd enabled=yes state=started
      become: true

    - name: restart machine
      shell: sleep 2 && shutdown -r noww
      async: 1
      poll: 0
      become: true
      ignore_errors: true

We can also create template. Ansible will inject values into the template and copy the final file to destination.

interface={{ interface }}
hw_mode=g
channel=10
auth_algs=1
macaddr_acl=0
wpa=2
wpa_key_mgmt=WPA-PSK
wpa_pairwise=CCMP
rsn_pairwise=CCMP
wpa_passphrase={{ wpa_passphrase }}
ssid={{ ssid }}

Ansible Execution

To deploy configuration, Ansible provides ansible-playbook command line. You must configure hosts file, it includes all RaspberryPi hosts.

Ansible command line

$> ansible-playbook pi_wifi.yml --inventory-file=./hosts \
    -u pi  -v \
    -e "ssid=RASPBERRY wpa_passphrase=1234567891011"

Warning

Please change wpa_passphrase and ssid correctly.

Ansible command line output

The command line output should look like the same.


$> ansible-playbook pi_wifi.yml --inventory-file=./hosts -u pi  -v --diff -e "ssid=RASPBERRY wpa_passphrase=12345"
No config file found; using defaults

PLAY [raspberrypi] *************************************************************

TASK [setup] *******************************************************************
ok: [192.168.0.11]

TASK [hostapd install] *********************************************************
ok: [192.168.0.11] => {"cache_update_time": 0, "cache_updated": false, "changed": false}

TASK [dnsmasq install] *********************************************************
changed: [192.168.0.11] => {"cache_update_time": 0, "cache_updated": false, "changed": true, "stderr": "", "stdout": "Reading package lists...\nBuilding dependency tree...\nReading state information...\nThe following extra packages will be installed:\n  dns-root-data dnsmasq-base libmnl0 libnetfilter-conntrack3\nThe following NEW packages will be installed:\n  dns-root-data dnsmasq dnsmasq-base libmnl0 libnetfilter-conntrack3\n0 upgraded, 5 newly installed, 0 to remove and 0 not upgraded.\nInst libmnl0 (1.0.3-5 Raspbian:stable [armhf])\nInst libnetfilter-conntrack3 (1.0.4-1 Raspbian:stable [armhf])\nInst dns-root-data (2014060201+2 Raspbian:stable [all])\nInst dnsmasq-base (2.72-3+deb8u1 Raspbian:stable [armhf])\nInst dnsmasq (2.72-3+deb8u1 Raspbian:stable [all])\nConf libmnl0 (1.0.3-5 Raspbian:stable [armhf])\nConf libnetfilter-conntrack3 (1.0.4-1 Raspbian:stable [armhf])\nConf dns-root-data (2014060201+2 Raspbian:stable [all])\nConf dnsmasq-base (2.72-3+deb8u1 Raspbian:stable [armhf])\nConf dnsmasq (2.72-3+deb8u1 Raspbian:stable [all])\n", "stdout_lines": ["Reading package lists...", "Building dependency tree...", "Reading state information...", "The following extra packages will be installed:", "  dns-root-data dnsmasq-base libmnl0 libnetfilter-conntrack3", "The following NEW packages will be installed:", "  dns-root-data dnsmasq dnsmasq-base libmnl0 libnetfilter-conntrack3", "0 upgraded, 5 newly installed, 0 to remove and 0 not upgraded.", "Inst libmnl0 (1.0.3-5 Raspbian:stable [armhf])", "Inst libnetfilter-conntrack3 (1.0.4-1 Raspbian:stable [armhf])", "Inst dns-root-data (2014060201+2 Raspbian:stable [all])", "Inst dnsmasq-base (2.72-3+deb8u1 Raspbian:stable [armhf])", "Inst dnsmasq (2.72-3+deb8u1 Raspbian:stable [all])", "Conf libmnl0 (1.0.3-5 Raspbian:stable [armhf])", "Conf libnetfilter-conntrack3 (1.0.4-1 Raspbian:stable [armhf])", "Conf dns-root-data (2014060201+2 Raspbian:stable [all])", "Conf dnsmasq-base (2.72-3+deb8u1 Raspbian:stable [armhf])", "Conf dnsmasq (2.72-3+deb8u1 Raspbian:stable [all])"]}
The following extra packages will be installed:
  dns-root-data dnsmasq-base libmnl0 libnetfilter-conntrack3
The following NEW packages will be installed:
  dns-root-data dnsmasq dnsmasq-base libmnl0 libnetfilter-conntrack3
0 upgraded, 5 newly installed, 0 to remove and 0 not upgraded.

TASK [dnsmasq.conf] ************************************************************
changed: [192.168.0.11] => {"changed": true}
--- before
+++ after: dynamically generated
@@ -0,0 +1,2 @@
+interface=wlan0
+dhcp-range=10.0.0.2,10.0.0.5,255.255.255.0,12h


TASK [hostapd.conf] ************************************************************
changed: [192.168.0.11] => {"changed": true}
--- before
+++ after: dynamically generated
@@ -0,0 +1,11 @@
+interface=wlan0
+hw_mode=g
+channel=10
+auth_algs=1
+macaddr_acl=0
+wpa=2
+wpa_key_mgmt=WPA-PSK
+wpa_pairwise=CCMP
+rsn_pairwise=CCMP
+wpa_passphrase=1234567891011
+ssid=RASPBERRY


TASK [hostapd] *****************************************************************
changed: [192.168.0.11] => {"changed": true}
--- before: /etc/default/hostapd
+++ after: dynamically generated
@@ -7,12 +7,12 @@
 # file and hostapd will be started during system boot. An example configuration
 # file can be found at /usr/share/doc/hostapd/examples/hostapd.conf.gz
 #
-#DAEMON_CONF=""
+DAEMON_CONF="/etc/hostapd/hostapd.conf"

 # Additional daemon options to be appended to hostapd command:-
-# 	-d   show more debug messages (-dd for even more)
-# 	-K   include key data in debug messages
-# 	-t   include timestamps in some debug messages
+#         -d   show more debug messages (-dd for even more)
+#         -K   include key data in debug messages
+#         -t   include timestamps in some debug messages
 #
 # Note that -B (daemon mode) and -P (pidfile) options are automatically
 # configured by the init.d script and must not be added to DAEMON_OPTS.


TASK [rc.local] ****************************************************************
changed: [192.168.0.11] => {"changed": true}
--- before: /etc/rc.local
+++ after: dynamically generated
@@ -17,4 +17,12 @@
   printf "My IP address is %s\n" "$_IP"
 fi

+ifconfig wlan0 down
+ifconfig wlan0 10.0.0.1 netmask 255.255.255.0 up
+#ifconfig wlan0 inet6 add fc00::7262:7069:0003/7
+iwconfig wlan0 power off
+
+service dnsmasq restart
+service hostapd restart
+
 exit 0


TASK [dnsmasq service] *********************************************************
changed: [192.168.0.11] => {"changed": true}

TASK [hostapd service] *********************************************************
changed: [192.168.0.11] => {"changed": true, "msg": "service state changed"}

PLAY RECAP *********************************************************************
192.168.0.11               : ok=10   changed=8    unreachable=0    failed=0

Et voila !!! If you reboot your raspberrypi, you should find a new Wifi Hotspot called RASPBERRY

Conclusion

I’ve created this Ansible playbook very quickly. The Ansible integration with a RaspberryPi is very easy because you just need a SSH connection to connect with. I think, I will continue to use Ansible with some of my RaspberryPi devices.