Friday, 15 July 2022

Unix Shell Redirection

Redirection operators > and >> can write into a file or a device.

  • > will overwrite existing file or crate a new file
  • >> will append text to existing file or create a new file

 

Example: redirecting command output into a file

$ touch temp.txt
$ echo "Hello, world!" > temp.txt
$ cat temp.txt
Hello, world!
$ echo "Hello, world!" > temp.txt
$ cat temp.txt
Hello, world!
$ echo "Hello, world!" >> temp.txt
$ cat temp.txt
Hello, world!
Hello, world!

Here are examples where redirect operators crated new files:

$ echo "Hello, world!" > temp2.txt
$ cat temp2.txt
Hello, world!
$ echo "Hello, world!" >> temp3.txt
$ cat temp3.txt
Hello, world!

Devices/files:

0 - stdin (standard input)
1 - stdout (standard output)
2 - stderr (error message output) 
/dev/null - special device (null device) which discards any input

 

Example: discarding command output messages (including error messages)

command > /dev/null 2>$1

2>$1 redirects stderr into stdout and > /dev/null redirects stdout into null device.

More compact version of the above line is:

command  &> /dev/null

&> /dev/null redirects both stdout and stderr into null device.


References:


What does “>” do vs “>>”?

Thursday, 7 July 2022

How to run a basic Ansible playbook locally


 

Here is an example of the simplest Ansible playbook:

hello_world.yaml:

---
- name: Run Ansible playbook locally
  hosts: localhost
  gather_facts: no

  tasks:
    - ansible.builtin.debug:
        msg: Hello, world!

 

ansible-playbook

 

We can run it with ansible-playbook which executes tasks from playbook on a defined hosts. This tool is installed within Ansible (core) installation:
 
$ which ansible-playbook
/usr/bin/ansible-playbook
 
To run the playbook:

$ ansible-playbook hello_world.yaml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [Run Ansible playbook locally] *********************************************************************************************************************************

TASK [ansible.builtin.debug] ****************************************************************************************************************************************
ok: [localhost] => {
    "changed": false,
    "msg": "Hello, world!"
}

PLAY RECAP **********************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


To see the changes made during execution of the playbook we can use --diff option:

$ ansible-playbook hello_world.yaml --diff

From $ ansible-playbook --help:

-D, --diff: when changing (small) files and templates, show the differences in those files; works great with --check

To dry run the playbook we need to use check mode (--check or -C):

$ ansible-playbook hello_world.yaml --check

From $ ansible-playbook --help:

-C, --check: don't make any changes; instead, try to predict some of the changes that may occur

Playbook is run in read-only mode, Ansible still connects to the hosts and checks their state.

If someone manually made changes on the host, Ansible detects that but with --check option it will not revert that state to the one defined in playbook. But we'll be able to see what has been changed:

TASK [task_name]
changed: [node1]

To see changes that would be made should the playbook be executed we can combine --diff and --check options:

$ ansible-playbook hello_world.yaml --diff --check

Example:

- name: Create directory and a file in it
  hosts: localhost
  tasks:
    - name: Create directory
      ansible.builtin.file:
          path: ./temp
          state: directory
          mode: '0755'

    - name: Create a file, using symbolic modes to set the permissions (equivalent to 0644)
      ansible.builtin.file:
        path: ./temp/foo.txt
        state: touch
        mode: u=rw,g=r,o=r

Dry run and diff output:

$ ansible-playbook multiple_plays.yaml --check --diff
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [Create directory and a file in it] ****************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************
ok: [localhost]

TASK [Create directory] *********************************************************************************************************************************************
--- before
+++ after
@@ -1,4 +1,4 @@
 {
     "path": "./temp",
-    "state": "absent"
+    "state": "directory"
 }

changed: [localhost]

TASK [Create a file, using symbolic modes to set the permissions (equivalent to 0644)] ******************************************************************************
ok: [localhost]

PLAY RECAP **********************************************************************************************************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

 

Validating tasks: check mode and diff mode — Ansible Documentation

 

ansible-navigator 

 

Ansible playbooks can also be run by ansible-navigator:

$ ansible-navigator run hello_world.yaml 

This will not print the output in terminal but we can find it in stdout value in JSON file (that gets created next to the YAML source file):

hello_world-artifact-2022-07-07T10:06:23.959652+00:00.json

To get the output in stdout, we can use --mode:

$ ansible-navigator run ./run_locally/hello_world.yaml --mode stdout
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'

PLAY [Run Ansible playbook locally] ********************************************

TASK [Print debug message] *****************************************************
ok: [localhost] => {
    "msg": "Hello, world!"
}

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 


---

Wednesday, 6 July 2022

Introduction to Ansible


Ansible helps:
  • automate the deployment
  • manage automation
  • orchestrate
  • manage configurations
  • deploy applications
  • provision/deprovision
  • deliver continuously
  • secure and comply
on
  • firewalls
  • load balancers
  • applications
  • containers
  • virtualization platforms
  • servers
  • clouds
  • storage
  • network devices
  • ...
 
Ansible is:
  • Infrastructure as Code (IaC) tool that allows using a single central location (Ansible control node) to monitor and control a large number of remote servers (hosts)
  • set of tools for orchestration, configuration management, deployment, and task execution mainly by using playbooks
  • used for configuring Linux and Windows operating systems over the ssh protocol 
 

Ansible VS Bash - When to use one over another?

 
 
 
 
The benefit of using Ansible is that commands are idempotent, this means that we can re-run commands and if the effect we desire is already there nothing will change, this is an improvement over creating our own scripts that will probably have unforeseen consequences when we run them multiple times. 
 

Ansible VS Terraform - When to use one over another?

Use Terraform to provision resources and then Ansible to install software on them.

 
Announcements of the latest releases: Ansible Announcements - Google Groups

 

Ansible Core 

 
Ansible Core components:
  • CLI - includes ansible, ansible-playbook, ansible-doc and other tools 
  • language - YAML, for crating set of rules for developing playbooks
  • framework - allows collections to be installed and operated from Ansible Hub and Ansible Galaxy
  • functions - includes conditionals, blocks, includes, loops and other imperatives 

 

Basic Ansible concepts:
  • playbook is a high-level organization of procedures using plays
  • plays are specific procedures for a group of hosts;
  • tasks are specific actions
  • modules are units of code
  • inventory is a list of managed nodes

Idempotency

In infrastructure automation, idempotency means to reach a specific end state that remains the same, no matter how many times the process is executed. This concept exists in most Ansible modules because after you specify the desired final state, Ansible will accomplish it. If you are using some of the lower-level modules, like command or shell, or developing your own modules, be careful to write code that will be idempotent and safe to repeat many times to get the same result. [How to use Ansible to document procedures | Opensource.com]


Ansible Playbooks

  • lists of tasks we want our remote hosts to perform
  • describe the desired state for operating system properties like files, services, filesystems etc...
  • can be saved and reused, allowing to automate complex processes
  • They are written as YAML documents. 
Playbook example:

playbook.yaml:

# Taken from Red Hat's "Ansible Basics: An Automation Technical Overview" Udemy course
---
- name: Install and start Apache
  hosts: web
  become: yes

  tasks:
    - name: httpd package is present
      yum:
        name: httpd
        state: latest

    - name: latest index.html file is present
      template:
        src: files/index.html
        dest: /var/www/html/

    - name: httpd is started
      service:
        name: httpd
        state: started

 
A valid yaml file starts with triple dash (---).

Ansible playbook consists of:
  • plays
    • single playbook can contain multiple plays
  • modules
    • tasks have 1:1 correlation with modules e.g. yum module, template module, service module
    • task name is optional but recommended
    • module has parameters e.g. yum has name and state (in the example above this instructs yum to install the latest httpd)
  • plugins

Running Playbooks


Playbooks is interpreted and run against one or more hosts, task by task. 
The order of the tasks defines the execution.
In each task the module does the actual work.
 
Output messages are coloured:

A task executed as expected, no change was made.
A task executed as expected, making a change.
A task failed to execute successfully. 

To run playbooks we can use ansible-playbook or Ansible Navigator

If we run playbook for the first time, we'll see in the output that host has been changed for each task:

 
PLAY [play_name] 

TASK [task0]
ok: [node1]
 
TASK [task1]
changed: [node1]
 
TASK [task1]
changed: [node1]
 
TASK [task1]
changed: [node1]
 
PLAY RECAP
node1: ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

 
Anisble follows the concept of idempotence: it knows what the state of the host should be (based on the playbook) and if it's already in that state, it will do nothing. So, next time we run the same playbook, nothing would be changed (provided that no manual changes were made on the host):

PLAY [play_name] 

TASK [task0]
ok: [node1]
 
TASK [task1]
ok: [node1]
 
TASK [task1]
ok: [node1]
 
TASK [task1]
ok: [node1]
 
PLAY RECAP
node1: ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0


This shows that Ansible is not just configuration management but also policy enforcement tool. We can schedule e.g. daily hosts & inventory audit by scheduling daily execution of this playbook via automation controller and each time it is run, Ansible will check if the state of the hosts is compliant with policies set in playbooks. If someone manually makes changes on hosts, that would be detected and related tasks would be executed reverting those changes to the desired state defined in the playbook.
 
 

Plays

  • they describe what is being automated
  • top level specification for a group of tasks
  • full list of its attributes
  • we must specify: 
    • hosts where play will be executed on (simultaneously, in parallel)
    • control behaviour (fact gathering, privilege level). E.g. privilege escalation is defined by become. yes means that Ansible will become sudo-er by default.
  • single playbook that executes on different hosts can contain multiple plays
Example:

- name: Install and start Apache
  hosts: web
  become: yes

Modules

  • "tools in the toolkit"
  • parametrized components with internal logic, representing a single step to be done
  • they "do" things in Ansible
  • each task is 1 to 1 with a module
  • can use any language but usually Python or Powershell (for Windows)
  • there are Ansible built-in modules (part of ansible-core and included in all Ansible installations), community modules, modules of 3rd party providers (AWS, Cisco, Google, ...) etc...: full list of modules
  • here are some built-in modules:

Example: template task is using template (ansible.builtin.template) module

    - name: latest index.html file is present
      template:
        src: files/index.html
        dest: /var/www/html/

 

Plugins

  • the "extra" bits that allow us to change behaviour or do extra things outside Ansible
  • technically, each module is a plugin
  • pieces of code that augment Ansible's core functionality
  • Ansible uses a plugin architecture to enable a rich, flexible and expandable feature set

Example #1: become plugin - for privilege escalation that can be done on a play or module level

 become: yes

Example #2: filter plugins - allow us to change the variable output of a particular task into a JSON, YAML or CSV

{{ some_variable | to_nice_json }}
{{ some_variable | to_nice_yaml }}

 

Ansible Inventory

  • systems/hosts or groups of hosts that a playbook runs against
  • Ansible works against multiple systems in an inventory
  • List of systems in your infrastructure that automation is executed against
  • Groups can be defined so certain playbooks are executed only on those hosts within the group and not on other hosts, within other groups.
  • Inventory is usually file based; could be a DB, GitHub
  • Can have multiple groups
  • Can have variables for each group or even host
    • Good practice is that these variables are used only to connect to devices e.g. (encrypted) username, password, type of connection (SSH, API, ..)
       
  • From Safespring | Säkra, lokala molntjänster
When working with static hosts in a data center, inventories are often also static textfiles maintained manually or semi-manually. However, inventories can also be dynamic, i.e. provided by scripts. 
When working with OpenStack, it is possible to use inventory scripts that queries the OpenStack API directly and produces a complete inventory of all instances with metadata, all the group memberships and so on, but oftentimes these scripts take a long time to run, and they generally need to run every time you run a playbook, thus making playbook runs orders of magnitude more time-consuming than static inventories. Also, they can put a heavy load on the OpenStack APIs if the inventory is frequently queried.

 
Example:
 
[web]
webserver1.example.com
webserver2.example.com
 
[db] 
dbserver1.example.com

[switches]
leaf01.internal.com
leaf02.internal.com
 
 
Use [group_name] format to define a group of hosts. e.g. web, db and switches are groups of hosts.

Example #2: static inventory includes systems with IP addresses as well as FQDN (Fully Qualified Domain Name).

[myservers]
10.42.0.3
10.42.0.24
10.42.0.101
host.example.com
 
Example #3 shows:
  • pinning a host to a particular IP address by using ansible_host keyword. Without this, DNS is used by default.
  • group variables by using [group_name:vars] construct
    • all is an implicitly included group, default group that every host is in it

[app1srv]
appserver01 ansible_host=10.42.0.3
appserver02 ansible_host=10.42.0.24
 
[web] 
webserver01

[web:vars]
apache_listen_port=8080
apache_root_path=/var/www/mywebdocs/

[all:vars]
ansible_user=ansible
ansible_ssh_private_key_file=/home/ansible/.ssh/id_rsa

Example #4 shows groups of groups, defined via [group_name:children] construct. They can also contain standalone hosts:

[nashville]
bnaapp01
bnaapp02
 
[atlanta]
atlapp03
atlapp04
 
[south:children] 
atlanta
nashville
hsvapp05

 

Ansible Roles

  • reusable automation actions
  • reusable playbooks
  • groups of tasks that usually get executed together
  • a reusable structure that groups our automation tasks and variables 
  • they are written once and can be shared
  • can be stored in collection

Example:

- name: Install and start Apache
  hosts: web
  become: yes
  roles:
     - common
     - webservers 
 

To see the recommended role directory structure the best is probably to explore one created by ansible-galaxy role init command:

$ ansible-galaxy init test_role
- Role test_role was created successfully

$ tree ./test_role/
./test_role/
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

These files are prepopulated while the others are empty:

$ cat ./test_role/meta/main.yml 
galaxy_info:
  author: your name
  description: your role description
  company: your company (optional)

  # If the issue tracker for your role is not on github, uncomment the
  # next line and provide a value
  # issue_tracker_url: http://example.com/issue/tracker

  # Choose a valid license ID from https://spdx.org - some suggested licenses:
  # - BSD-3-Clause (default)
  # - MIT
  # - GPL-2.0-or-later
  # - GPL-3.0-only
  # - Apache-2.0
  # - CC-BY-4.0
  license: license (GPL-2.0-or-later, MIT, etc)

  min_ansible_version: 2.1

  # If this a Container Enabled role, provide the minimum Ansible Container version.
  # min_ansible_container_version:

  galaxy_tags: []
    # List tags for your role here, one per line. A tag is a keyword that describes
    # and categorizes the role. Users find roles by searching for tags. Be sure to
    # remove the '[]' above, if you add tags to this list.
    #
    # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
    #       Maximum 20 tags per role.

dependencies: []
  # List your role dependencies here, one per line. Be sure to remove the '[]' above,
  # if you add dependencies to this list.


$ cat ./test_role/tests/inventory 
localhost

$ cat ./test_role/tests/test.yml 
---
- hosts: localhost
  remote_user: root
  roles:
    - test_role


The minimal setup for role directory is just a single file: ./test_role/tasks/main.yaml.



We can define roles:
  • in the directory named roles which should be in the same directory as the playbook
  • in the directory which is set as roles_path value (relative path to the directory where from we run  ansible-playbook)


Ansible Collections

  • the way to share a content for Ansible
  • simplified and consistent content delivery
  • data structure containing automation content:
    • modules
    • playbooks
    • roles
    • plugins
    • docs
    • tests
  • a file structure (tarball) for sharing content that community agreed
  • collection examples: Cisco IOS, Rist IOS, NetApp collection
  • there are public collections available for 100+ platforms e.g. AWS, CheckPoint, Google, Cisco, IBM, Microsoft, ...
  • can be created within organization e.g. network toolkit
  • standard unit of automation
  • allow we work asynchronously from the Ansible releases
  • collections can have dependencies on other collections


Ansible project structure:

nginx_core
    MANIFEST.json
    playbooks
        deploy-nginx.yml
        ...
    plugins
    README.md
    roles
        nginx
            defaults
            files
                ...
            tasks
            templates
                ...
        nginx_app_protect
        nginx_config
     

deploy-nginx.yml (collection):
 
---
-   name: Install NGINX Plus
    hosts: all
    tasks:
        - name: Install NGINX 
          include_role:
                name: nginxinc.nginx
          vars:
                nginx_type: plus
        - name: Install NGINX App Protect
          include_role:
                name: nginxinc.nginx_app_protect
         vars:
                nginx_app_protect_setup_license: false
                nginx_app_protect_remove_license: false
                nginx_app_protect_install_signatures: false 

 


Automation Execution Environments

 

Collections might have different dependencies e.g. Python version, APIs, SDKs, OS system libraries, particular Ansible version. Automation Execution Environments (AEE) keep runtime environment, collections and dependencies aligned by packaging all components together in a cloud-native way.

AEE = Universal Base Image (Collections + Libraries + Ansible Core) 

There are minimal AEEs available, that we can use to build our own AEEs on top of.

Execution Environment builder is used to build images.

Automation Content Navigator is used for running AEEs.  


Ansible Automation Execution

2 types:
  • local
    • for automating Network Devices / API endpoints (e.g. AWS)
    • module code is executed locally on the control node
    • this is run as parallel processes on the control node
    • Ansible Automation Platform can be installed as a cluster and scale this further
       
  • remote
    • for executions on Linux/Windows hosts
    • code is executed on the end system
    • module code is copied to the managed node, executed, then removed
    • during execution, these remote agents are sending back to control node JSON blobs with information on what action it performed, success of actions etc...
       

Configuration File

  • ansible.cfg file
  • basic configuration for Ansible
  • can be in multiple locations, with different precedence

 

Ansible Variables

 Example:


---
- name: variables demo playbook
  hosts: localhost
  vars:
     var_one: Hello
     var_two: world
     # example of variable unpacking (double curly braces):
     var_three: "{{var_one}}, {{var_one}}!"
  tasks:
  - name: print out var_three
   # debug module is handy for printing variable values (or any other messages)
   debug:
     msg: "{{ var_three}}" # "Hello, world!"


Ansible Facts

 They are variables with standard (pre-defined) names whose values are coming from hosts themselves (local host or remote, managed nodes), e.g. host name, or host IPv4 address:

---
- name: Output localhost facts
  hosts: localhost
  tasks:
  - name: Prints localhost facts
    debug:
      msg: The default IPv4 address of {{ ansible_fqdn }} is {{ ansible_default_ipv4.address }}

To see run this playbook:
 
$ ansible-playbook facts_demo.yaml
...
TASK [Prints Ansible facts] ***********************************************************************************************************************************************************
ok: [localhost] => {
    "msg": "The default IPv4 address of bojans_computer is 11.186.34.126"
}
...

Built-in module ansible.builtin.setup module – Gathers facts about remote hosts — Ansible Documentation collects all facts from a local Linux system:

This module is automatically called by playbooks to gather useful variables about remote hosts that can be used in playbooks. It can also be executed directly by /usr/bin/ansible to check what variables are available to a host. Ansible provides many facts about the system, automatically.

"automatically called by playbooks" means that each playbook implicitly has this line added in yaml:

gather_facts: true

For custom platforms there are different modules, e.g. for Cisco devices, there is cisco.ios.ios_facts module – Module to collect facts from remote devices. — Ansible Documentation.
 

To see values of all facts from a local host we need to specify setup module and set host pattern to localhost:

$ ansible -m setup localhost

To see values of all facts from all hosts:

$ ansible -m setup all

Facts can be used for host audits e.g. to check hardware attributes for vulnerabilities, uptime etc...They can also be used for creating dynamic reports e.g. like web page which shows attributes of all hosts in a system (see example network-automation/ansible_inventory_report: This repo contains an Ansible networking inventory report playbook (inventory.yml). This playbook exports hostname, platform, mgmt0 IP address and code version to a HTML file. The jinja2 template used for the website can also highlight the version of code if it doesn't match a desired version.). 


Troubleshooting

Provide the outputs for the following commands:

$ ansible --version
ansible [core 2.12.10]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/bojan/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = /home/bojan/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0]
  jinja version = 3.0.3
  libyaml = True


$ ansible-config dump 
ACTION_WARNINGS(default) = True
AGNOSTIC_BECOME_PROMPT(default) = True
ALLOW_WORLD_READABLE_TMPFILES(default) = False
ANSIBLE_CONNECTION_PATH(default) = None
ANSIBLE_COW_ACCEPTLIST(default) = ['bud-frogs', 'bunny', 'cheese', 'daemon', 'default', 'dragon', 'elephant-in-snake', 'elephant', 'eyes', 'hellokitty', 'kitty', 'luke>
ANSIBLE_COW_PATH(default) = None
ANSIBLE_COW_SELECTION(default) = default
...


Working with sensitive information


Passwords and other sensitive information can be protected with Ansible Vault. Vault can encrypt binary files, group_vars, host_vars, include_vars, and var_files. But this encrypted data is exposed when you run a playbook in -v (verbose) mode, so it's a good idea to combine it with the keyword no_log set to true to hide any task's information, as it indicates that the value of the argument should not be logged or displayed. [How to use Ansible to document procedures | Opensource.com]




Testing Ansible Playbooks


---

References



Introduction to Ansible Navigator

 


 
Ansible Navigator (ansible-navigator), available since Ansible v2, is:
  • A text-based user interface (TUI) for the Red Hat Ansible Automation Platform.
  • command based tool for creating, reviewing, and troubleshooting Ansible content, including inventories, playbooks, and collections.
To install it on a control node running the Ubuntu:

$ sudo apt install python3-pip
$ python3 -m pip install ansible-navigator --user

To check its install location:
 
$ which ansible-navigator
/home/bojan/.local/bin/ansible-navigator


To make it available in the current shell (terminal):
 
$ echo 'export PATH=$HOME/.local/bin:$PATH' >> ~/.profile
$ source ~/.profile
 
Let's check out its CLI commands:
 
$ ansible-navigator --help
Usage: ansible-navigator [options]

Options (global):
 -h     --help                                   Show this help message and exit
 --version                                       Show the application version and exit
 --rad  --ansible-runner-artifact-dir            The directory path to store artifacts generated by ansible-runner
 --rac  --ansible-runner-rotate-artifacts-count  Keep ansible-runner artifact directories, for last n runs, if set to 0 artifact directories won't be deleted
 --rt   --ansible-runner-timeout                 The timeout value after which ansible-runner will forcefully stop the execution
 --cdcp --collection-doc-cache-path              The path to collection doc cache (default: /home/bojan/.cache/ansible-navigator/collection_doc_cache.db)
 --ce   --container-engine                       Specify the container engine (auto=podman then docker) (auto|podman|docker) (default: auto)
 --co   --container-options                      Extra parameters passed to the container engine command
 --dc   --display-color                          Enable the use of color for mode interactive and stdout (true|false) (default: true)
 --ecmd --editor-command                         Specify the editor command (default: vi +{line_number} {filename})
 --econ --editor-console                         Specify if the editor is console based (true|false) (default: true)
 --ee   --execution-environment                  Enable or disable the use of an execution environment (true|false) (default: true)
 --eei  --execution-environment-image            Specify the name of the execution environment image (default: quay.io/ansible/creator-ee:v0.4.2)
 --eev  --execution-environment-volume-mounts    Specify volume to be bind mounted within an execution environment (--eev /home/user/test:/home/user/test:Z)
 --la   --log-append                             Specify if log messages should be appended to an existing log file, otherwise a new log file will be created per
                                                 session (true|false) (default: true)
 --lf   --log-file                               Specify the full path for the ansible-navigator log file (default: /home/bojan/dev/github/ansible-demo/ansible-
                                                 navigator.log)
 --ll   --log-level                              Specify the ansible-navigator log level (debug|info|warning|error|critical) (default: warning)
 -m     --mode                                   Specify the user-interface mode (stdout|interactive) (default: interactive)
 --osc4 --osc4                                   Enable or disable terminal color changing support with OSC 4 (true|false) (default: true)
 --penv --pass-environment-variable              Specify an existing environment variable to be passed through to and set within the execution environment (--penv
                                                 MY_VAR)
 --pa   --pull-arguments                         Specify any additional parameters that should be added to the pull command when pulling an execution environment
                                                 from a container registry. e.g. --pa='--tls-verify=false'
 --pp   --pull-policy                            Specify the image pull policy always:Always pull the image, missing:Pull if not locally available, never:Never
                                                 pull the image, tag:if the image tag is 'latest', always pull the image, otherwise pull if not locally available
                                                 (always|missing|never|tag) (default: tag)
 --senv --set-environment-variable               Specify an environment variable and a value to be set within the execution environment (--senv MY_VAR=42)
 --tz   --time-zone                              Specify the IANA time zone to use or 'local' to use the system time zone (default: utc)

Subcommands:
 {subcommand} --help
  builder                                        Build execution environment (container image)
  collections                                    Explore available collections
  config                                         Explore the current ansible configuration
  doc                                            Review documentation for a module or plugin
  exec                                           Run a command within an execution environment
  images                                         Explore execution environment images
  inventory                                      Explore an inventory
  lint                                           Lint a file or directory for common errors and issues
  replay                                         Explore a previous run using a playbook artifact
  run                                            Run a playbook
  settings                                       Review the current ansible-navigator settings
  welcome                                        Start at the welcome page
 
 
On the first run it pulls a Docker image which contains a demo material:

$ ansible-navigator
--------------------------------------------------------------------
Execution environment image and pull policy overview
--------------------------------------------------------------------
Execution environment image name:     quay.io/ansible/creator-ee:v0.4.2
Execution environment image tag:      v0.4.2
Execution environment pull arguments: None
Execution environment pull policy:    tag
Execution environment pull needed:    True
--------------------------------------------------------------------
Updating the execution environment
--------------------------------------------------------------------
Running the command: docker pull quay.io/ansible/creator-ee:v0.4.2
v0.4.2: Pulling from ansible/creator-ee
f0a2109a2528: Pulling fs layer
...
4ca545ee6d5d: Pull complete
4a7326c2ac2c: Pull complete
Digest: sha256:6b2160c2c4df4fe87a5efe4f33a16c6369228d1e99a0918db517652bd09b71e9
Status: Downloaded newer image for quay.io/ansible/creator-ee:v0.4.2
quay.io/ansible/creator-ee:v0.4.2

...and then shows TUI:


│Welcome
 1│—————————————————————————————————————————————————————————————————————————————
 2│
 3│Some things you can try from here:
 4│- :collections                                    Explore available collectio
 5│- :config                                         Explore the current ansible
 6│- :doc <plugin>                                   Review documentation for a
 7│- :help                                           Show the main help page
 8│- :images                                         Explore execution environme
 9│- :inventory -i <inventory>                       Explore an inventory
10│- :log                                            Review the application log
11│- :lint <file or directory>                       Lint Ansible/YAML files (ex
12│- :open                                           Open current page in the ed
13│- :replay                                         Explore a previous run usin
14│- :run <playbook> -i <inventory>                  Run a playbook in interacti
15│- :settings                                       Review the current ansible-
16│- :quit                                           Quit the application
17│
18│happy automating,
19│
20│-winston


^b/PgUp page up    ^f/PgDn page down    ↑↓ scroll    esc back    :help help

If we type :images we'll see a list of all Docker images on the current node. Those which are Ansible Execution Environments will be highlighted:


At the bottom we can see the menu:



So, if we type :17 we'll get a menu for the image #17:


Typing :2 will show the version of Ansible core and all collections:


 

Playbook Linting

 
Ansible Navigator's subcommand lint checks the playbook for any syntax errors.
 
$ ansible-navigator lint ./run_locally/hello_world.yaml 

...outputs the following:


 
 
---

How to install Ansible on Ubuntu

 


Ansible maintains only installation via pip although other installation ways are possible e.g. via apt packages. Installing Ansible — Ansible Community Documentation


Installing Ansible via pip


To install it via pip we first need to check that we have Python installed:

 $ which python
/home/bojan/anaconda3/bin/python

Next is checking whether pip is installed:

$ python3 -m pip -V
pip 20.1.1 from /home/bojan/anaconda3/lib/python3.8/site-packages/pip (python 3.8)

To install Ansible:

$ python3 -m pip install --user ansible
Collecting ansible
  Downloading ansible-6.7.0-py3-none-any.whl (42.8 MB)
    42.8 MB 5.4 MB/s 
Collecting ansible-core~=2.13.7
  Downloading ansible_core-2.13.13-py3-none-any.whl (2.1 MB)
    2.1 MB 8.8 MB/s 
Requirement already satisfied: cryptography in /home/bojan/anaconda3/lib/python3.8/site-packages (from ansible-core~=2.13.7->ansible) (2.9.2)
Requirement already satisfied: packaging in /home/bojan/anaconda3/lib/python3.8/site-packages (from ansible-core~=2.13.7->ansible) (20.4)
Collecting resolvelib<0.9.0,>=0.5.3
  Downloading resolvelib-0.8.1-py2.py3-none-any.whl (16 kB)
Requirement already satisfied: PyYAML>=5.1 in /home/bojan/anaconda3/lib/python3.8/site-packages (from ansible-core~=2.13.7->ansible) (5.3.1)
Collecting jinja2>=3.0.0
  Downloading Jinja2-3.1.3-py3-none-any.whl (133 kB)
      133 kB 12.6 MB/s 
Requirement already satisfied: six>=1.4.1 in /home/bojan/anaconda3/lib/python3.8/site-packages (from cryptography->ansible-core~=2.13.7->ansible) (1.15.0)
Requirement already satisfied: cffi!=1.11.3,>=1.8 in /home/bojan/anaconda3/lib/python3.8/site-packages (from cryptography->ansible-core~=2.13.7->ansible) (1.14.0)
Requirement already satisfied: pyparsing>=2.0.2 in /home/bojan/anaconda3/lib/python3.8/site-packages (from packaging->ansible-core~=2.13.7->ansible) (2.4.7)
Collecting MarkupSafe>=2.0
  Downloading MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (26 kB)
Requirement already satisfied: pycparser in /home/bojan/anaconda3/lib/python3.8/site-packages (from cffi!=1.11.3,>=1.8->cryptography->ansible-core~=2.13.7->ansible) (2.20)
Installing collected packages: resolvelib, MarkupSafe, jinja2, ansible-core, ansible
Successfully installed MarkupSafe-2.1.5 ansible-6.7.0 ansible-core-2.13.13 jinja2-3.1.3 resolvelib-0.8.1


Installation via apt package maanger


On a host we want to use as Ansible control node execute:
 
$ sudo apt update
$ sudo apt install ansible

To verify its install location:
 
$ which ansible
/usr/bin/ansible
 
To verify installation, let's check Ansible version:

$ ansible --version
ansible 2.9.6
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/bojan/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.8.10 (default, Mar 15 2022, 12:22:08) [GCC 9.4.0]
 
 
We can also check out Ansible Help Command:
 
$ ansible --help
usage: ansible [-h] [--version] [-v] [-b] [--become-method BECOME_METHOD]
               [--become-user BECOME_USER] [-K] [-i INVENTORY] [--list-hosts]
               [-l SUBSET] [-P POLL_INTERVAL] [-B SECONDS] [-o] [-t TREE] [-k]
               [--private-key PRIVATE_KEY_FILE] [-u REMOTE_USER]
               [-c CONNECTION] [-T TIMEOUT]
               [--ssh-common-args SSH_COMMON_ARGS]
               [--sftp-extra-args SFTP_EXTRA_ARGS]
               [--scp-extra-args SCP_EXTRA_ARGS]
               [--ssh-extra-args SSH_EXTRA_ARGS] [-C] [--syntax-check] [-D]
               [-e EXTRA_VARS] [--vault-id VAULT_IDS]
               [--ask-vault-pass | --vault-password-file VAULT_PASSWORD_FILES]
               [-f FORKS] [-M MODULE_PATH] [--playbook-dir BASEDIR]
               [-a MODULE_ARGS] [-m MODULE_NAME]
               pattern

Define and run a single task 'playbook' against a set of hosts

positional arguments:
  pattern               host pattern

optional arguments:
  --ask-vault-pass      ask for vault password
  --list-hosts          outputs a list of matching hosts; does not execute
                        anything else
  --playbook-dir BASEDIR
                        Since this tool does not use playbooks, use this as a
                        substitute playbook directory.This sets the relative
                        path for many features including roles/ group_vars/
                        etc.
  --syntax-check        perform a syntax check on the playbook, but do not
                        execute it
  --vault-id VAULT_IDS  the vault identity to use
  --vault-password-file VAULT_PASSWORD_FILES
                        vault password file
  --version             show program's version number, config file location,
                        configured module search path, module location,
                        executable location and exit
  -B SECONDS, --background SECONDS
                        run asynchronously, failing after X seconds
                        (default=N/A)
  -C, --check           don't make any changes; instead, try to predict some
                        of the changes that may occur
  -D, --diff            when changing (small) files and templates, show the
                        differences in those files; works great with --check
  -M MODULE_PATH, --module-path MODULE_PATH
                        prepend colon-separated path(s) to module library (def
                        ault=~/.ansible/plugins/modules:/usr/share/ansible/plu
                        gins/modules)
  -P POLL_INTERVAL, --poll POLL_INTERVAL
                        set the poll interval if using -B (default=15)
  -a MODULE_ARGS, --args MODULE_ARGS
                        module arguments
  -e EXTRA_VARS, --extra-vars EXTRA_VARS
                        set additional variables as key=value or YAML/JSON, if
                        filename prepend with @
  -f FORKS, --forks FORKS
                        specify number of parallel processes to use
                        (default=5)
  -h, --help            show this help message and exit
  -i INVENTORY, --inventory INVENTORY, --inventory-file INVENTORY
                        specify inventory host path or comma separated host
                        list. --inventory-file is deprecated
  -l SUBSET, --limit SUBSET
                        further limit selected hosts to an additional pattern
  -m MODULE_NAME, --module-name MODULE_NAME
                        module name to execute (default=command)
  -o, --one-line        condense output
  -t TREE, --tree TREE  log output to this directory
  -v, --verbose         verbose mode (-vvv for more, -vvvv to enable
                        connection debugging)

Privilege Escalation Options:
  control how and which user you become as on target hosts

  --become-method BECOME_METHOD
                        privilege escalation method to use (default=sudo), use
                        `ansible-doc -t become -l` to list valid choices.
  --become-user BECOME_USER
                        run operations as this user (default=root)
  -K, --ask-become-pass
                        ask for privilege escalation password
  -b, --become          run operations with become (does not imply password
                        prompting)

Connection Options:
  control as whom and how to connect to hosts

  --private-key PRIVATE_KEY_FILE, --key-file PRIVATE_KEY_FILE
                        use this file to authenticate the connection
  --scp-extra-args SCP_EXTRA_ARGS
                        specify extra arguments to pass to scp only (e.g. -l)
  --sftp-extra-args SFTP_EXTRA_ARGS
                        specify extra arguments to pass to sftp only (e.g. -f,
                        -l)
  --ssh-common-args SSH_COMMON_ARGS
                        specify common arguments to pass to sftp/scp/ssh (e.g.
                        ProxyCommand)
  --ssh-extra-args SSH_EXTRA_ARGS
                        specify extra arguments to pass to ssh only (e.g. -R)
  -T TIMEOUT, --timeout TIMEOUT
                        override the connection timeout in seconds
                        (default=10)
  -c CONNECTION, --connection CONNECTION
                        connection type to use (default=smart)
  -k, --ask-pass        ask for connection password
  -u REMOTE_USER, --user REMOTE_USER
                        connect as this user (default=None)

Some modules do not make sense in Ad-Hoc (include, meta, etc)


 
We can peek into the Ansible configuration file:
 
$ vi /etc/ansible/ansible.cfg
 
 
/etc/ansible/ansible.cfg:
 
# config file for ansible -- https://ansible.com/
# ===============================================

# nearly all parameters can be overridden in ansible-playbook
# or with command line flags. ansible will read ANSIBLE_CONFIG,
# ansible.cfg in the current working directory, .ansible.cfg in
# the home directory or /etc/ansible/ansible.cfg, whichever it
# finds first

[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

...


Version of the Ansible package available at apt package manager is much older than the latest Ansible release: Ubuntu – Package Search Results shows version 2.9.6 (for Ubuntu Focal, 20.04) while the latest Ansible release ( Releases and maintenance — Ansible Documentation) at time of publishing this article is 5.

To uninstall Ansible installed via default apt package repository:

$ dpkg --list | grep ansible
ii  ansible                                                     2.9.6+dfsg-1                        all          Configuration management, deployment, and task execution system

$ sudo apt purge ansible

To install the most recent version of Ansible built for your/given Ubuntu version we can use ppa:ansible/ansible:

$ sudo apt update
$ sudo apt install software-properties-common
$ sudo add-apt-repository --yes --update ppa:ansible/ansible
$ sudo apt install ansible 

$ dpkg --list | grep ansible
ii  ansible                                                     5.10.0-1ppa~focal                   all          batteries-included package providing a curated set of Ansible collections in addition to ansible-core
ii  ansible-core                                                2.12.7-1ppa~focal                   all          Ansible IT Automation

Note that this recent version separates ansible and ansible-core packages and their versions.

---


Troubleshooting Ansible Installation


I had the situation where:

$ ansible
Traceback (most recent call last):
  File "/home/bojan/.local/bin/ansible", line 5, in <module>
    from ansible.cli.adhoc import main
  File "/home/bojan/.local/lib/python3.10/site-packages/ansible/cli/__init__.py", line 73, in <module>
    jinja2_version = version('jinja2')
  File "/usr/lib/python3.10/importlib/metadata/__init__.py", line 996, in version
    return distribution(distribution_name).version
  File "/usr/lib/python3.10/importlib/metadata/__init__.py", line 969, in distribution
    return Distribution.from_name(distribution_name)
  File "/usr/lib/python3.10/importlib/metadata/__init__.py", line 548, in from_name
    raise PackageNotFoundError(name)
importlib.metadata.PackageNotFoundError: No package metadata was found for jinja2

$ which ansible
/home/bojan/.local/bin/ansible


Reinstalling Ansible fixed the problem:

$ python3 -m pip install --user ansible
Collecting ansible
  Downloading ansible-10.0.0-py3-none-any.whl (53.8 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 53.8/53.8 MB 5.4 MB/s eta 0:00:00
Collecting ansible-core~=2.17.0
  Downloading ansible_core-2.17.0-py3-none-any.whl (2.2 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.2/2.2 MB 6.5 MB/s eta 0:00:00
Requirement already satisfied: resolvelib<1.1.0,>=0.5.3 in /home/bojan/.local/lib/python3.10/site-packages (from ansible-core~=2.17.0->ansible) (0.5.4)
Requirement already satisfied: packaging in /home/bojan/.local/lib/python3.10/site-packages (from ansible-core~=2.17.0->ansible) (23.1)
Requirement already satisfied: PyYAML>=5.1 in /usr/lib/python3/dist-packages (from ansible-core~=2.17.0->ansible) (5.4.1)
Collecting jinja2>=3.0.0
  Downloading jinja2-3.1.4-py3-none-any.whl (133 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 133.3/133.3 KB 10.0 MB/s eta 0:00:00
Requirement already satisfied: cryptography in /usr/lib/python3/dist-packages (from ansible-core~=2.17.0->ansible) (3.4.8)
Requirement already satisfied: MarkupSafe>=2.0 in /usr/lib/python3/dist-packages (from jinja2>=3.0.0->ansible-core~=2.17.0->ansible) (2.0.1)
Installing collected packages: jinja2, ansible-core, ansible
  Attempting uninstall: ansible-core
    Found existing installation: ansible-core 2.15.0
    Uninstalling ansible-core-2.15.0:
      Successfully uninstalled ansible-core-2.15.0
Successfully installed ansible-10.0.0 ansible-core-2.17.0 jinja2-3.1.4

Verification:

$ ansible --version
ansible [core 2.17.0]
  config file = /home/bojan/dev/github/ansible-demo/ansible.cfg
  configured module search path = ['/home/bojan/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/bojan/.local/lib/python3.10/site-packages/ansible
  ansible collection location = /home/bojan/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/bojan/.local/bin/ansible
  python version = 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] (/usr/bin/python3)
  jinja version = 3.1.4
  libyaml = True




References:

Installing Ansible on specific operating systems — Ansible Documentation