Trying to use vars_prompt on main.yml task inside role but I get error:
tasks/main.yml file for role 'roleName' must contain a list of tasks
vars_prompt:
- name: 'variableName'
prompt: "Prompting User "
private: no
default: ''
- name: taskName
uri:
url: "{{ variableName }}"
register: response
ignore_errors: yes
- debug:
var: response
If I move the prompting to playbook main.yml it works but I need to be able to do it within the task.
Thoughts?
vars_prompt can only be defined on a play. A task list is so named because it can only consist of tasks; metadata like vars, vars_prompt, hosts, etc. can only be set at the play level.
Consider avoiding the use of vars_prompt if at all possible. If you need input from the user, have them provide it on the command line using -e variable=value or in a file and using -e #somefile.yml.
Related
I am using ansible 2.9.4. My goal is to deny run some playbook on all nodes by accident or without tags. This is my app.yaml:
- hosts: all
remote_user: root
vars:
server_domain: mydomain.com
project_name: project
tasks:
- name: checking limit arg
fail:
msg: "you must use -l or --limit - when you really want to use all hosts, use -l 'all'"
when: ansible_limit is not defined
run_once: true
- name: "suppress message if tag given"
set_fact: suppress_message=yes
tags: dev,test,prod
- name: "message"
fail:
msg: "You didn't choose environment 'dev,test,prod'"
when: suppress_message is not defined
roles:
- testrole
The problem is, when I am not use --limit option, the role testrole run successfully and then the error message occurs - too late if I already run it on all nodes.
Even when I specify tags --tags "mytag" it will not check if limit was specified.
By similar way I would like to force to use tags, so everytime when you run playbook, you should specify environment tag (dev, test, prod) - e.g ssh keys for different environments, configuration files, etc...
What I would expect from this, that If I not specified tag dev, test or prod, the suppress_message would not be specified so next task with name message would fail with message "You didn't choose environement".
The fact is, If I not specified any tag:
- supress message have state OK
- message is skipped
If I specify valid tag --tags "dev":
- supress message have state OK
- message is not even mentioned (I would expect skipping)
If I specify "invalid tag" --tags "dev123":
- supress message is not mentioned
- message is not mentioned
The solution for limit could be replace - hosts: all with - hosts: randomtext so when no limit is specified there will be no match but what about tags/environments? I am quiet lost about how ansible works. The logic about this decisions what will run is quiet chaotic from this example.
Below is an example playbook that should achieve what you need to do.
Hosts are defined in a myhosts variable on the command line, the first task will abort the play if this variable is not set
Through use of the two “special tags”, always and never, we can ensure that:
the above check always runs, and
the inclusion of your testrole never runs — unless dev, test, or prod tags are explicitly specified
There’s a helpful message before the inclusion of the testrole, so the user is not left confused if the play exits silently because of unset tags
- hosts: '{{ myhosts | default("localhost") }}'
tasks:
- name: Fail if hosts are not defined
run_once: true
fail:
msg: >
You must define hosts in the myhosts variable,
e.g. `-e myhosts=foo.example.com` on the command line
when: myhosts is undefined
tags:
- always
- name: Helpful message
run_once: true
debug:
msg: >
This playbook does nothing unless the environment is specified with
the `--tags` option on the command line (dev, test, or prod).
tags:
- always
- name: Include role only when tags are specified
include_role:
name: testrole
tags:
- never
- dev
- test
- prod
This would be then executed like so:
$ ansible-playbook app.yaml --extra-vars myhosts=foo.example.com --tags dev
Change tasks to pre_tasks.
The order is pre_tasks, roles, tasks, post_tasks.
https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html
Playbook ordering is irrelevant.
Another option is add a task to include the role. So change
roles:
- testrole
to
- include_role:
name: testrole
Try using tags like this -
tags:
- dev,test,prod
my playbook structure looks like:
- hosts: all
name: all
roles:
- roles1
- roles2
In tasks of roles1, I define such a variable
---
# tasks for roles1
- name: Get the zookeeper image tag # rel3.0
run_once: true
shell: echo '{{item.split(":")[-1]}}' # Here can get the string rel3.0 normally
with_items: "{{ret.stdout.split('\n')}}"
when: "'zookeeper' in item"
register: zk_tag
ret.stdout:
Loaded image: test/old/kafka:latest
Loaded image: test/new/mysql:v5.7
Loaded image: test/old/zookeeper:rel3.0
In tasks of roles2, I want to use the zk_tag variable
- name: Test if the variable zk_tag can be used in roles2
debug: var={{ zk_tag.stdout }}
Error :
The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'stdout'
I think I encountered the following 2 problems:
When registering a variable with register, when condition is added, this variable cannot be used in all groups. How to solve this problem? How to make this variable available to all groups?
is my title, How to use variables between different roles in ansible?
You're most likely starting a new playbook for a new host. Meaning all previous collected vars are lost.
What you can do is pass a var to another host with the add_host module.
- name: Pass variable from this play to the other host in the same play
add_host:
name: hostname2
var_in_play_2: "{{ var_in_play_1 }}"
--- EDIT ---
It's a bit unclear. Why do you use the when statement in the first place if you want every host in the play for it to be available?
You might want to use the group_vars/all.yml file to place vars in.
Also, using add_host should be the way to go as how I read it. Can you post your playbook, and the outcome of your playbook on a site, e.g. pastebin?
If there is any chance the var is not defined because of a when condition, you should use a default value to force the var to be defined when using it. While you are at it, use the debug module for your tests rather than echoing something in a shell
- name: Debug my var
debug:
msg: "{{ docker_exists | default(false) }}"
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.
Given a playbook like this:
- name: "Tasks for service XYZ"
hosts: apiservers
roles:
- { role: common }
Is there a way to reference the playbook's name ("Tasks for service XYZ")? (i.e. a variable)
EDIT:
My intention is to be able to reference the playbook's name in a role task, i.e. sending a msg via slack like
- name: "Send Slack notification indicating deploy has started"
slack:
channel: '#project-deploy'
token: '{{ slack_token }}'
msg: '*Deploy started* to _{{ inventory_hostname }}_ of `{{ PLAYBOOK_NAME }}` version *{{ service_version }}*'
delegate_to: localhost
tags: deploy
It was added in 2.8:
ansible_play_name
The name of the currently executed play. Added in 2.8.
No, the special variables for Ansible are documented here, and you can see that there is no variable to return the playbook name.
As mentioned in the comments, however, you can always do this:
---
- name: "{{ task_name }}"
hosts: localhost
vars:
task_name: "Tasks for service XYZ"
tasks:
- debug:
msg: "{{ task_name }}"
From your circumstances, it looks like you only want this for audit/notification purposes? In that case (and assuming unixy clients), using
lookup('file', '/proc/self/cmdline') | regex_replace('\u0000',' ')
will give you the entire command line that ansible-playbook was called with, parameters and all, which would include the playbook name. Depending on your circumstances, that might be a useful enough tradeoff.
I am trying to craft a list of environment variables to use in tasks that may have slightly different path on each host due to version differences.
For example, /some/common/path/v_123/rest/of/path
I created a list of these variables in variables.yml file that gets imported via roles.
roles/somerole/varables/main.yml contains the following
somename:
somevar: 'coolvar'
env:
SOME_LIB_PATH: /some/common/path/{{ unique_part.stdout }}/rest/of/path
I then have a task that runs something like this
- name: Get unique path part
shell: 'ls /some/common/path/'
register: unique_part
tags: workflow
- name: Perform some actions that need some paths
shell: 'binary argument argument'
environment: somename.env
But I get some Ansible errors about variables not being defined.
Alternatively I tried to predefine the unique_part.stdout in hopes of register overwriting predefined variable, but then I got other ansible errors - failure to template.
Is there another way to craft these variables based on command returns?
You can also use facts:
http://docs.ansible.com/set_fact_module.html
# Prepare unique variables
- hosts: myservers
tasks:
- name: Get unique path part
shell: 'ls /some/common/path/'
register: unique_part
tags: workflow
- name: Add as Fact per for each hosts
set_fact:
library_path: "{{ unique_part.stdout }}"
# launch roles that use those unique variables
- hosts: myservers
roles:
- somerole
This way you can dynamicaly add variable to your hosts before using them.
The vars files gets evaluated when it is read by Ansible. Your only chance would be to include a placeholder which you then later have to replace yourself, like this:
somename:
somevar: 'coolvar'
env:
SOME_LIB_PATH: '/some/common/path/[[ unique_part.stdout ]]/rest/of/path'
And then later in your playbook you can replace that placeholder:
- name: Perform some actions that need some paths
shell: 'binary argument argument'
environment: '{{ somename.env | replace("[[ unique_part.stdout ]]", unique_part.stdout) }}'