こちらの記事はAnsible lint Advent Calendar 2022 2日目の記事になります。
今回はAnsible lintのコマンドについて解説します。
$ ansible-lint --help
usage: ansible-lint [-h] [-L | -T]
[-f {rich,plain,md,json,codeclimate,quiet,pep8,sarif,docs}]
[-q]
[-P [{min,basic,moderate,safety,shared,production} ...]]
[-p] [--progressive] [--project-dir PROJECT_DIR]
[-r RULESDIR] [-R] [-s] [--write [WRITE_LIST]]
[--show-relpath] [-t TAGS] [-v] [-x SKIP_LIST]
[-w WARN_LIST] [--enable-list ENABLE_LIST] [--nocolor]
[--force-color] [--exclude EXCLUDE_PATHS] [-c CONFIG_FILE]
[--offline] [--version]
[lintables ...]
positional arguments:
lintables One or more files or paths. When missing it will
enable auto-detection mode.
options:
-h, --help show this help message and exit
-L, --list-rules List all the rules. For listing rules only the
following formats for argument -f are supported:
{plain, rich, md}
-T, --list-tags List all the tags and the rules they cover. Increase
the verbosity level with `-v` to include 'opt-in' tag
and its rules.
-f {rich,plain,md,json,codeclimate,quiet,pep8,sarif,docs}, --format {rich,plain,md,json,codeclimate,quiet,pep8,sarif,docs}
stdout formatting, json being an alias for
codeclimate. (default: rich)
-q quieter, reduce verbosity, can be specified twice.
-P [{min,basic,moderate,safety,shared,production} ...], --profile [{min,basic,moderate,safety,shared,production} ...]
Specify which rules profile to be used, or displays
available profiles when no argument is given.
-p, --parseable parseable output, same as '-f pep8'
--progressive Return success if it detects a reduction in number of
violations compared with previous git commit. This
feature works only in git repositories.
--project-dir PROJECT_DIR
Location of project/repository, autodetected based on
location of configuration file.
-r RULESDIR, --rules-dir RULESDIR
Specify custom rule directories. Add -R to keep using
embedded rules from /home/docs/checkouts/readthedocs.o
rg/user_builds/ansible-
lint/envs/latest/lib/python3.10/site-
packages/ansiblelint/rules
-R Keep default rules when using -r
-s, --strict Return non-zero exit code on warnings as well as
errors
--write [WRITE_LIST] Allow ansible-lint to reformat YAML files and run rule
transforms (Reformatting YAML files standardizes
spacing, quotes, etc. A rule transform can fix or
simplify fixing issues identified by that rule). You
can limit the effective rule transforms (the
'write_list') by passing a keywords 'all' or 'none' or
a comma separated list of rule ids or rule tags. YAML
reformatting happens whenever '--write' or '--write='
is used. '--write' and '--write=all' are equivalent:
they allow all transforms to run. The effective list
of transforms comes from 'write_list' in the config
file, followed whatever '--write' args are provided on
the commandline. '--write=none' resets the list of
transforms to allow reformatting YAML without running
any of the transforms (ie '--write=none,rule-id' will
ignore write_list in the config file and only run the
rule-id transform).
--show-relpath Display path relative to CWD
-t TAGS, --tags TAGS only check rules whose id/tags match these values
-v Increase verbosity level (-vv for more)
-x SKIP_LIST, --skip-list SKIP_LIST
only check rules whose id/tags do not match these
values
-w WARN_LIST, --warn-list WARN_LIST
only warn about these rules, unless overridden in
config file. Current version default value is: avoid-
implicit, experimental, fqcn[action], fqcn[redirect],
jinja[spacing], name[casing], name[play], role-name,
warning[empty-playbook], role-name[path]
--enable-list ENABLE_LIST
activate optional rules by their tag name
--nocolor disable colored output, same as NO_COLOR=1
--force-color Force colored output, same as FORCE_COLOR=1
--exclude EXCLUDE_PATHS
path to directories or files to skip. This option is
repeatable.
-c CONFIG_FILE, --config-file CONFIG_FILE
Specify configuration file to use. By default it will
look for '.ansible-lint' or '.config/ansible-lint.yml'
--offline Disable installation of requirements.yml
--version
基本的なコマンド
ansible-lint <ファイル名>
になります。ファイル名が省略された場合はAnsible lintがplaybook、role、collection等のファイルを自動的に検出しlint処理を実行します。
全てのルールを表示する 【-L, --list-rules】
ansible-lint -L
## もしくは
## ansible-lint --list-rules
情報量が多いです。
avoid-implicit │ Avoid implicit behaviors
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # avoid-implicit
│
│ This rule identifies the use of dangerous implicit behaviors, often also undocumented.
│
│ This rule will produce the following type of error messages:
│
│ • avoid-implicit[copy-content] is not a string as copy modules also accept these, but without
│ documenting them.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Write file content
│ ansible.builtin.copy:
│ content: { "foo": "bar" } # <-- should use explicit jinja template
│ dest: /tmp/foo.txt
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Write file content
│ vars:
│ content: { "foo": "bar" }
│ ansible.builtin.copy:
│ content: "{{ content | to_json }}" # explicit better than implicit!
│ dest: /tmp/foo.txt
version_added │ v6.8.0
tags │ unpredictability, experimental
severity │ MEDIUM
╵
╷
command-instead-of-module │ Using command rather than module.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # command-instead-of-module
│
│ This rule will recommend you to use a specific ansible module instead for tasks that are better served
│ by a module, as these are more reliable, provide better messaging and usually have additional features
│ like the ability to retry.
│
│ In the unlikely case that the rule triggers false positives, you can disable it by adding a comment like
│ # noqa: command-instead-of-module to the same line.
│
│ You can check the source of the rule for all the known commands that trigger the rule and their allowed
│ list arguments of exceptions and raise a pull request to improve them.
│
│ ## Problematic Code
│
│ ---
│ - name: Update apt cache
│ hosts: all
│ tasks:
│ - name: Run apt-get update
│ ansible.builtin.command: apt-get update # <-- better to use ansible.builtin.apt module
│
│ ## Correct Code
│
│ ---
│ - name: Update apt cache
│ hosts: all
│ tasks:
│ - name: Run apt-get update
│ ansible.builtin.apt:
│ update_cache: true
version_added │ historic
tags │ command-shell, idiom
severity │ HIGH
╵
╷
command-instead-of-shell │ Use shell only when shell functionality is required.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # command-instead-of-shell
│
│ This rule identifies uses of shell modules instead of a command one when this is not really needed.
│ Shell is considerably slower than command and should be avoided unless there is a special need for using
│ shell features, like environment variable expansion or chaining multiple commands using pipes.
│
│ ## Problematic Code
│
│ ---
│ - name: Problematic example
│ hosts: localhost
│ tasks:
│ - name: Echo a message
│ ansible.builtin.shell: echo hello # <-- command is better in this case
│ changed_when: false
│
│ ## Correct Code
│
│ ---
│ - name: Correct example
│ hosts: localhost
│ tasks:
│ - name: Echo a message
│ ansible.builtin.command: echo hello
│ changed_when: false
version_added │ historic
tags │ command-shell, idiom
severity │ HIGH
╵
╷
deprecated-bare-vars │ Using bare variables is deprecated.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # deprecated-bare-vars
│
│ This rule identifies possible confusing expressions where it is not clear if a variable or string is to
│ be used and asks for clarification.
│
│ You should either use the full variable syntax ('{{{{ {0} }}}}') or, whenever possible, convert it to a
│ list of strings.
│
│ ## Problematic code
│
│ ---
│ - ansible.builtin.debug:
│ msg: "{{ item }}"
│ with_items: foo # <-- deprecated-bare-vars
│
│ ## Correct code
│
│ ---
│ # if foo is not really a variable:
│ - ansible.builtin.debug:
│ msg: "{{ item }}"
│ with_items:
│ - foo
│
│ # if foo is a variable:
│ - ansible.builtin.debug:
│ msg: "{{ item }}"
│ with_items: "{{ foo }}"
version_added │ historic
tags │ deprecations
severity │ VERY_HIGH
╵
╷
deprecated-command-syntax │ Using command rather than an argument to e.g. file.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # deprecated-command-syntax
│
│ This rule identifies the use of shorthand (free-form) syntax as this is highly discouraged inside
│ playbooks, mainly because it can easily lead to bugs that are hard to identify.
│
│ While using the free-form from the command line is ok, it should never be used inside playbooks.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Perform chmod
│ ansible.builtin.command: creates=B chmod 644 A # <-- do not use shorthand
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Perform chmod
│ ansible.builtin.command: chmod 644 A
│ args:
│ creates: B
version_added │ historic
tags │ command-shell, deprecations
severity │ VERY_HIGH
╵
╷
deprecated-local-action │ Do not use 'local_action', use 'delegate_to: localhost'.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # deprecated-local-action
│
│ This rule recommends using delegate_to: localhost instead of the local_action.
│
│ ## Problematic Code
│
│ ---
│ - name: Task example
│ local_action: # <-- this is deprecated
│ module: boto3_facts
│
│ ## Correct Code
│
│ - name: Task example
│ boto3_facts:
│ delegate_to: localhost # <-- recommended way to run on localhost
version_added │ v4.0.0
tags │ deprecations
severity │ MEDIUM
╵
╷
deprecated-module │ Deprecated module.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # deprecated-module
│
│ This rule identifies deprecated modules in playbooks. You should avoid using deprecated modules because
│ they are not maintained, which can pose a security risk. Additionally when a module is deprecated it is
│ available temporarily with a plan for future removal.
│
│ Refer to the Ansible module index for information about replacements and removal dates for deprecated
│ modules.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Configure VLAN ID
│ ansible.netcommon.net_vlan: # <- Uses a deprecated module.
│ vlan_id: 20
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Configure VLAN ID
│ dellemc.enterprise_sonic.sonic_vlans: # <- Uses a platform specific module.
│ config:
│ - vlan_id: 20
│
│ (more)
version_added │ v4.0.0
tags │ deprecations
severity │ HIGH
╵
╷
empty-string-compare │ Don't compare to empty string.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # empty-string-compare
│
│ This rule checks for empty string comparison in playbooks. To ensure code clarity you should avoid using
│ empty strings in conditional statements with the when clause.
│
│ • Use when: var | length > 0 instead of when: var != "".
│ • Use when: var | length == 0 instead of when: var == "".
│
│ This is an opt-in rule. You must enable it in your Ansible-lint configuration as follows:
│
│ enable_list:
│ - empty-string-compare
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Shut down
│ ansible.builtin.command: /sbin/shutdown -t now
│ when: ansible_os_family == "" # <- Compares with an empty string.
│ - name: Shut down
│ ansible.builtin.command: /sbin/shutdown -t now
│ when: ansible_os_family !="" # <- Compares with an empty string.
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Shut down
│ ansible.builtin.shell: |
│ /sbin/shutdown -t now
│ echo $var ==
│ when: ansible_os_family
version_added │ v4.0.0
tags │ idiom, opt-in
severity │ HIGH
╵
╷
fqcn │ Use FQCN for builtin actions.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # fqcn
│
│ This rule checks for fully-qualified collection names (FQCN) in Ansible content.
│
│ Declaring an FQCN ensures that an action uses code from the correct namespace. This avoids ambiguity and
│ conflicts that can cause operations to fail or produce unexpected results.
│
│ The fqcn rule has the following checks:
│
│ • fqcn[action] - Use FQCN for module actions, such ...
│ • fqcn[action-core] - Checks for FQCNs from the ansible.legacy or ansible.builtin collection.
│ • fqcn[canonical] - You should use canonical module name ... instead of ...
│
│ In most cases you should declare the `ansible.builtin` collection for internal Ansible actions.
│ You should declare the `ansible.legacy` collection if you use local overrides with actions, such with as
│ the ``shell`` module.
│
│ This rule does not take [`collections`
│ keyword](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html#simplifying-module-n…
│ into consideration.
│ The `collections` keyword provided a temporary mechanism transitioning to Ansible 2.9.
│ You should rewrite any content that uses the `collections:` key and avoid it where possible.
│
│ ## Canonical module names
│
│ Canonical module names are also known as resolved module names and they are to be preferred for most
│ cases. Many Ansible modules have multiple aliases and redirects, as these were created over time while
│ the content was refactored. Still, all of them do finally resolve to the same module name, but not
│ without adding some performance overhead. As very old aliases are at some point removed, it makes to
│ just refresh the content to make it point to the current canonical name.
│
│ The only exception for using a canonical name is if your code still needs to be compatible with a very
│ old version of Ansible, one that does not know how to resolve that name. If you find yourself in such a
│ situation, feel free to add this rule to the ignored list.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Create an SSH connection
│ shell: ssh ssh_user@{{ ansible_ssh_host }} # <- Does not use the FQCN for the shell module.
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook (1st solution)
│ hosts: all
│ tasks:
│ - name: Create an SSH connection
│ # Use the FQCN for the legacy shell module and allow local overrides.
│ ansible.legacy.shell: ssh ssh_user@{{ ansible_ssh_host }} -o IdentityFile=path/to/my_rsa
│
│ ---
│ - name: Example playbook (2nd solution)
│ hosts: all
│ tasks:
│ - name: Create an SSH connection
│ # Use the FQCN for the builtin shell module.
│ ansible.builtin.shell: ssh ssh_user@{{ ansible_ssh_host }}
version_added │ v6.8.0
tags │ formatting
severity │ MEDIUM
╵
╷
galaxy │ Rule for checking collection version is greater than 1.0.0.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # galaxy
│
│ This rule identifies if the collection version mentioned in galaxy.yml is ideal in terms of the version
│ number being greater than or equal to 1.0.0.
│
│ This rule can produce messages such:
│
│ • galaxy[version-missing] - galaxy.yaml should have version tag.
│ • galaxy[version-incorrect] - collection version should be greater than or equal to 1.0.0
│
│ If you want to ignore some of the messages above, you can add any of them to the ignore_list.
│
│ ## Problematic code
│
│ # galaxy.yml
│ ---
│ name: foo
│ namespace: bar
│ version: 0.2.3 # <-- collection version should be >= 1.0.0
│ authors:
│ - John
│ readme: ../README.md
│ description: "..."
│
│ ## Correct code
│
│ # galaxy.yml
│ ---
│ name: foo
│ namespace: bar
│ version: 1.0.0
│ authors:
│ - John
│ readme: ../README.md
│ description: "..."
version_added │ v6.6.0 (last update)
tags │ metadata, opt-in, experimental
severity │ MEDIUM
╵
╷
ignore-errors │ Use failed_when and specify error conditions instead of using ignore_errors.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # ignore-errors
│
│ This rule checks that playbooks do not use the ignore_errors directive to ignore all errors. Ignoring
│ all errors in a playbook hides actual failures, incorrectly mark tasks as failed, and result in
│ unexpected side effects and behavior.
│
│ Instead of using the ignore_errors: true directive, you should do the following:
│
│ • Ignore errors only when using the {{ ansible_check_mode }} variable.
│ • Use register to register errors.
│ • Use failed_when: and specify acceptable error conditions.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Run apt-get update
│ ansible.builtin.command: apt-get update
│ ignore_errors: true # <- Ignores all errors, including important failures.
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Run apt-get update
│ ansible.builtin.command: apt-get update
│ ignore_errors: "{{ ansible_check_mode }}" # <- Ignores errors in check mode.
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Run apt-get update
│ ansible.builtin.command: apt-get update
│ ignore_errors: true
│ register: ignore_errors_register # <- Stores errors and failures for evaluation.
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Disable apport
│ become: "yes"
│ lineinfile:
│ line: "enabled=0"
│ dest: /etc/default/apport
│ mode: 0644
│ state: present
│ register: default_apport
│ failed_when: default_apport.rc !=0 and not default_apport.rc == 257 # <- Defines conditions that
│ constitute a failure.
version_added │ v5.0.7
tags │ unpredictability, experimental
severity │ LOW
╵
╷
inline-env-var │ Command module does not accept setting environment variables inline.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # inline-env-var
│
│ This rule checks that playbooks do not set environment variables in the ansible.builtin.command module.
│
│ You should set environment variables with the ansible.builtin.shell module or the environment keyword.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Set environment variable
│ ansible.builtin.command: MY_ENV_VAR=my_value # <- Sets an environment variable in the command
│ module.
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Set environment variable
│ ansible.builtin.shell: echo $MY_ENV_VAR
│ environment:
│ MY_ENV_VAR: my_value # <- Sets an environment variable with the environment keyword.
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Set environment variable
│ ansible.builtin.shell: MY_ENV_VAR=my_value # <- Sets an environment variable with the shell
│ module.
version_added │ historic
tags │ command-shell, idiom
severity │ VERY_HIGH
╵
╷
internal-error │ Unexpected internal error.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # internal-error
│
│ This error can also be caused by internal bugs but also by custom rules. Instead of just stopping tool
│ execution, we generate the errors and continue processing other files. This allows users to add this
│ rule to their warn_list until the root cause is fixed.
│
│ Keep in mind that once an internal-error is found on a specific file, no other rules will be executed on
│ that same file.
│
│ In almost all cases you will see more detailed information regarding the original error or runtime
│ exception that triggered this rule.
│
│ If these files are broken on purpose, like some test fixtures, you need to add them to the
│ exclude_paths.
│
│ ## Problematic code
│
│ ---
│ - name: Some title {{ # <-- Ansible will not load this invalid jinja template
│ hosts: localhost
│ tasks: []
│
│ ## Correct code
│
│ ---
│ - name: Some title
│ hosts: localhost
│ tasks: []
│
│ ## ERROR! No hosts matched the subscripted pattern
│
│ If you see this error, it means that you tried to index a host group variable that is using an index
│ above its size.
│
│ Instead of doing something like hosts: all[1] which assumes that you have at least two hosts in your
│ current inventory, you better write something like hosts: "{{ all[1] | default([]) }}, which is safe and
│ do not produce runtime errors. Use safe fallbacks to make your code more resilient.
version_added │ v5.0.0
tags │ core
severity │ VERY_HIGH
╵
╷
jinja │ Rule that looks inside jinja2 templates.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # jinja
│
│ This rule can report problems related to jinja2 string templates. The current version can report:
│
│ • jinja[spacing] when there are no spaces between variables and operators, including filters, like {{
│ var_name | filter }}. This improves readability and makes it less likely to introduce typos.
│ • jinja[invalid] when the jinja2 template is invalid, like {{ {{ '1' }} }}, which would result in a
│ runtime error if you try to use it with Ansible, even if it does pass the Ansible syntax check.
│
│ As jinja2 syntax is closely following Python one we aim to follow black formatting rules. If you are
│ curious how black would reformat a small sniped feel free to visit online black formatter site. Keep in
│ mind to not include the entire jinja2 template, so instead of {{ 1+2==3 }}, do paste only 1+2==3.
│
│ In ansible, changed_when, failed_when, until, when are considered to use implicit jinja2 templating,
│ meaning that they do not require {{ }}. Our rule will suggest the removal of the braces for these
│ fields.
│
│ ## Problematic code
│
│ ---
│ - name: Some task
│ vars:
│ foo: "{{some|dict2items}}" # <-- jinja[spacing]
│ bar: "{{ & }}" # <-- jinja[invalid]
│ when: "{{ foo | bool }}" # <-- jinja[spacing] - 'when' has implicit templating
│
│ ## Correct code
│
│ ---
│ - name: Some task
│ vars:
│ foo: "{{ some | dict2items }}"
│ bar: "{{ '&' }}"
│ when: foo | bool
│
│ ## Current limitations
│
│ In its current form, this rule presents the following limitations:
│
│ • Jinja2 blocks that have newlines in them will not be reformatted because we consider that the user
│ deliberately wanted to format them in a particular way.
│ • Jinja2 blocks that use tilde as a binary operation are ignored because black does not support tilde
│ as a binary operator. Example: {{ a ~ b }}.
│ • Jinja2 blocks that use dot notation with numbers are ignored because python and black do not allow
│ it. Example: {{ foo.0.bar }}
version_added │ v6.5.0
tags │ formatting
severity │ LOW
╵
╷
key-order │ Ensure specific order of keys in mappings.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # key-order
│
│ This rule recommends reordering key names in ansible content to make code easier to maintain and less
│ prone to errors.
│
│ Here are some examples of common ordering checks done for tasks and handlers:
│
│ • name must always be the first key for plays, tasks and handlers
│ • on tasks, the block, rescue and always keys must be the last keys, as this would avoid accidental
│ miss-indentation errors between the last task and the parent level.
│
│ ## Problematic code
│
│ ---
│ - hosts: localhost
│ name: This is a playbook # <-- name key should be the first one
│ tasks:
│ - name: A block
│ block:
│ - name: Display a message
│ debug:
│ msg: "Hello world!"
│ when: true # <-- when key should be before block
│
│ ## Correct code
│
│ ---
│ - name: This is a playbook
│ hosts: localhost
│ tasks:
│ - name: A block
│ when: true
│ block:
│ - name: Display a message
│ debug:
│ msg: "Hello world!"
│
│ ## Reasoning
│
│ Making decisions about the optimal order of keys for ansible tasks or plays is no easy task, as we had a
│ huge number of combinations to consider. This is also the reason why we started with a minimal sorting
│ rule (name to be the first), and aimed to gradually add more fields later, and only when we find the
│ proofs that one approach is likely better than the other.
│
│ ### Why I no longer can put when after a block?
│
│ Try to remember that in real life, block/rescue/always have the habit to grow due to the number of tasks
│ they host inside, making them exceed what a single screen. This would move the when task further away
│ from the rest of the task properties. A when from the last task inside the block can easily be confused
│ as being at the block level, or the reverse. When tasks are moved from one location to another, there is
│ a real risk of moving the block level when with it.
│
│ By putting the when before the block, we avoid that kind of risk. The same risk applies to any simple
│ property at the task level, so that is why we concluded that the block keys must be the last ones.
│
│ Another common practice was to put tags as the last property. Still, for the same reasons, we decided
│ that they should not be put after block keys either.
version_added │ v6.6.2
tags │ formatting, experimental
severity │ LOW
╵
╷
latest │ Result of the command may vary on subsequent runs.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # latest
│
│ The latest rule checks that module arguments like those used for source control checkout do not have
│ arguments that might generate different results based on context.
│
│ This more generic rule replaced two older rules named git-latest and hg-latest.
│
│ We are aware that there are genuine cases where getting the tip of the main branch is not accidental.
│ For these cases, just add a comment such as # noqa: latest to the same line to prevent it from
│ triggering.
│
│ ## Possible errors messages:
│
│ • latest[git]
│ • latest[hg]
│
│ ## Problematic code
│
│ ---
│ - name: Example for `latest` rule
│ hosts: localhost
│ tasks:
│ - name: Risky use of git module
│ ansible.builtin.git:
│ repo: "https://foosball.example.org/path/to/repo.git"
│ version: HEAD # <-- HEAD value is triggering the rule
│
│ ## Correct code
│
│ ---
│ - name: Example for `latest` rule
│ hosts: localhost
│ tasks:
│ - name: Safe use of git module
│ ansible.builtin.git:
│ repo: "https://foosball.example.org/path/to/repo.git"
│ version: abcd1234... # <-- that is safe
version_added │ v6.5.2
tags │ idempotency
severity │ MEDIUM
╵
╷
literal-compare │ Don't compare to literal True/False.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # literal-compare
│
│ This rule checks for literal comparison with the when clause. Literal comparison, like when: var ==
│ True, is unnecessarily complex. Use when: var to keep your playbooks simple.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Print environment variable to stdout
│ ansible.builtin.command: echo $MY_ENV_VAR
│ when: ansible_os_family == True # <- Adds complexity to your playbook.
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Print environment variable to stdout
│ ansible.builtin.command: echo $MY_ENV_VAR
│ when: ansible_os_family # <- Keeps your playbook simple.
version_added │ v4.0.0
tags │ idiom
severity │ HIGH
╵
╷
load-failure │ Failed to load or parse file.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # load-failure
│
│ Linter failed to process a YAML file, probably because it is either:
│
│ • contains unsupported encoding (only UTF-8 is supported)
│ • not an Ansible file
│ • it contains some unsupported custom YAML objects (!! prefix)
│ • it was not able to decrypt an inline !vault block.
│
│ This violation is not skippable, so it cannot be added to the warn_list or the skip_list. If a vault
│ decryption issue cannot be avoided, the offending file can be added to exclude_paths configuration.
version_added │ v4.3.0
tags │ core, unskippable
severity │ VERY_HIGH
╵
╷
loop-var-prefix │ Role loop_var should use configured prefix.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # loop-var-prefix
│
│ This rule avoids conflicts with nested looping tasks by configuring a variable prefix with loop_var.
│ Ansible sets item as the loop variable. You can use loop_var to specify a prefix for loop variables and
│ ensure they are unique to each task.
│
│ This rule can produce the following messages:
│
│ • [loop-var-prefix[missing] - Replace any unsafe implicit item loop variable by adding loop_var:
│ <loop_var_prefix>....
│ • [loop-var-prefix[wrong] - Ensure loop variables start with <loop_var_prefix>.
│
│ This is an opt-in rule. You must enable it in your Ansible-lint configuration as follows:
│
│ enable_list:
│ - loop-var-prefix
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Does not set a prefix for loop variables.
│ ansible.builtin.debug:
│ var: item
│ loop:
│ - foo
│ - bar # <- These items do not have a unique prefix.
│ - name: Sets a prefix that is not unique.
│ ansible.builtin.debug:
│ var: zz_item
│ loop:
│ - foo
│ - bar
│ loop_control:
│ loop_var: zz_item # <- This prefix is not unique.
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Sets a unique prefix for loop variables.
│ ansible.builtin.debug:
│ var: zz_item
│ loop:
│ - foo
│ - bar
│ loop_control:
│ loop_var: my_prefix # <- Specifies a unique prefix for loop variables.
│
│ (more)
tags │ idiom
severity │ MEDIUM
╵
╷
meta-incorrect │ meta/main.yml default values should be changed.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # meta-incorrect
│
│ This rule checks role metadata for fields with undefined or default values. Always set appropriate
│ values for the following metadata fields in the meta/main.yml file:
│
│ • author
│ • description
│ • company
│ • license
│
│ ## Problematic Code
│
│ ---
│ # Metadata fields for the role contain default values.
│ galaxy_info:
│ author: your name
│ description: your role description
│ company: your company (optional)
│ license: license (GPL-2.0-or-later, MIT, etc)
│
│ ## Correct Code
│
│ ---
│ galaxy_info:
│ author: Leroy Jenkins
│ description: This role will set you free.
│ company: Red Hat
│ license: Apache
version_added │ v4.0.0
tags │ metadata
severity │ HIGH
╵
╷
meta-no-info │ meta/main.yml should contain relevant info.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # meta-no-info
│
│ This rule checks role metadata for missing information. Always set appropriate values for the following
│ metadata fields in the meta/main.yml file, under galaxy_info key:
│
│ • platforms
│ • min_ansible_version
│
│ ## Problematic Code
│
│ ---
│ # The metadata fields for minimum Ansible version and supported platforms are not set.
│ galaxy_info:
│ min_ansible_version:
│
│ ## Correct Code
│
│ ---
│ galaxy_info:
│ min_ansible_version: "2.8"
│ platforms:
│ - name: Fedora
│ versions:
│ - all
version_added │ v4.0.0
tags │ metadata
severity │ HIGH
╵
╷
meta-no-tags │ Tags must contain lowercase letters and digits only.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # meta-no-tags
│
│ This rule checks role metadata for tags with special characters. Always use lowercase numbers and
│ letters for tags in the meta/main.yml file.
│
│ ## Problematic Code
│
│ ---
│ # Metadata tags contain upper-case letters and special characters.
│ galaxy_info:
│ galaxy_tags: [MyTag#1, MyTag&^-]
│
│ ## Correct Code
│
│ ---
│ # Metadata tags contain only lowercase letters and numbers.
│ galaxy_info:
│ galaxy_tags: [mytag1, mytag2]
version_added │ v4.0.0
tags │ metadata
severity │ HIGH
╵
╷
meta-video-links │ meta/main.yml video_links should be formatted correctly.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # meta-video-links
│
│ This rule checks formatting for video links in metadata. Always use dictionaries for items in the
│ meta/main.yml file.
│
│ Items in the video_links section must be in a dictionary and use the following keys:
│
│ • url
│ • title
│
│ The value of the url key must be a shared link from YouTube, Vimeo, or Google Drive.
│
│ ## Problematic Code
│
│ ---
│ galaxy_info:
│ video_links:
│ - https://youtu.be/this_is_not_a_dictionary # <- Does not use the url key.
│ - my_bad_key: https://youtu.be/aWmRepTSFKs # <- Uses an unsupported key.
│ title: Incorrect key.
│ - url: www.acme.com/vid # <- Uses an unsupported url format.
│ title: Incorrect url format.
│
│ ## Correct Code
│
│ ---
│ galaxy_info:
│ video_links:
│ - url: https://youtu.be/aWmRepTSFKs # <- Uses a supported shared link with the url key.
│ title: Correctly formatted video link.
version_added │ v4.0.0
tags │ metadata
severity │ LOW
╵
╷
name │ Rule for checking task and play names.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # name
│
│ This rule identifies several problems related to the naming of tasks and plays. This is important
│ because these names are the primary way to identify and document executed operations on console, logs or
│ web interface.
│
│ This rule can produce messages such:
│
│ • name[casing] - All names should start with an uppercase letter for languages that support it.
│ • name[missing] - All tasks should be named.
│ • name[play] - All plays should be named.
│ • name[template] - Jinja templates should only be at the end of 'name'. This helps with the
│ identification of tasks inside the source code when they fail. The use of templating inside name keys
│ is discouraged as there are multiple cases where the rendering of the name template is not possible.
│
│ If you want to ignore some of the messages above, you can add any of them to the skip_list.
│
│ ## Problematic code
│
│ ---
│ - hosts: localhost # <-- playbook missing a name key
│ tasks:
│ - name: create placefolder file # <-- not starting with a capital letter
│ ansible.builtin.command: touch /tmp/.placeholder
│
│ ## Correct code
│
│ ---
│ - name: Play for creating playholder
│ hosts: localhost
│ tasks:
│ - name: Create placeholder file
│ ansible.builtin.command: touch /tmp/.placeholder
version_added │ v6.5.0 (last update)
tags │ idiom
severity │ MEDIUM
╵
╷
no-changed-when │ Commands should not change things if nothing needs doing.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # no-changed-when
│
│ This rule checks that tasks return changes to results or conditions. Unless tasks only read information,
│ you should ensure that they return changes in the following ways:
│
│ • Register results or conditions and use the changed_when clause.
│ • Use the creates or removes argument.
│
│ You should use the when clause to run tasks only if a check returns a particular result.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Does not handle any output or return codes
│ ansible.builtin.command: cat {{ my_file | quote }} # <- Does not handle the command output.
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Handle shell output with return code
│ ansible.builtin.command: cat {{ my_file | quote }}
│ register: my_output # <- Registers the command output.
│ changed_when: my_output.rc != 0 # <- Uses the return code to define when the task has changed.
version_added │ historic
tags │ command-shell, idempotency
severity │ HIGH
╵
╷
no-free-form │ Rule for detecting discouraged free-form syntax for action modules.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # no-free-form
│
│ This rule identifies any use of free-form module calling syntax and asks for switching to the full
│ syntax.
│
│ Free-form syntax, also known as inline or shorthand, can produce subtle bugs. It can also prevent
│ editors and IDEs from providing feedback, autocomplete and validation for the edited line.
│
│ As long you just pass a YAML string that contains a `=` character inside as the
│ parameter to the action module name, we consider this as using free-formsyntax.
│ Be sure you pass a dictionary to the module, so the free-form parsing
│ is never triggered.
│
│ As raw module only accepts free-form, we trigger no-free-form[raw] only if we detect the presence of
│ executable= inside raw calls. We advice the explicit use of args: dictionary for configuring the
│ executable to be run.
│
│ ## Problematic code
│
│ ---
│ - name: Example with discouraged free-form syntax
│ hosts: localhost
│ tasks:
│ - name: Create a placefolder file
│ ansible.builtin.command: chdir=/tmp touch foo # <-- don't use free-form
│ - name: Use raw to echo
│ ansible.builtin.raw: executable=/bin/bash echo foo # <-- don't use executable=
│ changed_when: false
│
│ ## Correct code
│
│ ---
│ - name: Example that avoids free-form syntax
│ hosts: localhost
│ tasks:
│ - name: Create a placefolder file
│ ansible.builtin.command:
│ cmd: touch foo # <-- ansible will not touch it
│ chdir: /tmp
│ - name: Use raw to echo
│ ansible.builtin.raw: echo foo
│ args:
│ executable: /bin/bash # <-- explicit is better
│ changed_when: false
version_added │ v6.8.0
tags │ syntax, risk, experimental
severity │ MEDIUM
╵
╷
no-handler │ Tasks that run when changed should likely be handlers.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # no-handler
│
│ This rule checks for the correct handling of changes to results or conditions.
│
│ If a task has a when: result.changed condition, it effectively acts as a handler. The recommended
│ approach is to use notify and move tasks to handlers. If necessary you can silence the rule by add a #
│ noqa: no-handler comment at the end of the line.
│
│ ## Problematic Code
│
│ ---
│ - name: Example of no-handler rule
│ hosts: localhost
│ tasks:
│ - name: Register result of a task
│ ansible.builtin.copy:
│ dest: "/tmp/placeholder"
│ content: "Ansible made this!"
│ mode: 0600
│ register: result # <-- Registers the result of the task.
│ - name: Second command to run
│ ansible.builtin.debug:
│ msg: The placeholder file was modified!
│ when: result.changed # <-- Triggers the no-handler rule.
│
│ ---
│ # Optionally silences the rule.
│ when: result.changed # noqa: no-handler
│
│ ## Correct Code
│
│ The following code includes the same functionality as the problematic code without recording a result
│ variable.
│
│ ---
│ - name: Example of no-handler rule
│ hosts: localhost
│ tasks:
│ - name: Register result of a task
│ ansible.builtin.copy:
│ dest: "/tmp/placeholder"
│ content: "Ansible made this!"
│ mode: 0600
│ notify:
│ - Second command to run # <-- Handler runs only when the file changes.
│ handlers:
│ - name: Second command to run
│ ansible.builtin.debug:
│ msg: The placeholder file was modified!
│
│ (more)
version_added │ historic
tags │ idiom
severity │ MEDIUM
╵
╷
no-jinja-when │ No Jinja2 in when.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # no-jinja-when
│
│ This rule checks conditional statements for Jinja expressions in curly brackets {{ }}. Ansible processes
│ conditionals statements that use the when, failed_when, and changed_when clauses as Jinja expressions.
│
│ An Ansible rule is to always use {{ }} except with when keys. Using {{ }} in conditionals creates a
│ nested expression, which is an Ansible anti-pattern and does not produce expected results.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Shut down Debian systems
│ ansible.builtin.command: /sbin/shutdown -t now
│ when: "{{ ansible_facts['os_family'] == 'Debian' }}" # <- Nests a Jinja expression in a
│ conditional statement.
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Shut down Debian systems
│ ansible.builtin.command: /sbin/shutdown -t now
│ when: ansible_facts['os_family'] == "Debian" # <- Uses facts in a conditional statement.
version_added │ historic
tags │ deprecations
severity │ HIGH
╵
╷
no-log-password │ Password should not be logged.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # no-log-password
│
│ This rule ensures playbooks do not write passwords to logs when using loops. Always set the no_log: true
│ attribute to protect sensitive data.
│
│ While most Ansible modules mask sensitive data, using secrets inside a loop can result in those secrets
│ being logged. Explicitly adding no_log: true prevents accidentally exposing secrets.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Log user passwords
│ ansible.builtin.user:
│ name: john_doe
│ comment: John Doe
│ uid: 1040
│ group: admin
│ password: "{{ item }}"
│ with_items:
│ - wow
│ no_log: false # <- Sets the no_log attribute to false.
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Do not log user passwords
│ ansible.builtin.user:
│ name: john_doe
│ comment: John Doe
│ uid: 1040
│ group: admin
│ password: "{{ item }}"
│ with_items:
│ - wow
│ no_log: true # <- Sets the no_log attribute to a non-false value.
version_added │ v5.0.9
tags │ opt-in, security, experimental
severity │ LOW
╵
╷
no-prompting │ Disallow prompting.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # no-prompting
│
│ This rule checks for vars_prompt or the ansible.builtin.pause module in playbooks. You should enable
│ this rule to ensure that playbooks can run unattended and in CI/CD pipelines.
│
│ This is an opt-in rule. You must enable it in your Ansible-lint configuration as follows:
│
│ enable_list:
│ - no-prompting
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ vars_prompt: # <- Prompts the user to input credentials.
│ - name: username
│ prompt: What is your username?
│ private: false
│
│ - name: password
│ prompt: What is your password?
│ tasks:
│ - name: Pause for 5 minutes
│ ansible.builtin.pause:
│ minutes: 5 # <- Pauses playbook execution for a set period of time.
│
│ ## Correct Code
│
│ Correct code for this rule is to omit vars_prompt and the ansible.builtin.pause module from your
│ playbook.
version_added │ v6.0.3
tags │ opt-in, experimental
severity │ VERY_LOW
╵
╷
no-relative-paths │ The src argument should not use a relative path.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # no-relative-paths
│
│ This rule checks for relative paths in the ansible.builtin.copy and ansible.builtin.template modules.
│
│ Relative paths in a task most often direct Ansible to remote files and directories on managed nodes. In
│ the ansible.builtin.copy and ansible.builtin.template modules, the src argument refers to local files
│ and directories on the control node.
│
│ The recommended locations to store files are as follows:
│
│ • Use the files/ folder in the playbook or role directory for the copy module.
│ • Use the templates/ folder in the playbook or role directory for the template module.
│
│ These folders allow you to omit the path or use a sub-folder when specifying files with the src
│ argument.
│
│ If resources are outside your Ansible playbook or role directory you should use an absolute path with
│ the `src` argument.
│
│ Do not store resources at the same directory level as your Ansible playbook or tasks files.
│ Doing this can result in disorganized projects and cause user confusion when distinguishing between
│ resources of the same type, such as YAML.
│
│ See task paths in the Ansible documentation for more information.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Template a file to /etc/file.conf
│ ansible.builtin.template:
│ src: ../my_templates/foo.j2 # <- Uses a relative path in the src argument.
│ dest: /etc/file.conf
│ owner: bin
│ group: wheel
│ mode: "0644"
│
│ - name: Example playbook
│ hosts: all
│ vars:
│ source_path: ../../my_templates/foo.j2 # <- Sets a variable to a relative path.
│ tasks:
│ - name: Copy a file to /etc/file.conf
│ ansible.builtin.copy:
│ src: "{{ source_path }}" # <- Uses the variable in the src argument.
│ dest: /etc/foo.conf
│ owner: foo
│ group: foo
│ mode: "0644"
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Template a file to /etc/file.conf
│ ansible.builtin.template:
│ src: foo.j2 # <- Uses a path from inside templates/ directory.
│ dest: /etc/file.conf
│ owner: bin
│ group: wheel
│ mode: "0644"
│
│ - name: Example playbook
│ hosts: all
│ vars:
│ source_path: foo.j2 # <- Uses a path from inside files/ directory.
│ tasks:
│ - name: Copy a file to /etc/file.conf
│ ansible.builtin.copy:
│ src: "{{ source_path }}" # <- Uses the variable in the src argument.
│ dest: /etc/foo.conf
│ owner: foo
│ group: foo
│ mode: "0644"
version_added │ v4.0.0
tags │ idiom
severity │ HIGH
╵
╷
no-same-owner │ Do not preserve the owner and group when transferring files across hosts.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # no-same-owner
│
│ This rule checks that the owner and group do not transfer across hosts.
│
│ In many cases the owner and group on remote hosts do not match the owner and group assigned to source
│ files. Preserving the owner and group during transfer can result in errors with permissions or leaking
│ sensitive information.
│
│ When you synchronize files, you should avoid transferring the owner and group by setting owner: false
│ and group: false arguments. When you unpack archives with the ansible.builtin.unarchive module you
│ should set the --no-same-owner option.
│
│ This is an opt-in rule. You must enable it in your Ansible-lint configuration as follows:
│
│ enable_list:
│ - no-same-owner
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Synchronize conf file
│ ansible.posix.synchronize:
│ src: /path/conf.yaml
│ dest: /path/conf.yaml # <- Transfers the owner and group for the file.
│ - name: Extract tarball to path
│ ansible.builtin.unarchive:
│ src: "{{ file }}.tar.gz"
│ dest: /my/path/ # <- Transfers the owner and group for the file.
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Synchronize conf file
│ ansible.posix.synchronize:
│ src: /path/conf.yaml
│ dest: /path/conf.yaml
│ owner: false
│ group: false # <- Does not transfer the owner and group for the file.
│ - name: Extract tarball to path
│ ansible.builtin.unarchive:
│ src: "{{ file }}.tar.gz"
│ dest: /my/path/
│ extra_opts:
│ - --no-same-owner # <- Does not transfer the owner and group for the file.
tags │ opt-in
severity │ LOW
╵
╷
no-tabs │ Most files should not contain tabs.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # no-tabs
│
│ This rule checks for the tab character. The \t tab character can result in unexpected display or
│ formatting issues. You should always use spaces instead of tabs.
│
│ This rule does not trigger alerts for tab characters in the ``ansible.builtin.lineinfile`` module.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Do not trigger the rule
│ ansible.builtin.lineinfile:
│ path: some.txt
│ regexp: '^\t$'
│ line: 'string with \t inside'
│ - name: Trigger the rule with a debug message
│ ansible.builtin.debug:
│ msg: "Using the \t character can cause formatting issues." # <- Includes the tab character.
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Do not trigger the no-tabs rule
│ ansible.builtin.debug:
│ msg: "Using space characters avoids formatting issues."
version_added │ v4.0.0
tags │ formatting
severity │ LOW
╵
╷
only-builtins │ Use only builtin actions.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # only-builtins
│
│ This rule checks that playbooks use actions from the ansible.builtin collection only.
│
│ This is an opt-in rule. You must enable it in your Ansible-lint configuration as follows:
│
│ enable_list:
│ - only-builtins
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: all
│ tasks:
│ - name: Deploy a Helm chart for Prometheus
│ kubernetes.core.helm: # <- Uses a non-builtin collection.
│ name: test
│ chart_ref: stable/prometheus
│ release_namespace: monitoring
│ create_namespace: true
│
│ ## Correct Code
│
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Run a shell command
│ ansible.builtin.shell: echo This playbook uses actions from the builtin collection only.
tags │ opt-in, experimental
severity │ MEDIUM
╵
╷
package-latest │ Package installs should not use latest.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # package-latest
│
│ This rule checks that package managers install software in a controlled, safe manner.
│
│ Package manager modules, such as ansible.builtin.yum, include a state parameter that configures how
│ Ansible installs software. In production environments, you should set state to present and specify a
│ target version to ensure that packages are installed to a planned and tested version.
│
│ Setting state to latest not only installs software, it performs an update and installs additional
│ packages. This can result in performance degradation or loss of service. If you do want to update
│ packages to the latest version, you should also set the update_only parameter to true to avoid
│ installing additional packages.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Install Ansible
│ ansible.builtin.yum:
│ name: ansible
│ state: latest # <- Installs the latest package.
│
│ - name: Install Ansible-lint
│ ansible.builtin.pip:
│ name: ansible-lint
│ args:
│ state: latest # <- Installs the latest package.
│
│ - name: Install some-package
│ ansible.builtin.package:
│ name: some-package
│ state: latest # <- Installs the latest package.
│
│ - name: Install Ansible with update_only to false
│ ansible.builtin.yum:
│ name: sudo
│ state: latest
│ update_only: false # <- Updates and installs packages.
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Install Ansible
│ ansible.builtin.yum:
│ name: ansible-2.12.7.0
│ state: present # <- Pins the version to install with yum.
│
│ - name: Install Ansible-lint
│ ansible.builtin.pip:
│ name: ansible-lint
│ args:
│ state: present
│ version: 5.4.0 # <- Pins the version to install with pip.
│
│ - name: Install some-package
│ ansible.builtin.package:
│ name: some-package
│ state: present # <- Ensures the package is installed.
│
│ - name: Update Ansible with update_only to true
│ ansible.builtin.yum:
│ name: sudo
│ state: latest
│ update_only: true # <- Updates but does not install additional packages.
version_added │ historic
tags │ idempotency
severity │ VERY_LOW
╵
╷
parser-error │ AnsibleParserError.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ Ansible parser fails; this usually indicates an invalid file.
version_added │ v5.0.0
tags │ core
severity │ VERY_HIGH
╵
╷
partial-become │ become_user requires become to work as expected.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # partial-become
│
│ This rule checks that privilege escalation is activated when changing users.
│
│ To perform an action as a different user with the become_user directive, you must set become: true.
│
│ While Ansible inherits have of `become` and `become_user` from upper levels,
│ like play level or command line, we do not look at these values. This rule
│ requires you to be explicit and always define both in the same place, mainly
│ in order to prevent accidents when some tasks are moved from one location to
│ another one.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Start the httpd service as the apache user
│ ansible.builtin.service:
│ name: httpd
│ state: started
│ become_user: apache # <- Does not change the user because "become: true" is not set.
│
│ ## Correct Code
│
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Start the httpd service as the apache user
│ ansible.builtin.service:
│ name: httpd
│ state: started
│ become: true # <- Activates privilege escalation.
│ become_user: apache # <- Changes the user with the desired privileges.
version_added │ historic
tags │ unpredictability
severity │ VERY_HIGH
╵
╷
playbook-extension │ Use ".yml" or ".yaml" playbook extension.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # playbook-extension
│
│ This rule checks the file extension for playbooks is either .yml or .yaml. Ansible playbooks are
│ expressed in YAML format with minimal syntax.
│
│ The YAML syntax reference provides additional detail.
│
│ ## Problematic Code
│
│ This rule is triggered if Ansible playbooks do not have a file extension or use an unsupported file
│ extension such as playbook.json or playbook.xml.
│
│ ## Correct Code
│
│ Save Ansible playbooks as valid YAML with the .yml or .yaml file extension.
version_added │ v4.0.0
tags │ formatting
severity │ MEDIUM
╵
╷
risky-file-permissions │ File permissions unset or incorrect.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # risky-file-permissions
│
│ This rule is triggered by various modules that could end up creating new files on disk with permissions
│ that might be too open, or unpredictable. Please read the documentation of each module carefully to
│ understand the implications of using different argument values, as these make the difference between
│ using the module safely or not. The fix depends on each module and also your particular situation.
│
│ Some modules have a create argument that defaults to true. For those you either need to set create:
│ false or provide some permissions like mode: 0600 to make the behavior predictable and not dependent on
│ the current system settings.
│
│ Modules that are checked:
│
│ • ansible.builtin.assemble
│ • ansible.builtin.copy
│ • ansible.builtin.file
│ • ansible.builtin.get_url
│ • ansible.builtin.replace
│ • ansible.builtin.template
│ • community.general.archive
│ • community.general.ini_file
│
│ This rule does not take
│ [module_defaults](https://docs.ansible.com/ansible/latest/user_guide/playbooks_module_defaults.html)
│ configuration into account.
│ There are currently no plans to implement this feature because changing task location can also change
│ task behavior.
│
│ ## Problematic code
│
│ ---
│ - name: Unsafe example of using ini_file
│ community.general.ini_file:
│ path: foo
│ create: true
│ mode: preserve
│
│ ## Correct code
│
│ ---
│ - name: Safe example of using ini_file (1st solution)
│ community.general.ini_file:
│ path: foo
│ create: false # prevents creating a file with potentially insecure permissions
│ mode: preserve
│
│ - name: Safe example of using ini_file (2nd solution)
│ community.general.ini_file:
│ path: foo
│ mode: 0600 # explicitly sets the desired permissions, to make the results predictable
│ mode: preserve
│
│ (more)
version_added │ v4.3.0
tags │ unpredictability, experimental
severity │ VERY_HIGH
╵
╷
risky-octal │ Octal file permissions must contain leading zero or be a string.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # risky-octal
│
│ This rule checks that octal file permissions either contain a leading zero or are a string value.
│
│ Modules that are checked:
│
│ • ansible.builtin.assemble
│ • ansible.builtin.copy
│ • ansible.builtin.file
│ • ansible.builtin.replace
│ • ansible.builtin.template
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Unsafe example of declaring Numeric file permissions
│ ansible.builtin.file:
│ path: /etc/foo.conf
│ owner: foo
│ group: foo
│ mode: 644
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Safe example of declaring Numeric file permissions (1st solution)
│ ansible.builtin.file:
│ path: /etc/foo.conf
│ owner: foo
│ group: foo
│ mode: 0644 # <- Leading zero will prevent Numeric file permissions to behave in unexpected ways.
│ - name: Safe example of declaring Numeric file permissions (2nd solution)
│ ansible.builtin.file:
│ path: /etc/foo.conf
│ owner: foo
│ group: foo
│ mode: "644" # <- Being in a string will prevent Numeric file permissions to behave in unexpected
│ ways.
│
│ (more)
version_added │ historic
tags │ formatting
severity │ VERY_HIGH
╵
╷
risky-shell-pipe │ Shells that use pipes should set the pipefail option.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # risky-shell-pipe
│
│ This rule checks for the bash pipefail option with the Ansible shell module.
│
│ You should always set pipefail when piping output from a command to another. The return status of a
│ pipeline is the exit status of the command. The pipefail option ensures that tasks fail as expected if
│ the first command fails.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ tasks:
│ - name: Pipeline without pipefail
│ shell: false | cat
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ become: no
│ tasks:
│ - name: Pipeline with pipefail
│ shell: set -o pipefail && false | cat
│
│ - name: Pipeline with pipefail, multi-line
│ shell: |
│ set -o pipefail # <-- adding this will prevent surprises
│ false | cat
version_added │ v4.1.0
tags │ command-shell
severity │ MEDIUM
╵
╷
role-name │ Role name {0} does not match ^[a-z][a-z0-9_]*$ pattern.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # role-name
│
│ This rule checks role names to ensure they conform with requirements.
│
│ Role names must contain only lowercase alphanumeric characters and the underscore _ character. Role
│ names must also start with an alphabetic character.
│
│ For more information see the roles directory topic in Ansible documentation.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ roles:
│ - 1myrole # <- Does not start with an alphabetic character.
│ - myrole2[*^ # <- Contains invalid special characters.
│ - myRole_3 # <- Contains uppercase alphabetic characters.
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ roles:
│ - myrole1 # <- Starts with an alphabetic character.
│ - myrole2 # <- Contains only alphanumeric characters.
│ - myrole_3 # <- Contains only lowercase alphabetic characters.
│
│ (more)
version_added │ v4.3.0
tags │ deprecations, metadata
severity │ HIGH
╵
╷
run-once │ Run once should use strategy other than free.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # run-once
│
│ This rule warns against the use of run_once when strategy is set to free.
│
│ This rule can produce the following messages:
│
│ • run_once[play]: Play uses strategy: free.
│ • run_once[task]: Using run_once may behave differently if strategy is set to free.
│
│ For more information see the following topics in Ansible documentation:
│
│ • free strategy
│ • selecting a strategy
│ • run_once(playbook keyword) more info
│
│ ## Problematic Code
│
│ ---
│ - name: "Example with run_once"
│ hosts: all
│ strategy: free # <-- avoid use of strategy as free
│ gather_facts: false
│ tasks:
│ - name: Task with run_once
│ ansible.builtin.debug:
│ msg: "Test"
│ run_once: true # <-- avoid use of strategy as free at play level when using run_once at task level
│
│ ## Correct Code
│
│ - name: "Example without run_once"
│ hosts: all
│ gather_facts: false
│ tasks:
│ - name: Task without run_once
│ ansible.builtin.debug:
│ msg: "Test"
│
│ (more)
tags │ idiom, experimental
severity │ MEDIUM
╵
╷
schema │ Perform JSON Schema Validation for known lintable kinds.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # schema
│
│ The schema rule validates Ansible metadata files against JSON schemas. These schemas ensure the
│ compatibility of Ansible syntax content across versions.
│
│ This schema rule is mandatory. You cannot use inline noqa comments to ignore it.
│
│ Ansible-lint validates the schema rule before processing other rules. This prevents unexpected syntax
│ from triggering multiple rule violations.
│
│ ## Validated schema
│
│ Ansible-lint currently validates several schemas that are maintained in separate projects and updated
│ independently to ansible-lint.
│
│ ▌ Report bugs related to schema in their respective repository and not in the ansible-lint project.
│
│ Maintained in the ansible-lint project:
│
│ • schema[ansible-lint-config] validates ansible-lint configuration
│
│ Maintained in the ansible-navigator project:
│
│ • schema[ansible-navigator] validates ansible-navigator configuration
│
│ Maintained in the Ansible schemas project:
│
│ • schema[arg_specs] validates module argument specs
│ • schema[execution-environment] validates execution environments
│ • schema[galaxy] validates collection metadata.
│ • schema[inventory] validates inventory files that match inventory/*.yml.
│ • schema[meta-runtime] validates runtime information that matches meta/runtime.yml
│ • schema[meta] validates metadata for roles that match meta/main.yml. See role-dependencies or
│ role/metadata.py) for details.
│ • schema[playbook] validates Ansible playbooks.
│ • schema[requirements] validates Ansible requirements files that match requirements.yml.
│ • schema[tasks] validates Ansible task files that match tasks/**/*.yml.
│ • schema[vars] validates Ansible variables that match vars/*.yml and defaults/*.yml.
│
│ ## schema
│
│ For meta/main.yml files, Ansible-lint requires a galaxy_info.standalone property that clarifies if a
│ role is an old standalone one or a new one, collection based:
│
│ galaxy_info:
│ standalone: true # <-- this is a standalone role (not part of a collection)
│
│ Ansible-lint requires the standalone key to avoid confusion and provide more specific error messages.
│ For example, the meta schema will require some properties only for standalone roles or prevent the use
│ of some properties that are not supported by collections.
│
│ You cannot use an empty meta/main.yml file or use only comments in the meta/main.yml file.
version_added │ v6.1.0
tags │ core, experimental
severity │ VERY_HIGH
╵
╷
syntax-check │ Ansible syntax check failed.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # syntax-check
│
│ Our linter runs ansible-playbook --syntax-check on all playbooks, and if any of these reports a syntax
│ error, this stops any further processing of these files.
│
│ This error cannot be disabled due to being a prerequisite for other steps. You can exclude these files
│ from linting, but it is better to make sure they can be loaded by Ansible. This is often achieved by
│ editing the inventory file and/or ansible.cfg so ansible can load required variables.
│
│ If undefined variables cause the failure, you can use the jinja default() filter to provide fallback
│ values, like in the example below.
│
│ This rule is among the few unskippable rules that cannot be added to skip_list or warn_list. One
│ possible workaround is to add the entire file to the exclude_paths. This is a valid approach for special
│ cases, like testing fixtures that are invalid on purpose.
│
│ One of the most common sources of errors is failure to assert the presence of various variables at the
│ beginning of the playbook.
│
│ ## Problematic code
│
│ ---
│ - name: Bad use of variable inside hosts block (wrong assumption of it being defined)
│ hosts: "{{ my_hosts }}"
│ tasks: []
│
│ ## Correct code
│
│ ---
│ - name: Good use of variable inside hosts, without assumptions
│ hosts: "{{ my_hosts | default([]) }}"
│ tasks: []
version_added │ v5.0.0
tags │ core
severity │ VERY_HIGH
╵
╷
var-naming │ All variables should be named using only lowercase and underscores.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # var-naming
│
│ This rule checks variable names to ensure they conform with requirements.
│
│ Variable names must contain only lowercase alphanumeric characters and the underscore _ character.
│ Variable names must also start with either an alphabetic or underscore _ character.
│
│ For more information see the creating valid variable names topic in Ansible documentation.
│
│ ## Problematic Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ vars:
│ CamelCase: true # <- Contains a mix of lowercase and uppercase characters.
│ ALL_CAPS: bar # <- Contains only uppercase characters.
│ v@r!able: baz # <- Contains special characters.
│
│ ## Correct Code
│
│ ---
│ - name: Example playbook
│ hosts: localhost
│ vars:
│ lowercase: true # <- Contains only lowercase characters.
│ no_caps: bar # <- Does not contains uppercase characters.
│ variable: baz # <- Does not contain special characters.
version_added │ v5.0.10
tags │ idiom, experimental
severity │ MEDIUM
╵
╷
warning │ Other warnings detected during run.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # warning
│
│ warning is a special type of internal rule that is used to report generic runtime warnings found during
│ execution. As stated by its name, they are not counted as errors, so they do not influence the final
│ outcome.
│
│ • warning[empty-playbook] is raised when a playbook file has no content.
│ • warning[raw-non-string] indicates that you are using
│ [raw](http://docs.ansible.com/ansible/latest/collections/ansible/builtin/raw_module.html#ansible-col…
│ module with non-string arguments, which is not supported by Ansible.
version_added │ v6.8.0
tags │ core, experimental
severity │ LOW
╵
╷
yaml │ Violations reported by yamllint.
╶───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────╴
description │ # yaml
│
│ This rule checks YAML syntax and is an implementation of yamllint.
│
│ You can disable YAML syntax violations by adding yaml to the skip_list in your Ansible-lint
│ configuration as follows:
│
│ skip_list:
│ - yaml
│
│ For more fine-grained control, disable violations for specific rules using tag identifiers in the
│ yaml[yamllint_rule] format as follows:
│
│ skip_list:
│ - yaml[trailing-spaces]
│ - yaml[indentation]
│
│ If you want Ansible-lint to report YAML syntax violations as warnings, and not fatal errors, add tag
│ identifiers to the warn_list in your configuration, for example:
│
│ warn_list:
│ - yaml[document-start]
│
│ See the list of yamllint rules for more information.
│
│ Some of the detailed error codes that you might see are:
│
│ • yaml[brackets] - too few spaces inside empty brackets, or too many spaces inside brackets
│ • yaml[colons] - too many spaces before colon, or too many spaces after colon
│ • yaml[commas] - too many spaces before comma, or too few spaces after comma
│ • yaml[comments-indentation] - Comment not indented like content
│ • yaml[comments] - Too few spaces before comment, or Missing starting space in comment
│ • yaml[document-start] - missing document start "---" or found forbidden document start "---"
│ • yaml[empty-lines] - too many blank lines (...> ...)
│ • yaml[indentation] - Wrong indentation: expected ... but found ...
│ • yaml[key-duplicates] - Duplication of key "..." in mapping
│ • yaml[new-line-at-end-of-file] - No new line character at the end of file
│ • yaml[syntax] - YAML syntax is broken
│ • yaml[trailing-spaces] - Spaces are found at the end of lines
│ • yaml[truthy] - Truthy value should be one of ...
│
│ ## Problematic code
│
│ # Missing YAML document start.
│ foo: ...
│ foo: ... # <-- Duplicate key.
│ bar: ... # <-- Incorrect comment indentation
│
│ ## Correct code
│
│ ---
│ foo: ...
│ bar: ... # Correct comment indentation.
│
│ (more)
version_added │ v5.0.0
tags │ formatting, yaml
severity │ VERY_LOW
出力形式は「rich、plain、md
」を指定できます。何も指定しないとrich
形式の出力になります。
タグで定められたルールを表示する(ルールの小分類を表示する) 【-T, --list-tags】
それぞれのルールがどのように分類されているか表示します。またこれらの分類はタグを指定する事で指定されたタグのルールのみlintを実行することもできます。Ansible lintのルールの基本的な考え方が分かります。
ansible-lint -T
## もしくは
ansible-lint --list-tags
# List of tags and rules they cover
command-shell: # Specific to use of command and shell modules
- command-instead-of-module
- command-instead-of-shell
- deprecated-command-syntax
- inline-env-var
- no-changed-when
- risky-shell-pipe
core: # Related to internal implementation of the linter
- internal-error
- load-failure
- parser-error
- syntax-check
- warning
- schema
deprecations: # Indicate use of features that are removed from Ansible
- deprecated-bare-vars
- deprecated-command-syntax
- deprecated-local-action
- deprecated-module
- no-jinja-when
- role-name
experimental: # Newly introduced rules, by default triggering only warnings
- warning
- avoid-implicit
- galaxy
- ignore-errors
- key-order
- no-free-form
- no-log-password
- no-prompting
- only-builtins
- risky-file-permissions
- run-once
- schema
- var-naming
formatting: # Related to code-style
- yaml
- fqcn
- jinja
- key-order
- no-tabs
- playbook-extension
- risky-octal
idempotency: # Possible indication that consequent runs would produce different results
- latest
- no-changed-when
- package-latest
idiom: # Anti-pattern detected, likely to cause undesired behavior
- command-instead-of-module
- command-instead-of-shell
- empty-string-compare
- inline-env-var
- literal-compare
- loop-var-prefix
- name
- no-handler
- no-relative-paths
- run-once
- var-naming
metadata: # Invalid metadata, likely related to galaxy, collections or roles
- galaxy
- meta-incorrect
- meta-no-info
- meta-no-tags
- meta-video-links
- role-name
opt-in: # Rules that are not used unless manually added to `enable_list`
- empty-string-compare
- galaxy
- no-log-password
- no-prompting
- no-same-owner
- only-builtins
risk:
- no-free-form
security: # Rules related o potentially security issues, like exposing credentials
- no-log-password
syntax:
- no-free-form
unpredictability: # Warn about code that might not work in a predictable way
- avoid-implicit
- ignore-errors
- partial-become
- risky-file-permissions
unskippable: # Indicate a fatal error that cannot be ignored or disabled
- load-failure
yaml: # External linter which will also produce its own rule codes
- yaml
出力形式を指定する 【-f , --format 】
コマンドの実行結果の出力形式を指定します。指定できる形式は
- rich
- plain
- md
- json
- codeclimate
- quiet
- pep8
- sarif
- docs
になります。
ansible-lint
WARNING Listing 1 violation(s) that are fatal
roles/create-backup:1: role-name: Role name create-backup does not match ``^*$`` pattern. (warning)
Rule Violation Summary
count tag profile rule associated tags
1 role-name basic deprecations, metadata (warning)
Passed with min profile: 0 failure(s), 1 warning(s) on 15 files.
A new release of ansible-lint is available: 6.8.4 → 6.8.5 Upgrade by running: pip3 install --user --upgrade ansible-lint
ansible-lint -f json
[{"type": "issue", "check_name": "role-name", "categories": ["deprecations", "metadata"], "url": "https://ansible-lint.readthedocs.io/rules/role-name/", "severity": "info", "description": "Role name create-backup does not match ``^\\[a-z]\\[a-z0-9_]*$`` pattern.", "fingerprint": "b6a7a8b84b404967d988b9ffb58662ac0ed3115f22e6bda953b27e89632dac75", "location": {"path": "roles/create-backup", "lines": {"begin": 1}}}]
Rule Violation Summary
count tag profile rule associated tags
1 role-name basic deprecations, metadata (warning)
Passed with min profile: 0 failure(s), 1 warning(s) on 15 files.
A new release of ansible-lint is available: 6.8.4 → 6.8.5 Upgrade by running: pip3 install --user --upgrade ansible-lint
表示される情報を少なくする 【-q】
ansible-lint -q
roles/create-backup:1: role-name (warning)
Profileを指定する 【-P , --profile 】
適用するProfileを指定します。指定できるprofileは
- min
- basic
- moderate
- safety
- shared
- production
です。
何も指定しないとproduction
が適用されます。
通常 (何も指定しないとprofileにproductionが指定される)
ansible-lint
WARNING Listing 1 violation(s) that are fatal
roles/create-backup:1: role-name: Role name create-backup does not match ``^*$`` pattern. (warning)
Rule Violation Summary
count tag profile rule associated tags
1 role-name basic deprecations, metadata (warning)
Passed with min profile: 0 failure(s), 1 warning(s) on 15 files.
A new release of ansible-lint is available: 6.8.4 → 6.8.5 Upgrade by running: pip3 install --user --upgrade ansible-lint
必要最低限のlintを実行する (profileにminを指定する)
ansible-lint -p min
Passed with production profile: 0 failure(s), 0 warning(s) on 1 files.
A new release of ansible-lint is available: 6.8.4 → 6.8.5 Upgrade by running: pip3 install --user --upgrade ansible-lint
前回のcommitと比べて改善があればsuccessを返す 【--progress】
Gitでの開発が前提です。前回のcommit時と内容を比べ1つでもルール違反が少なくなっていればsuccessを返します。
ansible-lint --progress
プロジェクトディレクトリを指定する 【--project-dir 】
なぞのオプションです。プロジェクト内にあるGalaxy role等のパスを指定するとそのRoleをチェックします。しかしそのRoleに対してAnsible lintは実行されません。(Ansible lint 6.8系で動作確認時)
警告をエラーと見なす 【-s, --strict】
Ansible lintをstrictモードで実行します。
ansible-lint -s
## もしくは
ansible-lint --strict
YAMLファイルをAnsible lintの推奨する形式に書き直す 【--write】
YAMLファイルがAnsible lintが推奨する形式に書き換えられます。
ansible-lint --write
例えば
- name: Create WordPress install directory
ansible.builtin.file:
- dest: '{{ wp_install_path }}'
+ dest: "{{ wp_install_path }}"
state: directory
owner: apache
group: apache
mode: 0755
- recurse: yes
+ recurse: true
yaml-files:
- - '*.yaml'
- - '*.yml'
- - '.yamllint'
+ - "*.yaml"
+ - "*.yml"
+ - .yamllint
のようにフォーマットされます。
主にインデント、クウォート、スペース等がチェックされますが記事執筆時の6.8系では例えばfqdnの修正等のAnsible独自の機能やyamllintのkey-orderingのようなフォーマットは行われないみたいです。
タグで指定されたルール群のみ適用する 【-t TAGS, --tags TAGS】
ansible-lint --list-tags
で分類されているタグを利用して特定のルール群のみAnsible lintを適用します。
ansible-lint -t
例えばModuleのfqdnを記述していない場合は通常通りAnsible lintを実行すると以下のようなエラーが出ます。
---
- name: Install cowsay
package:
name: cowsay
state: present
ansible-lint tasks/main.yml
WARNING Listing 1 violation(s) that are fatal
tasks/main.yml:2: fqcn[action-core]: Use FQCN for builtin module actions (package).
You can skip specific rules or tags by adding them to your configuration file:
# .config/ansible-lint.yml
warn_list: # or 'skip_list' to silence them completely
- fqcn[action-core] # Use FQCN for builtin actions.
Rule Violation Summary
count tag profile rule associated tags
1 ]8;id=404615;https://ansible-lint.readthedocs.io/rules/fqcn\fqcn[action-core]]8;;\ production formatting
Failed after shared profile, 4/5 star rating: 1 failure(s), 0 warning(s) on 1 files.
Exited with code exit status 2
これをタグ名core
に属するルールのみ適用するとfqdn等のルールは適用されないためエラーになりません。
nsible-lint -t core tasks/main.yml
Passed with production profile: 0 failure(s), 0 warning(s) on 1 files.
Verboseモードで実行する 【-v, -vv】
ログの出力が細かくなります。v
は2つまでで3つ以上つけてもそれ以上表示は変わらないみたいです。
ansible-lint tasks/main.yml -v
INFO Set ANSIBLE_LIBRARY=/home/mamono210/.cache/ansible-compat/244210/modules:/home/mamono210/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
INFO Set ANSIBLE_COLLECTIONS_PATH=/home/mamono210/.cache/ansible-compat/244210/collections:/home/mamono210/.ansible/collections:/usr/share/ansible/collections
INFO Set ANSIBLE_ROLES_PATH=/home/mamono210/.cache/ansible-compat/244210/roles:/home/mamono210/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
INFO Using /home/mamono210/.cache/ansible-compat/244210/roles/mamono210.cowsay symlink to current repository in order to enable Ansible to find the role using its expected full name.
Passed with production profile: 0 failure(s), 0 warning(s) on 1 files.
ansible-lint tasks/main.yml -vv
DEBUG Logging initialized to level 10
DEBUG Options: Namespace(cache_dir='/home/mamono210/.cache/ansible-compat/244210', colored=True, configured=True, cwd=PosixPath('/home/mamono210/project'), display_relative_path=True, exclude_paths=['.cache', '.git', '.hg', '.svn', '.tox'], format='rich', lintables=['tasks/main.yml'], listrules=False, listtags=False, write_list=[], parseable=True, quiet=False, rulesdirs=['/home/mamono210/.local/lib/python3.11/site-packages/ansiblelint/rules'], skip_list=[], tags=[], verbosity=2, warn_list=['avoid-implicit', 'experimental', 'fqcn[action]', 'fqcn[redirect]', 'jinja[spacing]', 'name[casing]', 'name[play]', 'role-name', 'warning[empty-playbook]', 'role-name[path]'], kinds=[{'jinja2': '**/*.j2'}, {'jinja2': '**/*.j2.*'}, {'yaml': '.github/**/*.{yaml,yml}'}, {'text': '**/templates/**/*.*'}, {'execution-environment': '**/execution-environment.yml'}, {'ansible-lint-config': '**/.ansible-lint'}, {'ansible-lint-config': '**/.config/ansible-lint.yml'}, {'ansible-navigator-config': '**/ansible-navigator.{yaml,yml}'}, {'inventory': '**/inventory/**.{yaml,yml}'}, {'requirements': '**/meta/requirements.{yaml,yml}'}, {'galaxy': '**/galaxy.yml'}, {'reno': '**/releasenotes/*/*.{yaml,yml}'}, {'tasks': '**/tasks/**/*.{yaml,yml}'}, {'playbook': '**/playbooks/*.{yml,yaml}'}, {'playbook': '**/*playbook*.{yml,yaml}'}, {'role': '**/roles/*/'}, {'handlers': '**/handlers/*.{yaml,yml}'}, {'vars': '**/{host_vars,group_vars,vars,defaults}/**/*.{yaml,yml}'}, {'test-meta': '**/tests/integration/targets/*/meta/main.{yaml,yml}'}, {'meta': '**/meta/main.{yaml,yml}'}, {'meta-runtime': '**/meta/runtime.{yaml,yml}'}, {'arg_specs': '**/meta/argument_specs.{yaml,yml}'}, {'yaml': '.config/molecule/config.{yaml,yml}'}, {'requirements': '**/molecule/*/{collections,requirements}.{yaml,yml}'}, {'yaml': '**/molecule/*/{base,molecule}.{yaml,yml}'}, {'requirements': '**/requirements.{yaml,yml}'}, {'playbook': '**/molecule/*/*.{yaml,yml}'}, {'yaml': '**/{.ansible-lint,.yamllint}'}, {'yaml': '**/*.{yaml,yml}'}, {'yaml': '**/.*.{yaml,yml}'}], mock_filters=[], mock_modules=[], mock_roles=[], loop_var_prefix=None, var_naming_pattern=None, offline=False, project_dir='.', extra_vars=None, enable_list=[], skip_action_validation=True, strict=False, rules={}, profile=None, progressive=False, rulesdir=[], use_default_rules=False, config_file='/home/mamono210/project/.ansible-lint', version=False, cache_dir_lock=<filelock._unix.UnixFileLock object at 0x7f318b9f4d10>)
DEBUG /home/mamono210/project
DEBUG Loading custom .yamllint config file, this extends our internal yamllint config.
DEBUG Effective yamllint rules used: {'braces': {'level': 'error', 'forbid': False, 'min-spaces-inside': 0, 'max-spaces-inside': 0, 'min-spaces-inside-empty': -1, 'max-spaces-inside-empty': -1}, 'brackets': {'level': 'error', 'forbid': False, 'min-spaces-inside': 0, 'max-spaces-inside': 0, 'min-spaces-inside-empty': -1, 'max-spaces-inside-empty': -1}, 'colons': {'level': 'error', 'max-spaces-before': 0, 'max-spaces-after': 1}, 'commas': {'level': 'error', 'max-spaces-before': 0, 'min-spaces-after': 1, 'max-spaces-after': 1}, 'comments': {'level': 'error', 'require-starting-space': True, 'ignore-shebangs': True, 'min-spaces-from-content': 2}, 'comments-indentation': {'level': 'error'}, 'document-end': False, 'document-start': {'level': 'error', 'present': True}, 'empty-lines': {'level': 'error', 'max': 2, 'max-start': 0, 'max-end': 0}, 'empty-values': False, 'float-values': False, 'hyphens': {'level': 'error', 'max-spaces-after': 1}, 'indentation': {'level': 'error', 'spaces': 'consistent', 'indent-sequences': True, 'check-multi-line-strings': False}, 'key-duplicates': {'level': 'error'}, 'key-ordering': False, 'line-length': False, 'new-line-at-end-of-file': {'level': 'error'}, 'new-lines': {'level': 'error', 'type': 'unix'}, 'octal-values': False, 'quoted-strings': False, 'trailing-spaces': {'level': 'error'}, 'truthy': False}
INFO Set ANSIBLE_LIBRARY=/home/mamono210/.cache/ansible-compat/244210/modules:/home/mamono210/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
INFO Set ANSIBLE_COLLECTIONS_PATH=/home/mamono210/.cache/ansible-compat/244210/collections:/home/mamono210/.ansible/collections:/usr/share/ansible/collections
INFO Set ANSIBLE_ROLES_PATH=/home/mamono210/.cache/ansible-compat/244210/roles:/home/mamono210/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
INFO Using /home/mamono210/.cache/ansible-compat/244210/roles/mamono210.cowsay symlink to current repository in order to enable Ansible to find the role using its expected full name.
DEBUG Examining tasks/main.yml of type tasks
DEBUG Attempting to release lock 139850772598032 on /home/mamono210/.cache/ansible-compat/244210/.lock
DEBUG Lock 139850772598032 released on /home/mamono210/.cache/ansible-compat/244210/.lock
DEBUG Determined rule-profile order: {'internal-error': (0, 'min'), 'load-failure': (1, 'min'), 'parser-error': (2, 'min'), 'syntax-check': (3, 'min'), 'command-instead-of-module': (4, 'basic'), 'command-instead-of-shell': (5, 'basic'), 'deprecated-bare-vars': (6, 'basic'), 'deprecated-command-syntax': (7, 'basic'), 'deprecated-local-action': (8, 'basic'), 'deprecated-module': (9, 'basic'), 'inline-env-var': (10, 'basic'), 'key-order': (11, 'basic'), 'literal-compare': (12, 'basic'), 'jinja': (13, 'basic'), 'no-jinja-when': (14, 'basic'), 'no-tabs': (15, 'basic'), 'partial-become': (16, 'basic'), 'playbook-extension': (17, 'basic'), 'role-name': (18, 'basic'), 'schema': (19, 'basic'), 'name': (20, 'basic'), 'var-naming': (21, 'basic'), 'yaml': (22, 'basic'), 'name[template]': (23, 'moderate'), 'name[imperative]': (24, 'moderate'), 'name[casing]': (25, 'moderate'), 'no-free-form': (26, 'moderate'), 'spell-var-name': (27, 'moderate'), 'avoid-implicit': (28, 'safety'), 'latest': (29, 'safety'), 'package-latest': (30, 'safety'), 'risky-file-permissions': (31, 'safety'), 'risky-octal': (32, 'safety'), 'risky-shell-pipe': (33, 'safety'), 'galaxy': (34, 'shared'), 'ignore-errors': (35, 'shared'), 'layout': (36, 'shared'), 'meta-incorrect': (37, 'shared'), 'meta-no-info': (38, 'shared'), 'meta-no-tags': (39, 'shared'), 'meta-video-links': (40, 'shared'), 'meta-version': (41, 'shared'), 'meta-unsupported-ansible': (42, 'shared'), 'no-changed-when': (43, 'shared'), 'no-changelog': (44, 'shared'), 'no-handler': (45, 'shared'), 'no-relative-paths': (46, 'shared'), 'max-block-depth': (47, 'shared'), 'max-tasks': (48, 'shared'), 'unsafe-loop': (49, 'shared'), 'avoid-dot-notation': (50, 'production'), 'disallowed-ignore': (51, 'production'), 'fqcn': (52, 'production'), 'import-task-no-when': (53, 'production'), 'meta-no-dependencies': (54, 'production'), 'single-entry-point': (55, 'production'), 'use-loop': (56, 'production')}
Passed with production profile: 0 failure(s), 0 warning(s) on 1 files.
設定ファイルのパスを変更する 【-c , --config-file 】
Ansible lintの設定ファイルはプロジェクト内の .ansible-lint
もしくは .config/ansible-lint.yml
のどちらかに配置されることにより実行時に内容が反映されます。
設定ファイルのパスを状況に応じて変更する場合(例えば開発時とプロダクション時でAnsible lintの設定を変更したい等)はパスの指定で変更可能です。
requirements.ymlからインストールしない 【--offline】
謎のオプションです。Ansible lint以外のツールとの連携を図るために用意されているのかもしれません。
Ansible lintのバージョンを表示する 【--version】
Ansible lintのバージョン情報を表示します。
ansible-lint --version
ansible-lint 6.8.6 using ansible 2.13.6
Ansibleのバージョン情報等が必要な場合はansible --version
とansible-community --version
もあわせて実行します。
ansible --version
ansible [core 2.13.5]
config file = None
configured module search path = ['/home/mamono210/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/mamono210/.local/lib/python3.11/site-packages/ansible
ansible collection location = /home/mamono210/.ansible/collections:/usr/share/ansible/collections
executable location = /home/mamono210/.local/bin/ansible
python version = 3.11.0 (main, Oct 25 2022, 05:00:36) [GCC 10.2.1 20210110]
jinja version = 3.1.2
libyaml = True
ansible-community --version
Ansible community version 6.5.0