Chapter 4
Desired State Configurations
Invoking Modules
Using Common Modules
Command Modules and Idempotence
Ability to run ad-hoc commands is a useful feature of ansible to use once in a while....
but we are still focusing on the procedure (how) rather than describing desired state (what)
useradd -m abc
Modules allow us to manage independent components or entities of our infrastructure
such as,
packages
files
services
network interfaces
users & groups
cron jobs
mount points
by describing the
desired state
of each
properties
&
For Example,
user
name = xyz
state = present
uid = 5001
group = admins
entity
desired state
properties
properties
properties
params
Once you describe WHAT you want
using desired state configurations
you need not worry about how the state is achieved, whether to take an action and which action to take
Its Ansible's Job
1 : 1
There is typically a
between
mapping
to manage systems, networks, cloud, applications, containers....you name it....
a library of
maintained by ansible team
will always be shipped with ansible
maintained by community
currently shipped with ansible
may be shipped separately in future
Modules are typically executed as a part of ansible command
ansible app -m yum -b -a "name=ntp state=installed"
module name
key = value
arguments
params
Modules can also be called while writing tasks in Playbooks using YAML
- name: install ntp package
yum: >
name: ntp
state: present
Output
app1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
Module output is JSON data.
This is useful when you write your own modules, as you need not stick to python to create a custom module. Only requirement is you take inputs and outputs in the format Ansible recognizes.
ansible-doc
This utility helps you find list of modules, how to use those along with example snippets.
ansible-doc --help
ansible-doc --list | head
ansible-doc user
ansible-doc -s user
Lets use a module for
Installing vim utility
on load balancer
whose OS is CentOS
ansible lb -b -m yum -a "name=vim state=present"
ansible lb -b -a "yum install -y vim"
module
attributes (properties)
Running Ad Hoc Command
Using Module
(do not run this )
db | SUCCESS => {
"changed": true,
"msg": "",
"rc": 0,
"results": [
"Loaded plugins: fastestmirror, ovl\nSetting up Install Process
\nLoading mirror speeds from cached hostfile\n * base: mirror.fibergrid
.in\n * extras: mirror.fibergrid.in\n * updates: mirror.fibergrid.in\nR
host
whether changes were made ?
results
status
ansible db -b -m yum -a "name=vim state=present"
return code
Now lets try running it again
ansible db -b -m yum -a "name=vim state=present"
lb | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"msg": "Nothing to do",
"rc": 0,
"results": []
}
host
whether changes were made ? No
return code = 0
results = already installed
status
Thats idempotence in action
Ansible modules(most) are idempotent.
Ansible modules will compare the desired state (policy we set) with the current state of the entity on the system, and decide whether the action is necessary.
Packages
yum
apt
gem
pip
Files
copy
fetch
template
System
user
group
cron
mount
iptables
ping
Utilities
debug
assert
wait_for
Commands
command
shell
expect
Lets now take some problem statements to configure system entities, find the relevant modules, determine the desired state, parameters and execute using ansible utility
Problem Statement 1
Create a group
module : group
state = present
gid = 7045
host pattern = prod
name = admin
http://docs.ansible.com/ansible/group_module.html
Find the module which could manage the entity in question
Observe the usage documentation and learn about the parameters and examples
Determine the desired state
Determine the parameters to use and the values to assign
ansible prod -b -m group -a "name=admin gid=7045 state=present"
lb | SUCCESS => {
"changed": true,
"gid": 7045,
"name": "admin",
"state": "present",
"system": false
}
app1 | SUCCESS => {
"changed": true,
"gid": 7045,
"name": "admin",
"state": "present",
"system": false
}
db | SUCCESS => {
"changed": true,
"gid": 7045,
"name": "admin",
"state": "present",
"system": false
}
app2 | SUCCESS => {
"changed": true,
"gid": 7045,
"name": "admin",
"state": "present",
"system": false
}
module : group
state = present
gid = 7045
host pattern = prod
name = admin
Problem Statement 2
Create a user
name = abc
state = present
uid = 7001
host pattern = 'prod:!db'
http://docs.ansible.com/ansible/user_module.html
group = admin
module : user
module = abc
state = present
uid = 7001
host pattern = 'prod:!db'
group = admin
ansible 'prod:!db' -b -m user -a "name=abc uid=7001 group=admin state=present"
app2 | SUCCESS => {
"changed": true,
"comment": "",
"createhome": true,
"group": 7045,
"home": "/home/abc",
"name": "abc",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 7001
}
lb | SUCCESS => {
"changed": true,
"comment": "",
"createhome": true,
"group": 7045,
"home": "/home/abc",
"name": "abc",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 7001
}
app1 | SUCCESS => {
"changed": true,
"comment": "",
"createhome": true,
"group": 7045,
"home": "/home/abc",
"name": "abc",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 7001
}
root@control:/workspace/chap
module : user
Problem Statement 3
Copy a file
module : copy
dest = /tmp/test.txt
host pattern = app
http://docs.ansible.com/ansible/copy_module.html
src = test.txt
mode = 644
ansible app -m copy -a "src=test.txt dest=/tmp/test.txt mode=644"
app1 | SUCCESS => {
"changed": true,
"checksum": "dde78c32383a66378b6a8d7291deaed7ce4372bc",
"dest": "/tmp/test.txt",
"gid": 0,
"group": "root",
"md5sum": "ad80727e099718d25f8ece688b3c6efd",
"mode": "0644",
"owner": "root",
"size": 480,
"src": "/root/.ansible/tmp/ansible-tmp-1480603766.96-24842385908834
6/source",
"state": "file",
"uid": 0
}
app2 | SUCCESS => {
"changed": true,
"checksum": "dde78c32383a66378b6a8d7291deaed7ce4372bc",
"dest": "/tmp/test.txt",
"gid": 0,
"group": "root",
"md5sum": "ad80727e099718d25f8ece688b3c6efd",
"mode": "0644",
"owner": "root",
"size": 480,
"src": "/root/.ansible/tmp/ansible-tmp-1480603766.92-11939758265086
0/source",
"state": "file",
"uid": 0
}
module : copy
dest = /tmp/test.txt
host pattern = app
src = test.txt
mode = 644
It is recommended that you use a specialized module for the entity that you would like to manage or action you would want to take.......
copy a file
install a package on Redhat
manage vlan on Cisco NX OS
create a subnet on aws cloud
copy
yum
nxos_vlan
ec2_vpc_subnet
however, there will be situations where you may not find a module to take an action you desire, or you may have a very complex script which you would like to invoke
install an application from source (make, make install)
call an API for which there is no module
calling a ruby installer script for a third party application
to cover such cases ansible offers you a group of command modules
bypass modules subsystem and execute raw ssh command
raw
command
shell
script
expect
execute a command on a remote node without shell
execute a command on a remote node with /bin/sh
copy a script to the remote node and also execute it
execute a interactive command and auto respond to prompts
command
shell
does not use shell
invokes /bin/sh
does not have access to env
> < | ; & operators will not work
secure and recommended
has access to env, variables etc
> < | ; & operators will work
use selectively
ansible app -m command -a "free"
ansible app -m command -a "free | grep -i swap"
ansible app -m command -a "free" | grep -i swap
ansible app -m shell -a "free | grep -i swap"
Lets explore using command and shell modules
ansible app -vvvv -m raw -a "free | grep -i swap"
ansible app -vvvv -m shell -a "free | grep -i swap"
Learn the difference between raw and shell
ansible app -m command -a "mkdir /tmp/dir1"
Lets run a command to create a directory on remote nodes
ansible app -m command -a "mkdir /tmp/di
r1"
app2 | SUCCESS | rc=0 >>
app1 | SUCCESS | rc=0 >>
ansible app -m command -a "mkdir /tmp/dir1"
Lets run it again
ansible app -m command -a "mkdir /tmp/di
r1"
app1 | FAILED | rc=1 >>
mkdir: cannot create directory `/tmp/dir1': File exists
app2 | FAILED | rc=1 >>
mkdir: cannot create directory `/tmp/dir1': File exists
What is the result ?
why?
ansible app -m command -a "mkdir /tmp/dir1"
app1 | FAILED | rc=1 >>
mkdir: cannot create directory `/tmp/dir1': File exists
app2 | FAILED | rc=1 >>
mkdir: cannot create directory `/tmp/dir1': File exists
most shell utilities are not idempotent by nature
ansible's command modules simply invoke shell commands, and are not idempotent either
file already exists
creates is a parameters/argument to the command modules
checks for an existence of a file and decides to skip execution if the file is present
creates
is file present ?
yes
no
SKIP
EXECUTE
creates=/tmp/dir1
ansible app -m command -a "mkdir /tmp/dir1 creates=/tmp/dir1"
Run command with creates
# ansible app -m command -a "mkdir /tmp/dir1 creates=/tmp/dir1"
app2 | SUCCESS | rc=0 >>
skipped, since /tmp/dir1 exists
app1 | SUCCESS | rc=0 >>
skipped, since /tmp/dir1 exists