In my previous post related to Setting up and executing basic Ansible playbook, we have seen what Ansible is, learned how to setup Ansible and saw how to write a basic playbook and execute it.
In this post we will be looking into how to build our playbook when there is no python installed on the target machines. By default Ansible modules require python to be present in the target machines, since they are all written in python.
So first of all let's understand why this may be required and then we will proceed to the different ways to achieve that. There are mainly two case where this might be required:
- The first case is installing python-simplejson on older (Python 2.4 and before) hosts that need it as a dependency to run modules, since nearly all core modules require it.
- Another is speaking to any devices such as routers that do not have any Python installed. In any other case, using the shell or command module is much more appropriate.
So how do we achieve this in Ansible?
By using the raw module. It executes a low-down and dirty SSH command. To know more about the raw module in Ansible please visit here.
Note
If using raw from a playbook, you may need to disable fact gathering using gather_facts: no if you’re using raw to bootstrap python onto the machine.
- If you want to execute a command securely and predictably, it may be better to use the command or shell modules instead.
- the environment keyword does not work with raw normally, it requires a shell which means it only works if executable is set or using the module with privilege escalation (become).
Now let's write the same playbook as we did in the previous post in step-4 for creating a new user in the remote machine and enable ssh connection for the new user using the raw module. We are not going to use the raw module to simply bootstrap Python dependencies, rather we will see how to write the playbook so that it doesn't depend on python in the remote machines. So let's check out the playbook.
Note
For the below Ansible playbook I have assumed that we have a ansible_user and ansible_ssh_pass to run the playbook to the remote machines. Sample host file will be as follows,
192.0.2.50(your remote machine IP address or host name) ansible_user=<someuser> ansible_ssh_pass=<your_password>
Playbook1
- ---
-
- - hosts: all
-
- gather_facts: False
-
- become: True
- vars:
-
- user: user1
-
- passw: Password1$
- tasks:
- - name: Check if user exists
-
- register: user1_exists
-
- raw: getent passwd {{user}}
-
- ignore_errors: true
- - name: delete user1 user
-
- raw: userdel {{user}} -r
-
- when: user1_exists|success
- - name: add user user1
-
- raw: useradd -m {{user}}
- - name: add user password
-
- raw: usermod --password $(echo {{passw}} | openssl passwd -1 -stdin) {{user}}
-
- - name: make dir .ssh in crvp home
-
- raw: mkdir ../{{user}}/.ssh
- - name: change ownership to crvp user
-
- raw: chown {{user}}:{{user}} ../{{user}}/.ssh
- - name: give required permission to .ssh
-
- raw: chmod 777 ../{{user}}/.ssh/
- - name: create authorized_keys
-
- raw: touch ../{{user}}/.ssh/authorized_keys
- - name: change ownership to crvp user
-
- raw: chown {{user}}:{{user}} ../{{user}}/.ssh/authorized_keys
- - name: give required permission to authorized_keys
-
- raw: chmod 666 ../{{user}}/.ssh/authorized_keys
The above playbook is pretty self explanatory. The first thing to notice is the gather_facts which is set to false, since we are using ansible raw module.
In the variable section we have declared the user name and the password for the user which we are going to create in our remote machines.
In the task list, the 1st and the 2nd task is using a conditional and Jinja2 expressions. We have used condition and ignore_errors in the 1st task to make sure it gets executed even if there is an error response and the 2nd task will get executed depending on the response we get from task 1.To more about conditionals in Ansible please check here.
In the next script I have finally copied the ssh key from our Ansible host machine into authorized_keys in the remote machine, so that we can SSH into those remote machines using the new user and the new key.
Note
At the time when I was wrote these scripts, the become_user was not working due to a bug. Thus I had to break my script into two scripts and do the file copy operation in the 2nd scripts as the newly created user. Maybe it's fixed now; I will check once and update.
Playbook2
- — -
-
- - hosts: all
- gather_facts: False
- vars:
-
- user: user1
-
- passw: Password1$
- tasks:
- - name: local scp — copy key file into authorized_keys
-
- local_action: “command scp ../.ssh/user1_user.pub {{ansible_user}}@{{inventory_hostname}}:../{{user}}/.ssh/authorized_keys”
- - name: give required permission to .ssh for user
-
- raw: chmod 0700 ../{{user}}/.ssh/
-
- become: true
- - name: give required permission to authorized_keys for user
-
- raw: chmod 0600 ../{{user}}/.ssh/authorized_keys
-
- become: true
In this playbook as we mentioned we are copying the public key for ssh into the authorized_keys of the newly created user. For file copy I have used local_action module, which helps us to run any command in the ansible host itself rather than in the remote machines. Inside the local_action we have used scp command which is used to copy file over SSH so here it copies the ../.ssh/user1_user.pub file to ../user1/.ssh/authorized_keys.
To know more about local_action check here.
So after running these two scripts in any number of remote machines (mentioned in the host file), we should be able to ssh onto those remote machines with the new user that we have created.
Hope this post will be helpful for those who want to write playbook without python dependency in the remote machines.