Ansible Playbook for Windows or Linux - ansible

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

Related

How to run ansible in serial keeping facts between batches?

I have been trying to run a play in serial, 1 host at a time, to manage a rolling upgrade.
The problem I kept running in to was that when running in serial the facts was not kept between batches, making variables such as ansible_default_ipv4.address fail when asking for that variable for the entire group which the play was running on.
I have not been able to find somehting specific regarding this, but rather stumbled upon similar problems using --limit when running a playbook, so the work around I ended up trying after a bit of tinkering was fact cahing.
More specifically, I let the playbook run two sets of tasks.
First one being specifically to run on all hosts in the group, to gather all facts, and these then cached on file, for inventory as well.
For some reason though, when moving on to the play which was going to run in serial, these facts does not seem to be kept.
Does anyone have any ideas on how to solve or do a work around for this?
I believe it would also work to another related issue, being run_once runs once per batch, whilst it in this case would need to run once per ... run(?).
Not using serial made the play work as expected.
ansible.cfg
[defaults]
fact_caching = jsonfile
fact_caching_connection = /tmp/ansiblecache
fact_caching_timeout = 3600
[inventory]
cache=True
cache_plugin = jsonfile
playbook.yml
---
- name: installation & configuration | gather facts
hosts: cluster
become: yes
gather_facts: true
- name: installation & configuration
hosts: cluster
become: yes
serial: "{{ '1' if upgrade else '100%' }}"
gather_facts: false
roles:
- { role: myrole }

How can I run an ansible role locally?

I want to build a docker image locally and deploy it so it can then be pulled on the remote server I'm deploying to. To do this I first need to check out code from git to be built.
I have an existing role which installs git, sets up keys for reading from our repo etc. I want to run this role locally to check out the code I care about.
I looked at local action, delegate_to, etc but haven't figured out an easy way to do this. The best approach I could find was:
- name: check out project from git
delegate_to: localhost
include_role:
name: configure_git
However, this doesn't work I get a complaint that there is a syntax error on the name line. If I remove the delegate_to line it works (but runs on the wrong server). If I replace include_role with debug it will run locally. It's almost as if ansible explicitly refuses to run an included role locally, not that I can find that anywhere in the documentation.
Is there a clean way to run this, or other roles, locally?
Extract from the include_role module documentation
Task-level keywords, loops, and conditionals apply only to the include_role statement itself.
To apply keywords to the tasks within the role, pass them using the apply option or use ansible.builtin.import_role instead.
Ignores some keywords, like until and retries.
I actually don't know if the error you get is linked to delegate_to being ignored (I seriously doubt it is the case...). Meanwhile it's not the correct way to use it here:
- name: check out project from git
include_role:
name: configure_git
apply:
delegate_to: localhost
Moreover, this is most probably a bad idea. Let's imagine your play targets 100 servers: the role will run one hundred time (unless you also apply run_once: true). I would run my role "normally" on localhost in a dedicated play then do the rest of the job on my targets in the next one(s).
- name: Prepare env on localhost
hosts: localhost
roles:
- role: configure_git
- name: Do the rest on other hosts
hosts: my_group
tasks:
- name: dummy.
debug:
msg: "Dummy"

Is it safe to use Ansible shell module and gather_facts: no?

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.

Ansible Version Forcing

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.

Iterate over inventory facts [duplicate]

I'm sitting in front of a fairly complex Ansible project that we're using to set up our local development environments (multiple VMs) and there's one role that uses the facts gathered by Ansible to set up the /etc/hosts file on every VM. Unfortunately, when you want to run the playbook for one host only (using the -limit parameter) the facts from the other hosts are (obviously) missing.
Is there a way to force Ansible to gather facts on all hosts, even if you limit the playbook to one specific host?
We tried to add a play to the playbook to gather facts from all hosts, but of course that also gets limited to the one host given by the -limit parameter. If there'd be a way to force this play to run on all hosts before the other plays, that would be perfect.
I've googled a bit and found the solution with fact caching with redis, but since our playbook is used locally, I wanted to avoid the need for additional software. I know, it's not a big deal, but I was just looking for a "cleaner", Ansible-only solution and was wondering, if that would exist.
Ansible version 2 introduced a clean, official way to do this using delegated facts (see: http://docs.ansible.com/ansible/latest/playbooks_delegation.html#delegated-facts).
when: hostvars[item]['ansible_default_ipv4'] is not defined is a check to ensure you don't check for facts in a host you already know the facts about
---
# This play will still work as intended if called with --limit "<host>" or --tags "some_tag"
- name: Hostfile generation
hosts: all
become: true
pre_tasks:
- name: Gather facts from ALL hosts (regardless of limit or tags)
setup:
delegate_to: "{{ item }}"
delegate_facts: True
when: hostvars[item]['ansible_default_ipv4'] is not defined
with_items: "{{ groups['all'] }}"
tasks:
- template:
src: "templates/hosts.j2"
dest: "/etc/hosts"
tags:
- hostfile
...
In general the way to get facts for all hosts even when you don't want to run tasks on all hosts is to do something like this:
- hosts: all
tasks: [ ]
But as you mentioned, the --limit parameter will limit what hosts this would be applied to.
I don't think there's a way to simply tell Ansible to ignore the --limit parameter on any plays. However there may be another way to do what you want entirely within Ansible.
I haven't used it personally, but as of Ansible 1.8 fact caching is available. In a nutshell, with fact caching enabled Ansible will use a redis server to cache all the facts about hosts it encounters and you'll be able to reference them in subsequent playbooks:
With fact caching enabled, it is possible for machine in one group to reference variables about machines in the other group, despite the fact that they have not been communicated with in the current execution of /usr/bin/ansible-playbook.
This still seems to be an issue without a clean solution here in 2016, but newer versions of Ansible offer a "jsonfile" fact caching backend, which seems to be a decent compromise to installing Redis locally just to address this need. Now I just fire off an ansible all -m setup before running a playbook with the --limit option. Good enough for jazz!
http://docs.ansible.com/ansible/playbooks_variables.html#fact-caching
You could modify your playbook to:
...
- hosts: "{{ limit_hosts|default('default_group') }}"
tasks:
...
...
And when you run it, if some_var is not defined (normal state) then it will run on the default_group inventory group, BUT if you run it as:
ansible-playbook --extra-vars "limit_hosts=myHost" myplaybook.yml
Then it will only run on your myHost, but you could still have other sections with different hosts: .. declarations, for fact gathering, or anything else actually.

Resources