Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

I'm trying to use Ansible for automating the setup process of new server instances. One of the setup tasks changes the default SSH port, therefore requiring me to update the hosts list.

Is it possible to automate this by having Ansible fallback to a specified port if the connection could not be established to the default SSH port?

share|improve this question
add comment

2 Answers

You could try a local_action on the hosts to see if you can connect to the respective ports and register the one that succeeds and set that as a fact. You want to turn off gather facts because otherwise the setup module will fail when it tries to connect with the hosts which have already been reconfigured. Once you've done this play just add others below with gather_facts and all the rest.

- name: determine ssh port
  hosts: all
  gather_facts: false
    - name: test default ssh port
      local_action: wait_for port=22 timeout=5 host={{inventory_hostname}}
      register: default_ssh
      ignore_errors: True
    - name: set ansible_ssh_port to default
      set_fact: ansible_ssh_port = 22
      when: default_ssh.elapsed < 5
    - name: test ssh on high port
      local_action: wait_for port=5422 timeout=5 host={{inventory_hostname}}
      register: high_ssh
      ignore_errors: True
    - name: set ansible_ssh_port high
      set_fact: ansible_ssh_port=5422
      when: high_ssh.elapsed < 5

It was pointed out to me that this will blow out the time for playbooks where you use this. You could also set ansible_ssh_port in the vars section of plays that should only be run on hosts with reconfigured ssh port. e.g.

- name: change ssh ports
    - name: edit sshd_config
      lineinfile ..
      notify: restart ssh
     - name: restart ssh
       service: sshd state=restarted
- name: continue setup
    - ansible_ssh_port : 5422
share|improve this answer
add comment

@RichardSalts thanks for getting me started with this. I used nc to check ports which should be a lot faster. This is my bootstrap.xml:

Tested using ansible 1.5 (devel 3b8fd62ff9) last updated 2014/01/28 20:26:03

# Be sure to set the following variables for all hosts:
# vars:
#   oldsshport: 22
#   sshport: 555
# Might fail without setting remote_tmp = /tmp/ansible/$USER in your ansible.cfg. Also fix for directly below.
# Once host is setup most of the checks are skipped and works very quickly.
# Also, be sure to set non-standard shells in a different playbook later. Stick with /bin/bash until you can run apt install.
# Assumes root user has sshkey setup already. Not sure how to utilize the --ask-pass option. For now, use ssh-copy-id prior to running playbook on new host for root user (if needed).

# Test new ssh port
- name: ssh test nc {{ sshport }}
  local_action: shell nc -z -w5 {{ inventory_hostname }} {{ sshport }}
  register: nc_ssh_port
  failed_when: nc_ssh_port.stdout.find('failed') != -1
  changed_when: nc_ssh_port.stdout == ""
  ignore_errors: yes

# Set port to new port if connection success
- name: set ansible_ssh_port
  set_fact: ansible_ssh_port={{ sshport }}
  when: nc_ssh_port|success

# Fail back to old port if new ssh port fails
- name: ssh test nc port {{ oldsshport }}
  local_action: shell nc -z -w5 {{ inventory_hostname }} {{ oldsshport }}
  register: nc_ssh_default
  changed_when: nc_ssh_default.stdout == ""
  ignore_errors: yes
  when: nc_ssh_port|changed

# Set ansible to old port since new failed
- name: set ansible_ssh_port to {{ oldsshport }}
  set_fact: ansible_ssh_port={{ oldsshport }}
  when: nc_ssh_default|success and nc_ssh_port|changed

# Check if root user can ssh
- name: find user
  local_action: shell ssh -o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=5 -p {{ ansible_ssh_port }} root@{{ inventory_hostname }} exit
  register: ssh_as_root
  failed_when: ssh_as_root.stdout.find('failed') != -1
  changed_when: ssh_as_root.stderr.find('Permission denied') == -1

# If root user success, set this up to change later
- name: first user
  set_fact: first_user={{ ansible_ssh_user }}
  when: ssh_as_root|changed

# Set ssh user to root
- name: root user
  set_fact: ansible_ssh_user=root
  when: ssh_as_root|changed

# ANSIBLE FIX: /tmp/ansible isn't world-writable for setting remote_tmp = /tmp/ansible/$USER in ansible.cfg
- name: /tmp/ansible/ directory exists with 0777 permission
  file: path=/tmp/ansible/ owner=root group=root mode=0777 recurse=no state=directory
  changed_when: False
  sudo: yes

# Setup user accounts
- include: users.yml

# Set ssh user back to default user (that was setup in users.yml)
- name: ansible_ssh_user back to default
  set_fact: ansible_ssh_user={{ first_user }}
  when: ssh_as_root|changed

# Reconfigure ssh with new port (also disables non-ssh key logins and disable root logins)
- name: sshd.conf
  template: src=sshd_config.j2 dest=/etc/ssh/sshd_config owner=root group=root mode=0644
  register: sshd_config
  sudo: yes

# Force changes immediately to ssh
- name: restart ssh
  service: name=ssh state=restarted
  when: sshd_config|changed
  sudo: yes

# Use updated ssh port
- name: set ansible_ssh_port
  set_fact: ansible_ssh_port={{ sshport }}
  when: nc_ssh_port|changed
share|improve this answer
add comment

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.