I was writing an Ansible playbook to update Linux headers and build essentials for Debian operating system. But the playbook hang in gathering facts step. So after lots of search on internet I introduced gather_facts: no and then it ran successfully. But I want to know:
Is is OK to use gather_facts: no. Please give some understanding of gather_facts and what it does internally?
To get the kernel version I used the shell module. Is there any other Ansible command to get the kernel version of host machine?
- hosts: DEV1
become: yes
gather_facts: no
tasks:
- name: "Getting the debian kernal version"
shell:
cmd: uname -r
register: kernal_version_output
- name: "Debug content for kernal version"
debug:
msg: "kernal version is => {{ kernal_version_output.stdout }}"
- name: "Update apt-get repo and cache"
apt:
name:
- libreadline-gplv2-dev
- libreadline-dev
- linux-headers-{{ kernal_version_output.stdout }}
- build-essential
Regarding your questions
Please give some understanding of gather_facts
you may have a look into Playbook Vars Facts "Discovering variables: facts and magic variables" and the setup_module.
Quote: "With Ansible you can retrieve or discover certain variables containing information about your remote systems or about Ansible itself. Variables related to remote systems are called facts."
... and what it does internally?
For Ansible ad-hoc commands and the setup_module the source code of setup.py is a good starting point to research how it works internally. In summary, it is just executing some scripts to collect certain system information.
Is is OK to use gather_facts: no
Yes, of course. If you don't need specific information about the remote system to perform tasks and if commands and playbook do not depend on gathered information, you can leave it switched off.
To get the kernel version I used the shell module. Is there any other Ansible command to get the kernel version of host machine?
Even if that is an anti-pattern with Ansible, without facts there seems to be no other possibilities.
Related
I've created an ansible role that's able to deploy some software on systems that or either Windows or Linux based, but I'm having an issue deploying across both sets of servers simultaneously. The issue that I'm having is for the playbook that I've created that calls the role. The Linux tasks won't run if I don't include become: true, and if I do put that into the playbook, the Windows tasks won't run because {"msg": "The PowerShell shell family is incompatible with the sudo become plugin"}. An easy fix to the solution is to just include become: true for my Linux tasks but I was hoping there's a better way to do it than that. I was thinking about maybe running a conditional become (as demonstrated below), but I don't think that would work. Would love to hear your ideas.
playbook.yml
---
- hosts: all
become: "{{ 'no' if ansible_facts['os_family'] == 'Windows' else 'yes' }}"
roles:
- /path/to/role
We have multiple versions of ansible(2.3,2.3.0,2.8.6) we want to use a particular version '2.8.6'.
But whenever ansible execution starts, it uses a different version 2.4.2.0. If we check ansible --version it is ansible-playbook 2.4.2.0.
Is ansible and ansible-playbook versions are same?
How to force a version in ansible execution?
Please give your inputs.
Ansible is well-known to subtly break compatibility around variables, roles, includes and delegation. Therefore, it's considered to be the best practice to assert an Ansible version in a playbook before doing anything.
There are few things to assure:
Those checks should survive --tags option.
Those checks should survive --limit
Those checks should be relatively fast and cause as less verbosity as possible.
My current solution is:
Create playbook version_check.yaml
- import_playbook: version_check.yaml at the beginning of each playbook. (each, this is important, trust me).
version_check.yaml content (ansible 2.5+):
- hosts: all, localhost
gather_facts: false
run_once: true
tasks:
- name: Check ansible version
assert:
that:
- "ansible_version.full is version('2.8.0', '>=')"
- "ansible_version.full is version('2.9.0', '<')"
msg: >
Use Ansible to 2.8 to run this playbook
delegate_to: localhost
tags:
- always
As you can see, there are a lot here.
run_once do this once per Ansible run.
gather_facts: false speed it up a bit.
It is assigned to 'all, localhost', basically guaranee that it's in Ansible runlist regardless of any --limit
It has tag always almost guarantee to run in the case of use --tags. The single uncovered hole here is to use --skip always (which is insane).
There is an older answer with older syntax for versions (below 2.5):
- hosts: all, localhost
gather_facts: false
run_once: true
tasks:
- name: Check ansible version
assert:
that:
- "ansible_version.full | version_compare('2.8.0', '>=')"
- "ansible_version.full | version_compare('2.9.0', '<')"
msg: >
Use Ansible to 2.8 to run this playbook
delegate_to: localhost
tags:
- always
It's hard to say which version to use. If you are afraid of very old ansible, version_compare is better than version, but if you are sure it's at least 2.5+, you can use newer version syntax.
I am trying to get an Ansible task to print the version used while running on Windows 10.
I am currently trying something like this:
---
# Source: https://serverfault.com/a/695798
- name: Get version
win_shell: ansible --version
register: ansibleVersion
# How I chose to expose the version collected
- name: Display version
win_msg:
msg: "Ansible Version: {{ ansibleVersion.stdout }}"
display_seconds: 30
However, I am getting this output:
"stderr": "ansible : The term 'ansible' is not recognized as the name of a cmdlet, function, script file, or operable program. \r\nCheck the spelling of the name, or if a path was included, verify that the path is correct and try again.\r\n
Full disclosure, I am new to Ansible. I have tried win_command, win_shell, and am not really sure what all to try next.
The Windows machines can be provisioned using ansible but not installed on Windows.
You can configure the Windows machine from a Linux machine as the controller host.
And you can run the ansible-playbook from this controller host which will run on the windows machine.
---
- hosts: all
tasks:
- name: Get Windows version
win_shell: "systeminfo /fo csv | ConvertFrom-Csv | select OS*, System*, Hotfix* | Format-List"
register: windows_version
- name: Print Windows host information
debug:
msg: "{{ windows_version }}"
Save this as main.yml
Add the Windows host IP in hosts file
[win]
172.16.*.*
[win:vars]
ansible_user=user
ansible_password=password
ansible_connection=winrm
ansible_winrm_server_cert_validation=ignore
Run the playbook using the following command
ansible-playbook -i hosts main.yml
If you want ansible on Windows, then there are other installation methods to run it on Windows.
Also mentioned in the comments.
I have attached some links to setup ansible on Windows 10 subsytem for Linux,
Ansible - Windows Frequently asked questions
Using Ansible through Windows 10's Subsystem for Linux
Hope it solves your issue.
Thank you to all those who answered and commented. The articles were very informative, and I learned a much more about Ansible. The answers put me on the scent of the actual task I made.
To restate my comment on the original question, I had a misunderstanding. Because on my Windows machine I had to add a user ansible, I thought it was being run locally somehow. However, it turns out, Ansible deploys are being run from a Linux VM.
Once I had this misunderstanding cleared up, I realized I needed to use delegate_to: 127.0.0.1 in my Ansible task. Here is my Check Ansible version task:
---
# SEE: https://serverfault.com/a/695798/514234
- name: Check Ansible version
command: ansible --version
register: ansibleVersion
delegate_to: 127.0.0.1
- name: Print version
debug:
msg: "Ansible Version: {{ ansibleVersion.stdout }}"
Use-Case:
Playbook 1
when we first connect to a remote host/s, the remote host will already have some python version installed - the auto-discovery feature will find it
now we install ansible-docker on the remote host
from this time on: the ansible-docker docs suggest to use ansible_python_interpreter=/usr/bin/env python-docker
Playbook 2
We connect to the same host/s again, but now we must use the /usr/bin/env python-docker python interpreter
What is the best way to do this?
Currently we set ansible_python_interpreter on the playbook level of Playbook 2:
---
- name: DaqMon app
vars:
- ansible_python_interpreter: "{{ '/usr/bin/env python-docker' }}"
This works, but this will also change the python interpreter of the local actions. And thus the local actions will fail, because (python-docker does not exist locally).
the current workaround is to explicitly specify the ansible_python_interpreter on every local-action which is tedious and error-prone
Questions:
the ideal solution is, if we could add '/usr/bin/env python-docker' as fallback to interpreter-python-fallback - but I think this is not possible
is there a way to set the python interpreter only for the remote hosts - and keep the default for the localhost?
or is it possible to explicitly override the python interpreter for the local host?
You should set the ansible_python_interpreter on the host level.
So yes, it's possible to explicitly set the interpreter for localhost in your inventory.
localhost ansible_connection=local ansible_python_interpreter=/usr/bin/python
And I assume that you could also use set_fact on hostvars[<host>].ansible_python_interpreter on your localhost or docker host.
There is a brillant article about set_fact on hostvars ! ;-P
Thanks to the other useful answers I found an easy solution:
on the playbook level we set the python interpreter to /usr/bin/env python-docker
then we use a set_fact task to override the interpreter for localhost only
we must also delegate the facts
we can use the magic ansible_playbook_python variable, which refers to the python interpreter that was used on the (local) Ansible host to start the playbook: see Ansible docs
Here are the important parts at the start of Playbook 2:
---
- name: Playbook 2
vars:
- ansible_python_interpreter: "{{ '/usr/bin/env python-docker' }}"
...
tasks:
- set_fact:
ansible_python_interpreter: '{{ ansible_playbook_python }}'
delegate_to: localhost
delegate_facts: true
Try to use set_fact for ansible_python_interpreter at host level in the first playbook.
Globally, use the interpreter_python key in the [defaults] section of the ansible.cfg file.
interpreter_python = auto_silent
Ansible shows an error:
ERROR! no action detected in task. This often indicates a misspelled module name, or incorrect module path.
What is wrong?
The exact transcript is:
ERROR! no action detected in task. This often indicates a misspelled module name, or incorrect module path.
The error appears to have been in 'playbook.yml': line 10, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
---
- name: My task name
^ here
Reason #1
You are using an older version of Ansible which did not have the module you try to run.
How to check it?
Open the list of modules module documentation and find the documentation page for your module.
Read the header at the top of the page - it usually shows the Ansible version in which the module was introduced. For example:
New in version 2.2.
Ensure you are running the specified version of Ansible or later. Run:
ansible-playbook --version
And check the output. It should show something like:
ansible-playbook 2.4.1.0
Reason #2
You tried to write a role and put a playbook in my_role/tasks/main.yml.
The tasks/main.yml file should contain only a list of tasks. If you specified:
---
- name: Configure servers
hosts: my_hosts
tasks:
- name: My first task
my_module:
parameter1: value1
Ansible tries to find an action module named hosts and an action module named tasks. It doesn't, so it throws an error.
Solution: specify only a list of tasks in the tasks/main.yml file:
---
- name: My first task
my_module:
parameter1: value1
Reason #3
The action module name is misspelled.
This is pretty obvious, but overlooked. If you use incorrect module name, for example users instead of user, Ansible will report "no action detected in task".
Ansible was designed as a highly extensible system. It does not have a limited set of modules which you can run and it cannot check "in advance" the spelling of each action module.
In fact you can write and then specify your own module named qLQn1BHxzirz and Ansible has to respect that. As it is an interpreted language, it "discovers" the error only when trying to execute the task.
Reason #4
You are trying to execute a module not distributed with Ansible.
The action module name is correct, but it is not a standard module distributed with Ansible.
If you are using a module provided by a third party - a vendor of software/hardware or another module shared publicly, you must first download the module and place it in appropriate directory.
You can place it either in modules subdirectory of the playbook or in a common path.
Ansible looks ANSIBLE_LIBRARY or the --module-path command line argument.
To check what paths are valid, run:
ansible-playbook --version
and check the value of:
configured module search path =
Ansible version 2.4 and later should provide a list of paths.
Reason #5
You really don't have any action inside the task.
The task must have some action module defined. The following example is not valid:
- name: My task
become: true
I can't really improve upon #techraf answer https://stackoverflow.com/a/47159200/619760.
I wanted to add reason #6 my special case
Reason #6
Incorrectly using roles: to import/include roles as a subtask.
This does not work, you can not include roles in this way as subtasks in a play.
---
- hosts: somehosts
tasks:
- name: include somerole
roles:
- somerole
Use include_role
According to the documentation
you can now use roles inline with any other tasks using import_role or include_role:
- hosts: webservers
tasks:
- debug:
msg: "before we run our role"
- import_role:
name: example
- include_role:
name: example
- debug:
msg: "after we ran our role"
Put the roles at the right place inline with hosts
Include the roles at the top
---
- hosts: somehosts
roles:
- somerole
tasks:
- name: some static task
import_role:
name: somerole
hosts: some host
- include_role:
name: example
You need to understand the difference between import/include static/dynamic
I got this error when I referenced the debug task as ansible.builtin.debug
Causes a syntax failure in CI (but worked locally):
- name: "Echo the jenkins job template"
ansible.builtin.debug:
var: template_xml
verbosity: 1
Works locally and in CI:
- name: "Echo the jenkins job template"
debug:
var: template_xml
verbosity: 1
I believe - but have not confirmed - that the differences in local vs CI was ansible versions.
Local : 2.10
CI : 2.7
Explanation of the error :
No tasks to execute means it can not do the action that was described in your playbook
Root cause:
the installed version of Ansible doesn't support it
How to check :
ansible --version
Solution:
upgrade Ansible to a version which supports the feature you are trying to use
How to upgrade Ansible:
https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#selecting-an-ansible-version-to-install
Quick instruction for Ubuntu :
sudo apt update
sudo apt install software-properties-common
sudo apt-add-repository --yes --update ppa:ansible/ansible
sudo apt install ansible
P.S: followed this path and upgraded from version 2.0.2 to 2.9
After upgrade, same playbook worked like a charm
For me the problem occurred with "systemd" module. Turned out that my ansible --version was 2.0.0.2 and module was first introduced in version 2.2. Updating my ansible to latest version fixed the problem.
playbook.yaml
- name: "Enable and start docker service and ensure it's not masked"
systemd:
name: docker
state: started
enabled: yes
masked: no
Error
ERROR! no action detected in task
etc..
etc..
etc..
- name: "Enable and start docker service and ensure it's not masked"
^ here
In my case this was fix:
ansible-galaxy collection install ansible.posix