modules

the batteries included

Chapter 4

TOPICS

  • 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....

desired state

but we are still focusing on the procedure (how) rather than describing desired state (what)

procedural(HOW)

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
&

desired state

For Example,   

user 
name  = xyz
state = present
uid   = 5001
group = admins

entity

desired state

properties

properties

properties

params

desired state

Once you describe WHAT you want 

desired state

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

System 

Entity

module

There is typically a  

between 

mapping

mapping

Ansible ships with 

450+

modules

to manage systems, networks, cloud, applications, containers....you name it....

Batteries Included Approach

modules

a library of 

module catagories

SYSTEM

cloud

command

networking

packaging

clustering

database

files

inventory

messaging

notification

source control

remote management

utilities

windows

web infra

modules types

core

extras

  • maintained by ansible team
  • will always be shipped with ansible 
  • maintained by community
  • currently shipped with ansible
  • may  be shipped separately in future

invoking modules

usage

Modules are typically executed as a part of ansible command

 

ansible app -m apt -b -a "name=ntp  state=present"

module name

key = value

arguments

params

usage

Modules can also be called while writing tasks in Playbooks using YAML

- name: install ntp package
  apt: > 
    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. 

finding info 

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

invoking a module

Lets use a module for  
  • Installing vim utility
  • on load balancer 
  • whose OS is Ubuntu

procedural vs desired state

ansible lb  -b -m apt -a "name=vim  state=present" 
ansible lb -b -a "apt install -y vim" 

module

attributes (properties)

Running Ad Hoc Command

Using Module

(do not run this )

invoking a module

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

GE

ansible db -b -m apt -a "name=vim  state=present" 

return code

Now lets try running it again

GE

re run

ansible db -b -m apt -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

GE

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. 

Using common modules

common modules

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

GE

Lets now take some problem statements to configure system entities, find the relevant modules, determine the desired state,  parameters and execute using ansible utility

GE

Problem Statement  1

Create a group  

  • on all prod servers   
  • whose name is "admin"
  • whose gid  is "7045"

module : group

state = present

gid = 7045

host pattern = prod

name = admin

http://docs.ansible.com/ansible/group_module.html

approach

  • 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

GE

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

GE

Problem Statement  2

Create a user  

  • on all prod servers
  • except for db hosts   
  • whose name is "abc"
  • whose uid  is "7001"
  • who belongs to admin group
  • who has a home directory

name =  abc

state = present

uid = 7001

host pattern = 'prod:!db'

http://docs.ansible.com/ansible/user_module.html

group = admin

module :  user

GE

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

GE

Problem Statement  3

Copy a file

  • whose name is "test.txt"
  • to all app servers
  • at path /tmp/test.txt
  • whose permissions are set to 644

module : copy

dest = /tmp/test.txt

host pattern = app

http://docs.ansible.com/ansible/copy_module.html

src = test.txt

mode = 644

GE

touch test.txt
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

Comamnd modules

and idempotence

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

command modules

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

command modules

command modules

core

extras

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 modules

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"

GE

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"

GE

Learn the difference between raw and shell
ansible app -m command -a "mkdir /tmp/dir1"

GE

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 >>            

IDEMPOTENCE

ansible app -m command -a "mkdir /tmp/dir1"

GE

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?

IDEMPOTENCE

 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

IDEMPOTENCE

  • 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

making commands idempotent

creates

HOW ?

is file present ?

yes

no

SKIP

EXECUTE

creates=/tmp/dir1
ansible app -m command -a "mkdir /tmp/dir1 creates=/tmp/dir1"    

GE

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   

IDEMPOTENCE

2021 Ansible - Section 4 Part II : Modules

By School of Devops

2021 Ansible - Section 4 Part II : Modules

Loading the Batteries

  • 601