Make a line in an Ansible playbook optional - ansible

I have built a playbook to build a virtual server in F5. I want to make a line only execute if someone enters the variable. In this case the default_persistence_profile: line has a variable "{{ persistenceProfile }}". Sometimes the developers don't want persistence applied to their app but sometimes they do. I have found when I make the variable optional in the run task and don't select a persistence profile the task errors out. See playbook below:
- name: Build the Virtual Server
bigip_virtual_server:
state: present
partition: Common
name: "{{ vsName }}"
destination: "{{ vsIpAddress }}"
port: "{{ vsPort }}"
pool: "{{ poolName }}"
default_persistence_profile: "{{ persistenceProfile }}"
ip_protocol: tcp
snat: automap
description: "{{ vsDescription }}"
profiles:
- tcp
- http
- name: "{{ clientsslName }}"
context: client-side
- name: default-server-ssl
context: server-side

Ansible has a mechanism for omitting parameters using the default filter, like this:
- name: Build the Virtual Server
bigip_virtual_server:
state: present
partition: Common
name: "{{ vsName }}"
destination: "{{ vsIpAddress }}"
port: "{{ vsPort }}"
pool: "{{ poolName }}"
default_persistence_profile: "{{ persistenceProfile|default(omit) }}"
ip_protocol: tcp
snat: automap
description: "{{ vsDescription }}"
profiles:
- tcp
- http
- name: "{{ clientsslName }}"
context: client-side
- name: default-server-ssl
context: server-side
If persistenceProfile is unset, the default_persistence_profile parameter should not be passed to the bigip_virtual_server module.

Related

Conditional task based on template parameter fails

I'm trying to use Ansible to deploy to two regions in AWS, I have it working with one region at the moment. The issue is I want to only execute my primary_region or my secondary_region depending on the parameter from the template it's using.
My main.yaml:
- hosts: primary_region
name: Create ECR
tasks:
- name: "Create ECR Repository"
cloudformation:
state: "{{ state }}"
stack_name: "{{ stack_create_ecr.name }}"
profile: "{{ aws_profile }}"
region: "{{ inventory_hostname }}"
template_url: "{{ stack_create_ecr.template_url }}"
template_parameters:
"ansibleFile"
tags:
"{{ stack_create_ecr.tags }}"
tags:
- stack_create_ecr
when: stack_create_ecr.region == "primary_region" <-- This
- hosts: secondary_region
name: Create ECR
tasks:
- name: "Create ECR Repository"
cloudformation:
state: "{{ state }}"
stack_name: "{{ stack_create_ecr.name }}"
profile: "{{ aws_profile }}"
region: "{{ inventory_hostname }}"
template_url: "{{ stack_create_ecr.template_url }}"
template_parameters:
"ansibleFile"
tags:
"{{ stack_create_ecr.tags }}"
tags:
- stack_create_ecr
when: stack_create_ecr.region == "secondary_region" <-- This
The template that I am using
stack_create_ecr.yaml:
stack_create_ecr:
name: cloudFormationTemplateNameOmitted
template_url: S3BucketUrl
parameters:
RepoName: EcrRepoName
DevName: cloud-dev
tags:
ansible_playbook: "{{ ansible_playbook_tag }}"
region: primary_region <-- This is what I'm trying to use
Everytime I try to run my playbook I just get msg: 'argument template_parameters is of type <class ''str''> and we were unable to convert to dict: dictionary requested, could not parse JSON or key=value'
I have tried all sorts of things from putting quotes around the string in the when condition. Nothing seems to work, what am I doing wrong?
It seems you massed up with template_parameters prop. It should be a map but you provided a string:
template_parameters:
PropName: "ansibleFile"

Registering and using multiple variables in Ansible

I'm trying to pop VM instances, put them into different host groups (say webservers and devops/admin machines) and install what is needed on them in one single playbook.
I don't know what IP addresses, for instance, GCP will give these instances, and so i am trying to capture them in a variable for use later on in the playbook. I can capture them fine by using "register" but using them is proving tricky. For instance if I do.
- name: création des adresses statiques
gcp_compute_address:
name: "{{ item }}"
state: present
region: "{{ region }}"
project: "{{ gcp_project }}"
auth_kind: "{{ gcp_cred_kind }}"
service_account_file: "{{ gcp_cred_file }}"
scopes:
- https://www.googleapis.com/auth/compute
loop:
- adresse-1
- adresse-2
- adresse-3
- adresse-4
- adresse-5
register: address
The best way i have figured out to use these variables later on is:
network_interfaces:
- network: "{{ network.name }}"
access_configs:
- name: 'External NAT'
type: 'ONE_TO_ONE_NAT'
nat_ip:
- "{{ address.results[0].address }}"
- "{{ address.results[1].address }}"
- "{{ address.results[2].address }}"
Which fails miserably.
Please help ? How can I use the range of addresses I have created ?
I am going nuts over this
It is possible to add_host to the group webservers and proceed with the next play
- add_host:
name: "{{ item }}"
groups: webservers
loop: "{{ address.results|json_query('[*].address') }}"
- debug:
msg: "{{ groups['webservers'] }}"
- hosts: webservers
tasks:
- name: Configure cluster
...
The tasks below split the hosts into two groups
- set_fact:
my_hosts: "{{ address.results|json_query('[*].address') }}"
- add_host:
name: "{{ item }}"
groups: webservers1
loop: "{{ my_hosts[0:(my_hosts|length / 2)|int] }}"
- add_host:
name: "{{ item }}"
groups: webservers2
loop: "{{ my_hosts[(my_hosts|length / 2)|int:my_hosts|length] }}"
- debug:
msg: "{{ groups['webservers1'] }}"
- debug:
msg: "{{ groups['webservers2'] }}"
There is also GCE Dynamic Inventory and other 100+ gcp modules. You might want to start with Google Cloud Platform Guide.
FWIW, Ansible 2 Cloud Automation Cookbook covers leading providers incl. GCP.

Ansible: ec2_eni cannot attach interface

Using ansible I am trying to create ec2 instances and attach an extra network interface to each instance so that they will have two private IP addresses. However, for some reason, it seems that the ec2_eni module can create network interfaces, but will not attach them to the instances specified. What am I doing wrong? Below is my playbook:
---
- hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Create new servers
ec2:
region: "{{ region }}"
vpc_subnet_id: "{{ subnet }}"
group_id: "{{ sec_group }}"
assign_public_ip: yes
wait: true
key_name: '{{ key }}'
instance_type: t2.micro
image: '{{ ami }}'
exact_count: '{{ count }}'
count_tag:
Name: "{{ server_name }}"
instance_tags:
Name: "{{ server_name }}"
register: ec2
- name: Show ec2 instance json data
debug:
msg: "{{ ec2['tagged_instances'] }}"
- name: allocate new elastic IPs and associate it with instances
ec2_eip:
region: "{{ region }}"
device_id: "{{ item['id'] }}"
with_items: "{{ ec2['tagged_instances'] }}"
register: eips
- name: Show eip instance json data
debug:
msg: "{{ eips['results'] }}"
- ec2_eni:
subnet_id: "{{ subnet }}"
state: present
secondary_private_ip_address_count: 1
security_groups: "{{ sec_group }}"
region: "{{ region }}"
device_index: 1
description: "test-eni"
instance_id: "{{ item['id'] }}"
with_items: "{{ ec2['tagged_instances'] }}"
The strange thing is that the ec2_eni task succeeds, saying that it has attached the network interface to each instance when in reality it just creates the network interface and then does nothing with it.
As best I can tell, since attached defaults to None, but the module docs say:
Specifies if network interface should be attached or detached from instance. If ommited, attachment status won't change
then the code does what they claim and skips the attachment step.
This appears to be a bug in the documentation, which claims the default is 'yes' but is not accurate.

Use Jinja2 dict as part of an Ansible modules options

I have the following dict:
endpoint:
esxi_hostname: servername.domain.com
I'm trying to use it as an option via jinja2 for the vmware_guest but have been unsuccessful. The reason I'm trying to do it this way is because the dict is dynamic...it can either be cluster: clustername or esxi_hostname: hostname, both mutually exclusive in the vmware_guest module.
Here is how I'm presenting it to the module:
- name: Create VM pysphere
vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ username }}"
password: "{{ password }}"
validate_certs: no
datacenter: "{{ ansible_host_datacenter }}"
folder: "/DCC/{{ ansible_host_datacenter }}/vm"
"{{ endpoint }}"
name: "{{ guest }}"
state: present
guest_id: "{{ osid }}"
disk: "{{ disks }}"
networks: "{{ niclist }}"
hardware:
memory_mb: "{{ memory_gb|int * 1024 }}"
num_cpus: "{{ num_cpus|int }}"
scsi: "{{ scsi }}"
customvalues: "{{ customvalues }}"
cdrom:
type: client
delegate_to: localhost
And here is the error I'm getting when including the tasks file:
TASK [Preparation : Include VM tasks] *********************************************************************************************************************************************************************************
fatal: [10.10.10.10]: FAILED! => {"reason": "Syntax Error while loading YAML.
The error appears to have been in '/data01/home/hit/tools/ansible/playbooks/roles/Preparation/tasks/prepareVM.yml': line 36, column 4, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
"{{ endpoint }}"
hostname: "{{ vcenter_hostname }}"
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
exception type: <class 'yaml.parser.ParserError'>
exception: while parsing a block mapping
in "<unicode string>", line 33, column 3
did not find expected key
in "<unicode string>", line 36, column 4"}
So in summary, I'm not sure how to format this or if it is even possible.
The post from techraf sums up your problem, but for a possible solution, in the docs, especially regarding Jinja filters, there is the following bit:
Omitting Parameters
As of Ansible 1.8, it is possible to use the default filter to omit
module parameters using the special omit variable:
- name: touch files with an optional mode
file: dest={{item.path}} state=touch mode={{item.mode|default(omit)}} > with_items:
- path: /tmp/foo
- path: /tmp/bar
- path: /tmp/baz
mode: "0444"
For the first two files in the list, the default mode will be
determined by the umask of the system as the mode= parameter will not
be sent to the file module while the final file will receive the
mode=0444 option.
So it looks like what should be tried is:
esxi_hostname: "{{ endpoint.esxi_hostname | default(omit) }}"
# however you want the alternative cluster settings done.
# I dont know this module.
cluster: "{{ cluster | default(omit) }}"
This is obviously reliant on the vars to only have one choice set.
There is no way you could ever use the syntax you tried in the question, because firstly and foremostly Ansible requires a valid YAML file.
The closest workaround would be to use a YAML anchor/alias although it would work only with literals:
# ...
vars:
endpoint: &endpoint
esxi_hostname: servername.domain.com
tasks:
- name: Create VM pysphere
vmware_guest:
hostname: "{{ vcenter_hostname }}"
username: "{{ username }}"
password: "{{ password }}"
validate_certs: no
datacenter: "{{ ansible_host_datacenter }}"
folder: "/DCC/{{ ansible_host_datacenter }}/vm"
<<: *endpoint
name: "{{ guest }}"
state: present
guest_id: "{{ osid }}"
disk: "{{ disks }}"
networks: "{{ niclist }}"
hardware:
memory_mb: "{{ memory_gb|int * 1024 }}"
num_cpus: "{{ num_cpus|int }}"
scsi: "{{ scsi }}"
customvalues: "{{ customvalues }}"
cdrom:
type: client
delegate_to: localhost

Ansible Pass multiple vaules with single defined Variable

I need to add a server to service group every time I create a new server using the following task.
Task
- name: Create a service group
a10_service_group_v3:
validate_certs: no
host: "{{ item.0.a10_host }}"
state: "{{ item.1.service_state }}"
username: "{{ item.0.user }}"
password: "{{ item.0.pass }}"
service_group: "{{ item.1.group_name }}"
reset_on_server_selection_fail: yes
servers:
- name: "{{ item.1.server_name1 }}"
port: "{{ item.1.server_port1 }}"
overwrite: yes
write_config: yes
ignore_errors: yes
with_nested:
- "{{ a10 }}"
- "{{ service_group }}"
Variables:
service_group:
- group_name: bif_sg
service_state: present
server_name1: bif01
server_port1: 80
I need help with passing variables for server_name and server_port, let's say If I have 3 servers to add to service group in the task I need to add 3 times server_name1, server_port1
server_name2, server_port2 ......
Everytime I add server I need to update in the task as well :(
Is there a way to pass multiple times sever_name and serer_port with single defined value in the task.
I you expect server_group to have a list of servers, refactor your variable to have a list of servers and not a bunch of separate subkeys:
service_group:
- group_name: bif_sg
service_state: present
servers:
- name: bif01
port: 80
- name: bif02
port: 8080
And in your task:
...
servers: "{{ item.1.servers }}"
...

Resources