Properly generate random ports

Hello everyone :slight_smile:

I would like to implement a way to generate multiple random and unique ports that will be registered as facts, and then used in the next playbooks to setup some services.

This is currently the non-elegant solution I’m using :

---
- name: Define ports for containers
  hosts: debian
  gather_facts: no
  vars:
    port: "{{ 65535 | random(start=1024) }}"
  tasks:
  - name: Generate random ports
    ansible.builtin.set_fact:
      service1_port: "{{ port }}"
      service2_port: "{{ port }}"
      service3_port: "{{ port }}"
      cacheable: yes
    run_once: yes

  - ansible.builtin.debug:
      msg: Your service1 port is "{{ service1_port }}", service2 is "{{service2_port}}" and "{{ service3_port }}"

My questions are :

  • How to avoid potential collision when generating those (unique) numbers ?
  • Since the “random” filter break the idempotent nature of Ansible, should I be worried about it ? I’m planning to deploy this on a single server, so I don’t think it’ll be. Nevertheless, don’t hesitate to suggest if there is a more appropriate method for this.

In advance, thanks for helping a new user :blush:

an attempt…

---
- name: Define ports for containers
  hosts: localhost
  gather_facts: no
  tasks:
    - name: Initialize ports list
      set_fact:
        ports: []

    - name: Generate unique random ports
      set_fact:
        ports: "{{ ports + [1024 + (38927 | random)] }}"
      when: "1024 + (38927 | random) not in ports"
      loop: "{{ range(3) }}"

    - set_fact:
        service1_port: "{{ ports[0] }}"
        service2_port: "{{ ports[1] }}"
        service3_port: "{{ ports[2] }}"
        cacheable: yes

    - debug:
        msg: 
          - "Your service1 port is {{ service1_port }}"
          - "Your service2 port is {{ service2_port }}"
          - "Your service3 port is {{ service3_port }}"

Ports in the range 0-1023 are referred to as well-known ports. They’re reserved for standard services, so you generally want to avoid using them for custom applications. That’s why we start with port 1024 . The port 49151 is the upper limit of the registered port range. Ports above 49151 up to 65535 are dynamic or private ports. While you could use them, they might be less predictable in terms of what other software could dynamically allocate and use them. So, the range between 1024 and 49151 is often preferred for manually assigning ports for custom applications.

Feel free to modify… for whatever ports you want?

3 Likes

You can “idempotentize” randomly generated values on a per-host basis by seeding your random generator with something idempotent about the hosts. For instance, we have a playbook that schedules cron jobs across all our hosts with times picked using such a seeded random() in order to spread the jobs out fairly evenly across the work day, like so:

    rng_minute: "{{ 59 | random(seed=ansible_hostname) }}"
    rng_hour: "{{ 19 | random(seed=(ansible_machine_id), start=7) }}"

Be aware that you need to seed all such “idempotent random” calls because you can’t guarantee order of evaluation etc.

I’m sure something similar could work for your hosts’ ports, especially combined with @IPvSean 's excellent advice with respect to port ranges.

3 Likes

Thanks to both of you for your help :grinning:

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