When conditional from ssh command line in Ansible Role - ansible

I am new to ansible so any help would be appreciated.
I need to check if my remote Centos Servers have a writable /boot before I try and push VMware tools to them . Install will fail if it's read-only . How do I add another WHEN for this raw Linux command? I know if have to use register or standard out, but I cannot find examples to guide me .
RAW Linux Would be >
mount | grep boot
And I need to catch rw, the target must not be ro like in this example
>
/dev/sda1 on /boot type ext4 (ro,relatime,data=ordered)
I tried adding a task under the block like in the ansible documentation.
- name: Catch Targets with read only boot
tasks:
- command: mount | grep boot
register: boot_mode
- shell: echo "motd contains the word hi"
when: boot_mode.stdout.find('ro') != -1
---
- name: Wrapper for conditional tasks
block:
- name: Copy Files from Mirror to Remote Guest
get_url:
url: "{{ item }}"
dest: /tmp
owner: root
group: root
with_items:
- http://mirror.compuscan.co.za/repo/vmwaretools65u2/CentOS7/VMwareTools-10.3.5-10430147.tar.gz
- name: UnTAR the installer
unarchive:
src: /tmp/VMwareTools-10.3.5-10430147.tar.gz
dest: /tmp
remote_src: yes
- name: Run the PL install
become: yes
command: /tmp/vmware-tools-distrib/vmware-install.pl -d
- name: Perform Clean Up
file:
state: absent
path: "{{ item }}"
with_items:
- /tmp/vmware-tools-distrib/
- /tmp/VMwareTools-10.3.5-10430147.tar.gz
- name: Report on success or failure
service:
name: vmware-tools
state: started
enabled: yes
when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7'
ignore_errors: yes
I want the role/playbook to ignore Targets in read-only /boot mode.

Put stat task in front of the block
- stat:
path: /boot
register: boot_mode
Then add the condition to execute the block if /boot is writeable
when:
- boot_mode.stat.writeable
- ansible_distribution == 'CentOS'
- ansible_distribution_major_version == '7'

Related

Adding entry to end of line Ansible SLES 12

On SLES 12 server. Trying to append to this entry in my /etc/security/pam_winbind.conf file with this extra entry S-1-5-21-84296906-944397292-530207130-587119.
The line is
require_membership_of=S-1-5-21-84296906-944397292-530207130-496773,S-1-5-21-84296906-944397292-530207130-71056,S-1-5-21-84296906-944397292-530207130-218591
My playbook
---
- name: Configuring ad_access_filter for RHEL systems.
hosts: smt-test
become: yes
tasks:
- name: Taking Backup.
copy:
src: /etc/security/pam_winbind.conf
dest: /etc/security/pam_winbind.conf.backup
remote_src: yes
- name: Add HQCloud to the sssd.conf file
lineinfile:
path: /etc/security/pam_winbind.conf
backrefs: yes
regexp: '(^*2185915*)$'
line: '\1,S-1-5-21-84296906-944397292-530207130-587119'
- name: Add HQCloudScapeSupp to the sudoers file.
lineinfile:
path: /etc/sudoers
line: 'HQCloudScapeSupp ALL=(ALL) NOPASSWD: ALL'
- name: Restarting WinBind Service
service:
name: winbind
state: restarted
Since the pam_winbind.conf will be different on each server, how do I just add that entry to the end of that line regardless of the other memberships?
There are a few problems with your approach IMO
It might be possible to do add your membership line with only a regex and backrefences but achieving idempotence will be a real pain. Indeed, you actually need to add your required membership if it does not already exist anywhere in the string (it might be present but not in last position). If it is already present anywhere, you should not touch anything.
You are making a backup of your file separately where the lineinfile module can do this automatically for you and only when there is a change
you are unconditionally restarting your service where it should only restart when something has actually changed requiring a restart.
The below playbook addresses the above issues:
---
- name: Configuring ad_access_filter for RHEL systems.
hosts: smt-test
become: yes
vars:
config_file: /etc/security/pam_winbind.conf
required_member: S-1-5-21-84296906-944397292-530207130-587119
search_needle: require_membership_of=
search_regexp: "^{{ search_needle }}(.*)$"
tasks:
- name: slurp file content to get existing membership entries
slurp:
path: "{{ config_file }}"
register: slurped_file
- name: Add HQCloud to the sssd.conf file if it does not exist + backup if any change
vars:
file_content_lines: "{{ (slurped_file.content | b64decode).splitlines() }}"
requirement_line: "{{ file_content_lines | select('match', search_needle) | first }}"
existing_members: "{{ (requirement_line | regex_replace(search_regexp, '\\g<1>')).split(',') | map('trim') }}"
wanted_members: "{{ existing_members | union([required_member]) }}"
lineinfile:
path: "{{ config_file }}"
regexp: "{{ search_regexp }}"
backup: true
line: "{{ search_needle }}{{ wanted_members | join(',') }}"
- name: Add HQCloudScapeSupp to the sudoers file.
lineinfile:
path: /etc/sudoers
line: 'HQCloudScapeSupp ALL=(ALL) NOPASSWD: ALL'
# Not really sure this is needed
notify: Restart winbind
handlers:
- name: Restart winbind
service:
name: winbind
state: restarted

ansible error 'first argument must be string or compiled pattern'

I have this code in my playbook:
- hosts: standby
remote_user: root
tasks:
- name: replace hostname in config
replace:
path: /opt/agentd.conf
regexp: #\s+Hostname\=
replace: Hostname={{hname}}
backup: yes
- name: add database array in files
lineinfile:
path: /opt/zabbix_agent/share/scripts/{{ item }}
line: 'DBNAME_ARRAY=( {{dbname}} )'
insertafter: DB2PATH=/home/db2inst1/sqllib/bin/db2
backup: yes
with_items:
- Connections
- HadrAndLog
- Memory
- Regular
- name: restart service
shell: /etc/init.d/agent restart
register: command_output
become: yes
become_user: root
tags: restart
- debug: msg="{{command_output.stdout_lines}}"
tags: set_config_st
it will replace # Hostname= in a config file with Hostname= givenhostname and add an array in 4 scripts. array is the name of given database. then it will restart the agent to apply the changes.
when i run this command:
ansible-playbook -i /Ansible/inventory/hostfile /Ansible/provision/nconf.yml --tags set_config_st --extra-vars "hname=fazi dbname=fazidb"
i get this error:
first argument must be string or compiled pattern
i searched a bit but couldn't find the reason. what should i do?
The problem is in this line:
regexp: #\s+Hostname\=
You have to quote the regex because YAML comments start with #, so everything after the # will be ignored by ansible and that is why the error message occures.
So the correct line should be:
regexp: '#\s+Hostname\='
or
regexp: "#\s+Hostname\="
I think the problem is with indention. Please try as below.
- hosts: standby
remote_user: root
tasks:
- name: replace hostname in config
replace:
path: /opt/agentd.conf
regexp: #\s+Hostname\=
replace: Hostname={{hname}}
backup: yes
- name: add database array in files
lineinfile:
path: /opt/zabbix_agent/share/scripts/{{ item }}
line: 'DBNAME_ARRAY=( {{dbname}} )'
insertafter: DB2PATH=/home/db2inst1/sqllib/bin/db2
backup: yes
with_items:
- Connections
- HadrAndLog
- Memory
- Regular
- name: restart service
shell: /etc/init.d/agent restart
register: command_output
become: yes
become_user: root
tags: restart
- debug: msg="{{command_output.stdout_lines}}"
tags: set_config_st

When Condition with Ansible

I am new to Ansible, and I am writing a script to install a package when disk space is more then a limit. I am getting error like this >> error while evaluating conditional
---
- hosts: dev
become: true
become_user: root
tasks:
- name: Install zsh if enough space
yum:
name: zsh
state: latest
with_items: "{{ ansible_mounts}}"
when: item.mount == "/" and item.size_available > 10737400
I am giving the size in bytes. ( Is there a way to give the size in MB ? )
Thanks.
Ansible uses the YAML format, you need to use the right indent.
In YAML, the indent is important as closing brackets or semicolons in most programming languages.
with_items is not a definition for the yum module, it is a directive for Ansible, so it should be at the same level as when and the module call (e.g. yum). Both examples below should work:
---
- hosts: dev
become: true
become_user: root
tasks:
- name: Install zsh if enough space
yum:
name: zsh
state: latest
with_items: "{{ ansible_mounts }}"
when: item.mount == "/" and item.size_available > 10737400
or
---
- hosts: dev
become: true
become_user: root
tasks:
- name: Install zsh if enough space
with_items: "{{ ansible_mounts }}"
when: item.mount == "/" and item.size_available > 10737400
yum:
name: zsh
state: latest

Ansible - Create multiple folders if don't exist

Goal:
Create multiple directories if they don't exist.
Don't change permissions of existing folder
Current playbook:
- name: stat directories if they exist
stat:
path: "{{ item }}"
with_items:
- /data/directory
- /data/another
register: myvar
- debug: var=myvar.results
- name: create directory if they don't exist
file:
path: "{{ item.invocation.module_args.path }}"
state: directory
owner: root
group: root
mode: 0775
loop: "{{ stat.results }}"
# with_items: "{{ stat.results }}" # for older versions of Ansible
# when: myvar.results.stat.exists == false
The when statement is wrong.
I looked at the example provided; http://docs.ansible.com/ansible/stat_module.html. But this only works for a single folder.
Using Ansible modules, you don't need to check if something exist or not, you just describe the desired state, so:
- name: create directory if they don't exist
file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: 0775
loop:
- /data/directory
- /data/another
Ansible - Creating multiple folders without changing permissions of previously existing.
Working fine for me. Hope this works for you as well just try.
---
- name: "Creating multiple by checking folders"
hosts: your_host_name
tasks:
- block:
- name: "Checking folders"
stat:
path: "{{item}}"
register: folder_stats
with_items:
- ["/var/www/f1","/var/www/f2","/var/www/f3","/var/www/f4"]
- name: "Creating multiple folders without disturbing previous permissions"
file:
path: "{{item.item}}"
state: directory
mode: 0755
group: root
owner: root
when: item.stat.exists == false
loop:
- "{{folder_stats.results}}"
...
Starting from Ansible 2.5, loop should be used to iterate over a list, see https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#standard-loops
As the Ansible file module is idempotent, you do not have to check if the folders already exist.
For example:
- name: create backup directories
file:
path: "{{ item }}"
state: directory
owner: backup
group: backup
mode: 0775
loop:
- /backupdisk/certificates
- /backupdisk/mysql
- /backupdisk/wordpress

Ansible: How to delete files and folders inside a directory?

The below code only deletes the first file it gets inside the web dir. I want to remove all the files and folders inside the web directory and retain the web directory. How can I do that?
- name: remove web dir contents
file: path='/home/mydata/web/{{ item }}' state=absent
with_fileglob:
- /home/mydata/web/*
Note: I've tried rm -rf using command and shell, but they don't work. Perhaps I am using them wrongly.
Any help in the right direction will be appreciated.
I am using ansible 2.1.0.0
- name: Delete content & directory
file:
state: absent
path: /home/mydata/web/
Note: this will delete the directory too.
Remove the directory (basically a copy of https://stackoverflow.com/a/38201611/1695680), Ansible does this operation with rmtree under the hood.
- name: remove files and directories
file:
state: "{{ item }}"
path: "/srv/deleteme/"
owner: 1000 # set your owner, group, and mode accordingly
group: 1000
mode: '0777'
with_items:
- absent
- directory
If you don't have the luxury of removing the whole directory and recreating it, you can scan it for files, (and directories), and delete them one by one. Which will take a while. You probably want to make sure you have [ssh_connection]\npipelining = True in your ansible.cfg on.
- block:
- name: 'collect files'
find:
paths: "/srv/deleteme/"
hidden: True
recurse: True
# file_type: any # Added in ansible 2.3
register: collected_files
- name: 'collect directories'
find:
paths: "/srv/deleteme/"
hidden: True
recurse: True
file_type: directory
register: collected_directories
- name: remove collected files and directories
file:
path: "{{ item.path }}"
state: absent
with_items: >
{{
collected_files.files
+ collected_directories.files
}}
Using shell module (idempotent too):
- shell: /bin/rm -rf /home/mydata/web/*
If there are dot/hidden files:
- shell: /bin/rm -rf /home/mydata/web/* /home/mydata/web/.*
Cleanest solution if you don't care about creation date and owner/permissions:
- file: path=/home/mydata/web state=absent
- file: path=/home/mydata/web state=directory
I really didn't like the rm solution, also ansible gives you warnings about using rm.
So here is how to do it without the need of rm and without ansible warnings.
- hosts: all
tasks:
- name: Ansible delete file glob
find:
paths: /etc/Ansible
patterns: "*.txt"
register: files_to_delete
- name: Ansible remove file glob
file:
path: "{{ item.path }}"
state: absent
with_items: "{{ files_to_delete.files }}"
source: http://www.mydailytutorials.com/ansible-delete-multiple-files-directories-ansible/
try the below command, it should work
- shell: ls -1 /some/dir
register: contents
- file: path=/some/dir/{{ item }} state=absent
with_items: {{ contents.stdout_lines }}
That's what I come up with:
- name: Get directory listing
find:
path: "{{ directory }}"
file_type: any
hidden: yes
register: directory_content_result
- name: Remove directory content
file:
path: "{{ item.path }}"
state: absent
with_items: "{{ directory_content_result.files }}"
loop_control:
label: "{{ item.path }}"
First, we're getting directory listing with find, setting
file_type to any, so we wouldn't miss nested directories and links
hidden to yes, so we don't skip hidden files
also, do not set recurse to yes, since it is not only unnecessary, but may increase execution time.
Then, we go through that list with file module. It's output is a bit verbose, so loop_control.label will help us with limiting output (found this advice here).
But I found previous solution to be somewhat slow, since it iterates through the content, so I went with:
- name: Get directory stats
stat:
path: "{{ directory }}"
register: directory_stat
- name: Delete directory
file:
path: "{{ directory }}"
state: absent
- name: Create directory
file:
path: "{{ directory }}"
state: directory
owner: "{{ directory_stat.stat.pw_name }}"
group: "{{ directory_stat.stat.gr_name }}"
mode: "{{ directory_stat.stat.mode }}"
get directory properties with the stat
delete directory
recreate directory with the same properties.
That was enough for me, but you can add attributes as well, if you want.
Using file glob also it will work. There is some syntax error in the code you posted. I have modified and tested this should work.
- name: remove web dir contents
file:
path: "{{ item }}"
state: absent
with_fileglob:
- "/home/mydata/web/*"
Following up on the most upvoted answer here (which I cannot edit since "edit queue is full"):
- name: Delete content & directory
file:
state: absent
path: /home/mydata/web/
- name: Re-create the directory
file:
state: directory
path: /home/mydata/web/
While Ansible is still debating to implement state = empty
https://github.com/ansible/ansible-modules-core/issues/902
my_folder: "/home/mydata/web/"
empty_path: "/tmp/empty"
- name: "Create empty folder for wiping."
file:
path: "{{ empty_path }}"
state: directory
- name: "Wipe clean {{ my_folder }} with empty folder hack."
synchronize:
mode: push
#note the backslash here
src: "{{ empty_path }}/"
dest: "{{ nl_code_path }}"
recursive: yes
delete: yes
delegate_to: "{{ inventory_hostname }}"
Note though, with synchronize you should be able to sync your files (with delete) properly anyway.
Created an overall rehauled and fail-safe implementation from all comments and suggestions:
# collect stats about the dir
- name: check directory exists
stat:
path: '{{ directory_path }}'
register: dir_to_delete
# delete directory if condition is true
- name: purge {{directory_path}}
file:
state: absent
path: '{{ directory_path }}'
when: dir_to_delete.stat.exists and dir_to_delete.stat.isdir
# create directory if deleted (or if it didn't exist at all)
- name: create directory again
file:
state: directory
path: '{{ directory_path }}'
when: dir_to_delete is defined or dir_to_delete.stat.exist == False
Below code worked for me :
- name: Get directory listing
become: yes
find:
paths: /applications/cache
patterns: '*'
hidden: yes
register: directory_content_result
- name: Remove directory content
become: yes
file:
path: "{{ item.path }}"
state: absent
with_items: "{{ directory_content_result.files }}"
There is an issue open with respect to this.
For now, the solution works for me: create a empty folder locally and synchronize it with the remote one.
Here is a sample playbook:
- name: "Empty directory"
hosts: *
tasks:
- name: "Create an empty directory (locally)"
local_action:
module: file
state: directory
path: "/tmp/empty"
- name: Empty remote directory
synchronize:
src: /tmp/empty/
dest: /home/mydata/web/
delete: yes
recursive: yes
I want to make sure that the find command only deletes everything inside the directory and leave the directory intact because in my case the directory is a filesystem. The system will generate an error when trying to delete a filesystem but that is not a nice option. Iam using the shell option because that is the only working option I found so far for this question.
What I did:
Edit the hosts file to put in some variables:
[all:vars]
COGNOS_HOME=/tmp/cognos
find=/bin/find
And create a playbook:
- hosts: all
tasks:
- name: Ansible remove files
shell: "{{ find }} {{ COGNOS_HOME }} -xdev -mindepth 1 -delete"
This will delete all files and directories in the COGNOS_HOME variable directory/filesystem. The "-mindepth 1" option makes sure that the current directory will not be touched.
I have written an custom ansible module to cleanup files based on multiple filters like age, timestamp, glob patterns, etc.
It is also compatible with ansible older versions. It can be found here.
Here is an example:
- cleanup_files:
path_pattern: /tmp/*.log
state: absent
excludes:
- foo*
- bar*
Just a small cleaner copy & paste template of ThorSummoners answer, if you are using Ansible >= 2.3 (distinction between files and dirs not necessary anymore.)
- name: Collect all fs items inside dir
find:
path: "{{ target_directory_path }}"
hidden: true
file_type: any
changed_when: false
register: collected_fsitems
- name: Remove all fs items inside dir
file:
path: "{{ item.path }}"
state: absent
with_items: "{{ collected_fsitems.files }}"
when: collected_fsitems.matched|int != 0
Isn't it that simple ... tested working ..
eg.
---
- hosts: localhost
vars:
cleandir: /var/lib/cloud/
tasks:
- shell: ls -a -I '.' -I '..' {{ cleandir }}
register: ls2del
ignore_errors: yes
- name: Cleanup {{ cleandir }}
file:
path: "{{ cleandir }}{{ item }}"
state: absent
with_items: "{{ ls2del.stdout_lines }}"
- name: Files to delete search
find:
paths: /home/mydata/web/
file_type: any
register: files_to_delete
- name: Deleting files to delete
file:
path: '{{ item.path }}'
state: absent
with_items: "{{ files_to_delete.files }}"
I like the following solution:
- name: remove web dir contents
command:
cmd: "find . -path '*/*' -delete -print"
chdir: "/home/mydata/web/"
register: web_files_list
changed_when: web_files_list.stdout | length > 0
because it is:
simple
idempotent
fast
Assuming you are always in Linux, try the find cmd.
- name: Clean everything inside {{ item }}
shell: test -d {{ item }} && find {{ item }} -path '{{ item }}/*' -prune -exec rm -rf {} \;
with_items: [/home/mydata/web]
This should wipe out files/folders/hidden under /home/mydata/web
- name: delete old data and clean cache
file:
path: "{{ item[0] }}"
state: "{{ item[1] }}"
with_nested:
- [ "/data/server/{{ app_name }}/webapps/", "/data/server/{{ app_name }}/work/" ]
- [ "absent", "directory" ]
ignore_errors: yes
Below worked for me,
- name: Ansible delete html directory
file:
path: /var/www/html
state: directory

Resources