I am testing the variable feature in ansible along with include_vars and with_first_found.
My playbook is
---
- name: Load Variables from files.
include_vars: "{{ item }}"
with_first_found:
- "a.yml"
- "b.yml"
- "default.yml"
- name: another play
hosts: all
tasks:
- debug: msg="hello {{ http_port }}"
But when Im running this, I get the ERROR,
# ansible-playbook -i inventory/plat-inventory test.yaml
ERROR! 'with_first_found' is not a valid attribute for a Play
The error appears to have been in '/root/config-3.0.98/ansible/test.yaml': line 2, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
---
- name: Load Variables from files.
^ here
default.yml states :
http_port: 80
What am I missing here ?
As far as I know, include_vars should be a task, as the examples in its documentation
something like
- name: playbook
hosts: somehosts
gather_facts: false
tasks:
- name: "add some vars"
include_vars: somefile.yml
But then, those variables won't probably be available in the other playbook.
Related
I am writing a playbook where I am automating my deployment. In the command line I am passing some parameters which needs to be validated mandatory. I am using roles/tags to run my playbook. Below is my command -
ansible-playbook -i my-inventory my-main.yml --tags=copy,deploy -e my_release_version=1.0.0 -e target_env=prod
In my-main.yml I am first validating the parameters and then executing the roles. Now if i pass tags in the command it is not doing any validation and directly executing the tags which is the way ansible works i guess.
Is there a way to pre execute the steps as mentioned in my-main.yml before executing the tags?
and my-main.yml looks like below -
- hosts: localhost
connection: local
gather_facts: no
vars:
_allowed_envs:
- dev
- preprod
- prod
pre_tasks:
- name: Checking if the Target Environment is ok
fail:
msg: >-
Environment "{{ target_env }}" is not allowed.
Please choose a target environment from "{{ _allowed_envs | join(', ') }}"
when: not target_env in _allowed_envs
run_once: true
roles:
- role: copy
tags:
- copy
- role: deploy
tags:
- deploy
NOTE : My playbook will have roles/tags like copy,deploy and also stoptomcat, starttomcat. So when user only mention tags like stoptomcat and starttomcat I just want one input parameter to be validated target_env because in that case I would not want my_release_version.
Any help is appreciated.
There is nothing wrong with the pre_tasks or with your play; the pre_tasks are skipped because you are passing --tags in the command line while calling the playbook; hence tags take the highest precedence while invoking the play that is the reason your validation fails each time; If you want to run the pre_tasks, then you dont have to mention ----tags option in cli- and handle the roles-validation with pre-tasks by passing only -e options: something like this
---
- name: test_play
hosts: localhost
connection: local
gather_facts: false
vars:
_allowed_envs:
- dev
- preprod
- prod
pre_tasks:
- name: Checking if the Target Environment is ok
fail:
msg: >-
Environment "{{ target_env }}" is not allowed.
Please choose a target environment from "{{ _allowed_envs | join(', ') }}"
when: not target_env in _allowed_envs
run_once: true
tasks:
- name: include the dynamic role
include_role:
name: "{{ ROLE }}"
tags: "{{ tag_name }}"
and then run it ansible-playbook -i my-inventory my-main.yml -e my_release_version=1.0.0 -e target_env=dev -e ROLE=copy -e tag_name=copy
Hope this helps!
Cheers
I have the following ansible playbook that writes the content of the variable "hello" as a message (I got this code from an example online). I tried modifying it so it would write this to a local file however I got an error. The modified code and error message are below:
original code (successful):
- hosts: all
vars:
hello: world
tasks:
- name: Ansible Basic Variable Example
debug:
msg: "{{ hello }}"
modified code (unsuccessful):
- hosts: all
vars:
hello: world
tasks:
- name: Ansible Basic Variable Example
- local_action: copy content={{hello}} dest=~/ansible_logfile
debug:
msg: "{{ hello }}"
error message:
ERROR! no action detected in task. This often indicates a misspelled module name, or incorrect module path.
The error appears to have been in '/space/mathewLewis/towerCodeDeploy/playBooks/test.yml': line 5, column 5, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
tasks:
- name: Ansible Basic Variable Example
^ here
I would like to know how to write a variable to a file correctly
It's a simple syntax error.
A Task is an entry in the list of tasks, which in YAML is designated by a - (dash).
Task names are optional in Ansible.
Both copy and debug are modules, which are supposed to be a task's "action".
What the error message is telling you is that the task with name: Ansible Basic Variable Example does not have an action, which is because your local_action is a separate task, indicated by a -.
Fixing your example with appropriate names for the tasks:
- name: Write variable to file
local_action: copy content="{{hello}}" dest=~/ansible_logfile
- name: Output the variable
debug:
msg: "{{ hello }}"
Thomas Hirsh's answer is correct. However, I found this representation less confusing (I'm a newbie to ansible):
- name: "Controller"
hosts: "controller.jeff.ddns.net"
tasks:
- name: "Register a variable to be shared with the clients"
set_fact: shared_fact="Brother"
- name: "Client"
hosts: "client.jeff.ddns.net"
tasks:
- name: "writing to hostvars.json"
local_action: copy content="{{hostvars | to_json(indent=4) }}" dest="hostvars.json"
This example has two plays. The controller play only sets a variable. The client is what actually writes to the file. In this case, hostvars has a complicated structure, so I used the to_json(indent=4) filter to convert to a good .json file, suitable for use with jq .
I have a playbook in the format below:
---
- hosts: myIP
tasks:
- name: Install a yum package in Ansible example
yum:
name: ThePackageIWantToInstall
state: present
where myIP and ThePackageIWantToInstall are variables.
When the job template runs, I would like the user in the extra variables popup to be able to go with:
myIP = 192.168.1.1
ThePackageIWantToInstall = nano
As the documentation doesn't provide an example of supplying a variable via a job template, is this possible?
Yes.
- name: Do The Thing
hosts: "{{ foo }}"
roles:
- "{{ role }}"
Need mustaches and quotes.
to run from popup
(I don't use this, but it was suggested as an edit, thanks...)
foo: value
I have achieved similar thing with add_hosts. Here iam not installing package but creating file with name passed from command line. Any number of hosts (separated by commas can be passed from command line).
# cat addhost2.yml
- hosts: localhost
gather_facts: no
tasks:
- add_host:
name: "{{ item }}"
groups: hosts_from_commandline
with_items: "{{ new_hosts_passed.split(',') }}"
- hosts: hosts_from_commandline
tasks:
- name: Ansible create file with name passed from commandline
file:
path: "/tmp/{{ filename_from_commandline }}"
state: touch
# ansible-playbook -i hosts addhost2.yml --extra-vars='new_hosts_passed=192.168.3.104,192.168.3.113 filename_from_commandline=lathamdkv'
Hope this helps
I'm trying to look for a text pattern in a load balancer host from a worker host, using the following:
- name: A play
hosts: workers
tasks:
- name: Look for text pattern in delegated host
delegate_to: load-balancer-host
find:
paths: "$ENVIRONMENT_VARIABLE/subdir"
file_type: file
patterns: file.pattern
contains: 'text pattern'
register: aVariable
The problem is that I can't found any way to make $ENVIRONMENT_VARIABLE (this variable exists in the load-balancer-host) available for the play (it contains the directory, in load-balancer-host, from where I want to look for). ansible_env is only available for the workers but not for the load-balancer-host
I have tried...
- name: A play
hosts: workers
tasks:
- name: set fact
set_fact:
env_var: "{{ lookup('env', 'ENVIRONMENT_VARIABLE') }}"
delegate_to: load-balancer-host
- name: debug
debug:
msg: "{{ env_var }}"
... too, but it prints an empty string.
For users running Ansible 1.x, see kfreezy's answer.
For users running Ansible 2.x, I have found the following solution:
- hosts: workers
tasks:
- name: gather facts from lb
setup:
delegate_to: load-balancer-host
delegate_facts: false
This task will make $ENVIRONMENT_VARIABLE available in every worker ansible_env var. If you want to make $ENVIRONMENT_VARIABLE available in the load-balancer-host ansible_env, just set delegate_facts to True.
More info in ansible docs
Personally I would simplify your playbook by either adding the $ENVIRONMENT_VARIABLE as a variable in Ansible (probably in the host_vars for load-balancer-host) or running a play against load-balancer-host rather than use delegate_to. It might not make sense depending on what the other tasks are.
Here's a direct answer to your question though.
load-balancer-host's ansible_env will only be defined when the host is included in the playbook. You can add another play against the 'load-balancer-host' that will just gather facts. Then you can reference the facts from 'load-balancer-host' using hostvars in your subsequent plays against 'workers'. He's what it would look like.
- hosts: load-balancer-host
tasks:
- name: print debug message
debug:
msg: "this play is for gathering facts on the LB"
- name: A play
hosts: workers
tasks:
- name: Look for text pattern in delegated host
delegate_to: load-balancer-host
find:
paths: "{{ hostvars['load-balancer-host'].ansible_env.ENVIRONMENT_VARIABLE }}/subdir"
file_type: file
patterns: file.pattern
contains: 'text pattern'
register: aVariable
I'd like to pass a variable to an included Ansible playbook as follows:
---
- hosts: localhost
connection: local
vars:
my_group: foo
- include: site.yml hosts={{ my_group }}
Then, in site.yml...
---
- hosts: "{{ hosts }}"
...
Unfortunately, I get an error saying that my_group is undefined in site.yml. Ansible docs do say that:
Note that you cannot do variable substitution when including one playbook inside another.
Is this my case? Is there a way around it?
You can use this syntax, but my_group has to be defined at the global level. Now it's local to the first play - it's even clear from the indentation.
You can confirm this by running your playbook with --extra-vars my_group=foo.
But generally what you seem to want to achieve is done using in-memory inventory files and add_host module. Take this as an example:
- hosts: localhost
gather_facts: no
vars:
target_host: foo
some_other_variable: bar
tasks:
- add_host:
name: "{{ target_host }}"
groups: dynamically_created_hosts
some_other_variable: "{{ some_other_variable }}"
- include: site.yml
with site.yml:
---
- hosts: dynamically_created_hosts
tasks:
- debug:
var: some_other_variable
I added some_other_variable to answer the question from your comment "how do I make a variable globally available from inside a play". It's not global, but it's passed to another play as a "hostvar".
From what I see (and I can't explain why) in Ansible >=2.1.1.0, there must be an inventory file specified for the dynamic in-memory inventory to work. In older versions it worked with Ansible executed ad hoc, without an inventory file, but now you must run ansible-playbook with -i inventory_file or have an inventory file defined through ansible.cfg.