Testing a basic Ansible roles setup but got an error on the first line of a role main.yml, I am sure it's something silly though
play.yml
- hosts: myhosts
remote_user: myuser
roles:
- test
Directory structure
play.yml
roles/test/tasks/main.yml
main.yml
- hosts: all
user: myuser
gather_facts: no
tasks:
- name: ping all hosts
ping:
When I run ansible-playbook play.yml, I get the error
The offending line appears to be:
- hosts: all
^ here
It looks like a simple YAML parse error but if I run ansible-playbook main.yml, it works fine, so not sure what's going on. Any thoughts?
You cannot specify hosts in roles/test/tasks/main.yml. The hosts are specified in the play.yml file.
roles/test/tasks/main.yml is used to define the actions you want Ansible to perform. In your case to ping hosts, it could simply look like:
---
- ping:
This will perform the ping action on the hosts specified in your play.yml
Ansible has a set folder structure it can use to break down playbooks. The play.yml file, specifies which hosts to target and what roles to apply along with other top level controls.
Individual roles specified in the play.yml file are located in roles/X/, there is a certain folder structure that Ansible expects. It will look for tasks to run in the test role here roles/test/tasks/main.yml.
play.yml is just one playbook. You can create many in the same folder and call them with ansible-playbook.
The official documentation has a more detailed example of the recommended playbook directory structure
Related
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
I trying to create a simple paybook with a common role. Unfortunately I get stymied by ansible. I have looked up and down the internet for solution for this error.
The setup:
I am running ansible 2.7.4 on Ubuntu 18.04
directory structure:
~/Ansible_Do
playbook.yml
inventory (hosts file)
/roles
/common
/defaults
main.yml (other variables)
/tasks
main.yml
richard_e.yml
/vars
vars_and_stuff.yml (vault)
I have a simple playbook.yml
---
# My playbook 1
- hosts: test
- name: Go to common role to run tasks.
roles:
- common
tasks:
- name: echo something
shell: echo $(ip addr | grep inet)
...
I run this command to start the playbook:
~/Ansible_Do$ ansible-playbook -vv --vault-id #prompt -i ~/Ansible_Do/inventory playbook.yml
I enter the vault password continuing the playbook.
The playbook starts pulls facts from the test group of servers. Then reads the role and works to /roles/common. That calls the /common/tasks/main.yml file. This is where the error happens.
The error appears to have been in '/home/~/Ansible_Do/roles/common/tasks/main.yml': line 8, column 3
# Common/tasks file
---
- name: Bring variable from vault
include_vars:
file: vars_and_stuff.yml
name: My_password
- name: Super Richard <====== Error
become: yes
vars:
ansible_become_pass: "{{ My_password }}"
- import_tasks: ./roles/common/tasks/ricahrd_e.yml
...
The ./roles/common/tasks/ricahrd_e.yml is a simple testing task.
---
- name: say hi
debug:
msg: "Active server."
...
The error on "- name". I have checked online and in the Ansible docs to see if there is a key I'm missing. I found an example for include_vars in a /role/tasks (https://gist.github.com/halberom/ef3ea6d6764e929923b0888740e05211) showing proper syntax (I presume) in a simple role. The code works as parts, but not together.
I have reached what I can understand. I feel that is error is utterly simple and I am missing something (forest for the trees).
The error means exactly what it says, except the "module name" is not misspelled in your case, but missing altogether.
This...
- name: Super Richard <====== Error
become: yes
vars:
ansible_become_pass: "{{ My_password }}"
... is not a valid task definition, it does not declare an action.
An action in Ansible is a call to a module, hence "misspelled module name".
The error comes after name, because that's where Ansible expects the name of the "module" that you want to call, e.g. shell in your first example.
You are probably assuming that become is a "module", but it is not.
It is a "playbook keyword", in this case applied on the task level, which has the effect that you become another user for this task only.
But as the task has no action, you get this error.
See docs:
Playbook keywords
Understanding privilege escalation
After a bit of work I got the playbook to work. Knowing that 'become' is not a task was the start. I also found out how to pull the proper vars from the vault.
# My first playbook 1
- hosts: test
become: yes
vars_files:
- ./roles/common/vars/vars_and_stuff.yml
vars:
ansible_become_pass: "{{ My_password }}"
roles:
- common
tasks:
- name: echo something
shell: echo $(ip addr | grep inet)
The vars file access the vault and then vars: pulls the password used by become. With become in force I ran the other tasks in the common role with a last standalone task. Lastly, don't try to - name: at the top level of the playbook as it trigger a hosts undefined error.
I have created my own custom library, I added my custom library in the common folder of my repository. In that I need to pass variables dynamically. It's a confidential password, so I am using "vault" in ansible.
In that my requirement is how to pass include_vars in the tasks\main.yml before hosts.
e.g: mytasks.yml
- include_vars: sample_vault.yml
- include: sample_tasks.yml
- hosts: localhost
tasks:
name: "free task"
command: ls -a
my directory structure like this:
myfolder
- common
-library
-my file.py
- sample_tasks.yml
- mytasks
-mytasks.yml(my main master playbook file)
-sample_vault.yml (note:i create this using vault for confidential purpose)
- roles
-myrole
Here I need to run sample_tasks file using a variables passed in sample_vault.yml file before I execute the hosts tasks using ansible. If I use extra variable means password is visible so I don't need that.
When I use include_vars in my tasks/main.yml file, it shows the following error:
ERROR! 'include_vars' is not a valid attribute for a Play
You can't use include_vars this way, it's only available for use under tasks.
If sample_tasks.yml is a list of tasks, you also can't use it on playbook level. See my other answer for explanation.
You can use vars_files like this:
- hosts: localhost
vars_files:
- sample_vault.yml
tasks:
name: "free task"
command: ls -a
Or pass a file as extra variables:
ansible-playbook --ask-vault-pass -e #sample_vault.yml myplaybook.yml
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
I am trying to set up Ansible to be able to run a playbook according to what inventory group the host is in. For example, in the inventory, we have:
[group1]
host1.sub.domain.tld ansible_host=10.0.0.2
...
[group1:vars]
whatsmyplaybook=build-server.yml
Then we want to make a simple playbook that will more or less redirect to the playbook that is in the inventory:
---
- name: Load Playbook from inventory
include: "{{hostvars[server].whatsmyplaybook}}"
Where the "server" variable would be the host's FQDN, passed in from the command line:
ansible-playbook whatsmyplaybook.yml -e "server=host1.sub.domain.tld"
Our reasoning for this would be to have a server bootstrap itself from a fresh installation (PXE boot), where it will only really know its FQDN, then have a firstboot script SSH to our Ansible host and kick off the above command. However, when we do this, we get the below error:
ERROR! 'hostvars' is undefined
This suggests that the inventory is not parsed until a host list is provided, which sucks a lot. Is there another way to accomplish this?
A bit strange workflow, honestly.
Your setup doesn't work, because most of variables are not defined during playbook parse time.
You may be more lucky with defining single playbook with different plays for different groups (no need to set group var, just use correct host pattern (group name in my example)) and execute it limiting to specific host:
site.yml:
---
- hosts: group1
tasks:
- include: build-web-server-tasks.yml
- hosts: group2
tasks:
- include: build-db-server-tasks2.yml
command to provision specific server:
ansible-playbook -l host1.sub.domain.tld site.yml
You can develop your own dynamic inventory file so that all machines which needs to be bootstrapped will automatically added into your inventory and group respectively with out an manual entry in to the inventory file.
For developing dynamic inventory you can follow the below link:
http://docs.ansible.com/ansible/latest/dev_guide/developing_inventory.html
You can include multiple playbooks targeted to different groups as follows.
---
- hosts: all
tasks:
- include: build-web-server-tasks.yml
where: inventory_hostname in groups['group1']
- include: build-db-server-tasks2.yml
where: inventory_hostname in groups['group2']
inventory_hostname is the name of the hostname as configured in Ansible’s inventory host file. This can be useful for when you don’t want to rely on the discovered hostname ansible_hostname or for other mysterious reasons. If you have a long FQDN, inventory_hostname_short also contains the part up to the first period, without the rest of the domain.