Ansible-pull with no inventory limitations - ansible

I want to have a set of git repos with base application config playbooks in them such that all I need to do with any VM, regardless of its name or role or how long its been around, to get the base config installed is to run an ansible-pull command against this repo and I get a ready to use instance. The problem that I run into is that if I have a playbook local.yml that's set like so:
- hosts: localhost
connection: local
gather_facts: yes
user: root
[...]
or like so:
- hosts: all
connection: local
gather_facts: yes
user: root
[...]
I keep getting the following error:
# ansible-pull -d /tmp/myrepo -U 'http://mygithost/myrepo'
Starting ansible-pull at 2015-06-09 15:04:05
localhost | success >> {
"after": "2e4d1c637170f95dfaa8d16ef56491b64a9f931b",
"before": "d7520ea15baa8ec2c45742d0fd8e209c293c3487",
"changed": true
}
**ERROR: Specified --limit does not match any hosts**
The only way I've been able to avoid the error is to create an explicit inventory file with an explicit groupname and explicit hostnames that is then referred to with the '-i' flag like so:
# cat /tmp/myrepo/myinventory
[mygroup]
myhost1
myhost2
# cat /tmp/myrepo/local.yml
- hosts: mygroup
connection: local
gather_facts: yes
user: root
[...]
# ansible-pull -d /tmp/myrepo -i myinventory -U 'http://mygithost/myrepo'
But I don't want that, I want any host, no matter whether its name or role is known to be able to run an ansible-pull against the repo and run the playbook without having to explicitly configure the name of the host into the inventory. How do I do that?

Here is the workflow I use for ansible-pull within my VMs:
In the base VM I put a file named hosts at /etc/ansible:
# cat /etc/ansible/hosts
localhost ansible_connection=local
My local.yaml starts with
- hosts: localhost
gather_facts: yes
user: root
...
Now I can use ansible-pull without specifying a hosts file.

Related

Executing playbooks in groupings created in hosts.yaml file

Ansible Version: 2.8.3
I have the following hosts.yaml file for use in Ansible
I have applications that I want to deploy on potentially both rp_1 and rp_2
---
all:
vars:
docker_network_name: devopsNet
http_protocol: http
http_host: ansiblenode01_new.example.com
http_url: "{{ http_protocol }}://{{ http_host }}:{{ http_port }}/{{ http_context }}"
hosts:
ansiblenode01_new.example.com:
ansiblenode02_new.example.com:
children:
##################################################################
rp_1:
children:
httpd:
hosts:
ansiblenode01_new.example.com:
vars:
number_of_tools: 6
outside_port: 443
jenkins:
hosts:
ansiblenode01_new.example.com:
vars:
http_port: 4444
http_context: jenkins
artifactory:
hosts:
ansiblenode01_new.example.com:
vars:
http_port: 8000
http_context: artifactory
rp_2:
children:
httpd:
hosts:
ansiblenode02_new.example.com:
vars:
number_of_tools: 4
outside_port: 7090
jenkins:
hosts:
ansiblenode02_new.example.com:
vars:
http_port: 7990
http_context: jenkins
artifactory:
hosts:
ansiblenode02_new.example.com:
vars:
http_port: 8000
http_context: artifactory
The following python wrapper script is calling ansible-playbook in a loop to deploy the applications
#!/usr/bin/python
import yaml
import os
import getpass
with open('hosts.yaml') as f:
var = yaml.load(f)
sudo_pass = getpass.getpass(prompt="Please enter sudo password: ")
# Running individual ansible-playbook deployment for each application listed and uncommented under 'applications' object.
for network in var['all']['children']:
for app in var['all']['children'][network]['children']:
os.system('ansible-playbook deploy.yml --extra-vars "application='+app+' ansible_sudo_password='+sudo_pass+'"')
The problem I recognize is that both Ansible and Python will use the hosts.yaml file, but not use it the way I thought it would as I'm not too familiar with Ansible.
The hosts.yaml was written in a format that is required by Ansible.
The Python script will open the yaml file, make a dictionary out of it, and step through the dictionary and look for the application names to pass to the command line call. The problem is then that Python only passes the name of the app as a string to the invocation of ansible-playbook, the dictionary structure obviously doesn't get passed, so Ansible will then open the hosts.yaml file as well, but all it does is step through the yaml and look for the first occurrence of the app name that was passed as an argument when ansible-playbook was invoked, completely disregarding the structure I've created in the yaml file.
So basically only the rp_1 group in the yaml file will be executed since Ansible, I think reads through the yaml from top down and stops at the first occurrence, therefore all or parts of the rp_2 group will never be processed by Ansible if the group contains all or some of the same apps as rp_1, therefore running the same deployment twice.
Is there a way to invoke Ansible or some ways to set the playbooks up so that Ansible will recognize that in my hosts file, I have networks (rp_1, rp_2) that I want to setup and executes the playbooks in the grouping that I've created in the yaml file?
Ansible already has this built-in. You do not need a wrapper script.
To run the deploy.yml playbook on all hosts in your hosts.yaml (this is called "inventory" btw.) do this:
ansible-playbook -i hosts.yaml deploy.yml -bK
To only run it on rp_1, do this:
ansible-playbook -i hosts.yaml deploy.yml --limit rp_1 -bK
-b makes ansible become root
-K will make ansible ask for the password to become root
-i <file> specifies the inventory file
--limit <host/group> limits the execution to certain hosts or groups, you can also add more than one, as a comma-separated list (e.g., pr_1,rp_2)
You can also specify a list of hosts/groups in your playbook like this:
- name: do whatever you like
hosts:
- rp_1
- rp_2
become: yes
tasks:
- debug:
msg: "I'm running on {{ inventory_hostname }}!"
Further reading:
Discovering variables: facts and magic variables
How to build your inventory
Special variables
Using variables
Ansible examples
Accessing variables of "other" hosts: on serverfault and stackoverflow

Simple ansible example that connects to new server as root with password

I want to provision a new vps. The way this is typically done: 1) try login manually as a non-root user, and 2) if that fails then perform the provisioning.
But I can't connect. I can't even login as root. (I can ssh from the shell, so the password is correct.)
hosts:
[server]
42.42.42.42
playbook.yml:
---
- hosts: all
vars:
ROOT_PASSWORD: foo
gather_facts: no
tasks:
- name: set root password
set_fact: ansible_password={{ ROOT_PASSWORD }}
- name: try login with password
local_action: "command ssh -q -o BatchMode=yes -o ConnectTimeout=3 root#{{ inventory_hostname }} 'echo ok'"
ignore_errors: true
changed_when: false
# more stuff here...
I tried the following, but all don't connect:
I stored the password in a variable like above
I prompted for the password using ansible-playbook -k playbook.yml
I moved the password to the inventory file
[server]
42.42.42.42 ansible_user=root ansible_password=foo
I added the ssh flag -o PreferredAuthentications=password to force password auth
But none of the above connects. I always get the error
root#42.42.42.42: Permission denied (publickey,password).
If I remove -o BatchMode=yes then it prompts me for a password, and does connect. But that prevents automation, the idea is to do this without user intervention.
What am I doing wrong?
This is a new vps, nothing is set up yet - so I'm looking for the simplest possible example of a playbook that connects using root and a password.
You're close. The variable is ansible_ssh_password, not ansible_ssh_pass. The variables with _ssh in the name are legacy names, so you can juse use ansible_user and ansible_password instead.
If I have an inventory like this:
[server]
example ansible_host=192.168.122.148 ansible_user=root ansible_password=secret
Then I can run this command successfully:
$ ansible all -i hosts -m ping
example | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
If the above ad-hoc command works correctly, then a playbook should work correctly as well. E.g., still assuming the above inventory, I can use the following playbook:
---
- hosts: all
gather_facts: false
tasks:
- ping:
And I can call it like this:
$ ansible-playbook playbook.yml -i hosts
PLAY [all] ***************************************************************************
TASK [ping] **************************************************************************
ok: [example]
PLAY RECAP ***************************************************************************
example : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
...and it all works just fine.
Try using --ask-become-pass
ansible-playbook -k playbook.yml --ask-become-pass
That way it's not hardcoded.
Also, inside the playbook you can invoke:
---
- hosts: all
become: true
gather_facts: no
All SO answers and blog articles I've seen so far recommend doing it the way I've shown.
But after spending much time on this, I don't believe it could work that way, so I don't understand why it is always recommended. I noticed that ansible has changed its API many times, and maybe that approach is simply outdated!
So I came up with an alternative, using sshpass:
hosts:
[server]
42.42.42.42
playbook.yml:
---
- hosts: all
vars:
ROOT_PASSWORD: foo
gather_facts: no
tasks:
- name: try login with password (using out-of-band ssh connection)
local_action: command sshpass -p {{ ROOT_PASSWORD }} ssh -q -o ConnectTimeout=3 root#{{ inventory_hostname }} 'echo ok'
ignore_errors: true
register: exists_user
- name: ping if above succeeded (using in-band ssh connection)
remote_user: root
block:
- name: set root ssh password
set_fact:
ansible_password: "{{ ROOT_PASSWORD }}"
- name: ping
ping:
data: pong
when: exists_user is success
This is just a tiny proof of concept.
The actual use case is to try connect with a non-root user, and if that fails, to provision the server. The above is the starting point for such a playbook.
Unlike #larsks' excellent alternative, this does not assume python is installed on the remote, and performs the ssh connection test out of band, assisted by sshpass.

Host not found in Ansible inventory

I am trying to do some testing against a specific host with Ansible 2.5 but ansible can't figure out my inventory. I've either done something wrong or there's a bug. I've done this in the past but maybe something changed in 2.5
I have an inventory file specified like this:
localhost ansible_connection=local
testhost ansible_ssh_host=1.2.3.4
I have a playbook that runs totally fine if i just run it with ansible playbook.yml. It starts like this:
- hosts: localhost
become: yes
become_user: root
become_method: sudo
gather_facts: yes
If I run ansible-inventory --list I see both of my hosts listed as "ungrouped"
However, if I try to run my playbook against the remote host using ansible -l testhost playbook.yml it errors with the following:
[WARNING]: Could not match supplied host pattern, ignoring: playbook.yml
ERROR! Specified hosts and/or --limit does not match any hosts
I can't figure out how to actually make Ansible run against my remote host.
Your playbook specifies:
hosts: localhost
It will not run on testfile regardless of the arguments you supply. --limit does not replace the hosts declaration.
As your hosts are ungrouped, you need to change this to:
hosts: all
Then you can use limit option to filter the hosts from the given target group.
You are also using wrong command to run an Ansible playbook, it should be ansible-playbook not ansible (and although the effect is the same, the latter does not fail with an error in such case).
use simple method wherever you have to connect on local system? just specify connection : local to hosts block
- hosts: localhost
connection : local
become: yes
become_user: root

Ansible inventory discrepancy

I am new to Ansible, so I assume I am making a silly mistake, however when I try to run a playbook with roles for a group of hosts, Ansible doesn't see any hosts in some groups. In particular
Inventory has among others the following group:
[master]
clm01
It seems to be working OK with Ad-hoc commands:
:~/ansible/splunk# ansible master -i hosts -m ping -u USERNAME
clm01 | SUCCESS => {
"changed": false,
"ping": "pong"
}
However, when I try to run the following, Ansible can't see any hosts:
- name: initialize master
hosts: master
remote_user: USERNAME
become: yes
roles:
- cluster_master
[...]
ansible-playbook site.yml --ask-sudo-pass --list-hosts
[...]
play #2 (master): initialize master TAGS: []
pattern: [u'master']
hosts (0):
[...]
Some of the groups in the inventory are working with other plays defined in the same file, so I would assume there is a syntax error on my side. I have also tried changing group name, hoping I am using a reserved name etc.
It doesn't see any hosts, because you omitted -i hosts parameter in the second command.
Run the following:
ansible-playbook site.yml -i hosts --ask-sudo-pass --list-hosts
I think it needs to look like this:
- hosts: master
remote_user: USERNAME
become: yes
become_user: root
roles:
- cluster_master
That name tag is for plays.

Ansible playbook run for the servers in the list

quick question for Ansible Guru's. I want to run an ansible playbook for a specific set of boxes that I copied to a list.txt disregarding the inventory and the target block in ansible playbook:
---
- name: Ansible Runbook v.1.0
hosts: test1
gather_facts: yes
# serial: "10%"
When I am running the following command I am getting no hosts matched:
ansible-playbook playbook.yaml --tags "simplejson" -vvv -i /x/home/list.txt
PLAY [Ansible Runbook v.1.0] **************************************************
skipping: no hosts matched
$cat list.txt
hostname2b
Any ideas for a workaround ?
The reason of no host matching is that host test1, which is hardcoded in playbook, is not present in the inventory file that you specified from command line. The problem is ansible-playbook command does not accept any hosts parameter. So there is no direct way of getting around the hardcoded hosts test1.
However, there is a workaround for this as explained here. You can use a variable for hosts and specify all from command line for that variable. Something like this:
---
- name: Ansible Runbook v.1.0
hosts: "{{ host_param }}"
gather_facts: yes
Then pass that variable with extra-vars:
ansible-playbook playbook.yaml -i /x/home/list.txt --extra-vars="host_param=all" --tags "simplejson" -vvv

Resources