[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 ![](https://hackmd.io/_uploads/H15Be65qn.png) #### 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 :::