Use Ansible facts to convert netplan from DHCP to static

In my network I keep track of all static assigned IP addresses in my DHCP config. So I’ll connect a new device to my network and let DHCP give it a temp IP and arpwatch emails me its MAC address, then I decide what the perm address should be and add that to my DHCP config so the device reconnects at its new assigned IP.

But I prefer to configure servers with their static IP config so they’ll come up fine even if they come up faster than the DHCP server. So I’ll change the netplan to match the address the server already got from DHCP.

I saw solutions using a full template for the netplan config file but I decided to try the role by mrlesmithjr/ansible-netplan (GitHub - mrlesmithjr/ansible-netplan) and I’m pretty close but can’t figure out the last bit.

I created my own task with the following:

- name: DHCP to static
  ansible.builtin.include_role:
    name: mrlesmithjr.netplan
  vars:
    netplan_enabled: true
    netplan_config_file: /etc/netplan/00-installer-config.yaml
    netplan_configuration:
      network:
        version: 2
        ethernets:
          enp3s0:
            addresses:
              - "{{ ansible_default_ipv4.address }}/24"
            routes:
              - to: default
                via: "{{ ansible_default_ipv4.gateway }}"
            nameservers:
              addresses:
                - 192.168.0.70
                - 192.168.0.72
              search:
                "{{ ansible_dns.search }}"
    netplan_remove_existing: true
    netplan_check_install: true
    netplan_apply: true

The last bit is that I’d like to use the ansible_default_ipv4.interface to fill in the interface device name, but couldn’t figure out how to build that into the above.

Can anyone suggest a way to do that?

1 Like

Hello @sprior,

You could achieve that If you previously set a dict on a fact for your network config and then you pass the whole dict to the role, something like this:

- name: PLAYBOOK
  gather_facts: true
  hosts: localhost
  vars:
    my_iface: "{{ ansible_default_ipv4.interface }}"
    my_iface_data:
      addresses:
        - "{{ ansible_default_ipv4.address }}/24"
      routes:
        - to: default
          via: "{{ ansible_default_ipv4.gateway }}"
      nameservers:
        addresses:
          - 192.168.0.70
          - 192.168.0.72
        search:
          "{{ ansible_dns.search | default('not_found_in_facts') }}"
  tasks:

    - name: Task 1. prepare network dict
      ansible.builtin.set_fact:
        my_network:
          version: 2
          ethernets: "{{ { my_iface:my_iface_data } }}"

    - name: Task 2. Showing the new dict
      ansible.builtin.debug:
        var: my_network

    - name: Task 3. Build the actual netplan config file
      ansible.builtin.include_role:
        name: mrlesmithjr.netplan
      vars:
        netplan_enabled: true
        netplan_config_file: /etc/netplan/00-installer-config.yaml
        netplan_configuration:
          network: "{{ my_network }}"
        netplan_remove_existing: true
        netplan_check_install: true
        netplan_apply: true

I haven’t tested all the tasks since I didn’t wanted to install the role on my local machine, but I believe it should work. Debugging the my_network fact gives something like this:

eth0:
  addresses:
    - "172.23.188.206/24"
  nameservers:
    addresses:
      - "192.168.0.70"
      - "192.168.0.72"
    search: "not_found_in_facts"
  routes:
    - to: "default"
      via: "172.23.176.1"
version: 2

Hope it helps!

Cheers

Thanks, that seems to have worked! It also gives me a technique to study and get familiar with, I really appreciate the time you put into that.

1 Like

The form I went with is:

- name: Task 1. prepare network dict
  vars:
    my_iface: "{{ ansible_default_ipv4.interface }}"
    my_iface_data:
      addresses:
        - "{{ ansible_default_ipv4.address }}/24"
      routes:
        - to: default
          via: "{{ ansible_default_ipv4.gateway }}"
      nameservers:
        addresses:
          - 192.168.0.70
          - 192.168.0.72
        search:
          "{{ ansible_dns.search | default('not_found_in_facts') }}"
  ansible.builtin.set_fact:
    my_network:
      version: 2
      ethernets: "{{ { my_iface:my_iface_data } }}"

- name: Task 2. Showing the new dict
  ansible.builtin.debug:
    var: my_network

- name: Task 3. Build the actual netplan config file
  ansible.builtin.include_role:
    name: mrlesmithjr.netplan
  vars:
    netplan_enabled: true
    netplan_config_file: /etc/netplan/00-installer-config.yaml
    netplan_configuration:
      network: "{{ my_network }}"
    netplan_remove_existing: true
    netplan_check_install: true
    netplan_apply: true
1 Like

You’re welcome Steve! Also glad that you can you use my answer to learn Ansible further, not only to get your issue sorted :wink:

PS: I forgot to mention before; on this scenario we could also use the combine() plugin filter, It comes really handy when modifying data on existing dictionaries.

https://docs.ansible.com/ansible/latest/collections/ansible/builtin/combine_filter.html

See you around!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.