Ansible lint命令的选项

本文是Ansible lint Advent Calendar 2022的第二篇文章。

这次我们将解释一下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

基本的命令 de

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[wrong] – Ensure loop variables start with .

│ 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, –列出标签】

展示每个规则是如何分类的。同时,可以通过指定标签来仅对指定标签的规则执行Ansible 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)

指定要使用的配置文件【-P,–profile】。

請選擇適用的個人資料。可選個人資料如下:

    • min

 

    • basic

 

    • moderate

 

    • safety

 

    • shared

 

    production

是的。

如果没有指定任何内容,将应用”production”。

通常情况下,如果未指定任何内容,会将”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检查(将min设为profile)。

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进行开发。如果与上一次提交时的内容相比,即使有一个违反规则的情况减少,也会返回成功。

ansible-lint --progress

指定项目目录【–project-dir】。

这是一个神秘的选项。指定在项目中的Galaxy角色等路径将会检查该角色。然而,对于该角色,Ansible lint不会被执行。(在Ansible lint 6.8版本进行确认)

将警告视为错误【-s, –严格】

使用严格模式对 Ansible lint 进行执行。

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版本中,似乎不会执行Ansible独有功能如fqdn修正或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,即使添加了三个或更多也不会有任何变化。

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
bannerAds