[toc]
### Syntax-check and Linting in Ansible
#### Syntax Check
:::warning
ansible-playbook <playbook-name> --syntax-check
:::
#### Ansible Lint
:::warning
Ansible has a command line linter called ansible-lint. It does not come by default and can be installed very easily by using one of the following methods:
##### Using apt:
````yaml=
sudo apt install ansible-lint
````
##### Using pip:
````yaml=
python3 -m pip install --user ansible-lint
````
:::
##### How to use ansible-lint
````yaml=
Get help:
ansible-lint --help
Check a playbook for lint errors:
ansible-lint <playbook-name>
Check all ansible-lint rules:
ansible-lint -L
````
#### Listing the hosts affected by the playbook
````yaml=
ansible-playbook <playbook-name> --list-hosts
````
#### Limiting playbook to run on specific host or hostgroups
````yaml=
ansible-playbook <playbook-name> --limit mynode.mycompany.com
````
#### ask-pass and ask-become-pass
````yaml=
--ask-pass --> ask password for ssh (in case passwordless authentication not setup)
--ask-become-pass --> ask for sudo password, if needed
````
### Working with Jinja2 templates
#### installation Jinja2
````yaml=
sudo apt-get update -y
sudo apt-get install -y python-jinja2
````
#### Create a jinja template
````yaml=
mkdir templates && cd templates
## vi motd.j2
Welcome to {{ ansible_hostname }}!
This node is running on {{ ansible_distribution }} {{ ansible_distribution_version }}
The architecture of this node is .....
This Linux node is running on ...... Kernel
````
#### Write a playbook to use the template
````yaml=
## vi motd.yaml
--- ## Playbook to create motd file on worker nodes
- hosts: nodes
become: true
tasks:
- name: write motd file with host specific information
template:
src: motd.j2
dest: /etc/motd
owner: root
group: root
mode: '0644'
````
#### This is how your directory should look like now:
```
labsuser@controller:~/templates$ tree
.
├── motd.j2
└── motd.yaml
```
#### Run the playbook and validate the changes
````yaml=
Run the playbook:
ansible-playbook motd.yaml
Validate the changes:
run the following command on worker nodes:
cat /etc/motd
or
ssh to the worker nodes and verify the output from motd file
````
### Tags in Ansible
````yaml=
--- ## Playbook to install and configure Apache
- name: install and configure apache ## Play 1
hosts: nodes
become: yes
tasks:
- name: install apache ## Apache installation
apt:
name: apache2
state: present
- name: Upload index file. ## copy index.html file from controller to all web nodes
copy:
src: index.html
dest: /var/www/html/index.html
mode: 0755
notify: start the apache2 service
tags:
- installation
- package
- apache
handlers:
- name: start the apache2 service ## make sure the service is up and running
service:
name: apache2
state: restarted
- name: create multiple db users ## Play 2
hosts: all
become: yes
tasks:
- name: install elinks
user:
name: "{{item}}"
state: present
loop:
- db1
- db2
- db3
tags:
- db
- users
- name: install specified packages ## Play 3
hosts: all
become: yes
tasks:
- name: install elinks
apt:
name: "{{item}}"
state: present
loop:
- tree
- git
- elinks
- telnet
- screen
- net-tools
- nodejs
tags:
- package
- installation
````
:mag: More information on Tagging in Ansible --> https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_tags.html
### Running part of a playbook (Typically for troubleshooting)
````yaml=1
List all tags in the specified playbook:
ansible-playbook <playbookname> --list-tags
Run only specified tags:
ansible-playbook <playbookname> --tags <tag1,tag2>
Run everything except specified tags:
ansible-playbook <playbookname> --skip-tags <tag1,tag2>
Run from a specific play/task:
ansible-playbook:<playbookname> --start-at-task
Step-by-step execution of the playbook:
ansible-playbook <playbookname> --step
## This will cause ansible to stop on each task, and ask if it should execute that task. (y/n/c)
````
:mag: More information on ansible-playbook command --> https://docs.ansible.com/ansible/latest/cli/ansible-playbook.html
### Parallelism in Ansible
#### ad-hoc commands
````yaml=
Syntax:
-f, --fork
Example:
ansible all -m shell -a "ls /tmp" -f 2
````
#### Playbooks
````yaml=
--- ## Using playbook to install elinks package
- hosts: all
become: yes
serial:
- 3
- 25%
tasks:
- name: install elinks
apt:
name: elinks
update_cache: yes
state: present
````
:mag: *More information --> https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_strategies.html*
### Ansible Vault
Ansible Vault is used to secure and ecncrypt / decrypt files within Ansible. It can perform operations list below (but not limited to):
#### With ansible Vault, you can:
````yaml
- Encrypt an existing file/playbooks
- Decryt a file
- Edit and encrypted file
- Generate or reset key
````
#### Ansible-vault CLI sub-commands
````yaml=
{create,decrypt,edit,view,encrypt,encrypt_string,rekey}
create Create new vault encrypted file
decrypt Decrypt vault encrypted file
edit Edit vault encrypted file
view View vault encrypted file
encrypt Encrypt YAML file
encrypt_string Encrypt a string
rekey Re-key a vault encrypted file
````
#### Ansible Vault Tasks
````yaml=
Create an encrypted playbook:
ansible-vault create vault-demo.yaml
view the encrypted playbook:
ansible-vault view vault-demo.yaml
Edit the encrypted playbook:
ansible-vault edit vault-demo.yaml
decrypt the playbook:
ansible-vault decrypt vault-demo.yaml
encrypt the playbook again:
ansible-vault encrypt vault-demo.yaml
Run the playbook (and watch it faile as no password given):
ansible-playbook vault-demo.yaml
Run the playbook with the flag to ask for vault password:
ansible-playbook vault-demo.yaml --ask-vault-pass
Change the vault password for an existing file:
ansible-vault rekey vault-demo.yaml
````
:mag: *More information --> https://docs.ansible.com/ansible/latest/vault_guide/vault.html*
### Metadata in Ansible (Ansible Local Facts)
:::warning
**Create a directory where all local facts would be kept:**
```
mkdir /etc/ansible/facts.d
```
:arrow_right: *Please note that **'/etc/ansible'** directory would not be present on the managed nodes, so you have to manually create the directory in order to put all the fact files inside facts.d directory.*
**create your metadata files:**
1. Create a file called dc.fact
```
vi dc.fact
```
2. Put the following content:
```
[location]
datacenter=mumbai-dc
country=india
```
3. Create another file called cost.fact
```
vi cost.fact
```
4. Put the following content:
```
[cost]
costcenter=100005
department=marketing
```
**Once done, this is how your directory structure should look like:**
```
root@worker1:/etc/ansible# tree
.
├── facts.d
│ ├── cost.fact
│ └── dc.fact
```
**Run the ansible command to get the local facts:**
```
ansible localhost -m setup -a "filter=ansible_local"
```
Expected output:
```
root@Controller:/etc/ansible/facts.d# ansible localhost -m setup -a "filter=ansible_local"
localhost | SUCCESS => {
"ansible_facts": {
"ansible_local": {
"cost": {
"location": {
"country": "india",
"datacenter": "mumbai-dc"
}
},
"dc": {
"cost": {
"costcenter": "100005",
"department": "marketing"
}
}
},
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false
}
```
:::
### Error handling in Ansible

#### Ignore errors in ansible
```yaml=
--- ## Playbook example for error handling
- hosts: localhost
tasks:
- name: create-file
file:
path: /tmp/error.txt
mode: '0755'
## state: touch
## ignore_errors: yes
- name: ping localhost
action: ping
```
#### Using block/rescue/always in Ansible playbook:
```yaml=
--- ## Playbook example for error handling
- hosts: localhost
tasks:
- name: create-file
block:
- file:
path: /tmp/error.txt
mode: '0755'
## state: touch
rescue:
- debug: msg="this part is on screen because main block failed"
- name: ping localhost
action: ping
always:
- debug: msg="This part of the playbook always runs"
```
### References
:::info
- https://www.linuxtechi.com/configure-use-ansible-jinja2-templates/
- https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_tags.html
- https://docs.ansible.com/ansible/latest/cli/ansible-playbook.html
:::