Configuration of ansible-lint
Tags: ansible
Published: 2019-11-17 14:27:54

ansible-lint is a tool to check Ansible Playbook.

{% github https://github.com/ansible/ansible-lint %}

It helps you to find syntax problems like extra spaces or lines, or point out where doesn't follow Ansible best practice. 


```
[koh@kohs-MBP] ~/work/linttest
% ansible-lint site.yml
[201] Trailing whitespace
site.yml:6
        msg: hello

[403] Package installs should not use latest
site.yml:8
Task/Handler: install httpd

[koh@kohs-MBP] ~/work/linttest
%
```

Below is the list of default rules.

https://docs.ansible.com/ansible-lint/rules/default_rules.html

But each playbook or teams might have their own rules that oppose to Ansible best practice.

Here is how to configure ansible-lint. 
*Version of ansible-lint is `4.1.0`.

## Config with command line options

Rules are configurable with options of ansible-lint command.

```
[koh@kohs-MBP] ~/work/linttest
% ansible-lint --help
Usage: ansible-lint [options] playbook.yml [playbook2 ...]

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -L                    list all the rules
  -q                    quieter, although not silent output
  -p                    parseable output in the format of pep8
  --parseable-severity  parseable output including severity of rule
  -r RULESDIR           specify one or more rules directories using one or
                        more -r arguments. Any -r flags override the default
                        rules in
                        /Users/koh/.pyenv/versions/3.7.3/lib/python3.7/site-
                        packages/ansiblelint/rules, unless -R is also used.
  -R                    Use default rules in
                        /Users/koh/.pyenv/versions/3.7.3/lib/python3.7/site-
                        packages/ansiblelint/rules in addition to any extra
                        rules directories specified with -r. There is no need
                        to specify this if no -r flags are used
  -t TAGS               only check rules whose id/tags match these values
  -T                    list all the tags
  -v                    Increase verbosity level
  -x SKIP_LIST          only check rules whose id/tags do not match these
                        values
  --nocolor             disable colored output
  --force-color         Try force colored output (relying on ansible's code)
  --exclude=EXCLUDE_PATHS
                        path to directories or files to skip. This option is
                        repeatable.
  -c C                  Specify configuration file to use.  Defaults to
                        ".ansible-lint"
[koh@kohs-MBP] ~/work/linttest
%
```

Basically, you can configure everything with command line options but it is better to use a configuration file to share with your team, or to implement into CI.

Next is how to configure with a file.

## Configuration file

It should be placed at `./.ansible-lint` by default, or a file which specified with the ​option `-c`.
This is an example from the official doc.

```
exclude_paths:
  - ./my/excluded/directory/
  - ./my/other/excluded/directory/
  - ./last/excluded/directory/
parseable: true
quiet: true
rulesdir:
  - ./rule/directory/
skip_list:
  - skip_this_tag
  - and_this_one_too
  - skip_this_id
  - '401'
tags:
  - run_this_tag
use_default_rules: true
verbosity: 1
```

https://docs.ansible.com/ansible-lint/configuring/configuring.html#configuration-file

### exclude_paths (--exclude)

Specify paths or files to exclude from lint.

### parseable (-p)

Change the format of the output.
Each error will be converted to one line.

```
# parseable: false

[koh@kohs-MBP] ~/work/linttest
% ansible-lint site.yml
[201] Trailing whitespace
/Users/koh/work/linttest/roles/201/tasks/main.yml:4
    msg: hello

[202] Octal file permissions must contain leading zero or be a string
/Users/koh/work/linttest/roles/202/tasks/main.yml:2
Task/Handler: error 202

[203] Most files should not contain tabs
/Users/koh/work/linttest/roles/203/tasks/main.yml:4
    msg: "     tab"

[koh@kohs-MBP] ~/work/linttest
%
```

```
# parseable: true

[koh@kohs-MBP] ~/work/linttest
% ansible-lint site.yml
/Users/koh/work/linttest/roles/201/tasks/main.yml:4: [E201] Trailing whitespace
/Users/koh/work/linttest/roles/202/tasks/main.yml:2: [E202] Octal file permissions must contain leading zero or be a string
/Users/koh/work/linttest/roles/203/tasks/main.yml:4: [E203] Most files should not contain tabs
[koh@kohs-MBP] ~/work/linttest
%
```

### quiet (-q)

Reduce output.
Not completely 0 but a bit reduced.

```
# quiet: true

[koh@kohs-MBP] ~/work/linttest
% ansible-lint site.yml
[201] /Users/koh/work/linttest/roles/201/tasks/main.yml:4
[202] /Users/koh/work/linttest/roles/202/tasks/main.yml:2
[203] /Users/koh/work/linttest/roles/203/tasks/main.yml:4
[koh@kohs-MBP] ~/work/linttest
%
```

### rulesdir (-r)

Specify files or directories in which you put original rule files.

### skip_list (-x)

Specify tags or error IDs that should be skipped.
You can check about tags with `-T` option.

```
[koh@kohs-MBP] ~/work/linttest
% ansible-lint -T
ANSIBLE0002 ['[201]']
ANSIBLE0004 ['[401]']
ANSIBLE0005 ['[402]']
ANSIBLE0006 ['[303]']
ANSIBLE0007 ['[302]']
ANSIBLE0008 ['[103]']
ANSIBLE0009 ['[202]']
ANSIBLE0010 ['[403]']
ANSIBLE0011 ['[502]']
ANSIBLE0012 ['[301]']
ANSIBLE0013 ['[305]']
ANSIBLE0014 ['[304]']
ANSIBLE0015 ['[104]']
ANSIBLE0016 ['[503]']
ANSIBLE0017 ['[501]']
ANSIBLE0018 ['[101]']
ANSIBLE0019 ['[102]']
behaviour ['[503]']
bug ['[304]']
command-shell ['[305]', '[302]', '[304]', '[306]', '[301]', '[303]']
deprecated ['[105]', '[104]', '[103]', '[101]', '[102]']
formatting ['[104]', '[203]', '[201]', '[204]', '[206]', '[205]', '[202]']
idempotency ['[301]']
idiom ['[601]', '[602]']
metadata ['[701]', '[704]', '[703]', '[702]']
module ['[404]', '[401]', '[403]', '[402]']
oddity ['[501]']
readability ['[502]']
repeatability ['[401]', '[403]', '[402]']
resources ['[302]', '[303]']
safety ['[305]']
task ['[502]', '[503]', '[504]', '[501]']
[koh@kohs-MBP] ~/work/linttest
%
```

### tags (-t)

Opposite of skip_list, specify tags that you want to check.

### use_default_rules (-R)

If True, default rules are applied.
If you want to check only your original rules, set it to False.

### verbosity (-v)

Specify the verbosity of the output.
It only takes 2 kinds of values which are 0 or greater than 0.

```
        for file in files:
            if self.verbosity > 0:
                print("Examining %s of type %s" % (file['path'], file['type']))
            matches.extend(self.rules.run(file, tags=set(self.tags),
                           skip_list=self.skip_list))
```

https://github.com/ansible/ansible-lint/blob/5170c04201e71400421b27255280902c211f8548/lib/ansiblelint/__init__.py#L281

## Configure inside Playbook

By adding a comment, you can skip checks by lines.

Below is an example of the Playbook.

```
[koh@kohs-MBP] ~/work/linttest
% cat site.yml
---
- hosts: all
  tasks:
    - name: install latest httpd
      yum:
        name: httpd
        state: latest

    - name: install mysql
      yum:
        name: mysql
        state: installed
[koh@kohs-MBP] ~/work/linttest
%
```

This playbook throws 403 error.

```
[koh@kohs-MBP] ~/work/linttest
% ansible-lint site.yml
[403] Package installs should not use latest
site.yml:4
Task/Handler: install latest httpd

[koh@kohs-MBP] ~/work/linttest
%
```

If you really want to keep your httpd updated but not other packages, you can do like below. (you still should not do that though.)

```
[koh@kohs-MBP] ~/work/linttest
% cat site.yml
---
- hosts: all
  tasks:
    - name: install latest httpd
      yum:
        name: httpd
        state: latest # noqa 403

    - name: install mysql
      yum:
        name: mysql
        state: installed
[koh@kohs-MBP] ~/work/linttest
%
```

By adding `# noqa ID`, the line will be skipped from checks.

```
[koh@kohs-MBP] ~/work/linttest
% ansible-lint site.yml
[koh@kohs-MBP] ~/work/linttest
%
```

## Original rules

You can create your own rules.
There are some explanations on README and also I recommend you to have a look at scripts of the default rules.

https://github.com/ansible/ansible-lint#creating-custom-rules

https://github.com/ansible/ansible-lint/tree/master/lib/ansiblelint/rules

Here is my example of how to create my own rule.

```
[koh@kohs-MBP] ~/work/linttest
% cat origrules/True4BooleanRule.py
from ansiblelint import AnsibleLintRule


class True4BooleanRule(AnsibleLintRule):
    id = '99'
    shortdesc = 'Use "True" for boolean'
    description = 'We should "True" for boolean not "yes" or "true".'
    tags = ['formatting']

    def match(self, file, line):
        return ': yes' in line or ': true' in line
[koh@kohs-MBP] ~/work/linttest
%
```

Playbook can take `yes` , `true` to set `True` for boolean.
With this rule, it throws errors if `yes` or  `true` are used.
*This is a really cheap script only for this explanation please use it with your own risk.

In `.ansible-lint`

```
[koh@kohs-MBP] ~/work/linttest
% cat .ansible-lint
rulesdir:
  - ./origrules/
[koh@kohs-MBP] ~/work/linttest
%
```

With this site.yml, it throws 2 errors as intended.

```
[koh@kohs-MBP] ~/work/linttest
% cat site.yml
---
- hosts: all
  tasks:
    - name: this should be error
      service:
        name: httpd
        state: started
        enabled: yes

    - name: this should be error too
      service:
        name: mysqld
        state: started
        enabled: true

    - name: this is ok
      service:
        name: haproxy
        state: started
        enabled: True
[koh@kohs-MBP] ~/work/linttest
% ansible-lint site.yml
[99] Use "True" for boolean
site.yml:8
        enabled: yes

[99] Use "True" for boolean
site.yml:14
        enabled: true

[koh@kohs-MBP] ~/work/linttest
%
```

## Conclusion

Creating own rules is much easier than I expected.
So it is good to try one.