Manage pairs of servers with Ansible - ansible

My setup contains "pairs of servers" in the sense that every "instance" of my service consists of two servers (say database and web) on (usually) different hosts. That is, I don't have a single database server that many web servers talk to -- instead, each web server talks to its own database server. I want to manage multiple such pairs, but I don't know the best way to model the pairs using Ansible.
The main host organization tools in Ansible seem to be groups and roles. However, these only group together hosts of the same kind. I can use that to, for example, set up the necessary software on my database and web servers. However, I don't think I can use groups or roles to tell every web server to use exactly one of the database servers.
It also makes little sense to use an Ansible group to model one server pair, since I then cannot distinguish between the two different roles in these groups.
Although my setup consists of server pairs I'd also be interested in modeling multiple instances of n-to-m host relations (say n database servers and m web servers) in general.

You could just use ansible variables to provide the necessary links. E.g., in your inventory:
[webservers]
web0 dbserver=db0
web1 dbserver=db1
[dbservers]
db0
db1
Now, when running tasks on your web servers, you can simply refer to the dbserver variable to find the appropriate server. Assuming a standard naming scheme, you could automate this through the use of the set_fact module.
Update
You don't need to specify the links in both directions; you could use
something like this to find the webservers which should have access to
a given database server:
- name: allow access to webserver
command: >
iptables -A INPUT
-s {{hostvars[item].ansible_default_ipv4.address}}
with_items: "{{groups.webservers}}"
when: "hostvars[item].dbserver == {{inventory_hostname}}"

I ended up using the following approach: Each pair of servers gets their own group, and group variables are used to define which server runs on which host:
[test]
test-host
[test:vars]
web_server = test-host
db_server = test-host
[production]
web
db
[production:vars]
web_server = web
db_server = db
The tasks for web and database servers are then put into separate roles (web_server and db_server). I then switch between those roles based on the group variables:
- hosts: all
roles:
- { role: web_server, when: "web_server == inventory_hostname" }
- { role: db_server, when: "db_server == inventory_hostname" }
This setup allows me to easily define shared variables for roles and pairs, and it also allows me to easily target just one pair (via --limit).

Related

Ansible share variable across plays that use different inventories

I have two playbooks : create_infrastructure.yml and bootstrap_cluster.yml. The first playbook uses a static inventory that only defines the infrastructure group with one member : 127.0.0.1. This is used to build the infrastructure on which to deploy a kubernetes cluster.
The second playbook uses a dynamic inventory, hcloud.yml, and is responsible for bootstrapping a kubernetes cluster.
One of the roles executed in bootstrap_cluster.yml needs access to the control plane loadbalancer's IP address which is something known by the create_infrastructure.yml playbook.
I've tried using both the register option and the set_fact module but couln't seem to make it work. Indeed trying to access hostvars['infrastructure']['control_plane_ip'] from bootstrap_cluster.yml doesn't make much sense as the infrastructure group is not even defined in the dynamic inventory used by the playbook.
Any idea on how I might dynamically share a variable between playbook that targets different hosts?
If you launch playbooks in the same call, you can test my solution with a dummy host:
Pass Var between different hosts
# Combine multiple playbooks in the same call combine.yml for example:
- import_playbook: play1.yml
- import_playbook: play2.yml
If your playbooks are launched in two separate calls, the only solution is to create a file shared between the 2 playbooks.

Use cases of dynamic inventories VS static inventories with ansible

I am trying to query a list of ec2 instances via ansible using the ec2 plugin for dynamic inventories.
I can see the utility of using dynamic inventories. If new machines are added then ansible will automatically execute a play against them. But I also saw on the net that it is possible to spawn instances with ansible, and manually add the new hosts to the static list of hosts.
So my question is : What would be the use cases where we would use dynamic inventories vs static inventories ? i'm new to the realm of devops so i don't know how often we need to spawn instances automatically Vs doing it manually via the AWS console for example.
Thanks !
In case you use autoscaling groups you have to use dynamic inventories.
If you launch ec2s temporary as part of a build pipelines use dynamic inventories. e.g. you just want to test the deployment of your software and terminate the machine after that test.
If you want to disable ansible plays on some machines you can create dynamic inventories based on ec2 tags. e.g. you have a security play that runs an all web server each hour but a developer wants to test something on his machine. So he can tag his machine to be skipped. He doesn't need access to the inventory file (and you can run another play once at midnight to enable the security play again. so it won't be forgotten).
By the way: you can use ec2_instance_facts with filter options and add_host to create dynamic inventories during the playbooks run time.
e.g. you have three types of server "web", "app", "db". you tag the ec2s during launch with servertype: [web|app|db]. You can filter these ec2s with:
- name: collect ec2s
ec2_instance_facts:
region: "{{ region }}"
filters:
"tag:servertype": "{{ servertype_list }}"
register: ec2_list
and run your play selectively on a server group with an external variable ansible-playbook test.yml -e servertype_list=['web','app'] or ansible-playbook test.yml -e servertype_list=['db'].
So by tagging the machine you avoid to take care of a static inventory.

Ansible select host based on combination of EC2 tags

I have 2 VPCs, each of which has a VPN instance, whose configuration I would like to share. Both instances have a class tag, whose value is now vpn_dev, however, they should differ by the environment tags - one would have environment: default, and the other environment: london.
I also have 2 playbooks, one for each VPC. I would like to run my vpn role on exactly one of the instances based on the combination of the class and environment tags, i.e. select the instance that has class: vpn_dev and environment: london. E.g.
- name: Deploy developer VPN in AWS
hosts:
- tag_class_vpn_dev
- tag_environment_london
roles:
- vpn
However, this would of course install the role on all instances that have class: vpn_dev (regardless of environment), and on all instances that have environment: london (regardless of class).
Is there a way to achieve this? Currently it seems like the only way is to have a single tag that uniquely identifies an instance.
The lists of hosts in a playbook is an OR operation by default. You can use AND over to host groups with this syntax:
- hosts:
- tag_class_vpn_dev:&tag_environment_london
More about host patterns here
It is possible to apply some ansible role to one host for each tag combination, although I would not recommend it, because this approach IMHO is not idempotent.
One of the possible risk with this scenario is that when you re-run this playbook, selected host for some tag combination can be different from previous run, and therefore you will end up with two instances per this unique tag combination group.
My recommended approach would be to use some tag to mark host which is supposed to be used as VPN role target, and then use ansible to apply VPN role only to hosts marked by this tag.

Ansible: Including non-managed systems into the configuration

I'm writing an Ansible playbook, that sets up systems, where in certain configurations, some systems might be provided by another organization. A very simple example:
Inventory:
An app server
A db server
Playbook:
Setup up both servers, and add application.properties file to app server with IP, port and user/pass of the db server.
Works so far, but then a requirement comes, that in some deployments the DB server is provided by another organization, so I can't include it in inventory, as the setup step fails, yet I still want to generate the properties file for the app server (with db server info I get from other people).
What would be the least painful solution that would cover both scenarios (my own db server and provided db server), considering there's 6 such server types, not just 2 (so not just 2 different scenarios, there are many permutations of which server is provided for me and which is mine).
Edit:
To be more specific, the problem I have is that if I use vars when a system is not mine and facts when it is mine, then I have problems writing application.properties.j2 template, since facts and vars are referenced differently. How would use var in a template, but use a fact if var isn't defined?
I assume you have an inventory file per deployment. So an inventory file might look like this:
[app]
an-app.server
[db]
a-db.server
[deploymentA:children]
app
db
[deploymentA:vars]
db_ip=foo
dp_port=foo
db_user=foo
db_pass=foo
The deploymentA:vars you probably would store as group_vars/deploymentA but for simplicity I just put it into the inventory.
Now the playbook:
- hosts: db
roles:
- setup-db-server
- hosts: app
roles:
- setup-app-server
In a deployment where the db server is not managed by yourself, you simply would not have a db server defined in your inventory. The group db would be empty. In that case the first play will simply print "no hosts matched" or something like that and proceed to the next play.
In a deployment where the db server is managed by you, the role setup-db-server will be run and you can use the deploymentA:vars for configuration.
In your app play you have the deploymentA:vars no matter if the host is yours or not and can roll out the application.properties file.

Ansible: return value from playbook

Is there a way to return a value from one included playbook to use in another? Consider the following playbook:
---
- include: provisionPostgres.yml store=09123
- include: provisionWebserver.yml store=09123
Each of these included playbooks provisions an AWS instance, and then installs software on that instance. Postgres and my web app respectively.
The use case I'm trying to handle is to have the first included playbook return the IP address of my database server so that I can use it to point my web server to in the 2nd included playbook.
I know that I can register values from tasks to use in other tasks, but I can't find out how to return a value from a playbook.
How can I accomplish this in ansible?

Resources