Use pty as ansible connection

Well, sometimes, we can only use pty to access machines.

For example, Jump Server only allowed use ssh pty to type command, get the result.

But, how can I deploy application via ansible?

There are two good news:

  1. pexpect can interactive with the ssh session.
  2. ansible can use custom connection plugin.

Let’s try it

Let’s start at directory ansible-pty

Firstly, we should create a connection plugin.

Put those code to ansible-pty/plugins/pty.:

import base64
import re

import pexpect
from ansible.plugins.connection import ConnectionBase
from ansible.utils.display import Display

display = Display()

class Connection(ConnectionBase):
    transport = 'pty'
    _mark = "=" * 10

    def __init__(self, *args, **kwargs):
        super(Connection, self).__init__(*args, **kwargs) = self._play_context.remote_addr
        self.user = self._play_context.remote_user

    # Main public methods
    def exec_command(self, cmd, in_data=None, sudoable=True):
        ''' run a command on the remote host '''

        super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)

        display.vvv(u"ESTABLISH SSH CONNECTION FOR USER: {0}".format(self._play_context.remote_user),

        self._process.write(" ; echo $? && echo ")

        lines = b''
        while True:
            line = self._process.readline()
            if line.strip() == self._mark.encode():
            lines += line
        regex_pattern = r"((?P<output>(?:.|\n)*)(?:\r|\n)+)?(?P<retcode>\d+)(?:\r|\n)+"
        matches = re.match(regex_pattern, lines.decode(), re.MULTILINE)

        stdout ='output')
        if not stdout:
            stdout = ''
        returncode ='retcode')
        returncode = int(returncode)
        stderr = ''


        return returncode, stdout, stderr

    def put_file(self, in_path, out_path):
        ''' transfer a file from local to remote '''

        super(Connection, self).put_file(in_path, out_path)

        display.vvv(u"PUT {0} TO {1}".format(in_path, out_path),
        self._process.write("base64 -d > " + out_path)
        self._process.write(' && printf "' + self._mark + '\n"')
        with open(in_path, 'rb') as fd:
            while True:
                raw_content =
                if len(raw_content) == 0:
                encoded_content = base64.b64encode(raw_content)

        while True:
            line = self._process.readline()
            if line.strip() == self._mark.encode():


    def fetch_file(self, in_path, out_path):
        ''' fetch a file from remote to local '''

        raise NotImplemented()

    def _connect(self):
        process = pexpect.spawn('ssh {user}@{host}'.format(user=self.user,
        process.setwinsize(40, 160)


        self._connected = True
        self._process = process

    def _eat_prompt(self, process):
        # after logged in
        # there is some prompt like `[[email protected] ~]# `
        process.expect('\~\](?:\#|\$) '.encode())

    def close(self):
        self._connected = False

And make ansible can load this plugin. Put code to ansible.cfg:

connection_plugins = connection_plugins:./plugins/

Try pty connection with inventory files:

# inventory
vultr ansible_connection=pty ansible_user=root


ansible -i inventory -m setup all

You can get the machine information:

vultr | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
        "ansible_all_ipv6_addresses": [
        "ansible_apparmor": {
            "status": "disabled"

Other thought

  • Not implement fetch_file
  • And, some times the command output will confilct with _mark
  • Stderr is redriect to stdout

You also can upload, download file via pty.

So the jump server, and pty audit is almost useless. You can alway find way to hide what are you do.

3 thoughts on “Use pty as ansible connection

  1. Teja

    Can something like this be used to do the privilege escalation like sudo su – user.
    My question is can I use a plugin to alter the become behavior of ansible so that I can run commands are scripts even after switching to the different user?

    Currently Ansible doesn’t support this behavior and in git hub they mentioned they are not going to.

    1. Chuan

      Really nice. I was able to use this ‘expect’ idea to login to Cisco IOS and issue commands for testing. This is for login in to a production device that takes interactive login which Ansible other connection module cannot handle. Thanks.


Leave a Reply to Robert Lu Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.