Based on this document I am trying to check the /etc/hosts file exists. but Ansible giving
The error was: template error while templating string: unexpected '/'
error message
https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html#testing-paths
- debug:
msg: "host file already exists"
when: /etc/hosts is exists
What is the right way of using this testing condition?
Thanks
The section of the documentation to which you've linked is probably
not what you want -- that is only for testing paths on the
controller (that is, the host on which Ansible is running), rather
than the target host(s) of your play.
If that is what you want, you need to fix the syntax of your when
expression. The examples to which you've linked are all of the form
<variable> is <test>, so you would write something like:
- debug:
msg: "host file already exists"
when: etchosts is exists
vars:
etchosts: "/etc/hosts"
Here we define a string variable named etchosts containing our path.
In the above example we've defined it at the task level, but this
could also be defined at the play level, as a group_ or host_var, via
set_fact, etc.
To test for a file on a target host, use the stat module:
- name: check for /etc/hosts
stat:
path: /etc/hosts
register: etchosts
We register the result in a variable named etchosts so that we can use it in a subsequent task. For example:
- debug:
msg: "/etc/hosts exists"
when: etchosts.stat.exists
- debug:
msg: "/etc/hosts does not exist"
when: not etchosts.stat.exists
Related
I want to set a playbook level environment, but after I execute a couple of tasks. I have found that I could define a playbook level environment variable before definition of any tasks or task level environment variables. But, I haven't found how can I set-up an environment variable that can be used by all tasks following a task.
- name: server properties
hosts: kafka_broker
vars:
ansible_ssh_extra_args: "-o StrictHostKeyChecking=no"
ansible_host_key_checking: false
date: "{{ lookup('pipe', 'date +%Y%m%d-%H%M%S') }}"
copy_to_dest: "/export/home/kafusr/kafka/secrets"
server_props_loc: "/etc/kafka"
secrets_props_loc: "{{ server_props_loc }}/secrets"
environment:
CONFLUENT_SECURITY_MASTER_KEY: "{{ extract_key2 }}"
tasks:
- name: Create a directory if it does not exist
file:
path: "{{ copy_to_dest }}"
state: directory
mode: '0755'
- name: Find files from "{{ server_props_loc }}"
find:
paths: /etc/kafka/
patterns: "server.properties*"
# ... the rest of the task
register: etc_kafka_server_props
- name: Find files from "{{ secrets_props_loc }}"
find:
paths: /etc/kafka/secrets
patterns: "*"
# ... the rest of the task
register: etc_kafka_secrets_props
- name: Copy the files
copy:
src: "{{ item.path }}"
dest: "{{ copy_to_dest }}"
remote_src: yes
loop: "{{ etc_kafka_server_props.files + etc_kafka_secrets_props.files }}"
- name: set masterkey content value
set_fact:
contents: "{{ lookup('file', '/export/home/kafusr/kafka/secrets/masterkey.txt') }}"
extract_key2: "{{ contents.split('\n').2.split('|').2|trim }}"
I want to set CONFLUENT_SECURITY_MASTER_KEY after the set_facts task
Is it possible to set playbook level environment variable, but after defining some tasks
Thank you
UPDATE
Initially, when I was executing the playbook as originally defined, I was getting the error
fatal: [kafkaserver1]: FAILED! => {"msg": "The field 'environment' has an invalid value,
which includes an undefined variable. The error was: 'extract_key2' is undefined"}
which was expected as the variable extract_key2 was not set - before copying the files to desired directory.
After #Zeitounator's suggestion, when I added default to the environment variable's definition,
CONFLUENT_SECURITY_MASTER_KEY: "{{ extract_key2 | default('') }}"
I now get a different error
The new error is
TASK [set masterkey content value] ******************** fatal: [kafkaserver1]: FAILED! =>
{"msg": "The task includes an option with an undefined variable. The error was: 'contents' is undefined\n\n
The error appears to be in '/export/home/kafuser/tmp/so-71538207-question.yml': line 43, column 7, but may\n
be elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n
- name: set masterkey content value\n ^ here\n"}
Getting this on all 3 brokers in the console and I checked the file it exists
I did do a cat on that file, copying the path from error to make sure there is no typo, and the contents of that file are displayed on console.
Update 2
I am trying to figure out how to use slurp to get the info, with the same approach as #Zeitounator's example about using lookup.
This is what I am trying. The current definition, is of course, erroneous. Just wanted to show what I am trying to do. But, can it be done for slurp and am I on the right path?
environment:
CONFLUENT_SECURITY_MASTER_KEY: >-
{{
(
((slurp: src: /export/home/z8tpush/kafka/secrets/masterkey.txt)['content'] | b64decode ).split('\n').2.split('|').2|trim
)
}}
#Zeitounator - Will you be able to direct me to an example where a slurp or fetch module is defined to set-up an environment variable and where the value will get updated after the tasks that create the file are executed, similar to what you have shown with lookup filter? I would really appreciate it.
Note:
Ultimately, I want to use ansible to create a new kafka user using confluents CLI commands ( using shell or command module ), verify it in my directory and once satisfied, I will encrypt the security.properties file using the masterkey and copy it to the appropriate location where confluent is installed.
As already mentioned, you can
With Ansible Configuration Settings set environment variables globally
Setting the remote environment in a task
Regarding your question
I haven't found how can I set-up an environment variable that can be used by all tasks following a task.
You can set the environment on Block level, a logical groups of tasks too
Setting the remote environment: "When you set a value with environment: at the play or block level, it is available only to tasks within the play or block that are executed by the same user."
This means you would need to define a block for the next tasks
- name: Block of next task(s)
block:
- name: Next task
...
environment:
CONFLUENT_SECURITY_MASTER_KEY: "{{ extract_key2 }}"
Regarding your question
Is it possible to set playbook level environment variable, but after defining some tasks?
No, not on that level in that run as the playbook is already running.
Another option might be to distribute the tasks in question into an other role, playbook or task file and include_* it.
You cannot set_fact a var depending on an other var in the same block. Moreover, there is absolutely no need to set_fact here as long as your relevant tasks can live with an empty environment var until it is fully defined. The following environment declaration (untested) should work and return the key for every task running after your file exists.
environment:
CONFLUENT_SECURITY_MASTER_KEY: >-
{{
(
(
lookup('file', '/export/home/kafusr/kafka/secrets/masterkey.txt', errors='ignore')
| default('')
).split('\n').2
| default('')
).2
| default('')
| trim
}}
I have such a scenario, I need to use two files A.bin, B.bin, first look in the files directory, if not found, download directly from a server
- name: Send A.bin and B.bin
copy: src={{item}}.bin dest=/opt/
register: is_exist
failed_when: False
with_items:
- A
- B
- name: Download from a server A.bin, B.bin
shell: wget -P /opt/{{item.item}} {{base_url}}{{item.item}}.bin
when: item.item.exception is defined
with_items:
- is_exist.results
But this will give an error:
The conditional check 'item.item.exception is defined' failed. The error was: error while evaluating conditional (item.item.exception is defined): 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'item'
what should I do?
I'd suggest visually reviewing the structure of is_exist variable.
- debug: msg="{{ is_exist }}"
Then, if item.item.exception is the expected variable name, always check that item.item is defined to avoid has no attribute 'item' before testing its exception key:
when: item.item is defined and item.item.exception is defined
I have a file with last modified time shown as "1563886751.38" (with stat and mtime). I wanted to check if the time is same when I run the playbook.
I have defined a variable as file_time: 1563872351.
I wanted to check if this value is present in the mtime.
vars:
file_date: 1563872351
- name: test
stat:
path: /tmp/logrotate/testx1
register: sym
- debug:
msg: "{{same}}"
when: sym.stat.mtime.find(file_date) != -1
But I get the below error:
'dict object' has no attribute 'find'
I see there is not attribute find but is there some way to do this? I need to check if it contains the value, not equal to.
This can be achieved by converting both values to strings and using the in operator:
- debug:
msg: same
when: file_date|string in sym.stat.mtime|string
I don't know exactly what you would use that for, but perhaps this would be a better approach for you:
- debug:
msg: same
when: sym.stat.mtime|int == file_date
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'm trying to include a file only if it exists. This allows for custom "tasks/roles" between existing "tasks/roles" if needed by the user of my role. I found this:
- include: ...
when: condition
But the Ansible docs state that:
"All the tasks get evaluated, but the conditional is applied to each and every task" - http://docs.ansible.com/playbooks_conditionals.html#applying-when-to-roles-and-includes
So
- stat: path=/home/user/optional/file.yml
register: optional_file
- include: /home/user/optional/file.yml
when: optional_file.stat.exists
Will fail if the file being included doesn't exist. I guess there might be another mechanism for allowing a user to add tasks to an existing recipe. I can't let the user to add a role after mine, because they wouldn't have control of the order: their role will be executed after mine.
The with_first_found conditional can accomplish this without a stat or local_action. This conditional will go through a list of local files and execute the task with item set to the path of the first file that exists.
Including skip: true on the with_first_found options will prevent it from failing if the file does not exist.
Example:
- hosts: localhost
connection: local
gather_facts: false
tasks:
- include: "{{ item }}"
with_first_found:
- files:
- /home/user/optional/file.yml
skip: true
Thanks all for your help! I'm aswering my own question after finally trying all responses and my own question's code back in today's Ansible: ansible 2.0.1.0
My original code seems to work now, except the optional file I was looking was in my local machine, so I had to run stat through local_action and set become: no for that particular tasks, so ansible wouldn't attempt to do sudo in my local machine and error with: "sudo: a password is required\n"
- local_action: stat path=/home/user/optional/file.yml
register: optional_file
become: no
- include: /home/user/optional/file.yml
when: optional_file.stat.exists
In Ansible 2.5 and above, it can be done using tests like this:
- include: /home/user/optional/file.yml
when: "'/home/user/optional/file.yml' is file"
More details: https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html#testing-paths
I using something similar but for the file module and what did the trick for me is to check for the variable definition, try something like:
when: optional_file.stat.exists is defined and optional_file.stat.exists
the task will run only when the variable exists.
If I am not wrong, you want to continue the playbook even the when statement false?
If so, please add this line after when:
ignore_errors: True
So your tasks will be look like this:
- stat: path=/home/user/optional/file.yml
register: optional_file
- include: /home/user/optional/file.yml
when: optional_file.stat.exists
ignore_errors: True
Please let me know, if I understand your question correctly, or can help further. Thanks
I could spend time here to bash ansible's error handling provisions, but in short, you are right and you can't use stat module for this purpose due to stated reasons.
Simplest solution for most ansible problems is to do it outside ansible. E.g.
- shell: test -f /home/user/optional/file.yml # or use -r if you're too particular.
register: optional_file
failed_when: False
- include: /home/user/optional/file.yml
when: optional_file.rc == 0
- debug: msg="/home/user/optional/file.yml did not exist and was not included"
when: optional_file.rc != 0
* failed_when added to avoid host getting excluded from further tasks when the file doesn't exist.
Using ansible-2.1.0, I'm able to use snippets like this in my playbook:
- hosts: all
gather_facts: false
tasks:
- name: Determine if local playbook exists
local_action: stat path=local.yml
register: st
- include: local.yml
when: st.stat.exists
I get no errors/failures when local.yml does not exist, and the playbook is executed (as a playbook, meaning it starts with the hosts: line, etc.)
You can do the same at the task level instead with similar code.
Using stat appears to work correctly.
There's also the option to use a Jinja2 filter for that:
- set_fact: optional_file="/home/user/optional/file.yml"
- include: ....
when: optional_file|exists
The best option I have come up with so far is this:
- include: "{{ hook_variable | default(lookup('pipe', 'pwd') ~ '/hooks/empty.yml') }}"
It's not exactly an "if-exists", but it gives users of your role the same result. Create a few variables within your role and a default empty file. The Jinja filters "default" and "lookup" take care of falling back on the empty file in case the variable was not set.
For convenience, a user could use the {{ playbook_dir }} variable for setting the paths:
hook_variable: "{{ playbook_dir }}/hooks/tasks-file.yml"