I am trying to run multiple roles using with_items command, however I am getting error:
"ERROR! 'item' is undefined"
role.yml:
---
- hosts: '{{ host }}'
become: yes
roles:
- role: "{{item}}"
with_items: "{{ roles }}"
Here is my command:
ansible-playbook -i ./inventory/Dev ./playbooks/role.yml --extra-vars='{"host": "db", "roles": ["mysql", "apache"]}'
You cannot do it this way. with_ loops are not valid for roles.
If anything, you need to provide a list of roles to the roles: directive, so the syntax would be just like for the list of host groups hosts: '{{ host }}'. The problem is: Ansible does not resolve the variable for roles, so roles: '{{ roles }}' does not work.
What you can do, however, is to use include_role module in which you can access the variables.
No, include_role module doesn't take {{ item }} from the with_items as a value for name either.
So the only workaround I can think of (assuming you don't want to process the JSON beforehand) is to the include the roles statically:
tasks:
- include_role:
name: "mysql"
when: "'mysql' in roles"
- include_role:
name: "apache"
when: "'apache' in roles"
The roles need to exist on the control machine anyway, so all their names are predefined.
You cannot use with_ loop with roles directive.
A simple solution would be to use normal yaml list like below:
---
- hosts: '{{ host }}'
become: yes
roles:
- myrole1
- myrole2
Another example where you could declare the roles as well as pass corresponding variables to the role would be like below:
---
- hosts: '{{ host }}'
become: yes
roles:
- role:
name: myrole1
vars:
host: "db1"
myroles:
- mysql1
- apache1
- role:
name: myrole2
vars:
host: "db2"
myroles:
- mysql2
- apache2
Also avoid using ansible keywords as your variable names
Finally your command to run your playbook will be as below:
ansible-playbook -i ./inventory/Dev ./playbooks/role.yml
Related
I have a playbook which contains more than one plays. One of the plays generates a variable and stores it using the set_stats module as an artifact. The subsequent plays need to access the variable, but an error occurs that the given variable is undefined. How can I access a variable in the artifacts? (Btw using a workflow which would result in saving the variable in the extra_variables instead of the artifacts container is no option in this scenario)
The Problem in detail:
I have the following playbook which includes 2 plays which get executed on different hosts:
---
- hosts: ansible
roles:
- role_parse_strings
- hosts: all, !ansible
roles:
- role_setup_basics
- role_create_accounts
The role "role_parse_strings" in the first play generates the variable "users" which gets stored because of the set_stats module as an artifact. The following content lands in the artifact section of ansible awx:
users:
- username: user1
admin: true
- username: user2
admin: false
When the role "role_create_accounts" gets executed which tries to access the variable "users" in the following way...
- user: name={{ item.username }}
shell=/bin/bash
createhome=yes
groups=user
state=present
with_items: "{{ users }}"
..this error gets displayed:
{
"msg": "'users' is undefined",
"_ansible_no_log": false
}
You can use set_fact to share variable between hosts. Below example show how to share a file content via set_fact.
- hosts: host1
pre_tasks:
- name: Slurp the public key
slurp:
src: /tmp/ssh_key.pub
register: my_key_pub
- name: Save the public key
set_fact:
my_slave_key: >-
{{ my_key_pub['content'] | b64decode }}
- hosts: host2
vars:
slave_key: "{{ my_slave_key }}"
pre_tasks:
- set_fact:
my_slave_key: >-
{{ hostvars[groups["host1"][0]].my_slave_key | trim }}
We saved the content of public key as a fact name called my_slave_key and
assgined it another variable as slave_key in host2 with:
hostvars[groups["host1"][0]].my_slave_key
This is code of my ansible script .
---
- hosts: "{{ host }}"
remote_user: "{{ user }}"
ansible_become_pass: "{{ pass }}"
tasks:
- name: Creates directory to keep files on the server
file: path=/home/{{ user }}/fabric_shell state=directory
- name: Move sh file to remote
copy:
src: /home/pankaj/my_ansible_scripts/normal_script/installation/install.sh
dest: /home/{{ user }}/fabric_shell/install.sh
- name: Execute the script
command: sh /home/{{ user }}/fabric_shell/install.sh
become: yes
I am running the ansible playbook using command>>>
ansible-playbook send_run_shell.yml --extra-vars "user=sakshi host=192.168.0.238 pass=Welcome01" .
But I don't know why am getting error
ERROR! 'ansible_become_pass' is not a valid attribute for a Play
The error appears to have been in '/home/pankaj/go/src/shell_code/send_run_shell.yml': line 2, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
---
- hosts: "{{ host }}"
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
Please guide , what I am doing wrong.
Thanks in advance ...
ansible_become_pass is a connection parameter which you can set as variable:
---
- hosts: "{{ host }}"
remote_user: "{{ user }}"
vars:
ansible_become_pass: "{{ pass }}"
tasks:
# ...
That said, you can move remote_user to variables too (refer to the whole list of connection parameters), save it to a separate host_vars- or group_vars-file and encrypt with Ansible Vault.
Take a look on this thread thread and Ansible Page. I propose to use become_user in this way:
- hosts: all
tasks:
- include_tasks: task/java_tomcat_install.yml
when: activity == 'Install'
become: yes
become_user: "{{ aplication_user }}"
Try do not use pass=Welcome01,
When speaking with remote machines, Ansible by default assumes you are using SSH keys. SSH keys are encouraged but password authentication can also be used where needed by supplying the option --ask-pass. If using sudo features and when sudo requires a password, also supply --ask-become-pass (previously --ask-sudo-pass which has been deprecated).
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 want to write a bootstrapper playbook for new machines in Ansible which will reconfigure the network settings. At the time of the first execution target machines will have DHCP-assigned address.
The user who is supposed to execute the playbook knows the assigned IP address of a new machine. I would like to prompt the user for is value.
vars_prompt module allows getting input from the user, however it is defined under hosts section effectively preventing host address as the required value.
Is it possible without using a wrapper script modifying inventory file?
The right way to do this is to create a dynamic host with add_host and place it in a new group, then start a new play that targets that group. That way, if you have other connection vars that need to be set ahead of time (credentials/keys/etc) you could set them on an empty group in inventory, then add the host to it dynamically. For example:
- hosts: localhost
gather_facts: no
vars_prompt:
- name: target_host
prompt: please enter the target host IP
private: no
tasks:
- add_host:
name: "{{ target_host }}"
groups: dynamically_created_hosts
- hosts: dynamically_created_hosts
tasks:
- debug: msg="do things on target host here"
You could pass it with extra-vars instead.
Simply make your hosts section a variable such as {{ hosts_prompt }} and then pass the host on the command line like so:
ansible-playbook -i inventory/environment playbook.yml --extra-vars "hosts_prompt=192.168.1.10"
Or if you are using the default inventory file location of /etc/ansible/hosts you could simply use:
ansible-playbook playbook.yml --extra-vars "hosts_prompt=192.168.1.10"
Adding to Matt's answer for multiple hosts.
input example would be 192.0.2.10,192.0.2.11
- hosts: localhost
gather_facts: no
vars_prompt:
- name: target_host
prompt: please enter the target host IP
private: no
tasks:
- add_host:
name: "{{ item }}"
groups: dynamically_created_hosts
with_items: "{{ target_host.split(',') }}"
- hosts: dynamically_created_hosts
tasks:
- debug: msg="do things on target host here"
Disclaimer: The accepted answer offers the best solution to the problem. While this one is working it is based on a hack and I leave it as a reference.
I found out it was possible use a currently undocumented hack (credit to Bruce P for pointing me to the post) that turns the value of -i / --inventory parameter into an ad hoc list of hosts (reference). With just the hostname/ip address and a trailing space (like below) it refers to a single host without the need for the inventory file to exist.
Command:
ansible-playbook -i "192.168.1.21," playbook.yml
And accordingly playbook.yml can be run against all hosts (which in the above example will be equal to a single host 192.168.1.21):
- hosts: all
The list might contain more than one ip address -i "192.168.1.21,192.168.1.22"
Adding to Jacob's and Matt's examples, with the inclusion of a username and password prompt:
---
- hosts: localhost
pre_tasks:
- name: verify_ansible_version
assert:
that: "ansible_version.full is version_compare('2.10.7', '>=')"
msg: "Error: You must update Ansible to at least version 2.10.7 to run this playbook..."
vars_prompt:
- name: target_hosts
prompt: |
Enter Target Host IP[s] or Hostname[s] (comma separated)
(example: 1.1.1.1,myhost.example.com)
private: false
- name: username
prompt: Enter Target Host[s] Login Username
private: false
- name: password
prompt: Enter Target Host[s] Login Password
private: true
tasks:
- add_host:
name: "{{ item }}"
groups: host_groups
with_items:
- "{{ target_hosts.split(',') }}"
- add_host:
name: login
username: "{{ username }}"
password: "{{ password }}"
- hosts: host_groups
remote_user: "{{ hostvars['login']['username'] }}"
vars:
ansible_password: "{{ hostvars['login']['password'] }}"
ansible_become: yes
ansible_become_method: sudo
ansible_become_pass: "{{ hostvars['login']['password'] }}"
roles:
- my_role
I am creating a playbook which first creates a new username. I then want to run "moretasks.yml" as that new user that I just created. Currently, I'm setting remote_user for every task. Is there a way I can set it for the entire set of tasks once? I couldn't seem to find examples of this, nor did any of my attempts to move remote_user around help.
Below is main.yml:
---
- name: Configure Instance(s)
hosts: all
remote_user: root
gather_facts: true
tags:
- config
- configure
tasks:
- include: createuser.yml new_user=username
- include: moretasks.yml new_user=username
- include: roottasks.yml #some tasks unrelated to username.
moretasks.yml:
---
- name: Task1
copy:
src: /vagrant/FILE
dest: ~/FILE
remote_user: "{{newuser}}"
- name: Task2
copy:
src: /vagrant/FILE
dest: ~/FILE
remote_user: "{{newuser}}"
First of all you surely want to use sudo_user (remote user is the one that logs in, sudo_user is the one who executes the task).
In your case you want to execute the task as another user (the one previously created) just set:
- include: moretasks.yml
sudo: yes
sudo_user: "{{ newuser }}"
and those tasks will be executed as {{ newuser }} (Don't forget the quotes)
Remark: In most cases you should consider remote_user as a host parameter. It is the user that is allowed to login on the machine and that has sufficient rights to do things. For operational stuff you should use sudo / sudo_user
You could split this up into to separate plays? (playbooks can contain multiple plays)
---
- name: PLAY 1
hosts: all
remote_user: root
gather_facts: true
tasks:
- include: createuser.yml new_user=username
- include: roottasks.yml #some tasks unrelated to username.
- name: PLAY 2
hosts: all
remote_user: username
gather_facts: false
tasks:
- include: moretasks.yml new_user=username
There is a gotcha using separate plays: you can't use variables set with register: or set_fact: in the first play to do things in the second play (this statement is not entirely true, the variables are available in hostvars, but I recommend not using variables between roles). Defined variables like in group_vars and host_vars work just fine.
Another tip I'd like to give is to look into using roles http://docs.ansible.com/playbooks_roles.html. While it might seem more complicated at first, it's much easier to re-use them (as you seem to be doing with the "createuser.yml"). Looking at the type of things you are trying to achieve, the 'include all the things' path won't last much longer.
Kind of inline with your issue. Hope it helps. While updating my playbooks for Ansible 2.5 support for Cisco IOS network_cli connection
Credential file created with ansible-vault: auth/secrets.yml
---
creds:
username: 'ansible'
password: 'user_password'
Playbook:
---
- hosts: ios
gather_facts: yes
connection: network_cli
become: yes
become_method: enable
ignore_errors: yes
tasks:
- name: obtain login credentials
include_vars: auth/secrets.yml
- name: Set Username/ Password
set_fact:
remote_user: "{{ creds['username'] }}"
ansible_ssh_pass: "{{ creds['password'] }}"
- name: Find info for "{{ inventory_hostname }}" via ios_facts
ios_facts:
gather_subset: all
register: hardware_fact
Running playbook without auth/secrets.yml creds:
ansible-playbook -u ansible -k playbook.yml -l inventory_hostname