How to override some specific variables values in Ansible playbook - ansible

I have some multiple war files which I am downloading from nexus and creating the respective app folder on my remote host and copying the files.
Now there are 2 files which have a different name as war file but need to create a directory with different name. What I am doing right now is this -
- name: Ensuring the web deployment folder is created on tomcat nodes for each web application
file:
path: "{{tomcat_server_release_location}}/Release{{my_release_version}}/{{item}}"
state: directory
mode: 0777
with_items:
- "{{ apps }}"
- name: Copying the release from Admin server to tomcat nodes
copy: src={{admin_server_release_location}}/{{my_release_version}}/{{item}}-{{my_release_version}}.war dest={{tomcat_server_release_location}}/Release{{my_release_version}}/{{item}}
with_items:
- "{{ apps }}"
apps variable is defined like this -
webapps: ['test1','test2','test3','test4'].
Now test2 has a different name on the nexus it says test2-web.war but on the remote node I have to create a folder called just test2 and copy the war file in there.
Is there a way to override some variables at run time of playbook by using some condition or anything

If you want to override at runtime use extra vars.
From the docs --extra-vars "version=1.23.45"
Don't use conditions if you know a specific host should always be overwritten. I think placing it as a host var is best.
hosts:
myhost:
apps:
- test1
- trst2
See https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable for all of the various locations you can put variables and how to determine which will be used

It is possible to override variables by doing something like this:
- name: copy war
copy:
src: "{{ src_war | default('/path/to/my/war') }}"
dest: "{{ dest_war | default(src_war) }}"
Then at runtime you can set the variables src_war and dest_war either with extra vars, host/group_vars or any other way documented here: https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html
In your case I think it would be helpful to set a src & dest mapping:
- set_fact:
apps:
- {src: "test1"}
- {src: "test2", dest: "test2-web"}
- {src: "test3"}
- {src: "test4"}
Then you can use this mapping to achieve your goal:
- name: Ensuring the web deployment...
file:
path: {{ tomcat_server_release_location }}/Release{{ my_release_version }}/{{ item.src }}
state: directory
mode: 0777
with_items: "{{ apps }}"
- name: Copying the release...
copy:
src: "{{ admin_server_release_location }}/{{ my_release_version }}/{{ item.src }}-{{ my_release_version }}.war"
dest: "{{ tomcat_server_release_location }}/Release{{ my_release_version }}/{{ item.dest | default(item.src) }}"
with_items: "{{ apps }}"

Related

Ansible: how to achieve idempotence with tasks that append files on host (w/o reverting to initial state)

I am having a hard time getting to know how to create Ansible roles that are following the best practices according to documentation. The following use-case which I am looking at is e.g. enabling Filebeat on host. Filebeat can be configured by placing a module definition in /etc/filebeat/modules.d folder.
It works fine when I am adding modules. Idempotence is working, everytime, on each run of the role (playbook), a given set of modules is enabled.
But what I should do when I decide that a given module is not longer needed? I remove it from role, rerun a playbook, so that all other modules are enabled. But: the previous run enabled a module that I am not installing directly with role after changes. So my server state is still altered in a way that is different than the role is imposing itself.
My question is: should I take care of removing modules before I apply them so I always start from, let's say, fresh state?
E.g.:
- name: Remove modules
file:
dest: "/etc/filebeat/modules.d/{{ item }}"
state: absent
loop:
- "module1.yml"
- "module2.yml"
- "module3.yml" # It was being installed in previous role, but not now
- name: Enable modules via 'modules.d' directory
template:
src: "modules.d/{{ item }}"
dest: "/etc/filebeat/modules.d/{{ item }}"
mode: '0644'
loop:
- "module1.yml"
- "module2.yml"
So I remove module3.yml, because I remember that I've installed it before, and install module1.yml and module2.yml.
Instead of just installing what I need, no matter what has been installed before:
- name: Enable modules via 'modules.d' directory
template:
src: "modules.d/{{ item }}"
dest: "/etc/filebeat/modules.d/{{ item }}"
mode: '0644'
loop:
- "module1.yml"
- "module2.yml"
Leaving me with module1.yml and module2.yml (desired) and, unfortunately: module3.yml (from previous role).
How to manage that to avoid such situations? And avoid treating server as one big stateful machine that even if I run a role, the output is different than desired, because something has been done before that I cannot see in current Ansible role code.
Do you code revert playbooks in your Ansible workflow to revert to initial state when needed?
I am curious. Thanks in advance for your reply.
In a nutshell:
- name: Configure filebeat modules
hosts: all
vars:
fb_modules_d:
- file: module1.yml
state: present
- file: module2.yml
state: present
- file: module3.yml
state: absent
tasks:
- name: Make sure all needed module files are present
template:
src: "modules.d/{{ item.file }}"
dest: "/etc/filebeat/modules.d/{{ item.file }}"
mode: '0644'
loop: "{{ fb_modules_d | selectattr('state', '==', 'present') }}"
notifiy: restart_filebeat
- name: Make sure all disabled modules are removed
file:
dest: "/etc/filebeat/modules.d/{{ item.file }}"
state: "{{ item.state }}"
loop: loop: "{{ fb_modules_d | selectattr('state', '==', 'absent') }}"
notify: restart_filebeat
handlers:
- name: Restart filebeat service
listen: restart_filebeat
systemd:
name: filebeat
state: restarted
Note: I declared the variable inside the playbook for the example but that one one should most probably go inside your inventory (group or host level), and certainly not in a role (except in defaults for documentation)
A simpler and more robust solution is to remove all files not listed. This would be especially useful when you don't know what is the current state of the directory. For example, given the directory (relative to PWD for testing)
shell> tree etc/filebeat/modules.d
etc/filebeat/modules.d
└── module99.yml
The playbook below
shell> cat playbook.yml
- name: Configure filebeat modules
hosts: localhost
vars:
fb_modules_path: etc/filebeat/modules.d
fb_modules_d:
- module1.yml
- module2.yml
- module3.yml
tasks:
- name: Create modules
template:
src: "modules.d/{{ item }}"
dest: "{{ fb_modules_path }}/{{ item }}"
mode: '0644'
loop: "{{ fb_modules_d }}"
notify: restart_filebeat
- name: Remove modules
file:
dest: "{{ fb_modules_path }}/{{ item }}"
state: absent
loop: "{{ query('fileglob', fb_modules_path ~ '/*')|
map('basename')|
difference(fb_modules_d) }}"
notify: restart_filebeat
handlers:
- name: Restart filebeat service
listen: restart_filebeat
# systemd:
# name: filebeat
# state: restarted
debug:
msg: Restart filebeat
creates the files from the list and removes others
TASK [Create modules] ***************************************************
changed: [localhost] => (item=module1.yml)
changed: [localhost] => (item=module2.yml)
changed: [localhost] => (item=module3.yml)
TASK [Remove modules] ***************************************************
changed: [localhost] => (item=module99.yml)
RUNNING HANDLER [Restart filebeat service] ******************************
ok: [localhost] =>
msg: Restart filebeat
shell> tree etc/filebeat/modules.d
etc/filebeat/modules.d
├── module1.yml
├── module2.yml
└── module3.yml
The playbook is idempotent.

How to run a command on localhost to define variable for an Ansible playbook?

I'm very new to Ansible and trying to figure things out. I have a simple playbook to run on a remote host. To simplify drastically:
- hosts: all
name: build render VM
tasks:
- copy:
src: ./project_{{ project_id }}.yaml
dest: /app/project.yaml
owner: root
I would like to have project_id set to the output of this command, run on localhost: gcloud config get-value project. Ideally I'd like that to be stored into a variable or fact that can be used throughout the playbook. I know I can pass project_id=$(...) on the ansible cmd line, but I'd rather have it set up automatically in the playbook.
Taking for granted the given command only returns the id and nothing else.
With a task delegated to localhost:
- hosts: all
name: build render VM
tasks:
- name: get project id
command: gcloud config get-value project
register: gcloud_cmd
run_once: true
delegate_to: localhost
- name: set project id
set_fact:
project_id: "{{ gcloud_cmd.stdout }}"
- copy:
src: ./project_{{ project_id }}.yaml
dest: /app/project.yaml
owner: root
With a pipe lookup:
- hosts: all
name: build render VM
tasks:
- name: set project id from localhost command
set_fact:
project_id: "{{ lookup('pipe', 'gcloud config get-value project') }}"
run_once: true
- copy:
src: ./project_{{ project_id }}.yaml
dest: /app/project.yaml
owner: root

Ansible - Copy multiple files on remote host

I would like to copy few files on remote host , I used copy module as following but it copy all files under 'app' variable (dir) to 'backup_conf' variable (dir).
please advice here.
- name: backup configuration files
copy:
src: '{{ app }}'
dest: '{{ backup_conf }}'
remote_src: true
with_items:
- /bin/setenv.sh
- /conf/server.xml
Thank you, I fixed my issue:
- name: backup configuration files
copy:
src: "{{ item }}"
dest: "{{ backup_conf }}"
remote_src: true
with_items:
- /bin/setenv.sh
- /conf/server.xml

How to download and extract apache tomcat using ansible and rename unzaip file

I’m setting up a new setup for tomcat.
I wanna download and extract apache tomcat using ansible to several nodes of linux, but the thing is unzip file should be seen or rename as apache only. It doesn't matter what would be a version of tomcat?
- name: variable check
hosts: all
become: yes
vars:
tomcat_url: 'http://mirrors.estointernet.in/apache/tomcat/tomcat-8/v8.5.45/bin/apache-tomcat-8.5.45.tar.gz'
tasks:
- name:
get_url:
url: "{{tomcat_url}}"
dest: /opt
Here's complete working example:
---
- hosts: all
become: yes
vars:
tomcat_url: 'http://mirrors.estointernet.in/apache/tomcat/tomcat-8/v8.5.45/bin/apache-tomcat-8.5.45.tar.gz'
tasks:
- name: Download and Extract apache tomcat
unarchive:
src: "{{ tomcat_url }}"
dest: "/opt/"
remote_src: yes
- name: Synchronize the "{{ tomcat_url.split('/')[-1].rstrip('.tar.gz') }}" directory to apache on one remote host
synchronize:
src: "/opt/{{ tomcat_url.split('/')[-1].rstrip('.tar.gz') }}"
dest: "/opt/apache"
delegate_to: "{{ inventory_hostname }}"
- name: Remove the {{ tomcat_url.split('/')[-1].rstrip('.tar.gz') }} directory
file:
path: "/opt/{{ tomcat_url.split('/')[-1].rstrip('.tar.gz') }}"
state: absent

Read a file locally and use the vars remote in Ansible

I read a YAML file locally with the following playbook:
- name: Ensure the deploy_manifest var is defined and read deploy manifest
hosts: localhost
connection: local
gather_facts: False
tasks:
- assert:
that: deploy_manifest is defined
msg: |
Error: Must provide providers config path. Fix: Add '-e deploy_manifest=/path/to/manifest' to the ansible-playbook command
- name: Read deploy manifest
include_vars:
file: "{{ deploy_manifest }}"
name: manifest
register: manifest
- debug:
msg: "[{{ manifest.key }}]: {{ manifest.value }}"
with_dict: "{{ manifest.ansible_facts }}"
and then in the same playbook YAML file I run:
- name: Deploy Backend services
hosts: backend
remote_user: ubuntu
gather_facts: False
vars:
env: "{{ env }}"
services: "{{ manifest.ansible_facts }}"
tasks:
- include_role:
name: services_backend
when: backend | default(true) | bool
However it doesn't work because debug fails. It says that manifest is empty.
Which is the best way to read a YAML file or generally a configuration in a playbook and then have the variables passed in another playbook?
Your debug module doesn't say "that manifest is empty", it says the key manifest.key does not exist because it does not.
You registered a fact named manifest with:
register: manifest
You try to refer to a key of the above manifest named key and another key (!) named value:
msg: "[{{ manifest.key }}]: {{ manifest.value }}"
Please read Looping over Hashes chapter and acknowledge that (without using loop control) you refer to the iterated variable using item.
Please note that with name: manifest and register: manifest you read your vars file into manifest.ansible_facts.manifest.

Resources