Ansible: How to run command with shell output - shell

Self explanatory. I want to link based on $(which {{ item }}).
Already saw the register function, but as I need to do a nested loop I'm not sure how to use it.
name: Link bins to user path
command: 'ln -s \$(which {{ item.1 }}) /home/{{ item.0 }}/bin/{{ item.1 }}'
with_nested:
- "{{ jail_users }}"
- "{{ jail_user_commands }}
Output:
failed: [rousertest] (item=[u'bob', u'date']) => {"changed": true,
"cmd": ["ln", "-s", "$(which", "date)", "/home/bob/bin/date"], "delta":
"0:00:00.011825", "end": "2019-07-11 08:17:32.921705", "item": ["bob", "date"], "msg": "non-zero return code", "rc": 1, "start": "2019-07-11
08:17:32.909880", "stderr": "ln: target ‘/home/bob/bin/date’ is not a
directory", "stderr_lines": ["ln: target ‘/home/bob/bin/date’ is not a
directory"], "stdout": "", "stdout_lines": []
Of course I was expecting something like this:
sudo ansible server -i inventory -m shell -a 'echo $(which date)'
rousertest | SUCCESS | rc=0 >>
/usr/bin/date

Below is the play. Avoid using command module for link. Use file module with state=link.
- name: Link binary
hosts: all
gather_facts: true
vars:
files:
- date
- ls
users:
- user1
- user2
tasks:
- name: Find paths
command: which {{ item }}
with_items:
- "{{ files }}"
register: result
- name: Link bins to user path
file:
src: "{{ item.1.stdout }}"
dest: "/home/{{ item.0 }}/bin/{{ item.1.item }}"
owner: "{{ item.0 }}"
group: "{{ item.0 }}"
state: link
with_nested:
- "{{ users }}"
- "{{ result.results }}"

Related

Ansible: Creating multiples paths with usernames defined in vars file

How can I create multiple paths with usernames defined in vars file?
---
- hosts: hostname
gather_facts: false
vars_files:
- /home/ansible/usernames
tasks:
- name: Checking if home directory exists
with_items: "{{ username }}"
vars:
paths: &paths
- "/home/{{ username }}/"
- "/home/{{ username }}/data"
loop: "{{ paths }}"
become: yes
become_user: "{{ username }}"
stat:
path: "{{ paths }}"
register: play1
- name: Check paths from play1
debug:
var: play1
- name: Creating directories
loop: "{{ play1.results }}"
register: play2
become: yes
become_user: "{{ item.item }}"
file:
path: /home/{{ item.item }}/.ssh
state: directory
mode: '0700'
owner: "{{ item.item }}"
group: "system-group"
when: not item.stat.exists and not item.stat.pw_name == '"{{ item }}"'
- name: Display Creating directories
debug:
var: play2
Extract from input file.
username:
- user1
- user2
This is the error I'm getting:
ERROR: failed: [server] (item=/home/[u'user1', u'user2']/) => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "ansible_loop_var": "item", "changed": false, "item": "/home/[u'user1', u'user2']/", "module_stderr": "Shared connection to server closed.\r\n", "module_stdout": "sudo: unknown user: [uuser1,\r\nsudo: unable to initialize policy plugin\r\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}

Unable to default omit when synchronize attributes are multiline in Ansible

Below Ansible works fine when final_file_perm is defined:
- name: Copying from "{{ inventory_hostname }}" to this ansible server.
synchronize:
src: "{{ item }}"
dest: "{{ playbook_dir }}/tmpfiles/{{ Latest_Build_Number }}/"
rsync_opts: "{{ final_file_perm | default(omit) }}"
mode: pull
with_items:
- "{{ source_file.split() }}"
However, my requirement is to have multiple multiline rsync_opts on new line like below:
rsync_opts:
- "--chmod=F0775"
- "--chmod=D0775"
So I tried the following:
- name: Copying from "{{ inventory_hostname }}" to this ansible server.
synchronize:
src: "{{ item }}"
dest: "{{ playbook_dir }}/tmpfiles/{{ Latest_Build_Number }}/"
rsync_opts:
- "{{ final_file_perm | default(omit) }}"
- "{{ final_folder_perm | default(omit) }}"
mode: pull
with_items:
- "{{ source_file.split() }}"
The above does not work and give me the following error:
TASK [Copying from "remhost" to this ansible server.] *********************
failed: [remhost] (item=/u/files/inst.zip) => {"changed": false, "cmd": "/bin/rsync --delay-updates -F --compress --archive --rsh=/usr/share/centrifydc/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null __omit_place_holder__6fd53eb1f5a7fbe7c6691ba6f3aada2e52378343 __omit_place_holder__6fd53eb1f5a7fbe7c6691ba6f3aada2e52378343 --out-format=<<CHANGED>>%i %n%L remuser#remhost:/u/files/inst.zip /web/playbooks/filecopy/tmpfiles/124/", "item": "/u/files/inst.zip", "msg": "Unexpected remote arg: remuser#remhost:/u/files/inst.zip\nrsync error: syntax or usage error (code 1) at main.c(1344) [sender=3.1.2]\n", "rc": 1}
to retry, use: --limit #/web/playbooks/filecopy/copyfiles.retry
As you can see ansible synchronize module is not liking __omit_place_holder__6fd53eb1f5a7fbe7c6691ba6f3aada2e52378343 right after -o UserKnownHostsFile=/dev/null
I also tried the following:
rsync_opts: [ '{{ final_file_perm | default(omit) }}', '{{ final_folder_perm | default(omit) }}' ]
But, I get similar error as shared above.
Note: this works fine when final_file_perm and final_folder_perm are defined. Errors only when they are undefined and I wish to omit them.
Can you please propose a solution where I can use default(omit) for attributes on multi newlines?
Then you will want to build up the rsync_opts list conditionally, rather than having what amounts to None items in the list
- name: Copying from "{{ inventory_hostname }}" to this ansible server.
synchronize:
src: "{{ item }}"
dest: "{{ playbook_dir }}/tmpfiles/{{ Latest_Build_Number }}/"
rsync_opts: "{{ my_rsync_opts }}"
mode: pull
with_items:
- "{{ source_file.split() }}"
vars:
my_rsync_opts: >-
{{ [] +
([final_file_perm] if final_file_perm|d("") else []) +
([final_folder_perm] if final_folder_perm|d("") else []) }}

Ansible task to create user with looping and checks

I'm trying to create a task where I can set up users based in a group of environments and types of servers when I'm running a task for that specific env and server.
For example, let's say I'm running the playbook in the oracle servers for production, the task should only add the user1 and not user2.
So I've set this way the vars:
"internal_users": [
{
"username": user1,
"comment": "Name of the User",
"password": "$1$D3d32t4t53y5ytyttgtuudhkjfgt!fdfsdf.",
"env": ["prod","uat"],
"hosts": ["apache","oracle"]
},
{
"username": user2,
"comment": "Name of the User2",
"password": "$1$D3d32t4t53y5fdfvrgrt45234fdfsdf.",
"env": ["uat"],
"hosts": ["apache","oracle"]
}
]
I believe I can get the actual env and server that I'm running the playbook using the var vars['env'] for environment and inventory_hostname.
Then the task:
- name: Create Internal users
vars:
env: "{{ vars['env'] }}"
host: "{{ inventory_hostname }}"
user:
name: "{{ item.username }}"
comment: "{{ item.comment }}"
groups: wheel,internal_users
append: yes
shell: /bin/bash
password: "{{ item.password }}"
update_password: on_create
when: env in item.hosts
with_dict: "{{ internal_users }}"
It isn't working, could someone help with the logic to make it work?
Thank you.
Variable "internal_users" is a list not a dictionary. "loop" must be used instead of "with_dict". For example, the task running on "oracle"
- name: Create Internal users
debug:
msg:
- "{{ item.username }}"
- "{{ item.comment }}"
- "{{ item.password }}"
loop: "{{ internal_users }}"
when:
- env in item.env
- host in item.hosts
vars:
env: uat
host: "{{ inventory_hostname }}"
gives
"msg": [
"user1",
"Name of the User",
"$1$D3d32t4t53y5ytyttgtuudhkjfgt!fdfsdf."
]
"msg": [
"user2",
"Name of the User2",
"$1$D3d32t4t53y5fdfvrgrt45234fdfsdf."
]
Here is the final task working:
- name: Create Internal users
vars:
env: "{{ vars['env'] }}"
host: "{{ group_names[0] }}"
user:
name: "{{ item.username }}"
comment: "{{ item.comment }}"
groups: wheel,internal_users
append: yes
shell: /bin/bash
password: "{{ item.password }}"
update_password: on_create
when:
- env in item.env
- host in item.hosts
loop: "{{ internal_users }}"
Thanks to Vladimir Botka for the help.

Setting become_user conditionally

I am trying to shutdown databases in a loop, the catch is some databases run as a different user and others just run as oracle. I login as oracle user and run the playbook and if the database is run as oracle user it goes through fine. If it is running as a different user I would like to become that user (oracle user has permissions to do that).
Here is my main playbook:
[oracle#ansctrlsrv.localdomain epd3]$ cat test.yml
---
- hosts: testdrive
tasks:
- set_fact:
db_list: "{{ lookup('file', 'vars/' ~ inventory_hostname ~ '.dblist')|from_yaml }}"
- name: Shutdown running databases
include_tasks: shutdowndb.yml
loop: "{{ db_list }}"
DB list is as follows:
[oracle#ansctrlsrv.localdomain epd3]$ cat vars/dbsrv.localdomain.dblist
- ebs1
- ebs2
- ndb1
[oracle#ansctrlsrv.localdomain epd3]$ cat shutdowndb.yml
---
- debug: msg='Shutting down {{ item }}'
- name: Execute shutdown
shell: id "{{ item }}"
register: shutdown_output
become: "{{ item is search('ebs') | ternary('yes','no') }}"
become_user: "{{ item }}"
- debug: msg="{{ shutdown_output.stdout }}"
[oracle#ansctrlsrv.localdomain epd3]$ cat inventory
[testdrive]
dbsrv.localdomain
[oracle#ansctrlsrv.localdomain epd3]$ ansible-playbook -i inventory test.yml
TASK [Execute shutdown] ***
fatal: [dbsrv1.localdomain]: FAILED! => {"changed": false, "module_stderr": "Shared connection to dbsrv1.localdomain closed.\r\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1} ...ignoring
I tried this question on another thread but got closed, so trying another hand after realizing a few issues myself such as being unable to run blocks of code with a loop, etc.
Appreciate any help.
Set become user conditionally
Fix the line
shell: id "{{ item }}"
correct syntax (use shell only when necessary)
command: "id {{ item }}"
The playbook below
- hosts: testdrive
tasks:
- set_fact:
db_list: "{{ lookup('file', 'vars/' ~ inventory_hostname ~ '.dblist')|from_yaml }}"
- name: Shutdown running databases
include_tasks: shutdowndb.yml
loop: "{{ db_list }}"
with the included tasks
$ cat shutdowndb.yml
- debug:
msg:
- "shell: {{ 'shutdown.sh ' ~ item }}"
- "become: {{ item is search('ebs')|ternary('yes', 'no') }}"
- "become_user: {{ item }}"
give
"msg": [
"shell: shutdown.sh ebs1",
"become: yes",
"become_user: ebs1"
]
"msg": [
"shell: shutdown.sh ebs2",
"become: yes",
"become_user: ebs2"
]
"msg": [
"shell: shutdown.sh ndb1",
"become: no",
"become_user: ndb1"
]
Q: "Why the command whoami is still giving oracle rather than ebs1?"
A: Short answer: Because become is not set to True.
Debugging
1) Is it possible to become all of the users in db_list? Yes.
- hosts: test_01
become: no
remote_user: admin
vars:
db_list: ['ebs1', 'ebs2', 'ndb1']
tasks:
- command: whoami
become_user: "{{ item }}"
become: true
register: result
loop: "{{ db_list }}"
- debug:
msg: "{{ result.results|json_query('[].stdout') }}"
give
"msg": [
"ebs1",
"ebs2",
"ndb1"
]
2) Does search and ternary work properly? Yes.
- debug:
msg: "{{ item is search('ebs')|ternary(true, false) }}"
loop: "{{ db_list }}"
gives
"msg": true
"msg": true
"msg": false
3) Does become and become_user work properly?. Yes.
- command: whoami
become_user: "{{ item }}"
become: "{{ item is search('ebs')|ternary(true, false) }}"
register: result
loop: "{{ db_list }}"
- debug:
msg: "{{ result.results|json_query('[].stdout') }}"
give
"msg": [
"ebs1",
"ebs2",
"admin"
]

How can I use the mv module Ansible

I am trying to use the mv module on Ansible but I am not having luck.
In my initial attempt I did the following:
- name: changing the name of the file
shell: mv /tmp/bundle /opt/Rocket.Chat
And I get the following error:
FAILED! => {"changed": true, "cmd": "mv /tmp/bundle /opt/Rocket.Chat", "delta": "0:00:00.033553", "end": "2019-02-11 06:06:43.273787", "msg": "non-zero return code", "rc": 1, "start": "2019-02-11 06:06:43.240234", "stderr": "mv: cannot move ‘/tmp/bundle’ to ‘/opt/Rocket.Chat/bundle’: File exists", "stderr_lines": ["mv: cannot move ‘/tmp/bundle’ to ‘/opt/Rocket.Chat/bundle’: File exists"], "stdout": "", "stdout_lines": []}
So, I changed it to:
- name: create directory
file:
state: directory
path: "/opt/Rocket.Chat"
- name: copy the files
copy:
src: "/tmp/bundle"
dest: "/opt/Rocket.Chat"
remote_src: yes
- name: delete the other files
file: path=/tmp/bundle state=absent
My new error is:
FAILED! => {"changed": false, "msg": "Remote copy does not support recursive copy of directory: /tmp/bundle"}
Seems that the "copy module to work with recursive and remote_src" does not work yet, but will be supported from May 2019
Here is a workaround, edit the folder names to your setup.
# Copy all files and directories from /usr/share/easy-rsa to /etc/easy-rsa
- name: List files in /usr/share/easy-rsa
find:
path: /usr/share/easy-rsa
recurse: yes
file_type: any
register: find_result
- name: Create the directories
file:
path: "{{ item.path | regex_replace('/usr/share/easy-rsa','/etc/easy-rsa') }}"
state: directory
mode: "{{ item.mode }}"
with_items:
- "{{ find_result.files }}"
when:
- item.isdir
- name: Copy the files
copy:
src: "{{ item.path }}"
dest: "{{ item.path | regex_replace('/usr/share/easy-rsa','/etc/easy-rsa') }}"
remote_src: yes
mode: "{{ item.mode }}"
with_items:
- "{{ find_result.files }}"
when:
- item.isdir == False

Resources