Saturday, 29 October 2022

ec2-user on Amazon Linux 2




I'm running an EC2 instance based on Amazon Linux 2 AMI:

$ cat /etc/os-release 
NAME="Amazon Linux"
VERSION="2"
ID="amzn"
ID_LIKE="centos rhel fedora"
VERSION_ID="2"
PRETTY_NAME="Amazon Linux 2"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2"
HOME_URL="https://amazonlinux.com/"

SSH connection is created for ec2-user:

$ whoami
ec2-user

This user can perform a sudo command and I wanted to learn why. Let's check sudoers:

$ sudo cat /etc/sudoers
## Sudoers allows particular users to run various commands as
## the root user, without needing the root password.
##
## Examples are provided at the bottom of the file for collections
## of related commands, which can then be delegated out to particular
## users or groups.
## 
## This file must be edited with the 'visudo' command.

## Host Aliases
## Groups of machines. You may prefer to use hostnames (perhaps using 
## wildcards for entire domains) or IP addresses instead.
# Host_Alias     FILESERVERS = fs1, fs2
# Host_Alias     MAILSERVERS = smtp, smtp2

## User Aliases
## These aren't often necessary, as you can use regular groups
## (ie, from files, LDAP, NIS, etc) in this file - just use %groupname 
## rather than USERALIAS
# User_Alias ADMINS = jsmith, mikem


## Command Aliases
## These are groups of related commands...

## Networking
# Cmnd_Alias NETWORKING = /sbin/route, /sbin/ifconfig, /bin/ping, /sbin/dhclient, /usr/bin/net, /sbin/iptables, /usr/bin/rfcomm, /usr/bin/wvdial, /sbin/iwconfig, /sbin/mii-tool

## Installation and management of software
# Cmnd_Alias SOFTWARE = /bin/rpm, /usr/bin/up2date, /usr/bin/yum

## Services
# Cmnd_Alias SERVICES = /sbin/service, /sbin/chkconfig, /usr/bin/systemctl start, /usr/bin/systemctl stop, /usr/bin/systemctl reload, /usr/bin/systemctl restart, /usr/bin/systemctl status, /usr/bin/systemctl enable, /usr/bin/systemctl disable

## Updating the locate database
# Cmnd_Alias LOCATE = /usr/bin/updatedb

## Storage
# Cmnd_Alias STORAGE = /sbin/fdisk, /sbin/sfdisk, /sbin/parted, /sbin/partprobe, /bin/mount, /bin/umount

## Delegating permissions
# Cmnd_Alias DELEGATING = /usr/sbin/visudo, /bin/chown, /bin/chmod, /bin/chgrp 

## Processes
# Cmnd_Alias PROCESSES = /bin/nice, /bin/kill, /usr/bin/kill, /usr/bin/killall

## Drivers
# Cmnd_Alias DRIVERS = /sbin/modprobe

# Defaults specification

#
# Refuse to run if unable to disable echo on the tty.
#
Defaults   !visiblepw

#
# Preserving HOME has security implications since many programs
# use it when searching for configuration files. Note that HOME
# is already set when the the env_reset option is enabled, so
# this option is only effective for configurations where either
# env_reset is disabled or HOME is present in the env_keep list.
#
Defaults    always_set_home
Defaults    match_group_by_gid

# Prior to version 1.8.15, groups listed in sudoers that were not
# found in the system group database were passed to the group
# plugin, if any. Starting with 1.8.15, only groups of the form
# %:group are resolved via the group plugin by default.
# We enable always_query_group_plugin to restore old behavior.
# Disable this option for new behavior.
Defaults    always_query_group_plugin

Defaults    env_reset
Defaults    env_keep =  "COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS"
Defaults    env_keep += "MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE"
Defaults    env_keep += "LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES"
Defaults    env_keep += "LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE"
Defaults    env_keep += "LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY"

#
# Adding HOME to env_keep may enable a user to run unrestricted
# commands via sudo.
#
# Defaults   env_keep += "HOME"

Defaults    secure_path = /sbin:/bin:/usr/sbin:/usr/bin

## Next comes the main part: which users can run what software on 
## which machines (the sudoers file can be shared between multiple
## systems).
## Syntax:
##
## user MACHINE=COMMANDS
##
## The COMMANDS section may have other options added to it.
##
## Allow root to run any commands anywhere 
root ALL=(ALL) ALL

## Allows members of the 'sys' group to run networking, software, 
## service management apps and more.
# %sys ALL = NETWORKING, SOFTWARE, SERVICES, STORAGE, DELEGATING, PROCESSES, LOCATE, DRIVERS

## Allows people in group wheel to run all commands
%wheel ALL=(ALL) ALL

## Same thing without a password
# %wheel ALL=(ALL) NOPASSWD: ALL

## Allows members of the users group to mount and unmount the 
## cdrom as root
# %users  ALL=/sbin/mount /mnt/cdrom, /sbin/umount /mnt/cdrom

## Allows members of the users group to shutdown this system
# %users  localhost=/sbin/shutdown -h now

## Read drop-in files from /etc/sudoers.d (the # here does not mean a comment)
#includedir /etc/sudoers.d

Let's check what's in this included directory:

$ sudo ls /etc/sudoers.d/
90-cloud-init-users

$ sudo cat  /etc/sudoers.d/90-cloud-init-users
# Created by cloud-init v. 19.3-45.amzn2 on Sun, 23 Oct 2022 10:28:25 +0000

# User rules for ec2-user
ec2-user ALL=(ALL) NOPASSWD:ALL

The last line means the following: ec2-user on any host can run any command as any user.

Syntax: User <space> OnHost = (Runas-User:Group) <space> Commands
“who where = (as_whom) what”.

Example: root ALL = (ALL:ALL) ALL

If we have some other user that we want to perform some command (e.g. restart NGINX service) that requires root rights we'd need to have in sudoers file a line like this:

my_user ALL = (root) NOPASSWD: systemctl restart nginx

---
 
IMPORTANT: Don't use just any editor (vi, vim, nano, ...) to edit sudoers file. If changes contain syntax or formatting errors and they get saved, the next time sudo command is executed, it would fail with message similar to:
 

$ sudo cat /etc/sudoers.d/90-cloud-init-users
>>> /etc/sudoers.d/90-cloud-init-users: syntax error near line 11 <<<
sudo: parse error in /etc/sudoers.d/90-cloud-init-users near line 11
sudo: no valid sudoers sources found, quitting
sudo: unable to initialize policy plugin

 
 
(If this happens on EC2 instance you need to terminate (not just stop it!) and launch a new instance, with fresh, new, EBS volume.)

Always use visudo to edit sudoers file. visudo needs to be run with root privileges otherwise the permissions error is issued:

$ visudo
visudo: /etc/sudoers: Permission denied


 
To open non-default sudoers file use -f option:
 
$ sudo visudo -f /etc/sudoers.d/90-cloud-init-users


If we try to save invalid content, visudo will give us 3 options (press ENTER after What now? to see them):

$ sudo visudo -f /etc/sudoers.d/90-cloud-init-users
>>> /etc/sudoers.d/90-cloud-init-users: syntax error near line 9 <<<
What now?
Options are:
  (e)dit sudoers file again
  e(x)it without saving changes to sudoers file
  (Q)uit and save changes to sudoers file (DANGER!)

What now?
e



---

Resources:



https://gist.github.com/sheharyarn/f3d98e8cc859f092532b
https://unix.stackexchange.com/questions/429004/trying-to-run-service-nginx-restart-from-a-non-root-user
https://serverfault.com/questions/1053769/how-to-allow-php-exec-to-reload-nginx
https://askubuntu.com/questions/692701/allowing-user-to-run-systemctl-systemd-services-without-password

Friday, 28 October 2022

Configuring vi on Ubuntu

With fresh Ubuntu 22.04.1 LTS (Jammy Jellyfish) installation my vi editor in INSERT mode behaved like this:
  • Arrow Up/Down/Left/Right prints characters A, B, C, D
  • Backspace key is not deleting previous characters
 
My ~/.vimrc file contained:
 
$ set nocompatible
$ set backspace=2
 
To remedy this behaviour I had to change this file to:

set nocompatible
set backspace=indent,eol,start

To reload this configuration file and the changes can take effect in the current terminal:
 
$ source  ~/.vimrc

Wednesday, 19 October 2022

How to fix Ansible error "Failed to connect to the host via ssh: Unable to negotiate with 127.0.0.1"

On my Ubuntu 22.04 box I had a case where Packer was executing Ansible template and Gathering Facts task was failing with the following error:
 
...
amazon-ebs:
amazon-ebs: TASK [Gathering Facts] *********************************************************
==> amazon-ebs: failed to handshake
amazon-ebs: fatal: [default]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Unable to negotiate with 127.0.0.1 port 37203: no matching host key type found. Their offer: ssh-rsa", "unreachable": true}
amazon-ebs:
...
 

The solution is to allow authentication with RSA keys by making sure the following lines are present in /etc/ssh/ssh_config:
 
PubkeyAcceptedAlgorithms ssh-rsa
HostkeyAlgorithms ssh-rsa


Resources:

 

Friday, 14 October 2022

Symbolic Links (symlinks)

 


linux - Find out symbolic link target via command line - Server Fault


What are they?

  • files that contain a reference to another file or directory on the same system
  • like shortcuts on Windows OS


What is their purpose?

  • to avoid copying the same binary (usually a library) at multiple locations but simply creating a symlink where file is required to be
  • various clients might require the same file but with name in different format so instead of having multiple copies of the same file but with different names we'd have multiple symlink, each with the name that satisfies requirements of each service

How do they work?

  • opening/running the symlink would open/run the target file
  • editing the content of the symlink edits the content of the target file
  • if target file is deleted symlink becomes a dangling symlink
  • if symlink is deleted, target file remains unaffected
  • it is possible to create symlink that refers to another symlink [How can I create a symlink which points to another symlink?]


How to create them?

How to: Linux / UNIX create soft link with ln command

Use ln command:

NAME
       ln - make links between files

SYNOPSIS
       ln [OPTION]... [-T] TARGET LINK_NAME   (1st form)
       ln [OPTION]... TARGET                  (2nd form)
       ln [OPTION]... TARGET... DIRECTORY     (3rd form)
       ln [OPTION]... -t DIRECTORY TARGET...  (4th form)

DESCRIPTION
       In the 1st form, create a link to TARGET with the name LINK_NAME.  In the 2nd form, create a link to TARGET in the current directory.  In the 3rd and 4th forms, create links to each TARGET in DIRECTORY.  Create hard links by default, symbolic links with --symbolic.  By default, each destination (name of new link) should not already exist.  When creating hard links,  each  TARGET  must  exist.
       Symbolic links can hold arbitrary text; if later resolved, a relative link is interpreted in relation to its parent directory.

       Mandatory arguments to long options are mandatory for short options too.

       --backup[=CONTROL]
              make a backup of each existing destination file

       -b     like --backup but does not accept an argument

       -d, -F, --directory
              allow the superuser to attempt to hard link directories (note: will probably fail due to system restrictions, even for the superuser)

       -f, --force
              remove existing destination files

       -i, --interactive
              prompt whether to remove destinations

       -L, --logical
              dereference TARGETs that are symbolic links

       -n, --no-dereference
              treat LINK_NAME as a normal file if it is a symbolic link to a directory

       -P, --physical
              make hard links directly to symbolic links

       -r, --relative
              create symbolic links relative to link location

       -s, --symbolic
              make symbolic links instead of hard links

       -S, --suffix=SUFFIX
              override the usual backup suffix

       -t, --target-directory=DIRECTORY
              specify the DIRECTORY in which to create the links

       -T, --no-target-directory
              treat LINK_NAME as a normal file always

       -v, --verbose
              print name of each linked file

       --help display this help and exit

       --version
              output version information and exit

       The  backup  suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.  The version control method may be selected via the --backup option or through the VERSION_CONTROL environment variable.
       Here are the values:

       none, off
              never make backups (even if --backup is given)

       numbered, t
              make numbered backups

       existing, nil
              numbered if numbered backups exist, simple otherwise

       simple, never
              always make simple backups

       Using -s ignores -L and -P.  Otherwise, the last option specified controls behavior when a TARGET is a symbolic link, defaulting to -P.


Example:

$ sudo ln -s /usr/local/go/bin/go /usr/local/bin/go

Creating a symlink from one folder to another with different names?


Types of symlinks:

(!) Important: at the time the symlink is being used and resolved, target path (in ls command) is understood as a relative path to the parent directory of the symlink (when it doesn't start with /).

$ pwd
/home/beau
$ ln -s foo/bar.txt bar.txt
$ readlink -f /home/beau/bar.txt
/home/beau/foo/bar.txt

Or:

$ cd foo
$ ln -s foo/bar.txt ../bar.txt


How to list all symbolic links in the current directory?

$ find -type l

[man find]: If no paths are given, the current directory is used.
[How to list all symbolic links in a directory]


# save symlinks and their targets (relative to ./path/to/dir/) in csv file which will also be pushed to S3
cd ./path/to/dir/
find . -type l -ls | awk '{print $11,",",$13}' > symlinks.csv

How do I tell if a folder is actually a symlink and how do I fix it if it's broken?

Here are some ways that can be used to verify symlink:

$ stat ./data-vol/content/app/74.0.1365.76 
  File: ./data-vol/content/app/74.0.1365.76 -> data-vol/content/app/win/x86/74.0.1365.76
  Size: 45              Blocks: 0          IO Block: 4096   symbolic link
Device: fd01h/64769d    Inode: 26479224    Links: 1
Access: (0777/lrwxrwxrwx)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2019-07-12 17:17:09.278071996 +0100
Modify: 2019-07-12 17:17:08.666073171 +0100
Change: 2019-07-12 17:17:08.666073171 +0100
 Birth: -


$ stat -L ./data-vol/content/app/74.0.1365.76 
stat: cannot stat './data-vol/content/app/74.0.1365.76': No such file or directory

$ file -L ./data-vol/content/app/74.0.1365.76 
./data-vol/content/app/74.0.1365.76: cannot open `./data-vol/content/app/74.0.1365.76' (No such file or directory)

$ ls ./data-vol/content/app/74.0.1365.76 
./data-vol/content/app/74.0.1365.76

$ ll ./data-vol/content/app/74.0.1365.76 
lrwxrwxrwx 1 root root 45 Jul 12 17:17 ./data-vol/content/app/74.0.1365.76 -> data-vol/content/app/win/x86/74.0.1365.76

How to see full symlink path

$ readlink -f symlinkName

Hard links


How to create hardlink of one file in different directories in linux

Monday, 8 August 2022

Git strategies for applying bug fixes

$ mkdir test
 
$ cd test
 
$ git init
Initialised empty Git repository in /home/bojan/dev/test/.git/
 
$ git status
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)
 
$ touch foo.txt
 
$ vi foo.txt
 
foo.txt:
 
feature A {
   A.1
   A.2
   A.3
}
 
$ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    foo.txt

nothing added to commit but untracked files present (use "git add" to track)
 
$ git add foo.txt
 
$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
    new file:   foo.txt
 
$ git commit -m "Added feature A"
[master (root-commit) ae68f9e] Added feature A
 1 file changed, 5 insertions(+)
 create mode 100644 foo.txt

 
 
Let's now create a feature branch for feature B. Let's assume we add a bug in its first implementation.
 
$ git checkout -b feature/B
Switched to a new branch 'feature/B'
 
$ vi foo.txt 
 
foo.txt:
 
feature A {
   A.1
   A.2
   A.3
}

feature B {
   B.1
   B.2 - bug
   B.3
}
 
$ git status
On branch feature/B
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   foo.txt

no changes added to commit (use "git add" and/or "git commit -a")
 
$ git add foo.txt 
 
$ cat foo.txt
feature A {
   A.1
   A.2
   A.3
}

feature B {
   B.1
   B.2 - bug
   B.3
}
 
$ git commit -m "Added feature B"
[feature/B c2f2c7d] Added feature B
 1 file changed, 6 insertions(+)
 
$ git log
commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d (HEAD -> feature/B)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:36:48 2022 +0100

    Added feature B

commit ae68f9ea4bf55f54a8f40831df5b980dc30e5c6e (master)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:34:20 2022 +0100

    Added feature A
 
 
$ git rebase master
Current branch feature/B is up to date.
 
$ git checkout master
Switched to branch 'master'
 
$ git merge feature/B
Updating ae68f9e..c2f2c7d
Fast-forward
 foo.txt | 6 ++++++
 1 file changed, 6 insertions(+)

 
Let's assume we've now realized that there's a bug in the current implementation of feature B.
 
 
$ git log
commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d (HEAD -> master, feature/B)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:36:48 2022 +0100

    Added feature B

commit ae68f9ea4bf55f54a8f40831df5b980dc30e5c6e
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:34:20 2022 +0100

    Added feature A
 
 
$ git revert c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d
[master cd72121] Revert "Added feature B"
 1 file changed, 6 deletions(-)
 
$ git log
commit cd721217e154dc74cad4037135c0ea2c0b898696 (HEAD -> master)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:42:57 2022 +0100

    Revert "Added feature B"
    
    This reverts commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d.

commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d (feature/B)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:36:48 2022 +0100

    Added feature B

commit ae68f9ea4bf55f54a8f40831df5b980dc30e5c6e
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:34:20 2022 +0100

    Added feature A
 
 
We now want to fix the bug so we create a new branch, B2, by branching off feature/B:
 
$ git checkout feature/B
Switched to branch 'feature/B'
 
$ git checkout -b feature/B2
Switched to a new branch 'feature/B2'
 
$ cat foo.txt
feature A {
   A.1
   A.2
   A.3
}

feature B {
   B.1
   B.2 - bug
   B.3
}
 
 
$ vi foo.txt 

foo.txt:

feature A {
   A.1
   A.2
   A.3
}

feature B {
   B.1
   B.2 - bug fixed
   B.3
}
 
 
$ git status
On branch feature/B2
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   foo.txt

no changes added to commit (use "git add" and/or "git commit -a")
 
$ git commit -a -m "Fixed bug in feature B"
[feature/B2 29dbed4] Fixed bug in feature B
 1 file changed, 1 insertion(+), 1 deletion(-)
 
$ git status
On branch feature/B2
nothing to commit, working tree clean
 
$ cat foo.txt
feature A {
   A.1
   A.2
   A.3
}

feature B {
   B.1
   B.2 - bug fixed
   B.3
}

Before we merge feature branch to trunk, we first want to rebase it:
 
 
$ git rebase -i master
Auto-merging foo.txt
CONFLICT (content): Merge conflict in foo.txt
error: could not apply 29dbed4... Fixed bug in feature B

Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 29dbed4... Fixed bug in feature B
 
 
$ vi foo.txt 

We're taking all changes from feature branch.

$ git add foo.txt 
 
$ git rebase --continue
[detached HEAD b86cff8] Fixed bug in feature B
 1 file changed, 7 insertions(+)
Successfully rebased and updated refs/heads/feature/B2.
 
$ git log
commit b86cff82118bb6af2bf10a4adf584e924acaaed0 (HEAD -> feature/B2)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:47:55 2022 +0100

    Fixed bug in feature B

commit cd721217e154dc74cad4037135c0ea2c0b898696 (master)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:42:57 2022 +0100

    Revert "Added feature B"
    
    This reverts commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d.

commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d (feature/B)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:36:48 2022 +0100

    Added feature B

commit ae68f9ea4bf55f54a8f40831df5b980dc30e5c6e
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:34:20 2022 +0100

    Added feature A
 
$ git status
On branch feature/B2
nothing to commit, working tree clean
 
 
We can now merge feature branch into master:
 
 
$ git checkout master
Switched to branch 'master'
 
$ git status
On branch master
nothing to commit, working tree clean
 
$ git log
commit cd721217e154dc74cad4037135c0ea2c0b898696 (HEAD -> master)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:42:57 2022 +0100

    Revert "Added feature B"
    
    This reverts commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d.

commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d (feature/B)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:36:48 2022 +0100

    Added feature B

commit ae68f9ea4bf55f54a8f40831df5b980dc30e5c6e
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:34:20 2022 +0100

    Added feature A
 
$ git merge feature/B2
Updating cd72121..b86cff8
Fast-forward
 foo.txt | 7 +++++++
 1 file changed, 7 insertions(+)
 
$ git status
On branch master
nothing to commit, working tree clean
 
$ cat foo.txt
feature A {
   A.1
   A.2
   A.3
}

feature B {
   B.1
   B.2 - bug fixed
   B.3
}



We assumed here that we had the original feature branch available. Alternatively, we could have created a new branch off the master, reverse the reverse commit, apply the fix and then merge it back to master.

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 


---