Ansible: Generate a random string and share it between all hosts - random

I want a task that will generate a random string or timestamp and send that to all hosts in the play.
For example, if I do this:
- name: Create a unique ID
shell: random_string
register: unique_id
- name: store the unique ID
lineinfile:
dest: '/home/project/config.txt'
regexp: 'unique_id'
line: 'unique_id = "{{ unique_id }}'
This will generate the random string separately on each remote machine, so they won't match. I could generate it on the local machine using local_action, but it still would run separately for each host. So how can I ensure it will be the same for all hosts?

You could use run_once in combination with delegate_to or local_action to get it to generate your variable just once.
So your first task would just look like this:
- name: Create a unique ID
shell: random_string
run_once: true
delegate_to: 127.0.0.1
register: unique_id

Related

Copy json file from windows to linux in seperate awx jobs

I created a Worflow job in awx containing 2 jobs:
Job 1 is using the credentials of the windows server where we get the json file from. It reads the content and put it in a variable using set_stats
Job2 is using the credential of the server where to upload the json file. It reads the content of the variable set in the job 1 in the set_stats task and creates a json file with the content.
First job:
- name: get content
win_shell: 'type {{ file_dir }}{{ file_name }}'
register: content
- name: write content
debug:
msg: "{{ content.stdout_lines }} "
register: result
- set_fact:
this_local: "{{ content.stdout_lines }}"
- set_stats:
data:
test_stat: "{{ this_local }}"
- name: set hostname in a variable
set_stats:
data:
current_hostname: "{{ ansible_hostname }}"
per_host: no
Second job
- name: convert to json and copy the file to destination control node.
copy:
content: "{{ test_stat | to_json }}"
dest: "/tmp/{{ current_hostname }}.json"
How can I get the current_hostname, so that the the created json file is named <original_hostname>.json? In my case its concatenating the two hosts which I passed in the first job.
In my case its concatenating the two hosts which I passed in the first job
... which is precisely what you asked for since you used per_host: no as parameter to set_stats to gather the current_hostname stat globally for all host and that aggregate: yes is the default.
Anyhow, this is not exactly the intended use of set_stats and you are making this overly complicated IMO.
You don't need two jobs. In this particular case, you can delegate the write task to a linux host in the middle of a play dedicated to windows hosts (and one awx job can use several credentials).
Here is a pseudo untested playbook to give you the idea. You'll want to read the slurp module documentation which I used to replace your shell task to read the file (which is a bad practice).
Assuming your inventory looks something like:
---
windows_hosts:
hosts:
win1:
win2:
linux_hosts:
hosts:
json_file_target_server:
The playbook would look like:
- name: Gather jsons from win and write to linux target
hosts: windows_hosts
tasks:
- name: Get file content
slurp:
src: "{{ file_dir }}{{ file_name }}"
register: json_file
- name: Push json content to target linux
copy:
content: "{{ json_file.content | b64decode | to_json }}"
dest: "/tmp/{{ inventory_hostname }}.json"
delegate_to: json_file_target_server

How to create one common playbook-wide writeable variable in ansible?

I'm writing a playbook to create many user accounts across many servers. At the end I want to get output with credentials sorted by username.
I used set_fact with run_once but it seems that defined variable is not playbook-wide.
main.yml
- name: Create users
import_tasks: creation_task.yml
creation_task.yml
- name: Init variable for creds
set_fact:
creds: []
delegate_to: localhost
run_once: true
- name: Create specific users
include: create.yml
with_items:
- input_data
- .......
- name: Print output creds
debug: var=creds
run_once: true
create.yml
- name: some actions that actually create users
....
- name: add creds to list
set_fact:
creds: "{{ creds + [ {'hostname': inventory_hostname,'username':item.name,'password':password.stdout} ]}}"
- name: add splitter to list
set_fact:
creds: "{{ creds + [ '-----------------------------------------------------' ]}}"
This is actually working but i get output sorted by server because (as I think) every host reports his version of "creds" variable.
I'd like to create one variable that will be visible and writeable across all nested plays. So output would be sorted by input data but not hostname. Is it possible?
I'd use the following syntax, to fetch a variable set from a specific host via hostvars:
- debug:
msg: "{{ groups['my_host_name']|map('extract',hostvars,'my_variable_name')|list|first }}"
when: groups['my_host_name']|map('extract',hostvars,'my_variable_name')|list|first|length > 0
Then, you can loop over your hostnames to create an array of values and sort them.
Though, printing all servers hostnames, users & plain text passwords in a text file seems to be a security risk.

Use lineinfile to fill ansible inventory file

I have trivial task to fill particular ansible inventory file with the new deployed VMs names under their roles.
Here is my playbook:
- hosts: 127.0.0.1
tasks:
- name: Add host to inventory file
lineinfile:
dest: "{{inv_path}}"
regexp: '^\[{{role}}\]'
insertafter: '^#\[{{role}}\]'
line: "{{new_host}}"
Instead of adding new line after [role], ansible replace the string with the hostname. My aim is to naturally insert after it. If there is no matched role it should skip add new line.
How to achieve this? Thanks!
[role] is getting replaced because the regexp parameter is the regular expression Ansible will look for to replace with the line parameter. Just move that value to insertafter and get rid of regexp.
As for the other requirement you will need another task that will check if [role] exists first. Then your lineinfile task will evaluate the result of the checker task to determine if it should run.
- name: Check if role is in inventory file
shell: grep [{{ role }}] {{ inv_path }}
ignore_errors: True
register: check_inv
- name: Add host to inventory file
lineinfile:
dest: "{{ inv_path }}"
insertafter: '^\[{{role}}\]$'
line: "{{new_host}}"
when: check_inv.rc == 0

Registering Each Host Specific Value from Dictionary

We wanted to have a single playbook for all the deployments and the multiple hosts will be looped in. Ansible calls will be made from Jenkins pipeline by passing in the environments, for example dev6 and dev8
env1=dev6
env2=dev8
Pipeline Call:
ansible-playbook -i hosts --limit $env1:$env2 deploy_test.yml -e "env1={{$env1}} env2={{$env2}}"
I defined all the host specific variables (dev1,dev2......PERF8 etc.) in single file so it is easy to manage and maintain,
dev6:
- { deploy_domain: "Dev6Domain",
WL_Admin: "DEV6WLAdmin",
WL_Managed: "DEV6Managed" }
dev7:
- { deploy_domain: "Dev7Domain",
WL_Admin: "Dev7WLAdmin",
WL_Managed: "Dev7Managed" }
Playbook "Deploy_test.yml"
- hosts: all
vars_files:
- host_variables.yml
tasks:
- debug: msg='Target Domain is "{{ item[0].deploy_domain }}"'
with_nested:
- "{{ env1 }}"
- "{{ env2 }}"
The env1 and env2 values are being read from jenkins, no issues there
Problem-1: When the playbook runs on dev6 first, it takes dev8 values as well since it is defined under with_nested items.
Problem-2: How do I register the values specific to every environment?
for example, down the playbook when I say, mkdir /tmp/{{deploy_domain}, I need seperate values for dev6 and dev8.
Here is an example how you can read name-specific variable for every host:
hosts:
[dev6]
box1
[dev8]
box2
host_variables.yml:
dev6:
deploy_domain: "Dev6Domain"
WL_Admin: "DEV6WLAdmin"
WL_Managed: "DEV6Managed"
dev8:
deploy_domain: "Dev8Domain"
WL_Admin: "Dev8WLAdmin"
WL_Managed: "Dev8Managed"
I stripped out list level from original host_variables.yml, because it is not necessary in this case, there is always single element in the list.
deploy_test.yml:
- hosts: all
tasks:
- include_vars: host_variables.yml
- set_fact:
my_env: "{{ hostvars[inventory_hostname][group_names[0]] }}"
- debug: msg="My domain = {{ my_env.deploy_domain }}"
execution: ansible-playbook -i hosts --limit $env1:$env2 deploy_test.yml
This will execute deploy_test.yml for all hosts in groups set in env vars env1 and env2.
In the begining of playbook, we load everything from host_variables.yml as host facts.
And with set_fact extract variable named after current host's group name as my_env.
So box1 will have dev6 as my_env and box2 will have dev8.

Ansible: Get number of hosts in group

I'm trying to get the number of hosts of a certain group.
Imagine an inventory file like this:
[maingroup]
server-[01:05]
Now in my playbook I would like to get the number of hosts that are part of maingroup which would be 5 in this case and store that in a variable which is supposed to be used in a template in one of the playbook's tasks.
At the moment I'm setting the variable manually which is far from ideal..
vars:
HOST_COUNT: 5
vars:
HOST_COUNT: "{{ groups['maingroup'] | length }}"
Also without explicit group name:
vars:
HOST_COUNT: "{{ ansible_play_hosts | length }}"

Resources