- Foundations of Ansible
- Chef
- Puppet
- Salt
- Ansible Engine
- Managed over SSH Connections
- Supports SSH pipelining
- Inventory describes location of nodes
- Stores sensitive information in Ansible Vault
- Nodes are agentless
- Any host can be the control node, does not require a central server
- Installs and runs temporary modules on nodes
- Modules communicate via STDIN/STDOUT with a JSON based protocol
- Ansible Playbooks strive to be idempotent
- Stored as INI or YAML files
- Hosts can be specified using IP addresses or hostnames
- Hosts can be specified in groups, or group of groups
- Can be managed dynamically using plugins or scripts
- Host groups can be set up along: what, where, and when. E.g.:
# What [webservers] server1.local.net server2.local.net [dbservers] server3.local.net # Where [seattle] server1.local.net server3.local.net [frankfurt] server2.local.net # When [prod] server1.local.net [dev] server2.local.net server3.local.net
- Help on setting up inventories can be found at: How to build your inventory- Ansible Documentation
Configurations are stored in Playbooks. A Playbook is a YAML file that expresses:
- Configuration
- Deployments
- Orchestration
They allows Ansible to perform operation on Nodes. A Playbook maps groups of hosts to sets of roles. A role is represented by a set of Ansible tasks.
- REST API
- Web service
- Web console
- Hub to manage Ansible installations
- Commercial RedHat product
- Open source version: AWX
Ansible is capable of provisioning:
- Bare-metal servers
- Virtualized systems
- Cloud systems
- Storage systems
- Network devices
- Relies on OpenSSH and only requires an SSH key or password to manage a system
- Configuration describes a desired state
- Allows for a "dry run" execution
- Delivers and executes modules remotely
- Does not require root privileges but supports sudo
- Should be near managed nodes. If nodes are local, control node should be local; if nodes are in the cloud, control node should be in the cloud.
- Different pugins may have different requirements
- Number of file handles may need to be increased in MacOS
Requires:
- >= python2.4
- if == python2.4, then
python-simplejson
is required - looks for python at
/usr/bin/python
- SELinux systems require
libselinux-python
package - Ansible raw/script moduless do not require python, thus can be used to install python on managed nodes
Ansible can be configured via various sources, including (listed from lowest to highest priority):
.ansible,cfg
- Environment variables
- Command line options
- Playbook keywords
- Ansible variables: Set in playbooks using
vars:
or in the CLI using-e
flag
.ansible.cfg
file is searched in the following order, listed from highest to lowest priority:
- Location in
$ANSIBLE_CONFIG
environment variable ansible.cfg
in the current directory~/.ansible.cfg
: User home directory (yes.ansible.cfg
- hidden file, not a typo)/etc/ansible/ansible.cfg
# Check ansible version
ansible --version
# Set inventory file using the -i flag
# List all available hosts
ansible -i /path/to/hosts all --list-hosts
# -m flag can be used to send ansible ad-hoc commands to hosts
# Check communication with hosts
ansible -i /path/to/hosts all -m ping
# -a flag can be used to send regular linux command to hosts
# Check system uptime
ansible -i /path/to/hosts all -a "uptime"
# List avaibale ansible plugins
ansible-doc -l
# -t flag can be used to specify plugin type
# Plugin types include: become, cache, callback, cliconf, connection,
# httpapi, inventory, lookup, netconf, shell, strategy, vars
ansible-doc -l -t cache
# Info about specific plugin
ansible-doc file
# Snippet about specific plugin.
# Returns just the available options and their description
ansible-doc -s file
Ansible ad-hoc commands can use any of the ansible modules and are useful for:
- Automating single tasks
- testing commands before including them in playbooks
# *Note: the arrangement of the arguments is not strict*
ansible [ansible flags] [pattern] -m [module] -a "[module options]"
# To run commands directly in the hosts configured shell, use the shell module
# For command string that have a variable in them, single quotes must be used.
ansible all -m shell -a 'echo $PWD'
# To run simpler commands, we can use the command module by omitting the -m flag
ansible all -a "/sbin/reboot"
# Ansible by default spins up 5 processes at a time, -f flag can be used to set this per command
ansible all -a "/sbin/reboot" -f 10
# User to run a command as on the host can be specified with the -f flag
ansible all -a 'echo $PWD' -u user1
# -b and -K flags allow prvilege escalation to root and specification of user's password respectively
ansible all -a "/sbin/reboot" -b -K
The shell module:
- Runs commands in shell
- is more complex but more compatible
- support pipes and redirects
- support environment variables
The command module:
- Doesn't use a shell
- is simple and secure
- doesn't support pipes and redirects
- doesn't support environment variables
Patterns are used to include/exclude subsets of hosts, and can use wildcards or regular expressions. They can match:
- A single host
- an IP address
- an inventory group
- a set of groups
- all hosts
Some common patterns include:
all
or*
for all hostshost-name
for a specific hostgroup-name
for a group of hostsg1:g2
for all hosts that matchg1
org2
g1:!g2
for all hosts that matchg1
excluding those that matchg2
g1:&g2
for all hosts that are in bothg1
andg2
Patterns can be combined for more complex matches such as:webservers:dbservers:&test:!prod
# Touch a file
ansible -i $PATH_TO_HOSTS -m file -a 'path=~/file1.txt state=touch mode=700' rhhost1
# Change file permissions
ansible -i $PATH_TO_HOSTS -m file -a 'path=~/file1.txt mode=600' rhhost1
# Change file owner/group
ansible -i $PATH_TO_HOSTS -m file -a 'path=/home.user1/file1.txt mode=600 owner=root group=root' -b -K rhhost1
# Create a directory
ansible -i $PATH_TO_HOSTS -m file -a 'dest=/home/user1/newdir state=directory mode=700' rhhost1
# Delete directory recursively
ansible -i $PATH_TO_HOSTS -m file -a 'dest=/home/user1/newdir state=absent mode=700' rhhost1
# Create a text file with some content
# force=no means the contents of the file wouldn't be overwritten if file already exists
ansible -i $PATH_TO_HOSTS -m copy -a "dest=~/file2.txt content='file content' force=no mode=700" rhhost1
# Install package with yum module
# state=installed and state=present install the package
# state=latest installs/updates the package to latest version
# state=removed or state=absent uninstalls the package
ansible -i $PATH_TO_HOSTS -m yum -a "name=httpd state=installed" -b -K rhhost1
# Update package list
ansible -i $PATH_TO_HOSTS -m yum -a "name=* state=latest" -b -K rhhost1
# List installed packages
ansible -i $PATH_TO_HOSTS -m yum -a "list=installed" rhhost1
# yum group install
ansible -i $PATH_TO_HOSTS -m yum -a "name='@Development tools' state=installed" -b -K rhhost1
# Use the services module to start httpd
# state=reloaded reloads the service
# state=stopped stops the service
# enabled=yes to enable the service. i.e. start on boot
# disabled=yes to disable the service. i.e. do not start on boot
ansible -i $PATH_TO_HOSTS -m service -a "name=httpd state=started" -b -K rhhost1
# Check service status.
# Returns *"changed": false* if current state matches state that's being checked
# check started
ansible -i $PATH_TO_HOSTS -m service -a "name=httpd state=started" rhhost1 --check
# check enabled
ansible -i $PATH_TO_HOSTS -m service -a "name=httpd enabled=true" rhhost1 --check
# systemctl can also be run on the host via shell/command to manage services
ansible -i $PATH_TO_HOSTS -m shell -a "systemctl status httpd" rhhost1
# Add a new user using the user module
ansible -i $PATH_TO_HOSTS -m user -a "name=user2 home=/home/user2 shell=/bin/bash state=present" -b -K rhhost1
# Change user's primary group
ansible -i $PATH_TO_HOSTS -m user -a "name=user2 group=user2" -b -K rhhost1
# Add a supplementary group to user
ansible -i $PATH_TO_HOSTS -m user -a "name=user2 groups=wheel" -b -K rhhost1
# Delete user
ansible -i $PATH_TO_HOSTS -m user -a "name=user2 state=absent" -b -K rhhost1
# Add user group
ansible -i $PATH_TO_HOSTS -m group -a "name=group2 state=present" -b -K rhhost1
# Get all facts about a host
ansible -i $PATH_TO_HOSTS -m setup rhhost1
# To extract a subset of info
# Subsets include: all, network, min, hardware, virtual, facter
ansible -i $PATH_TO_HOSTS -m setup -a 'gather_subset=network' rhhost1
# ! can be used to negate subsets
# To get only hardware facts, run:
ansible -i $PATH_TO_HOSTS -m setup -a 'gather_subset=!all,!min,hardware' rhhost1
# To even fine-grain the info further and return ospecified section, we can filter
ansible -i $PATH_TO_HOSTS -m setup -a 'gather_subset=!all,!min,hardware filter=ansible_device_links' rhhost1
# Dump into file
ansible -i $PATH_TO_HOSTS -m setup rhhost1 --tree /path/to/file
An index of all ansible modules can be found at Module Index- Ansible Documentation.
Ansibble by default uses OpenSSh for SSH connections, if this is not available, it falls back to paramiko
which is a Python implementation of OpenSSH.
To authenticate SSH without keys (i.e. with a password) use --ask-password
or -k
flag on the CLI.
To specify a private key, use the ansible_ssh_private_key_file
option in the Ansible inventory or:
ansible -i $PATH_TO_HOSTS -m <module> -a "<options>" --private-key /path/to/key <hosts>
on the CLI.
Host key checking adds an extra layer of security by maintaining an ID database of hosts which it checks against to prevent man-in-the-middle attacks. However, this might result in increased maintenance (known_hosts
file might need to be modified in some cases). To disable host key checking, set host_key_checking=False
in ansible.cfg
or export ANSIBLE_HOST_KEY_CHECKING=False
on the control host.
Excluding SSH, there are other connections methods available to Ansible, these include:
- local: lets you run Playbooks on the control host itself
- docker: lets you run Playbooks in docker containers
- kubectl: lets you run Playbooks in kubernetes pods
- vmware_tools: lets you run Playbook on VMWare hosts
- podman, etc.
To get a full list, run:
ansible-doc -t connection -l
# Get more info about a single cpnnection method with
ansible-doc -t connection <plugin-name>
Is a simple and short way to get things done. Some available options (flags) include:
--list-host
: lists matching hosts and do nothing else--private-key
: specify private key--syntax-check
: check playbook syntax--playbook-dir
: specify playbook directory-e
: set environment variables-i
: specify inventory file-m
: specify Ansible module to use-a
: provide arguments for Ansible model in use-v
: set verbose mode
Ansible configuration can be viewed using the ansible-config
command.
# List available config
ansible-config list
# To view current config
ansible-config dump
# Use the --only-changed flag to view configs that defer from the default
ansible-config dump --only-changed
# List hosts categorized by groups
# use the -y option to output YAML instead of JSON
ansible-inventory --list
# output to file with --output flag
ansible-inventory --list --output /path/to/file
# get ASCII graph with --graph option
ansible-inventory --graph
There are man pages available for:
ansible-config
ansible-console
ansible-galaxy
ansible-inventory
ansible-playbook
ansible-pull
ansible-vault
ansible-doc
# Install Development tools
sudo yum group install -y "Development tools"
# Change hostname
sudo hostnamectl set-hostname <new-hostname>
# Install argcomplete for tab completion
sudo yum install -y python3-argcomplete
# Activate argcompelte
sudo activate-global-python-argcomplete3
# or on some systems
sudo activate-global-python-argcomplete
# Create ssh key
ssh-keygen
# Copy ssh key over to a different host
ssh-copy-id
-
Setup a
host-only-adapter
network to allow the VMs communicate with one another and the host. File > Host Network Manager -
Setup a
NAT
network to allow the VMs access the internet.
Below is a basic playbook for managing an apache http server:
# Playbook name
name: Apache server installed
# Hosts to match
hosts: webservers
# Get root privileges
become: yes
# Tasks (Plays)
tasks:
- name: Latest version installed
yum:
name: httpd
state: latests
- name: Server enabled and running
service:
name: httpd
state: started
enabled: yes
After setting up a playbook, it is often useful to check for syntax errors, this can be done with:
ansible-playbook --syntax-check /path/to/playbook
And run playbook with:
ansible-playbook /path/to/playbook -b -K