Server Fault is a question and answer site for system and network administrators. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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

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
  vars:
    custom_ssh_port: 222
  tasks:
    - 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={{custom_ssh_port}} timeout=5 host={{inventory_hostname}}
      register: high_ssh
      when: default_ssh.elapsed >= 5
      ignore_errors: true
    - name: set ansible_ssh_port high
      set_fact: ansible_ssh_port={{custom_ssh_port}}
      when: default_ssh.elapsed >= 5 and 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
  tasks:
    - name: edit sshd_config
      lineinfile ..
      notify: restart ssh
   handlers:
     - name: restart ssh
       service: sshd state=restarted
- name: continue setup
  vars:
    - ansible_ssh_port : 5422
  tasks:
    ...
share|improve this answer
    
Your strategy of port testing in conjunction with setting facts seems like an ideal approach for these cases. Thanks!!! – Jay Taylor Apr 30 '15 at 0:01

@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

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?

I also needed similar functionality, so I forked and patched the Ansible ssh plugin hoping that Ansible Inc. would adopt it; they didn't. It tests non-std ssh port specifications to see if they are open and reverts to the default ssh port if not. It's a very small patch, available at https://github.com/crlb/ansible.

share|improve this answer

Since you probably deploy your ssh config early, you really should keep this simple. Just configure your inventory with the target ansible_ssh_port and use -e when deploying your ssh configuration for the first time:

ansible-playbook bootstrap_ssh.yml -e 'ansible_ssh_port=22'

Note that ansible_ssh_port is deprecated in 2.0 (superseded by ansible_port)

share|improve this answer

I came up with a robust idempotent task list to put in a role to change the SSH port and handle connecting to the right port without having to change your inventory file. I've posted the details on my blog: https://dmsimard.com/2016/03/15/changing-the-ssh-port-with-ansible/

share|improve this answer
1  
We really do prefer answers to contain content not pointers to content which can easily be lost. – Iain Mar 16 at 14:28

Your Answer

 
discard

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.