Red Hat Certified Specialist in Ansible Automation (EX407) Preparation Course
- Understanding Core Components of Ansible
- Run Ad-Hoc Ansible Commands
- Inventory Management
- Create Ansible Plays and Playbooks
- Introduction to Playbooks and Common Modules
- Create Playbooks to Configure Systems to a Specified State
- Basic Playbook Syntax Demonstration
- Use Variables to Retrieve the Results of Running Commands
- Use Conditionals to Control Play Execution Part 1
- Use Conditionals to Control Play Execution Part 2
- Configure Error Handling
- Demo: Error Handling – Ignore Errors
- Demo: Error Handling – Block Groups
- Selectively Run Specific Tasks In Playbooks Using Tags
- LAB: Ansible Playbooks: The Basics
- LAB: Ansible Playbooks - Error Handling
- Create and Use Templates to Create Customized Configuration Files
- Work with Ansible Variables and Facts
- Create and Work with Roles
- Download roles from an Ansible Galaxy
- Managing Parallelism
- Use Ansible Vault in Playbooks to Protect Sensitive Data
- Install Ansible Tower and Use it to Manage Systems
- Use Documentation to Look Up Specific Information About Ansible Modules and Commands
- Ansible 2.7 Exam Update
This series of lessons lays the foundation for the remainder of the course content. Through a combination of lecture and command line demonstration, Students will gain a broad overview of Ansible. This particular lesson, focuses on Ansible inventories.
- Overview
- Invetories
- Modules
- Variables
- Facts
- Plays
- Playbooks
- Configuration files
-
Inventory files may simply consist of a list of hostnames but can be much more robust
-
It is also possible to define groups of hosts, host or group level variables, and groups of groups withing the inventory
-
There are a number of variables that may be used within the inventory to control how ansible connects to and interacts with target hosts
-
Commands to call ansible with
ping
module:ansible innaghiyev1c.mylabserver.com -m ping -k
- call ping module oninnaghiyev1c.mylabserver.com
host. Where-m ping
is ping module and-k
is key for asking passwordansible all -m ping -k
- call all defined hosts in your inventory list/etc/ansible/hosts/
ansible -i inv.ini httpd -m ping -k
- where-i
- inventory file place,httpd
hosts group name inside ofinv.ini
file
Output:
innaghiyev1c.mylabserver.com | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
This series of lessons lays the foundation for the remainder of the course content. Through a combination of lecture and command line demonstration, Students will gain a broad overview of Ansible. This particular lesson covers the following topics at a very high level: modules, variables, facts, plays, playbooks, and configuration files.
-
Modules
- Modules are essentially tools for particular tasks
- Modules can take (and usually do) take parameters
- They return JSON
- Can run from the command line or within a playbook
- There are a significant number of modules for many kinds of work
- Custom modules can be written
-
Variables
- Variable names should be letters, numbers and underscores
- Variables should always start with a letter
- Can be scoped by a group, host, or even ini a playbook
- Typically used for configuration values and various parameters
- Variables can also be used to store the return value of executed commands
- Ansible variables may also be dictionaries
- There are a number of predefined variables used by Ansible
-
Facts
- Facts provide certain information about given target host
- Facts are discovered by Ansible automatically when it reaches out to a host
- Fact gathering may be disabled
- Facts may be cached between playbook executions, but this is not default behavior
innaghiyev1c.mylabserver.com | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"172.31.37.72"
],
"ansible_all_ipv6_addresses": [
"2a05:d01c:2c7:d802:4db1:18cc:f13f:a1cf",
"fe80::9e:19ff:fe1e:7376"
],
"ansible_apparmor": {
"status": "enabled"
},
"ansible_architecture": "x86_64",
"ansible_bios_date": "10/16/2017",
"ansible_bios_version": "1.0",
-
Plays and playbooks
- The goal of a play is to map a group of hosts to some well-defined roles
- A play may use one or more modules to achieve a desired end state on a group of hosts
- A playbook is a series of plays
- A playbook may deploy new web servers, install a new application to existing application servers, and run SQL against some database servers to support the new application
-
Configuration Files
- Several possible locations (in order processed):
- ANSIBLE_CONFIG (an environment variable)
- ansible.cfg (in the current directory)
- .ansible.cfg (in the home directory)
- /etc/ansible/ansible.cfg (master configuration)
- Configuration can also be set in environment variables
- Some commonly used settings:
- ansible_managed
- forks
- Inventory
- Several possible locations (in order processed):
The Ansible master configuration file is reviewed on a live system in this demonstration. Key configuration values are discussed as well as how to modify those values.
- Let's see some default values of ansible.cfg file located by
/etc/ansible/ansible.cfg
directory
[defaults]
# some basic default values...
#inventory = /etc/ansible/hosts
#library = /usr/share/my_modules/
#module_utils = /usr/share/my_module_utils/
#remote_tmp = ~/.ansible/tmp
#local_tmp = ~/.ansible/tmp
#plugin_filters_cfg = /etc/ansible/plugin_filters.yml
#forks = 5
#poll_interval = 15
#sudo_user = root
#ask_sudo_pass = True
#ask_pass = True
#transport = smart
#remote_port = 22
#module_lang = C
#module_set_locale = False
- Another handy block is following
[privilege_escalation]
#become=True
#become_method=sudo
#become_user=root
#become_ask_pass=False
The very first step to harnessing the power of Ansible is configuring your environment. This activity goes over installing Ansible on a control node and configuring two managed servers for use with Ansible. We will also create a simple inventory and run an Ansible command to verify our configuration is correct.
Your CIO has greenlit a proof of concept for Ansible in your environment. You are to set up an Ansible control node in a test environment and verify basic functionality. You have three demo hosts, one to be the control node (control1
), and two to serve as managed nodes (node1
and node2
). You must complete the following steps:
- Install Ansible on the
control
node. - Configure the
ansible
user on the control node for ssh shared key access to managed nodes. Note: do not use a passphrase for the key pair. - Create a simple Ansible inventory on the control node in
/home/ansible/inventory
containing node1 and node2. - Configure sudo access for Ansible on
node1
andnode2
so that Ansible may usesudo
for any command with no password prompt. - Verify each managed node can be accessed by Ansible from the control node using the
ping
module. Redirect the output of a successful command to/home/ansible/output
.
Important Notes:
- The user
ansible
is already present on all servers for your convenience. - The
ansible
user has the same password as thecloud_user
. /etc/hosts
entries are present oncontrol1
for the managed nodes.
- To install Ansible on the control node, run ansible.
yum install ansible
- If package not found run
yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
Configure the ansible
user on the control node for ssh shared key access to managed nodes. Do not use a passphrase for the key pair.
-
To create a keypair for the ansible user on the control host, run the following:
sudo su - ansible
ssh-keygen
(accept all defaults: press enter for each prompt)
-
Copy the
public key
to bothnode1
andnode2
. -
As the ansible user on the control host:
ssh-copy-id node1
(accept the host key if prompted, authenticate as ansible user)ssh-copy-id node2
(accept the host key if prompted, authenticate as ansible user)
Create a simple Ansible inventory on the control node in /home/ansible/inventory
containing node1
and node2
.
- On the control host:
sudo su - ansible
(if not already ansible user)touch /home/ansible/inventory
echo "node1" >> /home/ansible/inventory
echo "node2" >> /home/ansible/inventory
Configure sudo access for Ansible on node1
and node2
such that Ansible may use sudo for any command with no password prompt.
- Log in to node1 as cloud_user and edit the sudoers file to contain appropriate access for the ansible user:
ssh cloud_user@node1
sudo visudo
- Add the following line to the file and save:
ansible ALL=(ALL) NOPASSWD: ALL
- Repeate these steps for
node2
.
Verify each managed node is able to be accessed by Ansible from the control node using the ping
module. Redirect the output of a successful command to /home/ansible/output
.
- To verify each node, run the following as the
ansible
user from the control host:ansible -i /home/ansible/inventory node1 -m ping
ansible -i /home/ansible/inventory node2 -m ping
- To redirect output of a successful command to
/home/ansible/output
:ansible -i /home/ansible/inventory node1 -m ping > /home/ansible/output
Learn how to use ad-hoc ansible commands for simple system managment. This lecture covers one of the key objectives for Red Hat exam 407.
-
Overview:
- What is an ad-hoc command in Ansible?
- Use cases for ad-hoc commands
- Ad-hoc vs Playbook
- Ansible command syntax
- Common modules
-
What is an ad-hoc command in Ansible
- You can run ansible either ad-hoc or as a playbook
- Both methods have the same capabilities
- Ad-hoc commands are effectively one-liners
-
Use cases for Ad-hoc
- Operational commands
- Checking log contents
- Daemon control
- Process management
- Informational commands
- Check installed software
- Check system properties
- Gather system performance information
- Research
- Work with unfamiliar modules on test systems
- Practice for playbook engineering
- Operational commands
- Let's use
yum
as an example of ad-hoc commandansible myserver.example.com -i inv.ini -m yum -b -a "name=elinks state=latest"
- install elinks with latest version-i
- key for inventory host file-m yum
- useyum
module-b
- become (by default become root). Can be changed in/etc/ansible/ansible.cfg
file-a "name=elinks state=latest"
- arguments. Foryum
module we're using name and state
ansible myserver.example.com -i inv.ini -m yum -b -a "name=elinks state=absent"
- state absent will uninstall elinks
- Let's work with a file module
ansible myserver.example.com -i inv.ini -m file -a "name=/home/user/newfile state=touch"
-m file
- using file module-a "name=/home/user/newfile state=touch"
- arguments name - for declaring path of file and state what to do with it.
ansible myserver.example.com -i inv.ini -m file -a "name=/home/user/newfile"
- get properties of the fileansible myserver.example.com -i inv.ini -m file -a "name=/home/user/newfile mode=0400"
- set a mode of the file to 0400ansible myserver.example.com -i inv.ini -m file -b -a "name=/home/user/newfile owner=root"
- we are changing file owner to root and using-b
key for become.ansible myserver.example.com -i inv.ini -m user -b -a "name=sam"
- create user sam-m user
- using user module-a "name=sam"
- as argument we indicate user name
ansible myserver.example.com -i inv.ini -m user -b -a "name=sam append=yes groups=wheel"
- add user to a wheel group.append=yes
- for appending group, otherwise it will wipe out previous groupsgroups=wheel
- define which group needs to be added
One of the keys to success with Ansible is being able to run ad-hoc
commands. The value of ad-hoc
commands is underscored by the fact that it is an objective of the Red Hat Certified Ansible Specialist exam. This exercise guides students through crafting many ad-hoc
commands which will not only build experience with the concept but also broaden the students' exposure to various Ansible command modules.
Some consultants have been employed to perform audits on a number of systems in your company's environment. You must create the user accounts noted in /home/ansible/userlist.txt
and set up the provided public keys for their accounts. The security team has built a jump host for the consultants to access production systems and provided the full key-pair to you so you may set up and test the connection. All hosts in dbsystems
will need the provided public key installed so the consultants may use key-pair authentication to access the systems. Also, you must ensure the auditd
service is enabled and running on all systems.
To summarize, you must do the following:
- Create the user accounts noted in
/home/ansible/userlist.txt
. - Copy the
authorized_keys
file for each user to the correct location so the new accounts can log in with ssh key authentication. - Ensure
auditd
is enabled and running on all systems.
Important notes:
-
For your convenience, Ansible is already on the control node. If you connect to the server by clicking on the Public IP address in your browser, make sure to change to the ansible user with the
su - ansible
command. -
The user
ansible
is present on all servers with appropriate shared keys for access to managed servers from the control node. Make sure to use this user to complete the commands. -
The
ansible
user has the same password ascloud_user
. -
The default Ansible inventory has been configured for you with the appropriate hosts and groups.
-
/etc/hosts
entries are present oncontrol1
for the managed servers.
ansible dbsystems -b -m user -a "name=consultant"
ansible dbsystems -b -m user -a "name=supervisor"
ansible dbsystems -b -m file -a "path=/home/consultant/.ssh state=directory owner=consultant group=consultant mode=0755"
ansible dbsystems -b -m copy -a "src=/home/ansible/keys/consultant/authorized_keys dest=/home/consultant/.ssh/authorized_keys mode=0600 owner=consultant group=consultant"
ansible dbsystems -b -m file -a "path=/home/supervisor/.ssh state=directory owner=supervisor group=supervisor mode=0755"
ansible dbsystems -b -m copy -a "src=/home/ansible/keys/supervisor/authorized_keys dest=/home/supervisor/.ssh/authorized_keys mode=0600 owner=supervisor group=supervisor"
ansible all -b -m service -a "name=auditd state=started enabled=yes"
In Ansible, inventories are crucially important as they serve as the foundation for ansible automation. This lecture extends on the basic inventory concpets already covered such as file format and location. Students will be introduced to the concept of static and dynamic inventories and learn about how inventories and variables work together.
- Use both static and dynamic inventories to define groups of hosts:
- What is the inventory
- File formats
- Statis vs. Dynamic
- Variables and inventories
- Utilize an existing dynamic inventory script:
- On dynamic inventories
- Some popular options
- An inventory is a list of hosts that Ansible manages
- Inventory location may be specified as follows:
- Default: /etc/ansible/hosts
- Specified by CLI: ansible -i
- Can be set in ansible.cfg
- The inventory file may contain hosts, patterns, groups, and variables
- You may specofy the inventory as a directory containing a series of inventory files (both static and dynamic)
- The inventory may be specified in YAML or INI format
- Can be static or dynamic
Being able to work with inventories and variables is an essential skill for any user of Ansible. This command line demonstration will show students the best practices for using variables within inventories.
Inside of inventory directory we have following structure:
[cloud_user@innaghiyev2c inventory]$ ls -l
total 4
drwxrwxr-x. 2 cloud_user cloud_user 23 Feb 28 05:18 group_vars
drwxrwxr-x. 2 cloud_user cloud_user 25 Feb 27 10:53 host_vars
-rw-rw-r--. 1 cloud_user cloud_user 127 Feb 27 10:41 inventory
Where:
inventory
- stores our hosts
[cloud_user@innaghiyev2c inventory]$ cat inventory
innaghiyev1c ansible_host=innaghiyev1c.mylabserver.com
[labservers]
innaghiyev3c.mylabserver.com
innaghiyev1c.mylabserver.com
group_vars
- contains group name of our hosts' group.labservers
- name of group, which contains variables
[cloud_user@innaghiyev2c inventory]$ ls -l group_vars/
total 4
-rw-rw-r--. 1 cloud_user cloud_user 50 Feb 28 05:18 labservers
[cloud_user@innaghiyev2c inventory]$ cat group_vars/labservers
logs : /var/log/messages
secure : /var/log/secure
host_vars
- contains hostname with stored variable inside.innaghiyev1c
- name of the host, which contains variables
[cloud_user@innaghiyev2c inventory]$ ls host_vars/
innaghiyev1c
[cloud_user@innaghiyev2c inventory]$ cat host_vars/innaghiyev1c
opt_dir : /opt
Inventories may be specified in INI or YAML format. This demonstration goes over how to use YAML to create an inventory. Students will benefit from a refresher on YAML syntax as well as review key details on Ansible inventories.
This is how YAML inventory file looks like:
all:
hosts:
innaghiyev1c.mylabserver.com:
children:
production:
hosts:
innaghiyev1c.mylabserver.com:
innaghiyev3c.mylabserver.com:
labservers:
hosts:
innaghiyev[1:3]c.mylabserver.com:
Being able to use dynamic inventories in essential skill for any Ansible specialist. This lecture goes over the details of how dynamic inventories in Ansible work.
Some Popular Options:
chmod +x script.py
- script for dynamic inventory must be executable. Output of your script must be in JSON formatansible all -i script.py -m ping
- usage of dynamic inventory is the same as a static one
Your company has decided the backup software license is frivolous and unnecessary. As a consequence, the license was not renewed. Your supervisor has created a simple script and an Ansible playbook to create an archive of select files, depending on pre-defined Ansible host groups, as a stop-gap measure. You will create the inventory file to complete the backup strategy.
You must do the following:
- Create the inventory file in /home/ansible/inventory.
- Configure the host group media to contain media1 and media2.
- Define the following variables for media with their accompanying values:
- media_content should be set to
/var/media/content/
. - media_index should be set to
/opt/media/mediaIndex
.
- media_content should be set to
- Configure the host group webservers to contain the hosts web1 and web2.
- Define the following variables for
webservers
with their accompanying values:- httpd_webroot should be set to
/var/www/
- httpd_config should be set to
/etc/httpd/
- httpd_webroot should be set to
- Define the variable script_files specifically for web1. The value of script_files should be set to
/usr/local/scripts
. - You can run
/home/ansible/scripts/backup.sh
to test your inventory. If you have correctly configured the inventory, it should not error. - Do not edit anything in /home/ansible/scripts/.
Important notes:
- For your convenience, Ansible has been installed on the control node.
- The user
ansible
has already been created on all servers with appropriate shared keys for access to managed servers from the control node. - The
ansible
user has the same password ascloud_user
. /etc/hosts
entries have been made on control1 for the managed servers.- Do not edit anything in
/home/ansible/scripts/
.
touch /home/ansible/inventory
echo "[media]" >> /home/ansible/inventory
echo "media1" >> /home/ansible/inventory
echo "media2" >> /home/ansible/inventory
mkdir /home/ansible/group_vars
touch /home/ansible/group_vars/media
echo "media_content: /var/media/content/" >> /home/ansible/group_vars/media
echo "media_index: /opt/media/mediaIndex" >> /home/ansible/group_vars/media
echo "[webservers]" >> /home/ansible/inventory
echo "web1" >> /home/ansible/inventory
echo "web2" >> /home/ansible/inventory
touch /home/ansible/group_vars/webservers
echo "httpd_webroot: /var/www/" >> /home/ansible/group_vars/webservers
echo "httpd_config: /etc/httpd/" >> /home/ansible/group_vars/webservers
mkdir /home/ansible/host_vars
touch /home/ansible/host_vars/web1
echo "script_files: /usr/local/scripts" >> /home/ansible/host_vars/web1
This less provides an overview of the section and reviews some of the common modules that will continue showing up as the playbook discussion occurs.
- Know how to work with commonly used Ansible modules
- Create playbooks to configure systems to a specified state
- Use variables to retrieve the results of running commands
- Use conditionals to control play execution
- Configure error handling
- Selectively run specified tasks in playbooks using tags
- Core modules to be familiar with:
- Working with files: copy, archive, unarchive, get_url
- user, group
- ping
- service
- yum
- Lineinfile module
- htpasswd
- Shell and command modules
- Script module
- Debug module
- https://docs.ansible.com/ansible/latest/modules/modules_by_category.html
Let's write some simple playbook.
--- #to mention that this is a playbook
- hosts: labservers #define hosts group
become: yes #become means sudo, by default is root user
tasks: #what to do on defined hosts
- name: install apache #task name, can be anything
yum: #what module to call
name: httpd #package name
state: latest #state of that package, version, absent etc.
- name: start and enable httpd #task name
service: #module name
name: httpd #service name
state: started #what is desired state of that service
enabled: yes #enable service, equal to systemctl enable httpd
- name: create index.html #name of task
file: #module name
path: /var/www/html/index.html #path of file to change
state: touch #what to do with that file, in our case - touch
- name: add a linex to index.html #task name
lineinfile: #module name
path: /var/www/html/index.html #path to the file
line: "Hello World" #check line, if not exist add it
ansible-playbook playbook.yml
- instead of ansible - ad-hoc run, we're running ansible-playbook to execute playbook file.ansible-playbook playbook.yml --limit innaghiyev1c.mylabserver.com
- set limits on playbook run and execute it only on limited host.
- by
register
we can get a lot of information regarding module. What has been changed, where etc - playbook with retriving output as a variable
---
- hosts: labservers
tasks:
- name: create file
file:
path: /tmp/newfile
state: touch
register: output
- debug: msg="Register output is {{output}}"
- name: edit file
lineinfile:
path: /tmp/newfile
line: "{{output.uid}}"
- As an output we will have following result
"msg": "Register output is {u'group': u'cloud_user', u'uid': 1004, u'dest': u'/tmp/newfile', u'changed': True, 'failed': False, u'state': u'file', u'gid': 1005, u'secontext': u'unconfined_u:object_r:user_tmp_t:s0', u'mode': u'0664', u'owner': u'cloud_user', u'diff': {u'after': {u'path': u'/tmp/ newfile', u'state': u'touch', u'atime': 1583240962.247869, u'mtime': 1583240962.247869}, u'before': {u'path': u'/tmp/newfile', u'state': u'file', u'atime': 1583240712.9548037, u'mtime': 1583240712.9548037}}, u'size': 5}"
}
- By
register
we're registering output in defined variable. In our case this is output variable. We can use this variable during playbook run.
Playbook with handler:
---
- hosts: labservers
become: yes
handlers:
- name: restart apache
service: name="httpd" state="restarted"
listen: "restart web"
tasks:
- name: change config
replace:
path: /etc/httpd/conf/httpd.conf
regexp: '^DocumentRoot.*$'
replace: 'DocumentRoot "/opt/www"'
backup: yes
notify: "restart web"
Explanation for playbook:
- handlers waiting for notify message from tasks
- once notify message appeared it's going to trigger handler in the end of play
- handler is not going to run again if there is no change made.
- doesn't matter if we run playbook several times, handler will not be triggered (what is useful, to avoid downtime of the service)
- When - if condition is true, it will execute. More similar to if-else condition
- With_items - will take each item in a list and loop through it
- With_files - really similar to With_items
Demonstration of how to use a loop in playbooks:
---
- hosts: labservers
become: yes
tasks:
- name: create users
user:
name: "{{item}}"
with_items:
- sam
- john
- bob
- Following playbook is going to run through
items
and create users sam john and bob listed inwith_items
block
Demonstration of how to use when condition in playbooks:
---
- hosts: labservers
become: yes
tasks:
- name: edit index
lineinfile:
path: /var/www/html/index.html
line: "I'm back!!!"
when:
- ansible_hostname == "innaghiyev1c"
- Condition is going to wait for proper hostname and apply your changes only on that node.
- Ignoring acceptable errors - you're familiar with error and you accept it, you can configure to ignore such errors
- Defining failure conditions - define what to do with errors. Stop run or proceed with execute
- Defining "Changed" - control what it means that something needs to be changed
- Blocks - if some of task inside of block fails it will to another block. That logic pre-configured already and it's going to switch to another block.
Demonstration of playbook:
---
- hosts: labservers
tasks:
- name: get files
get_url:
url: "http://{{item}}/index.html"
dest: "/tmp/{{item}}"
ignore_errors: yes
with_items:
- innaghiyev1c
- innaghiyev3c
- innaghiyev2c
- We're going to
ignore errors
and even if playbook failed it's going to execute - Output will look like this:
Block groups and rescues - like a try and catch.
---
- hosts: labservers
tasks:
- name: get file
block:
- get_url:
url: "http://innaghiyev3c/index.html"
dest: "/tmp/index_file"
rescue:
- debug: msg="The file doesn't exists!"
always:
- debug: msg="Play done!"
rescue
- that part of block will run if playbook run failedalways
- that part of block will run any time. No matter playbook run fails or not.
Here how rescue block is working:
How Ansible uses tags
- You can have several deployments in your playbook. Like database and application deployment
- By using tags we can deploy only application stage or only database stage
---
- hosts: web
become: yes
tasks:
- name: deploy app binary
copy:
src: /home/cloud_user/apps/hello
dest: /var/www/html/hello
tags:
- webdeploy
- hosts: db
become: yes
tasks:
- name: deploy db script
copy:
src: /home/cloud_user/apps/script.sql
dest: /opt/deb/scripts/script.sql
tags:
- dbdeploy
ansible-playbook tags_playbook.yml
- running playbook without tagsansible-playbook tags_playbook.yml --tags webdeploy
- running playbook only forwebdeploy
partansible-playbook tags_playbook.yml --tags dbdeploy
- running playbook only fordbdeploy
partansible-playbook tahs_playbook.yml --skip-tags webdeploy
- skip tagwebdeploy
and run the rest of playbook
Your company has been increasing the deployment of small broacher-style websites for clients. The head of IT has decided that each client should have their own web servers for better client isolation and has tasked you with creating concept automation to quickly deploy web-nodes with simple static website content.
You have been provided an ansible control node and 2 test lab servers (node1 and node2) that have been preconfigured with the ansible user and key.
You must create an ansible inventory in /home/ansible/inventory containing a host group named web. The web group should contain node1 and node2.
Furthermore, you must design an Ansible playbook that will execute the following tasks on your configured inventory: install httpd, start and enable the httpd service, and install a simple website provided on a repo server. Create the playbook in /home/ansible/web.yml. The simple website may be accessed from http://repo.example.com/website.tgz.
Summary tasks list:
- Create an inventory in /home/ansible/inventory containing a host group named web. The web group should contain node1 and node2.
- Create a playbook in /home/ansible/web.yml.
- Configure the playbook to install httpd on the web group.
- Configure the playbook to start and enable the httpd service on the web group.
- Configure the playbook to retrieve the website from http://repo.example.com/website.tgz on each server in the web group.
- Configure the playbook to unarchive the website into /var/www/html on all servers in the web group.
- Execute the playbook you created using the inventory you created to verify your work.
Important notes:
- For your convenience, Ansible has been installed on the control node.
- The user ansible is present on all servers with appropriate shared keys for access to managed servers from the control node.
- The ansible user has the same password as cloud_user.
- /etc/hosts entries have been made on control1 for the managed servers.
Create an Inventory in /home/ansible/inventory
. That Contains a Host Group Named web
. The web
Group Should Contain node1
and node2
echo "[web]" >> /home/ansible/inventory
echo "node1" >> /home/ansible/inventory
echo "node2" >> /home/ansible/inventory
echo "---" >> /home/ansible/web.yml
Using a text editor, such as vim, edit /home/ansible/web.yml to contain the following text block below the line containing "---":
- hosts: web
become: yes
tasks:
- name: install httpd
yum: name=httpd state=latest
Using a text editor such as vim, edit /home/ansible/web.yml to contain the following task block after the "install httpd task":
- name: start and enable httpd
service: name=httpd state=started enabled=yes
Configure the Playbook to Retrieve the Website from http://repo.example.com/website.tgz on Each Server in the web
Group
Using a text editor such as vim, edit /home/ansible/web.yml to contain the following task block after the "start and enable httpd" task:
- name: retrieve website from repo
get_url: url=http://repo.example.com/website.tgz dest=/tmp/website.tgz
Using a text editor such as vim, edit /home/ansible/web.yml to contain the following task block after the "retrieve website from repo" task:
- name: install website
unarchive: remote_src=yes src=/tmp/website.tgz dest=/var/www/html/
ansible-playbook -i /home/ansible/inventory /home/ansible/web.yml
We have to set up automation to pull down a data file, from a notoriously unreliable third-party system, for integration purposes. Create a playbook that attempts to pull down http://apps.l33t.com/transaction_list to localhost
. The playbook should gracefully handle the site being down by outputting the message "l33t.com appears to be down. Try again later." to stdout
. If the task succeeds, the playbook should write "File downloaded." to stdout
. No matter if the playbook errors or not, it should always output "Attempt completed." to stdout
.
If the report is collected, the playbook should write and edit the file to replace all occurrences of #BLANKLINE
with a line break \n
.
Tasks list summary:
- Create a playbook,
/home/ansible/report.yml
. - Configure the playbook to download http://apps.l33t.com/- transaction_list to
/home/ansible/transaction_list
onlocalhost
- and output "File downloaded." tostdout
. - Configure the playbook to handle connection failure by outputting - "l33t.com appears to be down. Try again later." to
stdout
. - Configure the playbook to output "Attempt Completed" to
stdout
, - whether it was successful or not. - Configure the playbook to replace all instances of
#BLANKLINE
with - the line break character\n
. - Run the playbook using the default inventory to verify whether things work or not.
Important notes:
- For convenience, Ansible has been installed on the control node.
- The user
ansible
already exists on all servers, with appropriate shared keys for access to the necessary servers from the control node. - The
ansible
user has the same password ascloud_user
. - All necessary Ansible inventories have already been created.
- apps.l337.com is unavailable by default.
- We may force a state change by running
/home/ansible/scripts/change_l33t.sh
.
echo "---" >> /home/ansible/report.yml
Configure the Playbook to Download http://apps.l33t.com/transaction_list to /home/ansible/transaction_list
on localhost
and Outputs the Message "File downloaded." to stdout
Using a text editor, such as vim, edit /home/ansible/report.yml
to contain the following text block below the line containing "---":
- hosts: localhost
tasks:
- name: download tranaction_list
get_url:
url: http://apps.l33t.com/transaction_list
dest: /home/ansible/transaction_list
- debug: msg="File downloaded"
Configure the Playbook to Handle Connection Failure by Outputting "l33t.com appears to be down. Try again later." to stdout
Using a text editor, such as vim, edit the tasks section in /home/ansible/report.yml
to contain the new lines as shown below. Note that the get_url
line was changed to include a leading hyphen:
---
- hosts: localhost
tasks:
- name: download transction_list
block:
- get_url:
url: http://apps.l33t.com/transaction_list
dest: /home/ansible/transaction_list
- debug: msg="File downloaded"
rescue:
- debug: msg="l33t.com appears to be down. Try again later."
Using a text editor, such as vim, edit /home/ansible/report.yml
to contain the following text block below the line containing "---":
---
- hosts: localhost
tasks:
- name: download transction_list
block:
- get_url:
url: http://apps.l33t.com/transaction_list
dest: /home/ansible/transaction_list
- debug: msg="File downloaded"
rescue:
- debug: msg="l33t.com appears to be down. Try again later."
always:
- debug: msg="Attempt completed."
Using a text editor, such as vim, edit /home/ansible/report.yml
to contain the following text block below the line containing "---":
---
- hosts: localhost
tasks:
- name: download transction_list
block:
- get_url:
url: http://apps.l33t.com/transaction_list
dest: /home/ansible/transaction_list
- replace:
path: /home/ansible/transaction_list
regexp: "#BLANKLINE"
replace: '\n'
- debug: msg="File downloaded"
rescue:
- debug: msg="l33t.com appears to be down. Try again later."
always:
- debug: msg="Attempt completed."
ansible-playbook /home/ansible/report.yml
This lecture covers how templates are used, why they are used, and how they are created. A successful Ansible Specialist must have an understanding of these concepts.
Overview:
- Template basics
- Template module
- Template file
Template basics
- Template give the ability to provide a skeletal file that can be dynamically completed using variables
- The most common template use case is configuration file management
- Templates are generally used by providing a template file on the ansible control node, and then using the template module within your playbook to deploy the file to a target server or group
- Templates are processed using the Jinja2 template language
Template Module
Template File
Our template network.j2
file will look like this:
My IP address is {{ ansible_default_ipv4.address }}.
{{ ansible_distribution }} is my OS version.
ansible_default_ipv4.address
andansible_distribution
- are gathered facts during playbook run
Playbook for calling our template will look like this:
---
- hosts: labservers
tasks:
- name: deploy local net file
template:
src: /home/cloud_user/template/network.j2
dest: /home/cloud_user/template/network.txt
Created file /home/cloud_user/template/network.txt
after playbook run will be:
My IP address is 142.21.46.232.
RedHat is my OS version.
This lecture broadly covers how to work with Ansible variables. Variable conventions, dictionary variables, magic variables and jinja2 filters are all covered conceptually as well as syntactically.
Updated Link https://jinja.palletsprojects.com/en/2.10.x/templates/
Overview:
-
Ansible variables
-
Dictionary variables
-
Magic variables and filters
-
What are facts?
-
How to use facts?
-
Facts.d - custom facts
-
Ansible variables
- Review on naming convention and quotes
- Some more places to define variables:
- vars, vars_files and vars_prompt
- Command line: ansible-playbook play.yml -e '{"myVar":"myValue","anotherVar":"anotherValue"}'
- Roles, blocks, and inventories
- Essential variable use:
- -debug: msg="Look! I'm using my variable {{myVar}}!"
- A note on quotes:
- name: "{{package}}"
-
Dictionary variables
- Magic Variables and Filters
Cookbook with variables usage:
---
- hosts: labservers
vars:
inv_file: /home/cloud_user/vars/inv.txt
tasks:
- name: create file
file:
path: "{{inv_file}}"
state: touch
- name: generate inventory
lineinfile:
path: "{{inv_file}}"
line: "{{ groups['labservers']|join(' ') }}"
inv_file
- defined variable before task startedgroups['labservers']
- magic variable withlabservers
host group in default inventory|join(' ')
- join our hostnames with space between them.
Content of the file after playbook run:
[cloud_user@innaghiyev2c playbook]$ cat ../vars/inv.txt
innaghiyev1c.mylabserver.com innaghiyev2c.mylabserver.com innaghiyev3c.mylabserver.com
Possible ways to define variable files:
var_files
- using it within the playbook to define variables fileansible-playbook var_files_playbook.yml -e "@../vars/users.lst"
- using-e
key to provide variable file path.
Here how users.lst
file looks like:
[cloud_user@innaghiyev2c playbook]$ cat ../vars/users.lst
staff:
- joe
- john
- bob
- sam
- mark
faculty:
- matt
- alex
- frank
other:
- will
- jack
Cookbook with variable file usage:
---
- hosts: labservers
vars:
userFile: /home/cloud_user/vars/list
tasks:
- name: create file #going to create file with defined path
file:
state: touch
path: "{{ userFile }}"
- name: list users #add inside of file users name looping through items
lineinfile:
path: "{{ userFile }}"
line: "{{ item }}"
with_items:
- "{{ staff }}"
- "{{ faculty }}"
- "{{ other }}"
What are facts?
- Facts are information discovered by Ansible about a target system
- There are two ways facts are collected:
- Using the setup module with an ad-hoc command:
ansible all -m setup
- Facts are gathered by default when a playbook is executed
- Using the setup module with an ad-hoc command:
- Fact gathering in playbooks may be disabled using the gather_facts attribute
How to use facts
Facts.d - custome facts
ansible labservers -m setup | less
- gather facts for labservers host groupansible labservers -m setup --limit innaghiyev2c.mylabserver.com -a "filter=*ip*"
- gathering facts with used filteransible labservers -m setup --limit innaghiyev2c.mylabserver.com -a "filter=*dist*"
innaghiyev2c.mylabserver.com | SUCCESS => {
"ansible_facts": {
"ansible_distribution": "RedHat",
"ansible_distribution_file_parsed": true,
"ansible_distribution_file_path": "/etc/redhat-release",
"ansible_distribution_file_search_string": "Red Hat",
"ansible_distribution_file_variety": "RedHat",
"ansible_distribution_major_version": "7",
"ansible_distribution_release": "Maipo",
"ansible_distribution_version": "7.7",
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false
}
Custom facts set up:
sudo mkdir -p /etc/ansible/facts.d
- createfacts.d
directorysudo vim /etc/ansible/facts.d/prefs.fact
- create custom fact with.fact
extension
[cloud_user@innaghiyev2c playbook]$ sudo cat /etc/ansible/facts.d/prefs.fact
[location]
type=physical
datacenter=Alexandrea
ansible labservers --limit innaghiyev2c.mylabserver.com -m setup -a "filter=ansible_local"
- if we call ansible_local facts. Output gonna be like that
innaghiyev2c.mylabserver.com | SUCCESS => {
"ansible_facts": {
"ansible_local": {
"prefs": {
"location": {
"datacenter": "Alexandrea",
"type": "physical"
}
}
},
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false
}
A colleague of yours was the unfortunate victim of a scam email, and their network account was compromised. Shortly after you finished helping them pack up their desk, your boss gave you the assignment to promote system security through deploying a hardened sudoers file. You will need to create an Ansible template of the sudoers file that meets the following criteria:
- A file named /etc/sudoers.d/hardened to deploy on all ansible inventory servers. WARNING: Do NOT edit the default sudoers file, doing so may break your exercise environment. Additionally, always validate any file placed in /etc/sudoers.d with
/sbin/visudo -cf <filename>
prior to deployment!! - Grant users in the sysops group the ability to run all commands as root for each local system by IP address. This would be what the entry in your result - file except with the target system's IP:
%sysops 34.124.22.55 = (ALL) ALL
. - Define the host_alias group WEBSERVERS to contain all servers in the ansible web inventory group:
Host_Alias WEBSERVERS = <host name>
- Define the host_alias group DBSERVERS to contain all servers in the ansible database inventory group:
Host_Alias DBSERVERS = <host name>
- Grant users in the httpd group the ability to
sudo su - webuser
on the WEBSERVERS hosts:%httpd WEBSERVERS = /bin/su - webuser
- Grant users in the dba group sudo su - dbuser on the DBSERVERS hosts:
%dba DBSERVERS = /bin/su - dbuser
- The file must be validated using
/sbin/visudo -cf
before deployment.
You will need to create an accompanying playbook in /home/ansible/security.yml
that will deploy this template to all servers in the default inventory.
Summary tasks list:
- Create a template sudoers file in */home/ansible/hardened.j2 *that produces a file with appropriate output for each host.
- The deployed file should resemble the following, except with the IP and hostnames customized appropriately:
%sysops 34.124.22.55 = (ALL) ALL
Host_Alias WEBSERVERS = server1, server2
Host_Alias DBSERVERS = serverA, serverB
%httpd WEBSERVERS = /bin/su - webuser
%dba DBSERVERS = /bin/su - dbuser
- Create a playbook in /home/ansible/security.yml that uses the template module to deploy the template on all servers in the default ansible inventory after validating the syntax of the generated file.
- Note: You may find it easier to have the play output to /home/ansible/test and validate manually using
/sbin/visudo -cf <filename>
before using the template module's validate. - IMPORTANT: Do not deploy any file to
/etc/sudoers.d/
without first validating with visudo! A syntax error in asudoers
file will break sudo on the system and require starting the exercise over again! - Note: The video shows the use of join(' ') which is a typo. To support multiple hosts in the sudoers file it should instead be join(', ')
- Note: You may find it easier to have the play output to /home/ansible/test and validate manually using
- Run the playbook and ensure the files deployed correctly.
Create a Template sudoers File in /home/ansible/hardened.j2
That Produces a File with Appropriate Output for Each Host
touch /home/ansible/hardened.j2
The Deployed File Should Resemble the Example File Except with the IP and hostnames Customized Appropriately
- Edit hardened.j2 to contain the following text:
%sysops {{ ansible_default_ipv4.address }} = (ALL) ALL
Host_Alias WEBSERVERS = {{ groups['web']|join(', ') }}
Host_Alias DBSERVERS = {{ groups['database']|join(', ') }}
%httpd WEBSERVERS = /bin/su - webuser
%dba DBSERVERS = /bin/su - dbuser
Create a Playbook in /home/ansible/security.yml
That Uses the Template Module to Deploy the Template on All Servers in the Default Ansible Inventory After Validating the Syntax of the Generated File
- Edit /home/ansible/security.yml to contain the following:
---
- hosts: all
become: yes
tasks:
- name: deploy sudo template
template:
src: /home/ansible/hardened.j2
dest: /etc/sudoers.d/hardened
validate: /sbin/visudo -cf %s
ansible-playbook /home/ansible/security.yml
Check the local /etc/sudoers.d/hardened on the ansible control node for the correct contents.
-
Tasks - the tasks directory contains the main list of tasks to be executed by the role.
- This directory must include a main.yml if that directroy is being used. You can think of main.yml as the entry point for the tasks section of the role.
-
Vars - the vars directory contains variables used within the role
- The vars directory is entered via a main.yml
- The vars directory is one of three primary ways to interact with variables within a role (aside from convetional variable use such as inventory). The other two ways are using the defautls directory and passing parameters to the role
- The vars directory has the highest level of precedence. It will override inventory variables as well
- The vars directory may only be overridded by variablers passed via CLI.
-
The defaults - directory that contains default variables for the role
- The defaults directory is entered via a main.yml
- The default directory is one of three primary was to interact with vairables within a role (aside from conventional variables use such as inventory). The other two ways are using the directory ans passing parameters to the role.
- The defaults directory has the lowest level of precedence.
- The defaults directory is only meant to provide a value to a variable if no other value is given.
Best practice dictates that you properly namespace your variables when working with a role to avoid conflicts.
-
The handlers directory contains handlers, which may be used by this role or even anywhere outside this role
- The directory is entered via a main.yml within the directory
-
More on handlers:
- Handlers are essentially tasks that my be flagged to run using the notify keyword
- The notify keyword will only flag the handler if a task block makes changes
- A handler will only be triggered once even if they are notified by multiple tasks
-
The files directory contains files which can be deployyed vis this role
- Files within this directory may be referenced without path throughout the role.
- Note this directory is for ordinary files (not var fies or templates)
-
The templates directory contains templates which can be deployed via this role
- Templates within this directory may be referenced without a path throughout the role.
In this terminal-side demonstration, a new role is created and then applied to a target host.
ansible-galaxy role init apache
- create an apache role with default structure by using ansible-galaxy
[cloud_user@innaghiyev2c ~]$ ll /etc/ansible/roles/apache/
total 4
drwxrwxr-x. 2 cloud_user cloud_user 21 Mar 9 08:27 defaults
drwxrwxr-x. 2 cloud_user cloud_user 6 Mar 9 08:27 files
drwxrwxr-x. 2 cloud_user cloud_user 21 Mar 9 08:27 handlers
drwxrwxr-x. 2 cloud_user cloud_user 21 Mar 9 08:27 meta
-rw-rw-r--. 1 cloud_user cloud_user 1328 Mar 9 08:27 README.md
drwxrwxr-x. 2 cloud_user cloud_user 21 Mar 9 08:27 tasks
drwxrwxr-x. 2 cloud_user cloud_user 6 Mar 9 08:27 templates
drwxrwxr-x. 2 cloud_user cloud_user 37 Mar 9 08:27 tests
drwxrwxr-x. 2 cloud_user cloud_user 21 Mar 9 08:27 vars
vim /etc/ansible/roles/tasks/main.yml
- we're going to start fromtasks
---
# tasks file for apache
- name: install apache
yum: name=httpd state=latest
- name: copy httpd.conf template
template:
src: httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
notify: restart httpd
- name: enable and start service
service:
name: httpd
enabled: yes
state: started
ls -l /etc/ansible/roles/apache/templates
- our templates for apache role stored in templates directory, no need to define full path for it
[cloud_user@innaghiyev2c templates]$ ls -l
total 12
-rw-r--r--. 1 root root 11765 Mar 9 08:47 httpd.conf.j2
vim /etc/ansible/roles/apache/defaults/main.yml
- we're going to setup default attributes.
---
# defaults file for apache
apache_server_admin: [email protected]
/etc/ansible/roles/apache/handlers/main.yml
- configuring handler
---
# handlers file for apache
- name: restart apache service
service: name=httpd state=restarted
listen: "restart httpd"
/etc/ansible/roles/install.yml
- configure playbook to use apache role
---
- hosts: labservers
become: yes
roles:
- apache
ansible-playbook install.yml
- finally run your playbook and check for results.
PLAY RECAP **********************************************************************************************************************************************************
innaghiyev1c.mylabserver.com : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
innaghiyev2c.mylabserver.com : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
innaghiyev3c.mylabserver.com : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
vim /etc/ansible/roles/install.yml
- let's change our default variable for apache_server_admin
---
- hosts: labservers
become: yes
roles:
- apache
vars:
apache_server_admin: [email protected]
vim -R /etc/httpd/conf/httpd.conf
- as we can see new ServerAdmin value has been updated
# ServerAdmin: Your address, where problems with the server should be
# e-mailed. This address appears on some server-generated pages, such
# as error documents. e.g. [email protected]
#
ServerAdmin [email protected]
Working with roles is further covered in a discussion on static vs dynamic roles followed by a lecture on how to create and work with role dependencies in Ansible. This lesson concludes with a demonstration of creating a new role that is dependent on another role.
We can put some conditions and tags to involve a roles inside of playbook:
---
- hosts: webservers
tasks:
- include_role:
name: apache
tags:
- RH_HTTPD
when "ansible_os_family == 'RedHat'"
You can configure roles to use dependencies:
---
dependencies:
- role: common
vars:
some_parameter: 3
- role: apache
vars:
apache_port: 80
-
Meta directory:
- The meta-directory defines certain meta data for the role.
- Relevant meta data includes role dependecies and vairous role level configurations such as allow_duplicates.
The meta-directroy is entered via a main.yml
-
Nesting:
- Roles my include other roles using the dependecies keyword.
- Dependent roles are applied prior to the role dependent on them
- A role using the same parameters will not be applied more than one time. This can cause complication with role dependencies
- Having
allow_duplicates: true
defined in meta/main.yml within a role will allow the role to be applied more than once.
-
ansible-galaxy init php-webserver
- let's generate php-webserver role. That roles is going to use apache role as a dependency role. -
sudo vim /etc/ansible/roles/php-webserver/tasks/main.yml
- write tasks for a php-webserver role
---
# tasks file for php-webserver
- name: install php
yum: name= {{ item }} state=latest
with_items:
- php
- php-gd
- php-pear
- php-mysql
notify: restart httpd
sudo vim /etc/ansible/roles/php-webserver/meta/main.yml
- configure dependency from meta directory. We only need dependencies block here
dependencies:
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
# if you add dependencies to this list.
- role: apache
sudo vim /etc/ansible/roles/install.yml
- our cookbook will look like this
---
- hosts: labservers
become: yes
roles:
- php-webserver
From output it can be seen that dependency apache role run first and then our php-webserver role.
PLAY [labservers] ********************************************************************************************************************************************
TASK [Gathering Facts] ********************************************************************************************************************************************
ok: [innaghiyev2c.mylabserver.com]
TASK [apache : install apache] ********************************************************************************************************************************************
ok: [innaghiyev2c.mylabserver.com]
TASK [apache : copy httpd.conf template] ***********************************************************************************************************************************
ok: [innaghiyev2c.mylabserver.com]
TASK [apache : enable and start service] ***********************************************************************************************************************************
ok: [innaghiyev2c.mylabserver.com]
TASK [php-webserver : install php] ***********************************************************************************************************************************
ok: [innaghiyev2c.mylabserver.com] => (item=php)
ok: [innaghiyev2c.mylabserver.com] => (item=php-gd)
ok: [innaghiyev2c.mylabserver.com] => (item=php-pear)
ok: [innaghiyev2c.mylabserver.com] => (item=php-mysql)
PLAY RECAP ************************************************************************************************************************
innaghiyev2c.mylabserver.com : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
You have just started a new job as the operations lead at a small company. There is currently no formal server baseline, and it makes for a mixed configuration environment that is consuming more support and maintenance than it should. You have decided to create a baseline process using Ansible by creating a baseline role. You have noted the following commonalities that should be included in the baseline role:
- Set /etc/motd based on a template.
- Install the latest Nagios client.
- Add the Nagios server to /etc/hosts.
- Create a noc user.
- Import the noc user's public key (copy authorized keys to /home/noc/.ssh/authorized_keys).
The role should be called "baseline" and should reside in /etc/ansible/roles on the ansible control node.
You will test your role on some newly requested webservers. A playbook called web.yml has been provided for you and deploys httpd to all servers in the web group (defined in your default inventory). You will need to edit the playbook to deploy the baseline role to the servers in the web group as well.
You will find the motd template, Nagios server IP information, the noc user's public key, and the web.yml playbook in /home/ansible/resources on the ansible control node.
Summary tasks list:
- Create the necessary directories and files for the baseline role.
- Configure the role to deploy the /etc/motd template.
- Configure the role to install the latest Nagios client.
- Configure the role to add an entry to /etc/hosts for the Nagios server.
- Configure the role to create the noc user and deploy the provided public key for the noc user on target systems (copy authorized_keys to /home/noc/.ssh authorized_keys with the owner and group owner set as noc and the mode as 0600).
- Edit web.yml to deploy the baseline role in addition to what it already does.
- Verify that your role works by deploying web.yml with Ansible.
Important notes:
- For your convenience, Ansible is already installed on the control node.
- The user ansible is on all servers with the appropriate shared keys for access to necessary servers from the control node.
- The ansible user has sudo access with no password. It uses the same password as cloud_user.
- All the necessary Ansible inventories have been created for you.
Run the following commands to create the structure needed for the role:
sudo mkdir /etc/ansible/roles/baseline && sudo chown ansible.ansible /etc/ansible/roles/baseline
mkdir /etc/ansible/roles/baseline/{templates,tasks,files}
echo "---" > /etc/ansible/roles/baseline/tasks/main.yml
cp /home/ansible/resources/motd.j2 /etc/ansible/roles/baseline/templates
- Create a file called
/etc/ansible/roles/baseline/tasks/deploy_motd.yml
with the following content:
---
- template:
src: motd.j2
dest: /etc/motd
- Edit
/etc/ansible/roles/baseline/tasks/main.yml
to include the following lines at the bottom of the file:
- name: configure motd
import_tasks: deploy_motd.yml
- Create a file called
/etc/ansible/roles/baseline/tasks/deploy_nagios.yml
with the following content:
---
- yum: name=nrpe state=latest
- Edit
/etc/ansible/roles/baseline/tasks/main.yml
to include the following lines at the bottom of the file (take care with the formatting.):
- name: deploy nagios client
import_tasks: deploy_nagios.yml
- Create a file called
/etc/ansible/roles/baseline/tasks/edit_hosts.yml
with the following content, substituting with the IP specified in/home/ansible/resources/nagios_info.txt
:
---
- lineinfile:
line: "<<PROVIDED>PROVIDED> nagios.example.com"
path: /etc/hosts
- Edit
/etc/ansible/roles/baseline/tasks/main.yml
to include the following lines at the bottom of the file:
- name: edit hosts file
import_tasks: edit_hosts.yml
Configure the Role to Create the noc User and Deploy the Provided Public Key for the noc User on Target Systems
-
Copy the file
/home/ansible/resources/authorized_keys*
to*/etc/ansible/roles/baseline/files/
. -
Create a file called
/etc/ansible/roles/baseline/tasks/deploy_noc_user.yml
with the following content:
---
- user: name=noc
- file:
state: directory
path: /home/noc/.ssh
mode: 0600
owner: noc
group: noc
- copy:
src: authorized_keys
dest: /home/noc/.ssh/authorized_keys
mode: 0644
owner: noc
group: noc
- Edit
/etc/ansible/roles/baseline/tasks/main.yml
to include the following lines at the bottom of the file:
- name: set up noc user and key
import_tasks: deploy_noc_user.yml
Edit /home/ansible/resources/web.yml
to the following:
---
- hosts: webservers
become: yes
roles:
- baseline
tasks:
- name: install httpd
yum: name=httpd state=latest
- name: start and enable httpd
service: name=httpd state=started enabled=yes
Run ansible-playbook /home/ansible/resources/web.yml
.
Ansible Galaxy - galaxy.ansible.com or github.com:
-
Essentially a large public repository of Ansible roles.
-
Role ship with readmes detailining role use and available variables.
-
Galaxy contains a large number of roles that are constantly evolving and increasing.
-
Galaxy can use git allowing for other role sources such as GitHub
-
Ansible ships with the ansible-galaxy command which may be used to install roles form Galaxy among other useful role management features.
-
ansible-galaxy
can also create new empty roles in your working directory like so:ansible-galaxy init <role_name>
-
Using
ansible-galaxy install <username.role>
, you can download roels from galaxy.ansible.com -
Roles installed in the roles_path may be listed using
ansible-galaxy list
-
Remove, search, and loging are other useful subcommands.
-
The -p flag allows specification of local role location (
ansible-galaxy
uses/etc/ansible/roles
by default) -
ansible-galaxy init mysql
- initiate mysql named role with required structure inside
[cloud_user@innaghiyev2c ~]$ ansible-galaxy init mysql
- Role mysql was created successfully
[cloud_user@innaghiyev2c ~]$ ll mysql/
total 4
drwxrwxr-x. 2 cloud_user cloud_user 21 Mar 10 09:29 defaults
drwxrwxr-x. 2 cloud_user cloud_user 6 Mar 10 09:29 files
drwxrwxr-x. 2 cloud_user cloud_user 21 Mar 10 09:29 handlers
drwxrwxr-x. 2 cloud_user cloud_user 21 Mar 10 09:29 meta
-rw-rw-r--. 1 cloud_user cloud_user 1328 Mar 10 09:29 README.md
drwxrwxr-x. 2 cloud_user cloud_user 21 Mar 10 09:29 tasks
drwxrwxr-x. 2 cloud_user cloud_user 6 Mar 10 09:29 templates
drwxrwxr-x. 2 cloud_user cloud_user 37 Mar 10 09:29 tests
drwxrwxr-x. 2 cloud_user cloud_user 21 Mar 10 09:29 vars
ansible-galaxy search elasticsearch
- search for an elasticsearch role on ansible-galaxy
Found 487 roles matching your search:
Name Description
---- -----------
0x0i.elasticsearch Elasticsearch, a real-time distributed search and analytics engine
0x0i.grafana Grafana - an analytics and monitoring observability platform
0x0i.kibana Kibana, an analytics and visualization platform designed to operate with Elasticsearch
1it.sudo Ansible role for managing sudoers
aaronpederson.fluentd Really simple management of data collection from logs or scripts. Installation from gem.
abelboldu.elasticsearch Elasticsearch for Linux.
abelboldu.logstash Logstash for Linux.
abraverm.kibana-management Kibana objects (dashboard, visualization) management
adamaod.Single-Instance-ELK Single Instance Elk Stack
AerisCloud.collectd This role takes care of adding collectd to any given server
AerisCloud.elasticsearch Installs ElasticSearch
AerisCloud.librato Install and configure the Librato Agent
AerisCloud.logstash This role is used to add log aggregation and forwarding capability to a given server
AerisCloud.repos Manage CentOS yum and Debian apt repositories
-
ansible-galaxy install elastic.elasticsearch
- install elasticsearch role from ansible-galaxy -
let's create simple playbook which will install role for us:
---
- hosts: localhost
become: yes
tasks:
- name: install role
command: ansible-galaxy install elastic.elasticsearch
sudo ansible-galaxy remove elastic.elasticsearch
- remove installed role
This lecture covers how to configure Ansible for higher performance using Ansible Forks. There is also a demonstration on how to use the serial keyword to batch host operations.
- It's posssible to control the number of hosts acted upon at once time by Ansible
- The Ansible process will create forks to execute actions in parallel.
- By default, the process will only for 5 times
- The number of forks can be set for a single command using -f flag with either the ansible or ansible-playbook commands.
- The default may be changed in ansible.cfg
- The serial keyword may also confine the number of simultaneous updates within a playbook
For number:
-
best practice recommends do not increase it more than 50
-
5 is pretty small number
-
you can increase your fork number up to 10 or 15 without risk
-
serial
- used to speficy on how many hosts playbook will be executed simultaneously
---
- hosts: labservers
become: yes
serial:
- 1 #run on one hosts
- 2 #run on two hosts
- 50% #run on 50% of hosts
tasks:
- name: add host entry
lineinfile:
path: /etc/hosts
line: "webserver mywebserver.labs.com"
- From run it can be seen that playbook first run on one host, then two and finally the rest
[cloud_user@innaghiyev2c ~]$ ansible-playbook serial.yml
PLAY [labservers] **********************************************************************************************************************************************************
TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [innaghiyev1c.mylabserver.com]
TASK [add host entry] ******************************************************************************************************************************************************
changed: [innaghiyev1c.mylabserver.com]
PLAY [labservers] **********************************************************************************************************************************************************
TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [innaghiyev2c.mylabserver.com]
ok: [innaghiyev3c.mylabserver.com]
TASK [add host entry] ******************************************************************************************************************************************************
changed: [innaghiyev2c.mylabserver.com]
changed: [innaghiyev3c.mylabserver.com]
PLAY RECAP *****************************************************************************************************************************************************************
innaghiyev1c.mylabserver.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
innaghiyev2c.mylabserver.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
innaghiyev3c.mylabserver.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
max_fail_percentage: 30
- we can use this key to provide percentage of failure during cookbook run. If 1/3 of our cookbook run will fail, then whole playbook gonna stop and concidered as failed
-
The
ansible-vault
command allows file encryption, and requires a password to unencrypt -
Command:
ansible-vault encrypt <file>
-
The ansible-vault rekey command will allow you to re-encrypt a file and reset the password.
-
To supply the vault password during play execution, you must use either of the
--ask-vault-password
or--ask-vault-file
flags. -
Ansible 2.4 introduces the
--vault-id
feature. -
It is also possible to set
no_log
within a module to censor sensitive log output -
vault-id
feature- going to replace
--ask-vault-password
or--ask-vault-file
flags - before you can only specify one password for whole vault
vault-id
provides a possibility to set several passwords for a single play.vault-id
will go through each password stored invault
for encypted file to find proper one.- it's possible to set up
label
forvault-id
- going to replace
As a demonstration let's create simple text file:
echo "Super secret word stored here" > secret.txt
ansible-vault encrypt secret.txt
- simply encrypt our file
[cloud_user@innaghiyev2c ~]$ ansible-vault encrypt secret.txt
New Vault password: <your vault password here>
Confirm New Vault password: <confirm your vault password here>
Encryption successful
[cloud_user@innaghiyev2c ~]$ cat secret.txt
- as an output we have this now
$ANSIBLE_VAULT;1.1;AES256
38643439333433636239326461326234386361306331366666636534623065343237393662363538
3635633736663639663162326166636561666639653930650a303762393030663230386438393361
64336461643063383564306230313037363166623735386164363964323265366332626138663266
3638643239626366660a613162316565303936396437393133336631346166636538336533653637
31396364666430653163306164336535333562343464376438663361663436643765
ansible-vault edit secret.txt
- if you want to edit encrypted fileansible-vault decrypt secret.txt
- decrypt your fileansible-vault encrypt_string 'The answer is 42' -n meaning
- you can encrypt pieces of your playbook, rather all filesansible-vault encrypt_string 'The answer is 42' -n meaning --vault-id dev@prompt
- provide vault-id with a labeldev
[cloud_user@innaghiyev2c ~]$ ansible-vault encrypt_string 'The answer is 42' -n meaning --vault-id dev@prompt
New vault password (dev):
Confirm new vault password (dev):
meaning: !vault |
$ANSIBLE_VAULT;1.2;AES256;dev
36333866373732363065613065643062383936656461626235326238643162303863343465373166
6431633033383432396638383463636636666364386165370a326337653336613564623363633362
31666264646662633365333237366631343130316136353939386131396432393233383732356261
6133353264626234630a353233366234343564653737383637633565623364633466343565623435
37393137383861373631636135616265613166323361356266353836626265356135
Encryption successful
- We have following a playbook for testing:
---
- hosts: localhost
vars_files:
- /home/cloud_user/secure
tasks:
- name: Output message
shell: echo {{ message }} > /home/cloud_user/deployed.txt
- let's create simple file with
password
word inside:
[cloud_user@innaghiyev2c ~]$ cat vault
password
ansible-vault encrypt --vault-id prod@vault secure
- encryptsecure
file by labeling it as aprod
and using filevault
we recently created
[cloud_user@innaghiyev2c ~]$ ansible-vault encrypt --vault-id prod@vault secure
Encryption successful
secure
file looks like that now:
[cloud_user@innaghiyev2c ~]$ cat secure
$ANSIBLE_VAULT;1.2;AES256;prod
64366564623135316434353863666465646330626435613865363839626565353738363861336134
6234353734313535623764393439666463613831356434310a336365663839393465333535313061
64303464336666343739373736653162333866663733393930646366643031326239616538316665
3963616365336631610a623566666265306432316435303032383435336165613432343761353165
66336333366632353166643638663865366231356430333034663135343266633636
- Let's try to run our
vault.yml
playbook
[cloud_user@innaghiyev2c ~]$ ansible-playbook vault.yml
ERROR! Attempting to decrypt but no vault secrets found
- Same command, but with
vault-id
providing.
[cloud_user@innaghiyev2c ~]$ ansible-playbook vault.yml --vault-id prod@vault
- If we run playbook with
-v
- verbose key. We will see content of the encrypted file
changed: [localhost] => {"changed": true, "cmd": "echo I am a walrus"
- That can be prevented by using simple
no_log: True
string.
---
- hosts: localhost
vars_files:
- /home/cloud_user/secure
tasks:
- name: Output message
shell: echo {{ message }} > /home/cloud_user/deployed.txt
no_log: true
- Now output of the playbook will look like that:
TASK [Output message] **********************************************************************************
changed: [localhost] => {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result", "changed": true}
In an effort to improve security, you have been tasked with securing an Ansible variable file. The variable file is to be used in an ansible job that creates a secure website. To do this, complete the following:
- Encrypt the file /home/ansible/secret using ansible-vault.
- Then configure a vault password file named /home/ansible/vault to be used to run the Ansible playbook /home/ansible/secPage.yml successfully with the encrypted secrets file.
- Verify your work by running the secPage.yml playbook using ansible-playbook and specifying your vault password file.
- Test that the site deployed correctly by trying to access http://node1/secure/classified.html using the user bond with the password james.
Summary tasks list:
- Encrypt /home/ansible/secret using the
ansible-vault
command. - Create /home/ansible/vault as a vault password file that may be used to access the encrypted secret file without prompt.
- Run the playbook /home/ansible/secPage.yml using your vault password file to validate your work.
- Verify that the secure page deployed correctly by attempting to access http://node1/secure/classified.html as the user bond with the password james.
- Run
ansible-vault encrypt /home/ansible/secret
and provide a simple password of your choosing. - Be sure to remember the password!
Create /home/ansible/vault as a vault password file that may be used to access the encrypted secret file without prompt.
- Run the command
echo "<Your_Vault_Password>" > /home/ansible/vault
. - Substitute <<Your_Vault_Password>Your_Vault_Password> with the password you chose in the previous task.
- Run the command
ansible-playbook --vault-password-file /home/ansible/vault /home/ansible/secPage.yml
. - If your encryption was configured correctly, you should get no errors.
Verify that the secure page deployed correctly by attempting to access http://node1/secure/classified.html as the user bond with the password james.
- Run
curl -u bond http://node1/secure/classified.html
and supply the password james when prompted. - The command should return the contents of classified.html regarding the weather in a certain city.
- Ansible Tower provides a web server interface to ansible.
- System requirements are somewhat heavy.
- Tower is only free for minimal use. Working with more than a few systems requires a paid license
- The two key benefitty of Ansible Tower are user permissioning and the audit trail (only provided with license)
Steps to install ansible Tower:
- Go to the following link https://docs.ansible.com/ansible-tower/latest/html/quickinstall/download_tower.html
- Download an un-tar your archive
- Get a free license (maximum 10 hosts)
- Read manual in README.md in your main directory that you just unarchived
Ansible Tower Deployment
========================
This collection of files provides a complete set of playbooks for deploying
the Ansible Tower software to a single-server installation. It is also to
install Tower to the local machine, or to a remote machine reachable by SSH.
For quickly getting started with installation and setup instructions, refer to:
- Ansible Tower Quick Installation Guide -- http://docs.ansible.com/ansible-tower/latest/html/quickinstall/index.html
- Ansible Tower Quick Setup Guide -- http://docs.ansible.com/ansible-tower/latest/html/quickstart/index.html
For more indepth documentation, refer to:
- Ansible Tower Installation and Reference Guide -- http://docs.ansible.com/ansible-tower/latest/html/installandreference/index.html
- Ansible Tower User Guide -- http://docs.ansible.com/ansible-tower/latest/html/userguide/index.html
- Ansible Tower Administration Guide -- http://docs.ansible.com/ansible-tower/latest/html/administration/index.html
- Ansible Tower API Guide -- http://docs.ansible.com/ansible-tower/latest/html/towerapi/index.html
To install or upgrade, start by editing the inventory file in this directory.
Uncomment and change the password from 'password' for the 3 variables below.
* admin_password
* pg_password
* rabbitmq_password
Tower can be installed in 3 different modes:
1. On a single machine. This is the default, and will install in this mode with
no modifications to the inventory file.
2. On a single machine with a remote PostgreSQL database. Supplying the pg_host
and pg_port variables will trigger this mode of installation.
3. Cluster/High Availability, multiple machines with a remote PostgreSQL database.
Adding multiple hosts to the [tower] inventory group will trigger this mode of
installation. Note that pg_host and pg_port are also required.
- Next one is ansible tower inventory file:
[tower]
localhost ansible_connection=local
[database]
[all:vars]
admin_password=''
pg_host=''
pg_port=''
pg_database='awx'
pg_username='awx'
pg_password=''
pg_sslmode='prefer' # set to 'verify-full' for client-side enforced SSL
rabbitmq_username=tower
rabbitmq_password=''
rabbitmq_cookie=cookiemonster
# Isolated Tower nodes automatically generate an RSA key for authentication;
# To disable this behavior, set this value to false
# isolated_key_generation=true
# SSL-related variables
# If set, this will install a custom CA certificate to the system trust store.
# custom_ca_cert=/path/to/ca.crt
# Certificate and key to install in nginx for the web UI and API
# web_server_ssl_cert=/path/to/tower.cert
# web_server_ssl_key=/path/to/tower.key
# Use SSL for RabbitMQ inter-node communication. Because RabbitMQ never
# communicates outside the cluster, a private CA and certificates will be
# created, and do not need to be supplied.
# rabbitmq_use_ssl=False
# Server-side SSL settings for PostgreSQL (when we are installing it).
# postgres_use_ssl=False
# postgres_ssl_cert=/path/to/pgsql.crt
# postgres_ssl_key=/path/to/pgsql.key
- some of the basic configuration stored in
/etc/tower/settings.py
-
You can create a new project and select SCM type (Manual, Git, Mercirial etc)
-
Inventories - in this tab we can manage our invetory files, editing and adding new hosts inside of inventory files.
- groups can be added inside of inventories tab
- ad-hoc commands can be executed from hosts tab
-
Credential - from this tab you can manage your users, access type, import your private key, prompt for password/passphrase and so on
-
Templates - the same as in ansible playbook
-
Jobs - you can see the status of your jobs from this tab
Two main ways to find documentation:
-
built-in ansible commands
-
ansible-doc
- by using this command we can search for any ansible module documentationansible-doc lineinfile
- get help aboutlineinfile
module
> LINEINFILE (/usr/lib/python2.7/site-packages/ansible/modules/files/lineinfile.py)
This module ensures a particular line is in a file, or replace an existing line using a back-referenced regular expression. This is primarily useful when you
want to change a single line in a file only. See the [replace] module if you want to change multiple, similar lines or check [blockinfile] if you want to
insert/update/remove a block of lines in a file. For other cases, see the [copy] or [template] modules.
* This module is maintained by The Ansible Core Team
OPTIONS (= is mandatory):
- attributes
The attributes the resulting file or directory should have.
To get supported flags look at the man page for `chattr' on the target system.
This string should contain the attributes in the same order as the one displayed by `lsattr'.
The `=' operator is assumed as default, otherwise `+' or `-' operators need to be included in the string.
(Aliases: attr)[Default: (null)]
type: str
version_added: 2.3
- backrefs
Used with `state=present'.
If set, `line' can contain backreferences (both positional and named) that will get populated if the `regexp' matches.
This parameter changes the operation of the module slightly; `insertbefore' and `insertafter' will be ignored, and if the `regexp' does not match anywhere in
the file, the file will be left unchanged.
If the `regexp' does match, the last matching line will be replaced by the expanded line parameter.
[Default: False]
type: bool
version_added: 1.1
- backup
Create a backup file including the timestamp information so you can get the original file back if you somehow clobbered it incorrectly.
[Default: False]
type: bool
- create
Used with `state=present'.
If specified, the file will be created if it does not already exist.
By default it will fail if the file is missing.
[Default: False]
type: bool
- firstmatch
Used with `insertafter' or `insertbefore'.
If set, `insertafter' and `insertbefore' will work with the first line that matches the given regular expression.
[Default: False]
type: bool
version_added: 2.5
- group
Name of the group that should own the file/directory, as would be fed to `chown'.
:
ansible-doc replace
- another example withreplace
module
# Prior to Ansible 2.7.10, using before and after in combination did the opposite of what was intended.
# see https://github.com/ansible/ansible/issues/31354 for details.
- name: Replace between the expressions (requires Ansible >= 2.4)
replace:
path: /etc/hosts
after: '<VirtualHost [*]>'
before: '</VirtualHost>'
regexp: '^(.+)$'
replace: '# \1'
- name: Supports common file attributes
replace:
path: /home/jdoe/.ssh/known_hosts
regexp: '^old\.host\.name[^\n]*\n'
owner: jdoe
group: jdoe
mode: '0644'
- name: Supports a validate command
replace:
path: /etc/apache/ports
regexp: '^(NameVirtualHost|Listen)\s+80\s*$'
replace: '\1 127.0.0.1:8080'
validate: '/usr/sbin/apache2ctl -f %s -t'
- name: Short form task (in ansible 2+) necessitates backslash-escaped sequences
replace: path=/etc/hosts regexp='\\b(localhost)(\\d*)\\b' replace='\\1\\2.localdomain\\2 \\1\\2'
- name: Long form task does not
replace:
path: /etc/hosts
regexp: '\b(localhost)(\d*)\b'
replace: '\1\2.localdomain\2 \1\2'
- name: Explicitly specifying positional matched groups in replacement
replace:
path: /etc/ssh/sshd_config
regexp: '^(ListenAddress[ ]+)[^\n]+$'
replace: '\g<1>0.0.0.0'
- name: Explicitly specifying named matched groups
replace:
path: /etc/ssh/sshd_config
regexp: '^(?P<dctv>ListenAddress[ ]+)(?P<host>[^\n]+)$'
replace: '#\g<dctv>\g<host>\n\g<dctv>0.0.0.0'
ansible-doc -s htpasswd
- with -s key we will get more consolidated view.
- name: manage user files for basic authentication
htpasswd:
attributes: # The attributes the resulting file or directory should have. To get supported flags look at the man page for `chattr' on the target system. This string should contain the
attributes in the same order as the one displayed by `lsattr'. The `=' operator is assumed as default, otherwise `+' or `-' operators need to be
included in the string.
create: # Used with `state=present'. If specified, the file will be created if it does not already exist. If set to "no", will fail if the file does not exist
crypt_scheme: # Encryption scheme to be used. As well as the four choices listed here, you can also use any other hash supported by passlib, such as md5_crypt and sha256_crypt, which are linux
passwd hashes. If you do so the password file will not be compatible with Apache or Nginx
group: # Name of the group that should own the file/directory, as would be fed to `chown'.
mode: # The permissions the resulting file or directory should have. For those used to `/usr/bin/chmod' remember that modes are actually octal numbers. You must either add a leading zero
so that Ansible's YAML parser knows it is an octal number (like `0644' or `01777') or quote it (like `'644'' or `'1777'') so Ansible receives a
string and can do its own conversion from string into number. Giving Ansible a number without following one of these rules will end up with a
decimal number which will have unexpected results. As of Ansible 1.8, the mode may be specified as a symbolic mode (for example, `u+rwx' or
`u=rw,g=r,o=r'). As of Ansible 2.6, the mode may also be the special string `preserve'. When set to `preserve' the file will be given the same
permissions as the source file.
name: # (required) User name to add or remove
owner: # Name of the user that should own the file/directory, as would be fed to `chown'.
password: # Password associated with user. Must be specified if user does not exist yet.
path: # (required) Path to the file that contains the usernames and passwords
selevel: # The level part of the SELinux file context. This is the MLS/MCS attribute, sometimes known as the `range'. When set to `_default', it will use the `level' portion of the policy if
available.
serole: # The role part of the SELinux file context. When set to `_default', it will use the `role' portion of the policy if available.
setype: # The type part of the SELinux file context. When set to `_default', it will use the `type' portion of the policy if available.
seuser: # The user part of the SELinux file context. By default it uses the `system' policy, where applicable. When set to `_default', it will use the `user' portion of the policy if
available.
state: # Whether the user entry should be present or not
unsafe_writes: # Influence when to use atomic operation to prevent data corruption or inconsistent reads from the target file. By default this module uses atomic operations to prevent data
corruption or inconsistent reads from the target files, but sometimes systems are configured or just broken in ways that prevent this. One example
is docker mounted files, which cannot be updated atomically from inside the container and can only be written in an unsafe manner. This option
allows Ansible to fall back to unsafe methods of updating files when atomic operations fail (however, it doesn't force Ansible to perform unsafe
writes). IMPORTANT! Unsafe writes are subject to race conditions and can lead to data corruption.
- Do not hesitate to use http://docs.ansible.com. Very similar to
ansible-doc
but nicer with all information about modules. In http view
The new exam now mentions, as an objective, needing to know how to install and configure an Ansible control node:
This lesson will help you to learn how to:
- Install required packages
- Create a static host inventory file
- Configure Ansible managed nodes
- Configure privilege escalation on managed nodes
- Validate a working configuration using ad-hoc Ansible commands
Following commands have been used during this process:
sudo yum install ansible
- install ansiblesudo useradd ansible
andsudo passwd ansible
- setup ansible usersudo visudo
- configure ansible user to run without password promptansible ALL=(ALL) NOPASSWD: ALL
- add this line to visudo
vim /etc/hosts
- add worker hosts to master node as aclient1
andclient2
vim /etc/ansible/hosts
- addclient1
andclient2
in your default inventory filessh-keygen
- generate ssh key on your master node, with ansible user.ssh-copy-id client1
- distribute ssh key to the worker nodesansible all -m ping
- run ad-hoc command usingping
module through all hostsansible all -m ping -b
- run ad-hoc command in escalated mode (-b) usingping
module through all hosts
Why shell scripts?
- Shell scripts can be used to hide complexity
- You can use shell scripts easily with Ansible ad-hoc commands
- People not experienced in Ansible can leverage them
- People not skilled in Ansible can create and use them
- There is no need to know yam and .yml formatiing
Ansible and Firewall Rules
-
There are Ansible modules that can be used with firewalls
-
The firewalld module like othersm, can be used to add or remove rules.
-
firewalld module - https://docs.ansible.com/ansible/latest/modules/firewalld_module.html
-
iptables module - https://docs.ansible.com/ansible/latest/modules/iptables_module.html
-
Playbook for installation and enabling firewalld:
---
- hosts: labservers
user: ansible
become: yes
gather_facts: no
tasks:
- name: install firewalld
action: yum name=firewalld state=installed
- name: enable firewalld on system boot
service: name=firewalld enabled=yes
- name: start service firewalld, if not started
service:
name: firewalld
state: started
- Second playbook will install elinks and httpd on your nodes
---
- hosts: labservers
user: ansible
become: yes
gather_facts: no
tasks:
- name: install elinks
action: yum name=elinks state=installed
- name: install httpd
action: yum name=httpd state=installed
- name: enable and start apache on system reboot
service: name=httpd enabled=yes state=started
-
elinks http://localhost
- our apache server accessible from internal network -
elinks http://client
- but it's not accessible from outside -
Let's create playbook to change this firewall rule:
---
- hosts: labservers
user: ansible
become: yes
gather_facts: no
tasks:
- firewalld:
service: http
permanent: yes
state: enabled
- name: restart service firewalld
service:
name: firewalld
state: restarted
The Archive Module
- There are Ansible modules that can be used for archive purposes.
- It is mentioned as a part of exam's objectives
- Archive module documentation page: https://docs.ansible.com/ansible/latest/modules/archive_module.html
- Assumes the compression source exists on the target
- Does not copy source files from the local system to the target before archiving
- You can delete source files after archiving by using the remove=true option.
The Unarchive module
-
The opposite module is unarchive
-
unarchive module documentation page: https://docs.ansible.com/ansible/latest/modules/unarchive_module.html
-
if a checksum is required, the use get_url or uri instead
-
By default it will copy from the source file to the target before unpacking
-
Let's create some backup-logs playbook. Which will archive directory for us and fetch it by hostnames.
---
- hosts: all
user: ansible
become: yes
gather_facts: no
tasks:
- name: Compress directory /var/log/ into /home/ansible/logs.zip
archive:
path: /var/log
dest: /home/ansible/logs.tar.gz
owner: ansible
group: ansible
format: gz
- name: Fetch the log files to the local filesystem
fetch:
src: /home/ansible/logs.tar.gz
dest: logbackup-{{ inventory_hostname }}.tar.gz
flat: yes
The Cron Module
- The cron module is used to manage crontab on your nodes.
- You can create environmnet variables as well as named crontab entries
- You should add a name with the crontrab entry so it can be removed easily with a playbook
e.g. name: "Job 0001"
- When managing environment variables, no comment line gets added; however, the module uses the name parameter to find the correct definition line.
The Cron Module - Extra Parameters
-
You remove the crontab entry by using
state: absent
in a playbook. -
The
name:
gets matched for a removal -
You can use the boolean disabled to comment out an entry (only works if
state=present
) -
Jobs can be set to run at reboot if required. Use the
special_time: reboot
if that is required. -
You can add a specific user if you need to set a crontab entry for a user (need to become root)
-
Use
insertafter
orinsertbefore
to add env entry before or after another env entry -
Let's create some cron playbook, which will run command to check space at 5am and 5pm
---
- hosts: all
user: ansible
become: yes
gather_facts: no
tasks:
- name: Ensure a job that runs at 5am and 5pm exists.
cron:
name: "Job 0001"
minute: "0"
hour: "5,17"
job: "df -h >> /tmp/diskspace"
- name: Creates an entry like "PATH=/opt/bin/" on top of crontab
cron:
name: PATH
env: yes
job: /opt/bin
- name: Create an entry like "APP_HOME=/srv/app" and insert it after PATH declaration
cron:
name: APP_HOME
env: yes
job: /srv/app
insertbefore: PATH
state: absent
- to remove all created crontab entries.
at software
- The at command is used to schedule jobs that will be running once in the future.
- It is used for single ad-hoc jobs and is not meant as a replacement for cron
- It is part of a group of commands that get installed with the at software
- The other commands are:
at
- executes commands at a specific time.atq
- lists the users pending jobsatrm
- deletes a job by its job numberbatch
- executes the command depending depending on specific system load levels. A value must be specified.- only
at
andatrm
are controlled via theat
module
at software - Service Details
-
It may not be on all systems, so verify its installation
-
On Red Hat or CentOS systems it is installed with a
yum install at
command -
The service is controlled via the
atd
daemon -
Cookbook for installing and enabling
at
---
- hosts: all
user: ansible
become: yes
gather_facts: no
tasks:
- name: install the at command for job scheduling
action: yum name=at state=installed
- name: enable and start service at if not started
service:
name: atd
state: started
enabled: yes
- Scheduling a task with
at
module
---
- hosts: all
user: ansible
become: yes
gather_facts: no
tasks:
- name: schedule a command to execute in 20 minutes as the ansible user
at:
command: df -h >> tmp/diskspace
count: 20
units: minutes
state: absent
- to remove scheduled task
Ansible Security Tasks
- Ansible is very useful as a security tool
- You can make security changes to many nodes at once
- You can apply changes to help with easily securing nodes
- You can check lots of nodes for vulnerabilities quickly
- It can work well with other tools that you may have in place
- Check for Ansible modules that can be used for security tasks
- Not just for Linux - can be used for OS X, Solaris, Windows, and others
- Can be used for devices such as NetApp or EMC storagfe, F5 and others
Some Ansible Modules for Security
-
selinux - Configures the SELinux mode and policy
-
firewalld and iptables- Both manage firewall policies
-
pamd - Manages PAM modules
-
Capable of working with Datadog, Nagios and other monitoring tools.
-
Manage users and groups (bulk add and delete users if you don't have SSO ability)
-
Can manage certificates such as OpenSSL or SSH
Let's us consider some examples.
ansible all -a /usr/bin/uptime
- check uptime of our nodes- We're going to create a playbook to check SELinux status
---
- hosts: all
user: ansible
become: yes
gather_facts: no
tasks:
- name: Enable SELinux
selinux:
policy: targeted
state: enforcing
- Let's add some user that expires and is a member of a group
---
- hosts: all
user: ansible
become: yes
gather_facts: no
tasks:
- name: Ensure group "developers" exists
group:
name: developers
state: present
sudo useradd tempuser
- create a usersudo passwd tempuser
- set a password for a usersudo grep tempuser /etc/shadow
- get password hash
tempuser:$6$U/WVBoCW$UX62EjlZLVucylus7N8NZ4/WV2o6kDFIMwaPAjNukwnVxYrF3tZhOCnJwnIXwxseRVrxybneDrYJuTXQ0hpAS0:18349:0:99999:7:::
- go to https://www.epochconverter.com/ to get epoch timestamp. Set your expire date there
- our cookbook will look like that
---
- hosts: all
user: ansible
become: yes
gather_facts: no
tasks:
- name: Add a consultant whose account you want to expire
user:
name: james20
shell: /bin/bash
groups: developers
append: yes
expires: 1585402826 #epoch time here
password: $6$U/WVBoCW$UX62EjlZLVucylus7N8NZ4/WV2o6kDFIMwaPAjNukwnVxYrF3tZhOCnJwnIXwxseRVrxybneDrYJuTXQ0hpAS0
# we added password hash above
tail /etc/passwd
- we can see that james20 user has been createdchage -l james20
- to see when account expires
Last password change : Mar 28, 2020
Password expires : never
Password inactive : never
Account expires : Apr 28, 2020
Minimum number of days between password change : 0
Maximum number of days between password change : 99999
Number of days of warning before password expires : 7