Shell commands with Ansible - cron

I would like to use Ansible in order to execute several commands. In my case, I would like to create a file system. Here my code:
- name: Create file system
raw: |
mkdir -p {{ out }} {{ scripts }} {{ sql }}
chown -R postgres:postgres {{ root_dir }}
lvcreate -n lv_test -L 5G data_vg
mkfs.ext4 {{ fs }}
mount {{ fs }} {{ home }}
when: (target_app == "app")
I want to do all these actions by using the root user. Any ideas?

You can just use the shell module similar to this:
- name: set a fact
hosts: localhost
tasks:
- name: add users
become: yes
become_user: root
shell: |
mkdir -p /tmp/blah/moreblah;
chown -R root:root /tmp/blah/moreblah
That being said there are modules like file that can do some of the lifting for you. E.g.
# create a directory if it doesn't exist
- file:
path: /etc/some_directory
state: directory
group: root
owner: root
mode: 0755
recurse: yes
In general, if you're using shell to perform everyday UNIX commands you're probably doing it wrong.
References
https://docs.ansible.com/ansible/latest/modules/file_module.html
https://docs.ansible.com/ansible/latest/modules/shell_module.html

Related

Ansible loop over multiple lists of variables

i hope everyone can help me with my ansible task problem. I deploy the snmp configurations via ansible on my servers and work with snmp-extend to trigger my scripts over snmp with certain OIDs. After my playbook has run and ansible deploy the snmp configurations, i manually execute the following command to become the OID for certain extend, for example:
snmptranslate -On NET-SNMP-EXTEND-MIB::nsExtendOutput1Line.\"folder-size-/home\"
This part i would like to do automatically via ansible, i have the variables:
snmp_mountpoints_extends:
- folder-size
- folder-avail
- folder-used
and in my inventory I define for host the following variables:
server1:
custom_mountpoints:
- /home
- /opt
my ansible part:
name: Generate OIDs for custom inventroy variables
become: yes
shell: 'snmptranslate -On NET-SNMP-EXTEND-MIB::nsExtendOutput1Line.\"{{ item }}-{{ custom_mountpoints[0] }}\"'
with_items:
"{{ snmp_mountpoints_extends }}"
register: custom_mountpoints_output
when:
- custom_mountpoints is defined
- name: print output from custom_mountpoints_output
debug: msg={{ custom_mountpoints_output }}
This work fine but only for first host variable /home. How can I iterate over my custom_mountpoints with each vars from snmp_mountpoints_extends?
thank you in advance
According Ansible documentation Iterating over nested lists you may use the following approach
---
- hosts:localhost
become: false
gather_facts: false
vars:
server1:
custom_mountpoints:
- /home
- /opt
snmp_mountpoints_extends:
- folder-size
- folder-avail
- folder-used
tasks:
- name: Iterating over nested lists
debug:
msg: "{{ item[0] }} and {{ item[1] }}"
loop: "{{ server1.custom_mountpoints | product(snmp_mountpoints_extend) | list }}"
resulting into the desired output.
/home and folder-size
/home and folder-avail
/home and folder-used
/opt and folder-size
/opt and folder-avail
/opt and folder-used

How to extract path from Ansible Output and use it else where within my playbook?

I want to extract the path out of the output given by the below command:
Command:
/opt/myapp/dir/bin/client -a 8101 -h localhost -u user -p pass properties-global-export
Output:
client: JAVA_HOME not set; results may vary
Exporting file: org.fusesource.fabric.agent.properties
Exporting file: local.app.node.vendor.service4.properties
Exporting file: local.app.node.data.service3.properties
Exporting file: local.app.node.vendor.service1.properties
Exporting file: local.app.node.vendor.service2.properties
Files dumped into directory: /opt/myapp/install-node/node-2.1.11/data/properties-tmp
and use /opt/myapp/install-node/node-2.1.11/data/properties-tmp path to perform a copy command within my playbook.
- name: Export Properties File
no_log: true
shell: "{{ fuse_esb_client_dir }} -h localhost -a {{ karaf_port }} -u {{ karaf_user }} -p {{ karaf_pass }} properties-global-export | grep -o '\''/[^ ]*'\ "
register: temp
- set_fact:
tempPath: "{{ temp.stdout }}"
- name: All Files Exported To Path
debug: var=temp.stdout
- name: Backup All Property Files to {{ backup_location }}
command: cp -r "{{ tempPath }}" "{{ backup_location }}"
Piped the output to grep grep -o '\''/[^ ]*'\ " The Extra '\' and \ on the start and end is used to escape the regex characters within the playbook. (Remove These if you want to test in your cli)
This Gave Me This Output
/opt/myapp/install-node/node-2.1.11/data/properties-tmp
Dumped that output with set_fact and used it within my playbook.
Hopefully someone finds this usefull. :))) Also for the pros like Jeff :) please let me know if I can improve anything within my playbook.

Iterating Ansible setup command

I want to use ansible setup module to retrieve hosts specs and I tried with a bash for loop.
Ansible version: 2.4
My hosts inventory has been defined in a group of machines which I called rhelmachines
I would like to collect the following list of variables called "specs"
declare -a specs=("ansible_all_ipv4_addresses" "ansible_processor" "ansible_processor_cores" "ansible_uptime_seconds")
I am then trying to include the ansible command in a for bash loop:
for i in "${specs[#]}"
do
ansible rhelmachines -m setup -a 'filter='$i'
done
how can I concatenate multiple filters in one connection only ?
Thanks!
With a little sed hackery to convert ansible's output to JSON, you can use jq to extract only the pieces you need:
ansible -m setup localhost | sed -e 's/^[[:alpha:]].*[|].* [>][>] {$/{/' | jq -n '
[inputs |
.ansible_facts as $facts |
$facts.ansible_hostname as $hostname |
{($hostname): {
"ipv4_addresses": $facts.ansible_all_ipv4_addresses,
"processor": $facts.ansible_processor[0],
"cores": $facts.ansible_processor_cores,
"uptime": $facts.ansible_uptime_seconds}}] | add'
...generates output of the form:
{
"my-current-hostname": {
"ipv4_addresses": [
"192.168.119.129"
],
"processor": "Intel(R) Core(TM) i7-6700HQ CPU # 2.60GHz",
"cores": 1,
"uptime": null
}
}
(run with ansible 1.4.5, which doesn't generate uptime).
As one of the possible solution, I implemented an Ansible code exploiting Ansible facts. I implemented it first gathering the ansible facts. Then, I used a local_action and a loop. The loop indices are the several ansible facts. For every fact I am writing out a line of a file. In this way I am getting a file composed by all the ansible facts I declared in the loop for the rhelmachines.
---
- hosts: rhelmachines
gather_facts: True
tasks:
- name: Gather Facts
setup:
gather_subset=all
register: facts
- debug:
msg: "{{ facts.ansible_facts.ansible_all_ipv4_addresses }}"
- name: copy content from facts to output file
local_action:
module: lineinfile
line: "{{ item }}"
path: /tmp/assessments/facts.txt
loop:
- "{{ facts.ansible_facts.ansible_all_ipv4_addresses }}"
- "{{ facts.ansible_facts.ansible_all_ipv6_addresses }}"
I took #Luigi Sambolino answer and make it better. His answer was failing on more than one host in the inventory. He proposed using lineinfile which has one con in this situation - every fact that was the same as other machines were omitted. Other drawback was that results wasn't sorted together, all was mixed.
I needed to take some basic information about systems, like IP, OS version, and so on. Here's my playbook:
- hosts: all
gather_facts: true
ignore_unreachable: true
tasks:
- name: get the facts
setup:
gather_subset=all
register: facts
- name: remove file
local_action:
module: file
path: results
state: absent
- name: save results in file
local_action:
module: shell
cmd: echo "{{ item }}" >> results
with_together:
- "{{ facts.ansible_facts.ansible_default_ipv4.address }}"
- "{{ facts.ansible_facts.ansible_architecture }}"
- "{{ facts.ansible_facts.ansible_distribution }}"
- "{{ facts.ansible_facts.ansible_distribution_version }}"
- "{{ facts.ansible_facts.ansible_hostname }}"
- "{{ facts.ansible_facts.ansible_kernel }}"
Now the results look like this:
...
['10.200.1.21', 'x86_64', 'Ubuntu', '18.04', 'bacula', '4.15.18-7-pve']
['10.200.2.53', 'x86_64', 'Ubuntu', '18.04', 'webserver', '4.15.18-27-pve']
...
Square brackets can be deleted by sed and we have a nice CSV file that can be used with any spreadsheet, for example.
specs=( "ansible_all_ipv4_addresses"
"ansible_processor"
"ansible_processor_cores"
"ansible_uptime_seconds" )
args=( )
for spec in "${specs[#]}"; do args+=( -a "$spec" ); done
ansible rhelmachines -m setup "${args[#]}"
...will result in your final command being equivalent to:
ansible rhelmachines -m setup \
-a ansible_all_ipv4_addresses \
-a ansible_processor \
-a ansible_processor_cores \
-a ansible_uptime_seconds

Edit current user's shell with ansible

I'm trying yo push my dot files and some personal configuration files to a server (I'm not root or sudoer). Ansible connects as my user in order to edit files in my home folder.
I'd like to set my default shell to usr/bin/fish.
I am not allowed to edit /etc/passwd so
user:
name: shaka
shell: /usr/bin/fish
won't run.
I also checked the chsh command but the executable prompt for my password.
How could I change my shell on such machines ? (Debian 8, Ubuntu 16, Opensuse)
I know this is old, but I wanted to post this in case anyone else comes back here looking for advise like I did:
If you're running local playbooks, you might not be specifying the user and expecting to change the shell of user you're running the playbook as.
The problem is that you can't change the shell without elevating the privileges (become: yes), but when you do - you're running things as root. Which just changes the shell of the root user. You can double check that this is the case by looking at /etc/passwd and seeing what the root shell is.
Here's my recipe for changing the shell of the user running the playbook:
- name: set up zsh for user
hosts: localhost
become: no
vars:
the_user: "{{ ansible_user_id }}"
tasks:
- name: change user shell to zsh
become: yes
user:
name: "{{ the_user }}"
shell: /bin/zsh
This will set the variable the_user to the current running user, but will change the shell of that user using root.
I ended up using two ansible modules :
ansible expect
ansible prompt
First I record my password with a prompt :
vars_prompt:
- name: "my_password"
prompt: "Enter password"
private: yes
And then I use the module expect to send the password to the chsh command :
tasks:
- name: Case insensitve password string match
expect:
command: "chsh -s /usr/bin/fish"
responses:
(?i)password: "{{ my_password }}"
creates: ".shell_is_fish"
The creates sets a lock file avoiding this task to be triggered again. This may be dangerous because the shell could be changed after and ansible will not update it (because of the lock still present). You may want to avoid this behaviour.
Here is how I do it:
- name: Set login shell of user {{ ansible_env.USER }} to `/bin/zsh` with `usermod`
ansible.builtin.command: usermod --shell /bin/zsh {{ ansible_env.USER }}
become: true
changed_when: false
Ubuntu 16
add first line in ~/.bashrc
/usr/bin/fish && exit

Ansible - Mode 755 for directories and 644 for files recursively

I'd like to allow anyone to list and read all files in my directory tree, but I don't want to make the files executable :
dir
\subdir1
file1
\subdir2
file2
...
\subdirX
fileX
The following task makes my directories and files readable, but it makes all the files executable as well:
- name: Make my directory tree readable
file:
path: dir
mode: 0755
recurse: yes
On the other hand, if I choose mode 0644, then all my files are not executable, but I'm not able to list my directories.
Is it possible to set mode 755 for all directories and 644 for all files in a directory tree?
Since version 1.8, Ansible supports symbolic modes. Thus, the following would perform the task you want:
- name: Make my directory tree readable
file:
path: dir
mode: u=rwX,g=rX,o=rX
recurse: yes
Because X (instead of x) only applies to directories or files with at least one x bit set.
The Ansible file/copy modules don't give you the granularity of specifying permissions based on file type so you'd most likely need to do this manually by doing something along these lines:
- name: Ensure directories are 0755
command: find {{ path }} -type d -exec chmod -c 0755 {} \;
register: chmod_result
changed_when: "chmod_result.stdout != \"\""
- name: Ensure files are 0644
command: find {{ path }} -type f -exec chmod -c 0644 {} \;
register: chmod_result
changed_when: "chmod_result.stdout != \"\""
These would have the effect of recursing through {{ path }} and changing the permissions of every file or directory to the specified permissions.
Due to a "inadequate" implementation Ansible supports symbolic modes only partially (see explanation below).
Other than using the command line chmod, setting mode to u=rwX,g=rX,o=rX with Ansible will not allways be sufficient to get your files set to 644.
The resulting permissions will also depend on the original mode of the file!
As stated in the docs for chmod and already pointed out by some comments to other answers of this question:
If file permission for either u, g, or o is executable, then X will set the file permission also to x.
For example. If one file has mode 740 -rwxr-----, setting mode u=rwX,g=rX,o=rX with ansible, you will get 755 -rwxr-xr-x instead the expected 644 -rw-r--r--.
Despite that this is not what you wanted, it will make the file executable by group and others with unwanted security problems.
In those cases, with Ansible you will need two steps to set file the permissions to 644.
- file:
path: /path/to/dir
state: directory
recurse: yes
mode: '{{ item }}'
loop:
- '-x'
- 'u=rwX,g=rX,o=rX'
Note that if you wanted mode 744 for files you'll need u=rwx,g=rX,o=rX (first x lowercase!). This will work in Ansible as for the current implementation BUT this is not the way command line chmod works. See **Symbolic nodes with* chmod below.
Why is this so:
Ansible states in the function _symbolic_mode_to_octal to include things like: u=rw-x+X,g=r-x+X,o=r-x+X.
Nonetheless if the given mode is g=-x+X, ansible ignores the -x perm.
The function _symbolic_mode_to_octal iterates over the given permissions,
when it comes to X the function _get_octal_mode_from_symbolic_perms doesn't compare the requested permissions to the already applied but to the original ones, thus ignoring it:
This is probably a bug in Ansible.
The simplest and efficient way is to delegate to a shell command: As proposed in #BruceP's answer.
If for some reason you are averse to use 'workarounds' and need an ansible-ish way to solve the problem AND you don't care about performance you could try the following:
NOTE: This will take very, very long depending on the number of files and directories!
- name: example
hosts: 192.168.111.123
become: yes
gather_facts: no
vars:
path_to_dir: /path/to/dir
target_mode_for_directories: 755
target_mode_for_files: 644
tasks:
- name: collect list of directories '{{ path_to_dir }}'
find:
paths: '{{ path_to_dir }}'
recurse: yes
file_type: directory
register: found_directories
- name: set collected directories to mode '{{ target_mode_for_directories }}'
file:
dest: '{{ item.path }}'
mode: '{{ target_mode_for_directories }}'
loop: '{{ found_directories.files }}'
- name: collect list of files under '{{ path_to_dir }}'
find:
paths: '{{ path_to_dir }}'
recurse: yes
file_type: file
register: found_files
- name: set collected files to mode '{{ target_mode_for_files }}'
file:
dest: '{{ item.path }}'
mode: '{{ target_mode_for_files }}'
loop: '{{ found_files.files }}'
Symbolic modes with chmod
Remember that setting symbolic modes with chmod can be very tricky.
See the following examples that simply differ on the order of lowercase and uppercase X, i.e. u=X,g=X,o=x (o=lowercase x) vs. u=x,g=X,o=X (u=lowercase x) which results in 001 ---------x vs. 111 ---x--x--x:
$ sudo chmod -R 000 path/to/file; \
sudo chmod -R u=X,g=X,o=x path/to/file; \
sudo find path/to/file -printf ""%03m" "%M" "%p\\n"";
001 ---------x path/to/file
$ sudo chmod -R 000 path/to/file; \
sudo chmod -R u=x,g=X,o=X path/to/file; \
sudo find path/to/file -printf ""%03m" "%M" "%p\\n"";
111 ---x--x--x path/to/file
This is due to the fact that the perms are processed first for u, then for g and last for o. In the first example X won't apply for files because there is no x perm. In the second case X will apply for files after u=x has been set, thus setting both g=x and o=x
This worked for me:
- file:
path: dir
mode: a-x
recurse: yes
- file:
path: dir
mode: u=rwX,g=rX,o=rX
recurse: yes
Remove execution permission first from all, otherwise group and others get execution permission to files.
See chmod man pages about X-directive:
execute/search only if the file is a directory or already has execute
permission for some user (X)
Also this works:
- shell: "chmod -R a-x,u=rwX,g=rX,o=rX dir"
For some reason combination of these two does not work:
- file:
path: dir
mode: a-x,u=rwX,g=rX,o=rX
recurse: yes

Resources