How to copy files to a destination with variable in ansible? - ansible

I have a destination for file copy as a variable file_path, how can I copy files to that destination using template/copy modules?
Tried:
---
- hosts: localhost
connection: local
tasks:
- name: variable
command: echo "emptyDir"
register: file_path
- name: check var value
debug:
var: file_path
verbosity: 2
- name: copy mod
template:
src: "{{ item }}"
dest: "{{ file_path }}"
mode: 0755
with_items:
- "file1.txt"
- "file2.txt"
Error:
If copy module is used
copyModule error
If template module is used
templateModule error
But throws some dict - string error.
Any help is appreciated.
TIA

Check the issue could be with stdout, need to use file_path.stdout. Else it will be a dictionary.

Related

Create Local File With Ansible Template From Variables

I'm running an ansible playbook against a number of ec2 instances to check if a directory exists.
---
- hosts: all
become: true
tasks:
- name: Check if foo is installed
stat:
path:
/etc/foo
register: path
- debug: msg="{{path.stat.exists}}"
And I would like to generate a localfile that lists the private IP addresses of the ec2 instances and states whether or not the directory foo does exist.
I can get the private IP addresses of the instances with this task
- name: Get info from remote
shell: curl http://169.254.169.254/latest/meta-data/local-ipv4
register: bar
- debug: msg="{{bar.stdout}}"
How do I create a local file with content
IP address: 10.100.0.151 directory foo - false
IP address: 10.100.0.152 directory foo - true
I've tried adding a block for this as such
- hosts: localhost
become: false
vars:
installed: "{{bar.stdout}}"
status: "{{path.stat.exists}}"
local_file: "./Report.txt"
tasks:
- name: Create local file with info
copy:
dest: "{{ local_file }}"
content: |
"IP address {{ installed }} foo - {{ status }}"
But it doesn't look like I can read values of variables from earlier steps.
What am I doing wrong please?
A similar question has been answered here.
Basically what you want is to reference it through the host var variable.
This should work.
- hosts: localhost
become: false
vars:
local_file: "./Report.txt"
tasks:
- name: Create local file with info
lineinfile:
path: "{{ local_file }}"
line:
"IP Address: {{ hostvars[item]['bar'].stdout }} - Installed: {{ hostvars[item]['path'].stat.exists }}"
with_items: "{{ query('inventory_hostnames', 'all') }}"
And this should populate your local ./Report.txt file, with the info you need.
I've used the ec2_metadata_facts module to get the IP address us ingansible_ec2_local_ipv4
I've also created the directory /tmp/testdir on the second host.
- hosts: test_hosts
gather_facts: no
vars:
directory_name: /tmp/testdir
tasks:
- ec2_metadata_facts:
- name: check if directory '{{ directory_name }}' exsists
stat:
path: "{{ directory_name }}"
register: path
# I make the outputfile empty
# because the module lineinfile(as I know) can't overwrite a file
# but appends line to the old content
- name: create empty output file
copy:
content: ""
dest: outputfile
delegate_to: localhost
- name: write output to outputfile
lineinfile:
dest: outputfile
line: "IP Address: {{ ansible_ec2_local_ipv4 }} {{ directory_name }} - {{ path.stat.exists }}"
state: present
with_items: "{{ groups.all }}"
# with_items: "{{ ansible_play_hosts }}" can also be used here
delegate_to: localhost
The outputfile looks like:
IP Address: xxx.xx.x.133 /tmp/testdir - False
IP Address: xxx.xx.x.45 /tmp/testdir - True

How to create directory and file inside the same directory in ansible using single task

Is there any way to create directory and file inside the same directory in ansible using a single task? Currently, in my task, I'm creating a directory using a file module, state= directory. How to touch a file inside that directory in the same task?
You could use a loop:
- name: Create a directory with a file inside
file:
path: "{{ item.path }}"
state: "{{ item.state }}"
loop:
- { path: /tmp/foo, state: directory }
- { path: /tmp/foo/bar.txt, state: touch }
#Dom H has given it correctly i just wanted it to give it more convenient way in playbook.
the playbook will look like :
---
- name: Creating directory and files
hosts: localhost
become: yes
become_user: root
tasks:
- name: Create a directory with a file inside
file:
path: "{{ item.path }}"
state: "{{ item.state }}"
loop:
- { path: /tmp/foo, state: directory }
- { path: /tmp/foo/bar.txt, state: touch }
and we can actually test before run it using:
ansible-playbook -i localhost mkdir.yml --check

Ansible find module giving error "does not seem to be a valid directory or it cannot be accessed" absolute path

Ansible find module isn't working as expected.
So i have three instances
One is test node , second controller node and third is from where i am running my ansible playbook
I am trying to generate ssh-keys on test_nodes and then fetching the public keys from those nodes. This is working fine.
Then I am trying to appending these public keys in the authorized_keys file of a different host(controller_node). For this, I am using the find module to get list of files and then loop over these files in authorized_key module.
I was using :
- name: Set authorized key file taken from file
authorized_key:
user: absrivastava
key: "{{ lookup('file','item') }}"
state: present
#with_file:
- "/home/absrivastava/ANSIBLE/ssh-keys/*/home/ribbon/.ssh/id_rsa.pub" This didnt work
#with_filetree:
- "/home/absrivastava/ANSIBLE/ssh-keys/*/home/ribbon/.ssh/id_rsa.pub" This was not appending data
But it didnt seem to work. So i am using find to get list of files and then iterate over them.
- name: Generate ssh keys
hosts: media_nodes
gather_facts: false
tasks:
- name: key generation
openssh_keypair:
path: ~/.ssh/id_ssh_rsa
force: True
register: public_key
- debug:
var: public_key.public_key
- name: fetch public key from all nodes
fetch:
src: ~/.ssh/id_ssh_rsa.pub
dest: ssh-keys/
- name: Controller play
hosts: controller
gather_facts: false
tasks:
- name: Find list of public key files
find:
paths: /home/abhilasha/ANSIBLE/ssh-keys/
file_type: file
recurse: yes
patterns: ".*pub"
use_regex: yes
register: files_matched
- name: debug files matched
debug:
var: files_matched.files
- name: Debug files_matched loop
debug:
var: item.path
loop: "{{ files_matched.files|flatten(levels=1) }}"
loop_control:
label: "{{ item.path }}"
- name: Set authorized key file taken from file
authorized_key:
key: "{{ lookup('file','item') }}"
state: present
with_file:
- "{{ files_matched.files }}"
- name: Find list of public key files
This play is not working giving error
TASK [Find list of public keys] *****************************************************************************************************************************************************************************************************************
ok: [test_controller] => {"changed": false, "examined": 0, "files": [], "matched": 0, "msg": "/home/abhilasha/ANSIBLE/ssh-keys/ was skipped as it does not seem to be a valid directory or it cannot be accessed\n"}
Okay so i got the issue , i was using hosts: controller for this play but the files are on my test VM instance .
But I am not sure how to still solve my problem. I want to use publoc keys on my local and then append it to controller server
- name: Fetch public key files from localhost
gather_facts: false
hosts: 127.0.0.1
connection: local
tasks:
- name: Find list of public keys
find:
paths: ssh-keys/
file_type: file
recurse: yes
patterns: "pub"
use_regex: yes
hidden: yes
register: files_matched
- name: Debug files_matched loop
debug:
var: item.path
loop: "{{ files_matched.files|flatten(levels=1) }}"
loop_control:
label: "{{ item.path }}"
- name: Add Public keys to controller authorized keys
hosts: controller
gather_facts: false
tasks:
- name: Set authorized key file taken from file
authorized_key:
key: "{{ lookup('file','item') }}"
state: present
with_file:
- "{{ files_matched.files }}"
I am unable to use files_matched variable outside the scope of that play. How can i make this work. Thanks in advance
Q: "msg": "/home/abhilasha/ANSIBLE/ssh-keys/ was skipped as it does not seem to be a valid directory or it cannot be accessed\n"
A: Take a look at the directory ssh-keys/ at controller and check the content. Instead of
paths: /home/abhilasha/ANSIBLE/ssh-keys/
find it in the same path
path: ssh-keys/
it has been fetch to
dest: ssh-keys/
Can you change the paths as below and try
- name: fetch public key from all nodes
fetch:
src: ~/.ssh/id_ssh_rsa.pub
dest: /tmp/ssh-keys/
- name: Find list of public key files
find:
paths: /tmp/ssh-keys/
file_type: file
recurse: yes
patterns: ".*pub"
use_regex: yes
register: files_matched
If you are trying to copy/find files from your local machine to the remote, by default the find module will run on the remote, not your local machine. As a result, the error will be thrown if those directories don't exist on your remote.
So you just tell it to "find" on your local machine by specifying delegate_to: localhost and it should work.
tasks:
- find:
paths:
- local_dir1
- local_dir2
file_type: file
patterns: '*.tgz'
register: files_output
# Execute task on this host instead of the target (inventory_hostname).
delegate_to: localhost
- block:
- set_fact:
files: "{{ files_output.files | map(attribute='path') }}"
- set_fact:
files: '{{ files + more }}'
vars:
more:
- '{{playbook_dir}}/remote.sh'
- debug:
msg: '{{ item }}'
loop: '{{ files }}'

Use registered variables in other roles

Hello guys I have a Problem.
The Problem I am having at the moment, is that the role to copy the files will skip all the files no matter if the file with the filenames is empty or not.
In Role1 I want to save the output of cat for each file. In Role2 in the when conditional, I want the task to skip if the registered output is == "".
Role1:
---
- name: copy files
shell: "cat path{{ item }}files"
register: checkempty
loop:
- test1
- test2
- test3
- test4
Role2:
---
- name: Copy Files
copy:
src: "{{ var1 }}{{ var2 }}{{ var3 }}{{ var4 }}{{ item }}/"
dest: "{{ copy_dest_sys }}" #destination path
loop: "{{ lookup('file', 'pathtofile/file').split('\n')}}"
when: hostvars['localhost'].checkempty.results == ""
Playbook:
- name: check emptiness
hosts: localhost
become: yes
vars_files:
- ../variables/varsfile
roles:
- ../variables/role1
- name: Copy Files to prod/stag
hosts: "{{hosts_exec}}"
become: yes
vars_files:
- ../vars/recommendation-delta.yml
roles:
- ../roles/role2
How can I set a registered variable with with_items and compare the output of it to ""(nothing)?
Can somebody help me with this issue?
When you register a variable, it is set only on the specific host on which that task was executing. So if you are running a role on localhost that does this:
---
- name: Check if sys files Empty
command: if [ ! -s filenames/"{{ item }}"files ]; then echo "{{ item }}fileempty"; fi
register: checkempty
loop:
- sys
- wifi
- recoprop
- udfprop
Then you would reference it like this when running tasks on another host:
hostvars["localhost"].checkempty
For example:
---
- name: Copy sys Files to prod/stag
copy:
src: "{{ git_dest }}{{ git_sys_files }}{{ item }}/"
dest: "{{ copy_dest_sys }}" #destination path
loop: "{{ lookup('file', '/home/ansible/repo/hal_ansible/scripts/delta-reco/filenames/sysfiles').split('\n')}}"
when: 'hostvars["localhost"].checkempty.stdout == "sysfileempty"'
You can read more about this in the "Using Variables" documentation.
I've made some corrections to your when syntax here as well. In general, you should never use {{...}} markers in a when condition because a when condition is always evaluated as a Jinja expression.
However, you have another problem:
Your "Check if sys files Empty" task is using the command module, but you're trying to run a shell script. That will always fail. You need to use the shell module instead:
---
- name: Check if sys files Empty
shell: if [ ! -s filenames/"{{ item }}"files ]; then echo "{{ item }}fileempty"; fi
register: checkempty
loop:
- sys
- wifi
- recoprop
- udfprop

Ansible access same variables from multiple Json files

I have multiple .json files on local host where I place my playbook:
json-file-path/{{ testName }}.json
{{ testName }}.json are: testA.json, testB.json, testC.json ... etc.
All .json files have same keys with different values like this:
json-file-path/testA.json:
{
“a_key”: “a_value1”
“b_key”: “b_value1”
}
json-file-path/testB.json:
{
“a_key”: “a_value2”
“b_key”: “b_value2”
}
json-file-path/testC.json:
{
“a_key”: “a_value3”
“b_key”: “b_value3”
}
.....
I need to access the key-value variables from all .json files and if the values meet some condition, I will perform some task in target host. For example, I have:
a_value1=3
a_value2=4
a_value3=1
I go through my .json file one by one, if a_key[value]>3, I will copy this .json file to target host, otherwise skip the task. In this case, I will only copy testC.json to target host.
How would I achieve this? I was thinking of re-constructing my .json files using {{ testName }} as dynamic key of dict like this:
{
“testName”: “testA”
{
“a_key”: “a_value1”
“b_key”: “b_value1”
}
So I can access my variable as {{ testName}}.a_key. So far I haven’t been able to achieve this.
I have tried the following in my playbook:
—-
- host: localhost
tasks:
- name: construct json files
vars:
my_vars:
a_key: “{{ a_value }}”
b_key: “{{ b_value }}”
with_dict: “{{ testName }}”
copy:
content: “{{ my_vars | to_nice_json }}”
dest: /json-file-path/{{ testName }}.json
My updated playbook are:
/mypath/tmp/include.yaml:
—-
- hosts: remote_hostName
tasks:
- name: load json files
set_fact:
json_data: “{{ lookup(‘file’, item) | from_json }}”
- name: copy json file if condition meets
copy:
src: “{{ item }}”
dest: “{{ /remote_host_path/tmp}}/{{item | basename }}”
delegate_to: “{{ remote_hostName }}”
when: json_data.a_key|int>5
/mypath/test.yml:
—-
- hosts: localhost
vars:
local_src_ dir: /mypath/tmp
remote_host: remote_hostName
remote_dest_dir: /remote_host_path/tmp
tasks:
- name: looping
include: include.yaml
with_fileglob:
- “{{ local_src_dir }}/*json”
All json files on localhost under /mypath/tmp/.
Latest version of playbook. It is working now:
/mypath/tmp/include.yaml:
—-
- name: loafing json flies
include_vars:
file: “{{ item }}”
name: json_data
- name: copy json file to remote if condition meets
copy:
src: “{{ item }}”
dest: ‘/remote_host_path/tmp/{{item | basename}}’
delegate_to: “{{ remote_host }}”
when: json_data.a_key > 5
/mypath/test.yml:
—-
- hosts: localhost
vars:
local_src_dir: /mypath/tmp
remote_host: remote_hostName
remote_dest_dir: /remote_host_path/tmp
tasks:
- name: looping json files
include: include.yaml
with_fileglob:
- “{{ local_src_dir }}”/*json”
I am hoping that I have understood your requirements correctly, and that this helps move you forward.
Fundamentally, you can load each of the JSON files so you can query the values as native Ansible variables. Therefore you can loop through all the files, read each one, compare the value you are interested in and then conditionally copy to your remote host via a delegated task. Therefore, give this a try:
Create an include file include.yaml:
---
# 'item' contains a path to a local JSON file on each pass of the loop
- name: Load the json file
set_fact:
json_data: "{{ lookup('file', item) | from_json }}"
- name: Delegate a copy task to the remote host conditionally
copy:
src: "{{ item }}"
dest: "{{ remote_dest_dir }}/{{ item | basename }}"
delegate_to: "{{ remote_host }}"
when: json_data.a_key > value_threshold
then in your playbook:
---
- hosts: localhost
connection: local
# Set some example vars, tho these could be placed in a variety of places
vars:
local_src_dir: /some/local/path
remote_host: <some_inventory_hostname>
remote_dest_dir: /some/remote/path
value_threshold: 3
tasks:
- name: Loop through all *json files, passing matches to include.yaml
include: include.yaml
loop: "{{ lookup('fileglob', local_src_dir + '/*json').split(',') }}"
Note: As you are running an old version of Ansible, you may need older alternate syntax for all of this to work:
In your include file:
- name: Load the json file
set_fact:
include_vars: "{{ item }}"
- name: Delegate a copy task to the remote host conditionally
copy:
src: "{{ item }}"
dest: "{{ remote_dest_dir }}/{{ item | basename }}"
delegate_to: "{{ remote_host }}"
when: a_key > value_threshold
and in your playbook:
- name: Loop through all *json files, passing matches to include.yaml
include: include.yaml
with_fileglob:
- "{{ local_src_dir }}/*json"

Resources