Automating Deployment Environment Ansible based on hostname (nested variable name) - ansible

Using an ansible playbook, I have three stages of development aka. test stage prod and want to access a dictionary with settings based on the stages. My ./defaults/main.yaml would have:
prod:
proxy_url: prod.example.com
stage:
proxy_url: stage.example.com
test:
proxy_url: test.example.com
I set a custom fact based on the machine hostname. As a convention I use the stage as a postfix like app1-test
- set_fact: deployment="{{ ansible_hostname.split('-')[-1] | lower }}"
This creates the fact deployment as test like so:
"{{ deployment }}"
Now when I want to access my variables this works:
"{{ test['proxy_host'] }}"
But this not:
"{{ (deployment)['proxy_host'] }}"
What am I missing?

Solution thanks to #mdaniel:
{{ vars[deployment]["proxy_host"] }}

Related

Ansible import role run conditionally

I am writing a parent ansible role that runs another role though import_role. The idea that this sibling role (staticdev.pyenv) only runs when an argument pyenv_python_versions is passed, otherwise this is skipped.
According to the official documentation, I tried the following approach:
parent/tasks/main.yml
---
- name: Install pyenv
import_role:
name: staticdev.pyenv
vars:
pyenv_owner: "{{ ansible_env.USER }}"
pyenv_path: "{{ ansible_env.HOME }}/pyenv"
pyenv_global: "{{ pyenv_global }}"
pyenv_python_versions: "{{ pyenv_python_versions }}"
pyenv_virtualenvs: []
when: pyenv_python_versions
I am using currently ansible 4.1.0 (core 2.11.1), and when I test it on Debian 11 (image: cisagov/docker-debian11-ansible:latest) it executes the role anyway, even without any value for pyenv_python_versions. when is not being considered and I also tried with include_role. Complete logs can be found here.
Any idea?
UPDATE: changed condition when from to pyenv_python_versions as suggested by #lonetwin.
The problem was role import was replicating variables from the imported role (pyenv_global, pyenv_python_versions and pyenv_virtualenvs), in this case you solve it just by omitting imported role params (they will be overwritten if you create new defaults for them).
Solution:
---
- name: Install pyenv
import_role:
name: staticdev.pyenv
vars:
pyenv_owner: "{{ ansible_env.USER }}"
pyenv_path: "{{ ansible_env.HOME }}/pyenv"
when: pyenv_python_versions

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.

include_role combined with_items for role name

I want to selectively execute roles based on a list of roles passed to the play book. However, this fails
- name: create container definitions for selected services
include_role:
name: "{{ item }}"
with_items: "{{ selected_service_list }}"
with
ERROR! 'item' is undefined
I gather it is impossible to use a list of role names to control when we execute roles. Let me know if you know how to do this
For ansible latest 2020-07 one can follow this (example from ansible documentation)
For ansible 2.3 one should upgrade or can use this approach
- { role: myservice, when: "'myservice' in selected_service_list" }

Ansible aws_ec2 inventory plugin - dynamic boto_profile

I am using the aws_ec2 inventory plugin and would like to pass the boto_profile in as a var at runtime.
I am trying to run the following command:
ansible-playbook playbook.yml --extra-vars profile=foo
Inside my aws_ec2.yml plugin file I have:
boto_profile: "{{ profile }}"
This returns the error:
The config profile ({{ profile }}) could not be found
I am able to use the profile var inside my playbook. I am using the ec2 module with profile: "{{ profile }}" That seems to work if I define a static inventory.
Is it possible to pass the profile var into the dynamic inventory file?
Jinja2 templates are not applicable to inventory configuration files.
Use environment variable AWS_PROFILE or AWS_DEFAULT_PROFILE to set profile at runtime.
Like: AWS_PROFILE=foo ansible-playbook playbook.yml
I'm using this in GitLab CI/CD and had the same issue, however you can look up the env variables in the dynamic inventory like this:
plugin: amazon.aws.aws_ec2
aws_access_key: "{{ lookup('env','AWS_ACCESS_KEY_ID') }}"
aws_secret_key: "{{ lookup('env','AWS_SECRET_ACCESS_KEY') }}"
regions:
- eu-central-1
groups:
webservers: "'app-server' in tags.Type"
and I cen set this up in the CI/CD under variables: and this is passed to docker container.

jenkins_configure_proxy role missing in Ansible

I have Ansible 2.4.0. I am trying to configure Jenkins proxy based on Ansible Jenkins DevOps Roles documentation:
- hosts: master
roles:
- jenkins_configure_proxy:
jenkins_home: "{{ jenkins_home }}"
proxy_host: "{{ proxy_host }}"
proxy_port: "{{ proxy_port }}"
become: true
environment: "{{proxy_env}}"
When trying to execute ansible_playbook I get:
ERROR! role definitions must contain a role name
The error appears to have been in '/Users/me/projects/jenkins/jenkins.yml': line 10, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
roles:
- jenkins_configure_proxy:
^ here
That pretty strange, because other roles like jenkins_plugin work fine.
What am I doing wrong?
You use role syntax without parameters to apply role with parameters, see example:
- roles:
# without or with default parameters
- jenkins_configure_proxy
# with custom parameters
- role: jenkins_configure_proxy
jenkins_home: "{{ jenkins_home }}"
proxy_host: "{{ proxy_host }}"

Resources