Ansible - How to apply a different ssh config to a specific host? - ansible

Below is the play for applying the ssh config to a specific host:
- name: Add a host in the configuration
community.general.ssh_config:
ssh_config_file: "{{ ssh_config_path }}"
host: "example.com"
state: present
We have two variants of ssh config: ssh_config1 & ssh_config2
ssh_config1 needs to be applied on group(group1) of hosts
ssh_config2 needs to be applied on a specific single host host1.abc.com(not in group1)
How should the play perform this functionality?
Requirement is to apply a specific

Taking for granted that:
this playbook will target any hosts in your inventory
you have a valid inventory already containing group1, the specific host host1.abc.com and any other
you want to skip that config task for any host not having ssh_config_path configured.
you have removed any definition of the ssh_config_path variable from any other place in your environment
In group_vars/group1.yml
---
ssh_config_path: /path/to/ssh_config1
In host_vars/host1.abc.com.yml
---
ssh_config_path: /path/to/ssh_config2
Then the following (untested) playbook should meet your requirement
---
- hosts: all
gather_facts: false
tasks:
- name: Add a host in the configuration
community.general.ssh_config:
ssh_config_file: "{{ ssh_config_path }}"
host: "example.com"
state: present
when: ssh_config_path is defined
Alternatively, if you want to play the above only on the given hosts, with the exact same configuration and inner task, you can simply replace the host: stanza above with:
hosts: group1:host1.abc.com

Related

Ansible role dependencies and facts with delegate_to

The scenario is: I have several services running on several hosts. There is one special service - the reverseproxy/loadbalancer. Any service needs to configure that special service on the host, that runs the rp/lp service. During installation/update/deletion of a random service with an Ansible role, I need to call the ReverseProxy role on the specific host to configure the corresponding vhost.
At the moment I call a specific task file in the reverse proxy role to add or remove a vhost by the service with include_role and set some vars (very easy example without service and inventory specific vars).
- name: "Configure ReverseProxy"
include_role:
name: reverseproxy
tasks_from: vhost_add
apply:
delegate_to: "{{ groups['reverseproxy'][0] }}"
vars:
reverse_proxy_url: "http://{{ ansible_fqdn }}:{{ service_port }}/"
reverse_proxy_domain: "sub.domain.tld"
I have three problems.
I know, it's not a good idea to build such dependencies between roles and different hosts. I don't know a better way, especially if you think about the situation, where you need to do some extra stuff after creating the vhost (f.e. configure the service via REST API, which needs the external fqdn). In case of two separate playbooks with "backend"-service and "reverseproxy"-service - then I need a third playbook for configuring "hanging" services. Also I'm not sure, if I can retrieve the correct backend URL in the reverse proxy role (only think about the HTTP scheme or paths). That sounds not easy, or?
Earlier I had separate roles for adding/removing vhosts to a reverseproxy. This roles didn't have dependencies, but I needed to duplicate several defaults and templates and vars etc. which isn't nice too. Then I've changed that to a single role. Of course - in my opinion, this isn't really that, what a "role" should be. A role is something like "webserver" or "reverseproxy" (a state). But not something like "add_vhost_to_reverseproxy" (a verb). This would be something like a playbook - but is calling a parameterized playbook via a role a good idea/possible? The main problem is, that the state of reverseproxy is the sum of all services in the inventory.
In case of that single included role, including it, starts also all dependent roles (configure custom, firewall, etc.). Nevertheless in that case I found out, that the delegation did not use the facts of the delegated host.
I tested that with the following example - the inventory:
all:
hosts:
server1:
my_var: a
server2:
my_var: b
children:
service:
hosts:
server1:
reverseproxy:
hosts:
server2:
And playbook which assigns a role-a to the group webserver. The role-a has a task like:
- block:
- setup:
- name: "Include role b on delegated {{ groups['reverseproxy'][0] }}"
include_role:
name: role-b
delegate_to: "{{ groups['reverseproxy'][0] }}"
delegate_facts: true # or false or omit - it has no effect on Ansible 2.9 and 2.10
And in role-b only outputing the my_var of the inventory will output
TASK [role-b : My_Var on server1] *******************
ok: [server1 -> <ip-of-server2>] =>
my_var: a
Which says me, that role-b that should be run on server2 has the facts of server1. So - configuring the "reverseproxy" service is done in context of the "backend"-service. Which would have several other issues - when you think about firewall-dependencies etc. I can avoid that, by using tags - but then I need to run the playbook not just with the tag of the service, but also with all tags I want to configure, and I cannot use include_tasks with args-apply-tags anymore inside a role that also includes other roles (the tags will applied to all subtasks...). I miss something like include_role but only that specific tags or ignore dependencies. This isn't a bug, but has possible side effects in case of delegate_to.
I'm not really sure, what is the question? The question is - what is a good way to handle dependencies between hosts and roles in Ansible - especially when they are not on the same host?
I am sure I do not fully understand your exact problem, but when I was dealing with load balancers I used a template. So this was my disable_workers playbook:
---
- hosts: "{{ ip_list | default( 'jboss' ) }}"
tasks:
- name: Tag JBoss service as 'disabled'
ec2_tag:
resource: "{{ ec2_id }}"
region: "{{ region }}"
state: present
tags:
State: 'disabled'
delegate_to: localhost
- action: setup
- hosts: httpd
become: yes
become_user: root
vars:
uriworkermap_file: "{{ httpd_conf_dir }}/uriworkermap.properties"
tasks:
- name: Refresh inventory cache
ec2_remote_facts:
region: "{{ region }}"
delegate_to: localhost
- name: Update uriworkermap.properties
template:
backup: yes
dest: "{{ uriworkermap_file }}"
mode: 0644
src: ./J2/uriworkermap.properties.j2
Do not expect this to work as-is. It was v1.8 on AWS hosts, and things may have changed.
But the point is to set user-defined facts, on each host, for that host's desired state (enabled, disabled, stopped), reload the facts, and then run the Jinja template that uses those facts.

Ansible: Trigger the task only when previous task is successful and the output is created

I am deploying a VM in azure using ansible and using the public ip created in the next tasks. But the time taken to create the public ip is too long so when the subsequent task is executed, it fails. The time to create the ip also varies, it's not fixed. I want to introduce some logic where the next task will only run when the ip is created.
- name: Deploy Master Node
azure_rm_virtualmachine:
resource_group: myResourceGroup
name: testvm10
admin_username: chouseknecht
admin_password: <your password here>
image:
offer: CentOS-CI
publisher: OpenLogic
sku: '7-CI'
version: latest
Can someone assist me here..! It's greatly appreciated.
I think the wait_for module is a bad choice because while it can test for port availability it will often give you false positives because the port is open before the service is actually ready to accept connections.
Fortunately, the wait_for_connection module was designed for exactly the situation you are describing: it will wait until Ansible is able to successfully connect to your target.
This generally requires that you register your Azure VM with your Ansible inventory (e.g. using the add_host module). I don't use Azure, but if I were doing this with OpenStack I might write something like this:
- hosts: localhost
gather_facts: false
tasks:
# This is the task that creates the vm, much like your existing task
- os_server:
name: larstest
cloud: kaizen-lars
image: 669142a3-fbda-4a83-bce8-e09371660e2c
key_name: default
flavor: m1.small
security_groups: allow_ssh
nics:
- net-name: test_net0
auto_ip: true
register: myserver
# Now we take the public ip from the previous task and use it
# to create a new inventory entry for a host named "myserver".
- add_host:
name: myserver
ansible_host: "{{ myserver.openstack.accessIPv4 }}"
ansible_user: centos
# Now we wait for the host to finished booting. We need gather_facts: false here
# because otherwise Ansible will attempt to run the `setup` module on the target,
# which will fail if the host isn't ready yet.
- hosts: myserver
gather_facts: false
tasks:
- wait_for_connection:
delay: 10
# We could add additional tasks to the previous play, but we can also start
# new play with implicit fact gathering.
- hosts: myserver
tasks:
- ...other tasks here...

How to define when condition based on matching string

I am writing a playbook where i need to select host which will be a part of group which starts with name "hadoop". The host will be supplied as an extra variable in term of parent group. The task is about upgrading the java on all machines with repo but there are certain servers which dont have repo configured or are in dmz and can only use there local repo... i need to enable local_rpm:true so that when the playbook execute the server which belong to hadoop group have this fact enabled.
I tried like below :
- hosts: '{{ target }}'
gather_facts: no
become: true
tasks:
- name: enable local rpm
set_fact:
local_rpm: true
when: "'hadoop' in group_names"
tags: always
and then importing my role based on tag
It's probably better to use group_vars in this case.
https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#group-variables

How to add host to group in Ansible Tower inventory?

How can I add a host to a group using tower_group or tower_host modules?
The following code creates a host and a group, but they are unrelated to each other:
---
- hosts: localhost
connection: local
gather_facts: false
tasks:
- tower_inventory:
name: My Inventory
organization: Default
state: present
tower_config_file: "~/tower_cli.cfg"
- tower_host:
name: myhost
inventory: My Inventory
state: present
tower_config_file: "~/tower_cli.cfg"
- tower_group:
name: mygroup
inventory: My Inventory
state: present
tower_config_file: "~/tower_cli.cfg"
Docs mention instance_filters parameter ("Comma-separated list of filter expressions for matching hosts."), however do not provide any usage example.
Adding instance_filters: myhost to the tower_group task has no effect.
I solved it using Ansible shell module and tower-cli. I Know that create a ansible module is better than it, but to a fast solution...
- hosts: awx
vars:
tasks:
- name: Create Inventory
tower_inventory:
name: "Foo Inventory"
description: "Our Foo Cloud Servers"
organization: "Default"
state: present
- name: Create Group
tower_group:
inventory: "Foo Inventory"
name: Testes
register: fs_group
- name: Create Host
tower_host:
inventory: "Foo Inventory"
name: "host"
register: fs_host
- name: Associate host group
shell: tower-cli host associate --host "{{fs_host.id}}" --group "> {{fs_group.id}}"
This isn't natively available in the modules included with Tower, which are older and use the deprecated tower-cli package.
But it is available in the newer AWX collection, which uses the awx CLI, as long as you have a recent enough Ansible (2.9 should be fine).
In essence, install the awx collection through a requirements file, or directly like
ansible-galaxy collection install awx.awx -p ./collections
Add the awx.awx collection to your playbook
collections:
- awx.awx
and then use the hosts: option to tower_group:.
- tower_group:
name: mygroup
inventory: My Inventory
hosts:
- myhost
state: present
You can see a demo playbook here.
Be aware though that you may need preserve_existing_hosts: True if your group already contains other hosts. Unfortunately there does not seem to be an easy way to remove a single host from a group.
In terms of your example this would probably work:
---
- hosts: localhost
connection: local
gather_facts: false
collections:
- awx.awx
tasks:
- tower_inventory:
name: My Inventory
organization: Default
state: present
tower_config_file: "~/tower_cli.cfg"
- tower_host:
name: myhost
inventory: My Inventory
state: present
tower_config_file: "~/tower_cli.cfg"
- tower_group:
name: mygroup
inventory: My Inventory
state: present
tower_config_file: "~/tower_cli.cfg"
hosts:
- myhost

How do I apply an Ansible task to multiple hosts from within a playbook?

I am writing an ansible playbook to rotate IAM access keys. It runs on my localhost to create a new IAM Access Key on AWS. I want to push that key to multiple other hosts' ~/.aws/credentials files.
---
- name: Roll IAM access keys
hosts: localhost
connection: local
gather_facts: false
strategy: free
roles:
- iam-rotation
In the iam-rotation role, I have something like this:
- name: Create new Access Key
iam:
iam_type: user
name: "{{ item }}"
state: present
access_key_state: create
key_count: 2
with_items:
- ansible-test-user
register: aws_user
- set_fact:
aws_user_name: "{{ aws_user.results.0.user_name }}"
created_keys_count: "{{ aws_user.results.0.created_keys | length }}"
aws_user_keys: "{{ aws_user.results[0]['keys'] }}"
I want to use push the newly created access keys out to jenkins builders. How would I use the list of hosts from with_items in the task? The debug task is just a placeholder.
# Deploy to all Jenkins builders
- name: Deploy new keys to jenkins builders
debug:
msg: "Deploying key to host {{item}}"
with_items:
- "{{ groups.jenkins_builders }}"
Hosts file that includes the list of hosts I want to apply to
[jenkins_builders]
builder1.example.org
builder2.example.org
builder3.example.org
I am executing the playbook on localhost. But within the playbook I want one task to execute on remote hosts which I'm getting from the hosts file. The question was...
How would I use the list of hosts from with_items in the task?
Separate the tasks into two roles. Then execute the first role against localhost and the second one against jenkins_builders:
---
- name: Rotate IAM access keys
hosts: localhost
connection: local
gather_facts: false
strategy: free
roles:
- iam-rotation
- name: Push out IAM access keys
hosts: jenkins_builders
roles:
- iam-propagation
Per AWS best practices recommendations, if you are running an application on an Amazon EC2 instance and the application needs access to AWS resources, you should use IAM roles for EC2 instead of keys:
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html
You can use
delegate_to: servername
in the task module, it will run only on the particular

Resources