Primer: Basic Working with Ansible

At a Carlsbad Full Stack Meetup in 2013 I found out about Ansible, a DevOps software method. I initially reviewed information about it but quickly discovered it had some bugs that made it difficult to learn at the time.

In early April of this year I took a closer look at Ansible again and found that it was working much better and decided to become more familiar with the basics. That led to this blog post today.

Working with Ansible requires patience and some knowledge of Unix and Linux operating systems, some Bash and Python programming skills using a command line interface. Know how to create, install, and use public and private encrypted keys for working with SSH.

I used my Mac, my Raspberry Pi, and an Amazon Cloud Server instance for today’s article. You’ll need to be familiar with those technologies to proceed. You may be eligible for an Amazon Free Tier that you can work with using Ansible and other technologies to expand your skill set.

There is a listing of references near the end of this article I found during the preparation of this material that you may find helpful too. You may wish to review that information now.

Keep in mind that there is very much more to Ansible than what I present. Use your Internet search engine wisely and expect to make mistakes; that’s part of the learning process.

To begin, you only need to install Ansible and Python 2.7.x on your local computer. See the Reference section for that process for your computer operating system. Later you perhaps may want to add Amazon Command Line Interface tools if you go the Amazon Cloud Instance route. 🙂

Using the command line interface on your computer create these files:

Inventory: This is the place to describe the ansible_hosts file shown below. I locate mine in my home directory on whatever host I install Ansible.

# http://docs.ansible.com/intro_inventory.html
# Default location of ‘hosts’ file is at /etc/ansible/hosts
# That location can be changed by Ansible’s config file

# Local host
localhost ansible_ssh_host=127.0.0.1 ansible_ssh_port=22 ansible_ssh_user=[host_username]

# Don’s Raspberry Pi
[rpi]
rpi ansible_ssh_host=xx.xx.xx.xx ansible_ssh_port=22 ansible_ssh_user=[host_username]

# Verify AWS Server IP is current
[don_aws_server]
don_aws_server ansible_ssh_host=xx.xx.xx.xx ansible_ssh_port=22 ansible_ssh_user=[host_username]

Ansible Config: This is a place where your can define many variables and paths used by Ansible. The normal default installation provides a starting point so look there and explore.

# http://docs.ansible.com/intro_configuration.html
# The Ansible Config default pathname is usually found at /etc/ansible/config.cfg

# You can set the Playbook path to whatever you like
# I created a Playbook directory at ~/.ansible/playbooks for my Playbooks

SSH Config: I use this file to assist the keys in working with the various ansible hosts.

# http://ivetetecedor.com/how-to-set-up-an-ssh-config-file-in-mac-os-x/
# The default location is usually found at .ssh/config.cfg

# Localhost
Host 127.0.0.1
IdentityFile ~/.ssh/[that_host_key_here]
IdentitiesOnly yes
Port 22

# Raspberry Pi:
Host xx.xx.xx.xx
IdentityFile ~/.ssh/[that_host_key_here]
IdentitiesOnly yes
Port 22

# Don’s AWS Server:
Host xx.xx.xx.xx
IdentityFile ~/.ssh/[that_host_key_here]
IdentitiesOnly yes
Port 22

For two of my Ansible Playbook examples you’ll need to create some directories for the Bash script and Python program and their output as text files. If you don’t want to use playbooks, you can skip these steps.

These programs shown below work on my Mac. The versions that work on my Raspberry Pi and AWS EC2 instance require different paths and perhaps syntax as well. Consider those a learning experience as it was for me.

To allow Ansible to communicate with a Mac, you’ll need to enable the System Preferences->Sharing Pane->Remote Login as shown in the image below:

mac_system_sharing_pref

Scripts: The Bash and Python scripts execute when called and produce output written to files on the particular host. The link underneath each bold playbook name is to the actual online Script text file.

Bash Script: I create a directory in my Home directory named, “mybash_scripts” and “cd” to that location as shown below:

$ cd mybash_scripts
$ ls -la
total 16
drwxr-xr-x 4 dwlarson staff 136 Apr 28 15:07 .
drwxr-xr-x+ 119 dwlarson staff 4046 May 17 09:19 ..
-rwxr-xr-x 1 dwlarson staff 305 May 2 15:10 hello_script.sh
-rwxr-xr-x 1 dwlarson staff 107 Apr 28 14:59
$

I then create the Bash script, “hello_script.sh” and set it’s file permissions to be executable. Otherwise it won’t run when invoked.

Link

#!/bin/bash

# Define a timestamp function http://stackoverflow.com/questions/17066250/create-timestamp-variable-in-bash-script
timestamp() {
date +”%Y-%m-%d_%H-%M-%S”
}

file=”~/tmp/my_output.txt”
[[ -f “$file” ]] && rm -f “$file”
echo ‘Hello from’ $( hostname)’!’ $(timestamp) > ~/tmp/my_output.txt

Bash Script Output

Link

$ cat ~/tmp/my_output.txt
Hello from [host_username]! 2015-05-17_11-43-26
$

Python Script:  I create a directory in my Home directory named, “mypython_scripts” and “cd” to that location as shown below:

$ cd mypython_scripts
$ ls -la
total 16
drwxr-xr-x 5 dwlarson staff 170 May 3 15:55 .
drwxr-xr-x+ 119 dwlarson staff 4046 May 17 09:19 ..
-rw-r–r– 1 dwlarson staff 89 May 17 10:29 python_output.txt
-rwxr-xr-x 1 dwlarson staff 316 May 17 10:29 python_time_to_file.py
$

I then create Python script, “python_time_to_file.py” and set it’s file permissions to be executable. Otherwise it won’t run when invoked. Note: I found that different using Python on different Operating Systems require different syntax. These worked on my Mac.

Link

#!/usr/bin/python

# Import module datetime
import time
import datetime

# Import system
import sys

# On Mac us this next command
sys.stdout = open(‘/Users/your-system_username_here/mypython_scripts/python_output.txt’, ‘w’)

# Now print the time
now = datetime.datetime.now()

print “Current date and time using str method of datetime object: ->”
print str(now)

Python Script Output

Link

$ cat python_output.txt
Current date and time using str method of datetime object: ->
2015-05-17 10:29:15.548350
$

Note: You can run these scripts by themselves on the Command Line to verify that they produce the result you expect.

At this point you should consider backing up your system and taking a short break. The rest of the article shows you how to run some Ansible commands and review the results as output. Also the Playbooks will be shown along with their outputs on a properly established directory structure and other files created properly.

List of Simple Ansible Commands: You may select each line as you wish and execute in the command line window.

# Working Ansible commands (Working as of 05/17/2014)
# Turn on ‘remote Login’ on MacOS X System Preferences
# Might need to cd to .ansible before running commands
# To verify the appropriate Ansible software is installed, use command below
# ansible –version

# Ping Local computer
ansible -c local -m ping all

# Ping Remote computer
ansible -c local -m ping don_aws_server

# Identify Local computer
ansible localhost -a “whoami”

# Identify Remote servers (if any)
ansible all -a “whoami”

# Setup Local computer
ansible -m setup localhost

# Setup Remote servers
ansible -m setup don_aws_server -i ~/[path_to_inventory_file] -u [host_username]

# How long has host been running since last restart?
ansible all -m command -a “uptime”

# What is the user name on host?
ansible -m shell -a ‘uname -a’ localhost

ansible -m shell -a ‘uname -a’ [host_username]

Sample Command Line Examples (from above list): Obviously your Host names and particular security factors will be different than mine are:

ansible -c local -m ping all

$ ansible -c local -m ping all
powerstrip | success >> {
“changed”: false,
“ping”: “pong”
}

localhost | success >> {
“changed”: false,
“ping”: “pong”
}

don_aws_server | success >> {
“changed”: false,
“ping”: “pong”
}

rpi | success >> {
“changed”: false,
“ping”: “pong”
}

$

ansible -c local -m ping don_aws_server

$ ansible -c local -m ping don_aws_server
don_aws_server | success >> {
“changed”: false,
“ping”: “pong”
}

$

ansible -m setup don_aws_server (partial and truncated output results)

$ ansible -m setup don_aws_server
don_aws_server | success >> {
“ansible_facts”: {
“ansible_date_time”: {
“date”: “2015-05-17”,
“day”: “17”,
“epoch”: “1431885414”,
“hour”: “17”,
“iso8601”: “2015-05-17T17:56:54Z”,
“iso8601_micro”: “2015-05-17T17:56:54.103953Z”,
“minute”: “56”,
“month”: “05”,
“second”: “54”,
“time”: “17:56:54”,
“tz”: “UTC”,
“tz_offset”: “+0000”,
“weekday”: “Sunday”,
“year”: “2015”
},
“changed”: false
}

$

ansible all -m command -a “uptime”

$ ansible all -m command -a “uptime”
localhost | success | rc=0 >>
11:04 up 1:47, 4 users, load averages: 1.18 1.11 1.11

powerstrip | success | rc=0 >>
11:04:33 up 20 days, 19:18, 1 user, load average: 0.20, 0.12, 0.08

don_aws_server | success | rc=0 >>
18:04:34 up 16 min, 1 user, load average: 0.00, 0.01, 0.05

rpi | success | rc=0 >>
11:04:41 up 18 min, 1 user, load average: 0.00, 0.01, 0.05

$

Ansible Playbooks: These are more sophisticated examples for Ansible. They require more work to understand and establish. They reveal just the beginning of the power playbook offer for working with remote computers.

For each of the three playbook examples, I’ll show the playbook instructions first and then the resulting output next. The link underneath each bold playbook name is to the actual online text file.

helloworld.yml

Link


– name: Hello World!
hosts: all

tasks:

– shell: echo “Hi! AWX is working”

# http://docs.ansible.com/debug_module.html
# https://gist.github.com/analytically/5760207
– debug: msg=”Host {{ inventory_hostname }} has address {{ ansible_default_ipv4.address }}”

# Execute a shell script
# I verified this script is executing properly depending on Ansible host selected
– script: ~/mybash_scripts/hello_script.sh

helloworld.yml Output

Link

$ ansible-playbook -l localhost .ansible/playbooks/awx/helloworld.yml

PLAY [Hello World!] ***********************************************************

GATHERING FACTS ***************************************************************
ok: [localhost]

TASK: [shell echo “Hi! AWX is working”] ***************************************
changed: [localhost]

TASK: [debug msg=”Host {{ inventory_hostname }} has address {{ ansible_default_ipv4.address }}”] ***
ok: [localhost] => {
“msg”: “Host localhost has address xx.xx.xx.xx”
}

TASK: [script ~/mybash_scripts/hello_script.sh] *******************************
changed: [localhost]

PLAY RECAP ********************************************************************
localhost : ok=4 changed=2 unreachable=0 failed=0

$

helloworld_rpi_only.yml

Link


– name: Hello World for Rpi Only!
hosts: rpi
vars:
contents: “{{ lookup(‘file’, ‘/Users/local_system_username_here/tmp/received_temp/rpi/home/remote_system_username_here/tmp/my_output.txt’) }}”
python_contents: “{{ lookup(‘file’, ‘/Users/local_system_username_here/mypython_scripts/rpi/home/remote_system_username_here/mypython_scripts/python_output.txt’) }}”

tasks:

– shell: echo “Hi! AWX is working”

# http://docs.ansible.com/debug_module.html
# https://gist.github.com/analytically/5760207
– debug: msg=”Host {{ inventory_hostname }} has address {{ ansible_default_ipv4.address }}”

# Execute a shell script
# I verified this script is executing properly depending on Ansible host selected
– script: ~/mybash_scripts/hello_script.sh

# Fetch file from remote host
# Review file at cat /Users/local_system_username_here/tmp/received_temp/rpi/home/remote_system_username_here/tmp/my_output.txt
– fetch: src=/home/remote_system_username_here/tmp/my_output.txt dest=~/tmp/received_temp/ fail=yes

# Display contents at Fetched file
– debug: msg=”Content of file is -> {{ contents }}”

# Execute a python script
# I verified this script is executing properly depending on Ansible host selected
– command: ~/mypython_scripts/python_time_to_file.py

# Fetch file from remote host
# Review file at cat /Users/local_system_username_here/mypython_scripts/rpi/home/remote_system_username_here/mypython_scripts/python_output.txt
– fetch: src=/home/remote_system_username_here/mypython_scripts/python_output.txt dest=~/mypython_scripts/ fail=yes

# Display python_contents at Fetched file
– debug: msg=”Content of file is {{ python_contents }}”

helloworld_rpi_only.yml Output

Link

$ ansible-playbook -l rpi .ansible/playbooks/awx/helloworld_rpi_only.yml

PLAY [Hello World for Rpi Only!] **********************************************

GATHERING FACTS ***************************************************************
ok: [rpi]

TASK: [shell echo “Hi! AWX is working”] ***************************************
changed: [rpi]

TASK: [debug msg=”Host {{ inventory_hostname }} has address {{ ansible_default_ipv4.address }}”] ***
ok: [rpi] => {
“msg”: “Host rpi has address xx.xx.xx.xx”
}

TASK: [script ~/mybash_scripts/hello_script.sh] *******************************
changed: [rpi]

TASK: [fetch src=/home/remote_system_username_here/tmp/my_output.txt dest=~/tmp/received_temp/ fail=yes] ***
changed: [rpi]

TASK: [debug msg=”Content of file is -> {{ contents }}”] **********************
ok: [rpi] => {
“msg”: “Content of file is -> Hello from raspberrypi! 2015-05-17_11-45-10”
}

TASK: [command ~/mypython_scripts/python_time_to_file.py] *********************
changed: [rpi]

TASK: [fetch src=/home/remote_system_username_here/mypython_scripts/python_output.txt dest=~/mypython_scripts/ fail=yes] ***
changed: [rpi]

TASK: [debug msg=”Content of file is {{ python_contents }}”] ******************
ok: [rpi] => {
“msg”: “Content of file is Current date and time using str method of datetime object: ->\n2015-05-17 11:45:12.345923”
}

PLAY RECAP ********************************************************************
rpi : ok=9 changed=5 unreachable=0 failed=0

$

helloworld_dons_aws_server_only.yml

Link


– name: Hello World for Don’s AWS Only!
hosts: don_aws_server
vars:
contents: “{{ lookup(‘file’, ‘/Users/local_system_username_here/tmp/received_temp/don_aws_server/home/remote_system_username_here/tmp/my_output.txt’) }}”
python_contents: “{{ lookup(‘file’, ‘/Users/local_system_username_here/tmp/received_temp/don_aws_server/home/remote_system_username_here/mypython_scripts/python_output.txt’) }}”

tasks:

– shell: echo “Hi! AWX is working”

# http://docs.ansible.com/debug_module.html
# https://gist.github.com/analytically/5760207
– debug: msg=”Host {{ inventory_hostname }} has address {{ ansible_default_ipv4.address }}”

# Execute a shell script
# I verified this script is executing properly depending on Ansible host selected
– script: ~/mybash_scripts/hello_script.sh

# Fetch file from remote host
# Review file at cat /Users/local_system_username_here/tmp/received_temp/don_aws_server/home/remote_system_username_here/tmp/my_output.txt
– fetch: src=/home/remote_system_username_here/tmp/my_output.txt dest=/Users/local_system_username_here/tmp/received_temp/ fail=yes

# Display contents at Fetched file
# Verify via cat /home/remote_system_username_here/tmp/my_output.txt
– debug: msg=”Content of file is -> {{ contents }}”

# Execute a python script
# Verify via cat /Users/local_system_username_here/tmp/received_temp/don_aws_server/home/remote_system_username_here/mypython_scripts/python_output.txt
# I verified this script is executing properly depending on Ansible host selected
– command: /home/remote_system_username_here/mypython_scripts/python_time_to_file.py

# Fetch file from remote host
# Review file at cat /Users/local_system_username_here/mypython_scripts/rpi/home/pi/mypython_scripts/python_output.txt
– fetch: src=/home/remote_system_username_here/mypython_scripts/python_output.txt dest=/Users/local_system_username_here/tmp/received_temp/ fail=yes

# Display python_contents at Fetched file
– debug: msg=”Content of file is {{ python_contents }}”

helloworld_dons_aws_server_only.yml Output

Link

$ ansible-playbook -l don_aws_server .ansible/playbooks/awx/helloworld_dons_aws_server_only.yml

PLAY [Hello World for Don’s AWS Only!] ****************************************

GATHERING FACTS ***************************************************************
ok: [don_aws_server]

TASK: [shell echo “Hi! AWX is working”] ***************************************
changed: [don_aws_server]

TASK: [debug msg=”Host {{ inventory_hostname }} has address {{ ansible_default_ipv4.address }}”] ***
ok: [don_aws_server] => {
“msg”: “Host don_aws_server has address xx.xx.xx.xx”
}

TASK: [script ~/mybash_scripts/hello_script.sh] *******************************
changed: [don_aws_server]

TASK: [fetch src=/home/ubuntu/tmp/my_output.txt dest=/Users/local_system_username_here/tmp/received_temp/ fail=yes] ***
changed: [don_aws_server]

TASK: [debug msg=”Content of file is -> {{ contents }}”] **********************
ok: [don_aws_server] => {
“msg”: “Content of file is -> Hello from ip-xx.xx.xx.xx! 2015-05-17_18-48-04”
}

TASK: [command /home/ubuntu/mypython_scripts/python_time_to_file.py] **********
changed: [don_aws_server]

TASK: [fetch src=/home/remote_system_username_here/mypython_scripts/python_output.txt dest=/Users/local_system_username_here/tmp/received_temp/ fail=yes] ***
changed: [don_aws_server]

TASK: [debug msg=”Content of file is {{ python_contents }}”] ******************
ok: [don_aws_server] => {
“msg”: “Content of file is Current date and time using str method of datetime object: ->\n2015-05-17 18:48:07.591403”
}

PLAY RECAP ********************************************************************
don_aws_server : ok=9 changed=5 unreachable=0 failed=0

$

This blog post was three weeks in the making. The scripts and output on this webpage won’t convey the whole process as clearly the same as properly setting up the material and running them on the command line.

The best part I discovered about playbooks are their ability to perform complicated scripts on remote hosts and return results to the playbook while its executing, possibly initiating other actions depending on returned values!

As the Internet of Things Age heats up, tools like Ansible may be useful in controlling large numbers of IoT devices. That may seem far-fetched now, but so was the World Wide Web just over 25 years ago…

References:

Did you like this? Share it:

About Don Larson

Using computer technology since June 1980.
This entry was posted in Amazon Web Services, Ansible, Internet of Things, Personal, Programming Languages, Python, Raspberry Pi, Technology. Bookmark the permalink.