How to pass today's date and time as argument in ansible - ansible

I am running python script using ansible.
Here is my playbook -
- name: Run python script
command: python Test.py -StartDate 2020-10-01T00:00:00 -EndDate 2020-11-05T00:00:00
register: result
- debug: msg="{{result.stdout}}"
I want this playbook to use EndDate as todays date when I run script. How can I use latest date and time in same format I have written every time I run script without having to change manually every day?

Q: "How can I use the latest date and time ...?"
A: Use Ansible facts variable ansible_date_time. For example, given the script below
shell> cat test.sh
#!/bin/sh
echo $1 $2
The playbook
shell> cat playbook.yml
- hosts: localhost
tasks:
- name: Run script
command: "{{ playbook_dir }}/test.sh
2020-10-01T00:00:00
{{ ansible_date_time.date }}T00:00:00"
register: result
- debug:
var: result.stdout
gives (abridged)
shell> ansible-playbook playbook.yml
result.stdout: 2020-10-01T00:00:00 2020-11-07T00:00:00
Notes
"gather_facts: true" (default) is needed to collect Ansible facts.
The values of date and time will be collected on a remote host when a playbook starts.
If needed there are other attributes in the dictionary
ansible_date_time:
date: '2020-11-07'
day: '07'
epoch: '1604779525'
hour: '21'
iso8601: '2020-11-07T20:05:25Z'
iso8601_basic: 20201107T210525040700
iso8601_basic_short: 20201107T210525
iso8601_micro: '2020-11-07T20:05:25.040817Z'
minute: '05'
month: '11'
second: '25'
time: '21:05:25'
tz: CET
tz_offset: '+0100'
weekday: Saturday
weekday_number: '6'
weeknumber: '44'
year: '2020'
The variable ansible_date_time will not be updated automatically when a playbook runs. For example
- debug:
var: ansible_date_time.iso8601_micro
- debug:
var: ansible_date_time.iso8601_micro
- debug:
var: ansible_date_time.iso8601_micro
give (abridged)
ansible_date_time.iso8601_micro: '2020-11-07T20:16:09.481237Z'
ansible_date_time.iso8601_micro: '2020-11-07T20:16:09.481237Z'
ansible_date_time.iso8601_micro: '2020-11-07T20:16:09.481237Z'
Run module setup to update Ansible facts including the variable ansible_date_time. For example
- debug:
var: ansible_date_time.iso8601_micro
- setup:
- debug:
var: ansible_date_time.iso8601_micro
- setup:
- debug:
var: ansible_date_time.iso8601_micro
give (abridged)
ansible_date_time.iso8601_micro: '2020-11-07T20:16:09.481237Z'
ansible_date_time.iso8601_micro: '2020-11-07T20:16:10.759533Z'
ansible_date_time.iso8601_micro: '2020-11-07T20:16:11.475873Z'
Frequently running setup to update the date and time only is overkill. In this case, consider running command and register result instead.

assuming the T00:00:00 is always fixed, you could declare a variable using the lookup plugin, see an example below the exec_date variable and the modified command task:
---
- hosts: localhost
gather_facts: false
vars:
exec_date: "{{ lookup('pipe', 'date +%Y-%m-%d') }}T00:00:00"
tasks:
- name: print
debug: var=exec_date
- name: Run python script
command: "python Test.py -StartDate 2020-10-01T00:00:00 -EndDate {{ exec_date }}"
register: result
- debug: msg="{{result.stdout}}"
If you want to pass the current time too instead of a fixed T00:00:00, you could use the below:
vars:
exec_date: "{{ lookup('pipe', 'date +%Y-%m-%dT%H:%M:%S') }}"
cheers

Related

Unable to correctly use a variable in ansible

I have the following scenario:
The inventory file is laid out as below:
[my_host]
host
server
[host:children]
hostm
hosts
[hostm]
host01
[hosts]
host02
host03
The group_vars file(s) is as below:
host.yml
user: user01
ansible_python_interpreter: /usr/bin/python3
The host_vars file(s) is as below:
host01.yml
id: ABC
nr: 00
host02.yml
id: DEF
nr: 20
host03.yml
id: GHI
nr: 02
Now using the above, I'm trying to run a playbook as described below:
custom.yml
- hosts: "{{ v_host | default([]) }}"
remote_user: root
tasks:
- name: Run the shell script
become: true
become_user: "{{ user }}"
become_method: su
become_exe: "su -"
ansible.builtin.shell: cleanipc {{ item.nr }} remove
with_items:
- "{{ v_host }}"
register: shell_result
no_log: false
changed_when: false
- name: print message
ansible.builtin.debug:
var: shell_result.stdout_lines
To run the playbook, I use the below command:
>ansible-playbook -i /path-to-inventory-file/file custom.yml -e 'v_host=host'
I'm trying to get the playbook to run the shell command on all child nodes of 'host', i.e., 'host01', 'host02' and 'host03', with the value of the variable 'nr' automatically substituted for each host.
I tried changing the lookup of the variable using hostvars as below:
ansible.builtin.shell: cleanipc {{ hostvars[item]['nr'] }} remove
But this didn't work either. Thank you for any help or guidance you can provide.
Thank you!

Creating ansible playbook for cronjob with random minute and hour

I have the following Ansible playbook to create a cronjob on red hat Linux servers, this works well, but the problem is that it runs every time irrespective of whether the same cronjob is already present or not. I know it runs because it generates new hour and minute values every time. Is there any way to make it not run when this job is already present in crontab?
- hosts: all
tasks:
- name: Generate randon minute
set_fact:
random_minute: "{{ 59 | random }}"
run_once: yes
- debug: var=random_minute
- name: Generate random hour
set_fact:
random_hour: "{{ 23 | random }}"
run_once: yes
- debug: var=random_hour
- name: "Cronjob Entry"
cron:
name: "### oscompliance cronjob"
minute: "{{ random_minute }}"
hour: "{{ random_hour }}"
day: "*"
month: "*"
weekday: "*"
job: /users/user1/scripts/os_check >> /tmp/os_checkoutput 2>&1

Ansible how to set_fact with condition or var base on condition

I have 3 types of servers: dev, qa and prod. I need to send files to server specific home directories, i.e:
Dev Home Directory: /dev/home
QA Home Directory:/qa/home
PROD Home Directory: /prod/home
I have var set as boolean to determine the server type and think of using set_fact with condition to assign home directories for the servers. My playbook looks like this:
---
- hosts: localhost
var:
dev: "{{ True if <hostname matches dev> | else False }}"
qa: "{{ True if <hostname matches qa> | else False }}"
prod: "{{ True if <hostname matches prod> | else False }}"
tasks:
- set_facts:
home_dir: "{{'/dev/home/' if dev | '/qa/home' if qa | default('prod')}}"
However, then I ran the playbook, I was getting error about 'template expected token 'name', got string> Anyone know what I did wrong? Thanks!
Use match test. For example, the playbook
shell> cat pb.yml
- hosts: localhost
vars:
dev_pattern: '^dev_.+$'
qa_pattern: '^qa_.+$'
prod_pattern: '^prod_.+$'
dev: "{{ hostname is match(dev_pattern) }}"
qa: "{{ hostname is match(qa_pattern) }}"
prod: "{{ hostname is match(prod_pattern) }}"
tasks:
- set_fact:
home_dir: /prod/home
- set_fact:
home_dir: /dev/home
when: dev|bool
- set_fact:
home_dir: /qa/home
when: qa|bool
- debug:
var: home_dir
gives (abridged)
shell> ansible-playbook pb.yml -e hostname=dev_007
home_dir: /dev/home
Notes:
The variable prod is not used because /prod/home is default.
prod/home is assigned to home_dir first because it's the default. Next tasks can conditionally overwrite home_dirs.
Without the variable hostname defined the playbook will crash.
A simpler solution that gives the same result is creating a dictionary of 'pattern: home_dir'. For example
- hosts: localhost
vars:
home_dirs:
dev_: /dev/home
qa_: /qa/home
prod_: /prod/home
tasks:
- set_fact:
home_dir: "{{ home_dirs|
dict2items|
selectattr('key', 'in' , hostname)|
map(attribute='value')|
list|
first|default('/prod/home') }}"
- debug:
var: home_dir
Adding an alternative method to achieve this. This kind of extends the group_vars suggestion given by #mdaniel in his comment.
Ansible has a ready-made mechanism to build the variables based on the inventory hosts and groups. If you organize your inventory, you can avoid a lot of complication in trying to match host patterns.
Below is a simplified example, please go through the link above for more options.
Consider an inventory file /home/user/ansible/hosts:
[dev]
srv01.dev.example
srv02.dev.example
[qa]
srv01.qa.example
srv02.qa.example
[prod]
srv01.prod.example
srv02.prod.example
Using group_vars:
Then you can have below group_var files in /home/user/ansible/group_vars/ (matching inventory group names):
dev.yml
qa.yml
prod.yml
In dev.yml:
home_dir: "/dev/home"
In qa.yml:
home_dir: "/qa/home"
In prod.yml:
home_dir: "/prod/home"
Using host_vars:
Or you can have variables specific to hosts in host_vars directory /home/user/ansible/host_vars/:
srv01.dev.example.yml
srv01.prod.example.yml
# and so on
In srv01.dev.example.yml:
home_dir: "/dev/home"
In srv01.prod.example.yml:
home_dir: "/prod/home"
These variables will be picked based on which hosts you run the playbook, for example the below playbook:
---
- hosts: dev
tasks:
- debug:
var: home_dir
# will be "/dev/home"
- hosts: prod
tasks:
- debug:
var: home_dir
# will be "/prod/home"
- hosts: srv01.dev.example
tasks:
- debug:
var: home_dir
# will be "/dev/home"
- hosts: srv01.prod.example
tasks:
- debug:
var: home_dir
# will be "/prod/home"

Return Variable from Included Ansible Playbook

I have seen how to register variables within tasks in an ansible playbook and then use those variables elsewhere in the same playbook, but can you register a variable in an included playbook and then access those variables back in the original playbook?
Here is what I am trying to accomplish:
This is my main playbook:
- include: sub-playbook.yml job_url="http://some-jenkins-job"
- hosts: localhost
roles:
- some_role
sub-playbook.yml:
---
- hosts: localhost
tasks:
- name: Collect info from Jenkins Job
script: whatever.py --url "{{ job_url }}"
register: jenkins_artifacts
I'd like to be able to access the jenkins_artifacts results back in main_playbook if possible. I know you can access it from other hosts in the same playbook like this: "{{ hostvars['localhost']['jenkins_artifacts'].stdout_lines }}"
Is it the same idea for sharing across playbooks?
I'm confused what this question is about. Just use the variable name jenkins_artifacts:
- include: sub-playbook.yml job_url="http://some-jenkins-job"
- hosts: localhost
debug:
var: jenkins_artifacts
This might seem complicated but I love doing this in my Playbooks:
rc defines the name of the variable which contains the return value
ar gives the arguments to the include tasks
master.yml:
- name: verify_os
include_tasks: "verify_os/main.yml"
vars:
verify_os:
rc: "isos_present"
ar:
image: "{{ os.ar.to_os }}"
verify_os/main.yml:
---
- name: check image on device
ios_command:
commands:
- "sh bootflash: | inc {{ verify_os.ar.image }}"
register: image_check
- name: check if available
shell: "printf '{{ image_check.stdout_lines[0][0] }}\n' | grep {{ verify_os.ar.image }} | wc -l"
register: image_available
delegate_to: localhost
- set_fact: { "{{ verify_os.rc }}": "{{ true if image_available.stdout == '1' else false }}" }
...
I can now use the isos_present variable anywhere in the master.yml to access the returned value.

Ansible set_fact across plays

I have to run an ansible playbook to execute the following tasks
1) Calculate date in YYYY_MM_DD format and then use this prefix to download some file from aws to my local machine. The filename is of the following format 2015_06_04_latest_file.csv
2) I have to then create a folder by the name 2015_06_04 into multiple hosts and upload this file there.
This is my current playbook -
---
- hosts: 127.0.0.1
connection: local
sudo: yes
gather_facts: no
tasks:
- name: calculate date
shell: date "+%Y_%m_%d" --date="1 days ago"
register: output
- name: set date variable
set_fact: latest_date={{ item }}
with_items: output.stdout_lines
- local_action: command mkdir -p /tmp/latest_contracts/{{ latest_date }}
- local_action: command /root/bin/aws s3 cp s3://primarydatafolder/data/{{ latest_date }}_latest_data.csv /tmp/latest_contracts/{{ latest_date }}/ creates=/tmp/latest_contracts/{{ latest_date }}/latest_data.csv
register: result
ignore_errors: true
- local_action: command /root/bin/aws s3 cp s3://secondarydatafolder/data/{{ latest_date }}_latest_data.csv /tmp/latest_contracts/{{ latest_date }}/ creates=/tmp/latest_contracts/{{ latest_date }}/latest_data.csv
when: result|failed
# remove the date prefix from the downloaded file
- local_action: command ./rename_date.sh {{ latest_date }}
ignore_errors: true
- hosts: contractsServers
sudo: yes
gather_facts: no
tasks:
- name: create directory
file: path={{item.path}} state=directory mode=0775 owner=root group=root
with_items:
- {path: '/var/mukul/contracts/{{ latest_date }}' }
- {path: '/var/mukul/contracts/dummy' }
- name: copy dummy contracts
copy: src=dummy dest=/var/mukul/contracts/
- name: delete previous symlink
shell: unlink /var/mukul/contracts/latest
ignore_errors: true
- name: upload the newly created latest date folder to the host
copy: src=/tmp/latest_contracts/{{ latest_date }} dest=/var/mukul/contracts/
- name: create a symbolic link to the folder on the host and call it latest
action: file state=link src=/var/mukul/contracts/{{ latest_date }} dest=/var/mukul/contracts/latest
As per ansible's documentation on set_fact variable, this variable latest_date should be available across plays. However, ansible fails with the following message
failed: [192.168.101.177] => (item={'path': u'/var/mukul/contracts/{# latest_date #}'}) => {"failed": true, "item": {"path": "/var/mukul/contracts/{# latest_date #}"}}
msg: this module requires key=value arguments (['path=/var/mukul/contracts/{#', 'latest_date', '#}', 'state=directory', 'mode=0775', 'owner=root', 'group=root'])
It looks as if the second playbook is unable to get the value of the latest_date fact. Can you please tell me where i'm making a mistake?
Facts are host specific. As the documentation about set_fact says, "[v]ariables [set with set_fact] are set on a host-by-host basis".
Instead, I'd try using run_once as defined in Delegation, rolling updates, and local actions, like this:
- hosts: contractsServers
tasks:
- name: Determine date
local_action: shell: date "+%Y_%m_%d" --date="1 days ago"
register: yesterday
always_run: True
changed_when: False
run_once: True
- name: Do something else locally
local_action: ...
register: some_variable_name
always_run: True
changed_when: False
run_once: True
- name: Do something remotely using the variables registered above
...
You could enable fact-caching. You will need to set up a local redis instance where facts then will be stored.

Resources