Ansible undefined variable - amazon-web-services

i have role for Route53 (http://docs.ansible.com/ansible/route53_module.html) and task is:
- name: "SET DNS record"
route53:
aws_access_key: "ACCESSKEY"
aws_secret_key: "SECRET"
command: "create"
zone: "{{ dns_zone }}"
hosted_zone_id: "{{ dns_zone_id }}"
record: "{{ item.dns_record }}"
type: "{{ item.dns_type }}"
ttl: "{{ item.dns_ttl }}"
value: "{{ item.dns_value }}"
failover: "{{item.failover}}"
health_check: "{{item.health_check}}"
identifier: "{{item.identifier}}"
with_items:
- "{{ dns_records }}"
Example of DNS records:
dns_records:
- dns_record: "try1.example.com"
dns_type: "A"
dns_ttl: "3600"
dns_value: "1.1.1.1"
failover: "PRIMARY"
health_check: "aeejas728asd"
identifier: "identifier01"
- dns_record: "try2.example.com"
dns_type: "A"
dns_ttl: "3600"
dns_value: "2.2.2.2"
If i run paybook role FAILS because second value have undefined failover, health_check and identifier. How can i set expression for IF else?
My try:
identifier: "{{item.identifier if item.identifier is defined else '!'}}"
is not working. I need IF not defined THEN IGNORE VALUE.
Thanks for help!

From the docs:
identifier: "{{item.identifier|default(omit)}}"

Related

Check if EC2 instances exists and running then skip deployment

I am trying to run a playbook that calls a role to deploy some EC2 instances, everything works fine except that I want to put a condition when the EC2 instance exists and in running state to skip the deployment I used the following to retrieve the ec2_infos :
## Check if an instance with same name exist on AWS
- name: Get {{ ec2_name }} infos
ec2_instance_info:
region: "us-east-1"
filters:
"tag:Name": "{{ ec2_name }}"
instance-state-name: [ "running"]
register: ec2_infos
- name: DEBUG
debug: msg="{{ aws_ec2_infos }}"
and on the Deployment stage my condition is as follows :
- name: "{{ ec2_description }} - {{ ec2_name }}"
cloudformation:
stack_name: "some name "
state: "present"
region: "{{ aws_region }}"
template: "PATH/ec2.json"
template_parameters:
Name: "{{ ec2_name }}"
Description: "{{ ec2_description }}"
KeyName: "{{key_name }}"
KmsKeyId: "{{ key_id }}"
GroupSet: "{{ id }}"
IamInstanceProfile: "{{ name }}"
Type: "OS"
**when: ec2_infos.state[0].name != 'running'**
but I get an error that says :
"msg": "The conditional check 'aws_ec2_infos.state[0].name != 'running'' failed. The error was: error while evaluating conditional (aws_ec2_infos.state[0].name != 'running'): 'dict object' has no attribute
I think I am missing something in my condition but I can't find what exactly. Any tip or advice is more than welcome
As #benoit and #mdaniel said the error was in my understanding the condition should be :
aws_ec2_infos.instances[0].state.name != 'running'

Ansible route53_zone doesn't create private and public hosted zones

I want to create two hosted zones 1 private and 1 public hosted zones. I have already created a vpc. Somehow when I run my ansible script it only creates 1 hosted zone. If the public zone task is first it will create a public zone and if the private zone task is first it will create a private zone only. I don't know if there is a bug in the module or I am doing something wrong.
---
- name: Create private hosted Zone
route53_zone:
zone: "{{ private_hosted_zone_name }}"
state: present
vpc_id: "{{ vpc_id }}"
vpc_region: "{{ vpc_region }}"
register: private_hosted_zone
- name: Print private zone id
debug:
msg: "{{ private_hosted_zone.set.zone_id }}"
- name: Set private zone ID in a variable
set_fact:
private_zone_id: "{{ private_hosted_zone.set.zone_id }}"
- name: Create public hosted Zone
route53_zone:
zone: "{{ public_hosted_zone_name }}"
state: present
register: public_hosted_zone
- name: Print public zone id
debug:
msg: "{{ public_hosted_zone.set.zone_id }}"
- name: Set public zone ID in a variable
set_fact:
public_zone_id: "{{ public_hosted_zone.set.zone_id }}"
My code for hosted zone is here:
---
- name: Create private hosted Zone
route53_zone:
zone: "{{ private_hosted_zone_name }}"
state: present
vpc_id: "{{ vpc_id }}"
vpc_region: "{{ vpc_region }}"
register: private_hosted_zone
- name: Print private zone id
debug:
msg: "{{ private_hosted_zone.set.zone_id }}"
- name: Set private zone ID in a variable
set_fact:
private_zone_id: "{{ private_hosted_zone.set.zone_id }}"
- name: Create public hosted Zone
route53_zone:
zone: "{{ public_hosted_zone_name }}"
state: present
register: public_hosted_zone
- name: Print public zone id
debug:
msg: "{{ public_hosted_zone.set.zone_id }}"
- name: Set public zone ID in a variable
set_fact:
public_zone_id: "{{ public_hosted_zone.set.zone_id }}"
Any help will be highly appreciated.
I managed to resolve this issue by upgrading Ansible from version 2.3 to version 2.4. Hope this will help someone else too.
I think there’s an issue with your yaml. I’d use yamllint to make sure it’s standard then try again. Notably, your indentations look inconsistent.

How to create the Count tags name with sequential numbers using ansible script

I create 2 windows aws machine using exact_count tag as 2.
It creates the both of 2 AWS machine with same name.
For example:
1) itg-Web-windows
2) itg-web-windows
I want to create the machine Name as instance_tags:
1)itg-windows-web-1
2)itg-windows-web-2
Below are my code:
name: ensure instances are running
ec2:
region: "{{ region }}"
image: "{{ image_id }}"
group_id: sg-1234
vpc_subnet_id: subnet-5678
instance_tags:
Name: "itg-windows-web"
exact_count: 2
count_tag:
Name: "itg-windows-web"`
register: ec2_result
This will create servers with name tags web_server_1, web_server_3 and web_server_5:
- name: create instances
ec2:
- image: <your_ami>
instance_type: t2.micro
key_name: <your_ssh_key>
region: us-east-1
vpc_subnet_id: <your_subnet_id>
count_tag:
Name: "web_server_{{ item }}"
exact_count: 1
instance_tags:
Name: "web_server_{{ item }}"
with_items: ['1', '3', '5']
Use the below ansible template:
---
- name: A sample template
hosts: local
connection: local
gather_facts: False
tasks:
- name: create instance
ec2:
keypair: test-ssh-key
instance_type: t2.micro
image: ami-abcd1234
wait: yes
instance_tags:
ec2type: web
exact_count: "{{ count }}"
count_tag:
ec2type: web
region: us-east-1
vpc_subnet_id: subnet-1234abcd
register: ec2
- name: generate sequence id for tagging
debug: msg="{{ item }}"
no_log: true
with_sequence: start="{{ startindex }}" end="{{ count }}" format=%02d
register: sequence
- name: tag instances
no_log: true
ec2_tag:
region: us-east-1
resource: "{{ item.0.id }}"
tags:
Name: "itg-windows-web-{{ item.1.msg }}"
with_together:
- "{{ ec2.instances }}"
- "{{ sequence.results }}"
command:
ansible-playbook -i ./hosts ec2-basic.yml --extra-vars "startindex=1
count=2"
Invocation-1:
ansible-playbook -i ./hosts ec2-basic.yml --extra-vars "startindex=1 count=2"
This will create 2 instances and attach name tag itg-windows-web-01 and itg-windows-web-02 to it.
Invocation 2:
ansible-playbook -i ./hosts ec2-basic.yml --extra-vars "startindex=3 count=4"
This will add 2 more instances and attach name tag itg-windows-web-03 and itg-windows-web-04 to it.
All these instances are grouped by ec2type tag.
How it works:
Use a custom tag other than Name tag for attribute count_tag. If you use Name tag, then the same tag-value is assigned for all the instances that are created(which defeats your purpose). In the above script, I have used ec2type: web as my instance_tags and count_tag. So ansible will use this tag to determine how many nodes should run based on the specific tag criteria.
The count value which you pass is assigned to exact_count in the template. Also you can have further control by passing startindex which controls the start of sequence.
with_sequence generates a sequence based on your input. Click here to read more about it.
with_together loops over parallel set of data. Click here to read more about it.
Using the above ansible loops, we append 01, 02 ... and so on to itg-windows-web text and add it to the instance Name tag.

is it possible to add a condition statement in .yml file ansible?

I have created a role and a playbook , Now in my playbook , I am defining Ec2 tags such as :
instance_tags: "Name={{ name }},bld_env={{ bld_env }},server={{ app_name }}-{{ bld_env }}"
I also have to make a condition here for bld_env thats suppose if bld_env is "test", add more tags .
can you help me out with tagging in case of conditional statement ?
Eg:
My
tagging.yml
file :
- name: Launch EC2 host
hosts: localhost
connection: local
gather_facts: False
vars:
bld_env: "{{ bld_env }}"
count: 1
instance_type: "{{ size }}"
image: "{{ image }}"
ansible_ssh_user: "ec2-user"
region: "{{ region }}"
pem_path: "~/.ssh"
group_name: "ec2hosts"
vpc_subnet_id: "{{ lookup('ini', 'vpc_subnet section=vpc file=./{{ bld_env }}.ini') }}"
assign_public_ip: "{{ ip }}"
keypair: "{{ key }}"
access_key: "{{ access }}"
secret_key: "{{ secret }}"
instance_tags: "Name={{ name }},bld_env={{ bld_env }},server={{ app_name }}-{{ bld_env }}"
security_group: "[{{ group }}]"
roles:
- ec2
Now as you can see the instance_tags here , I want to add a condition under this instance_tags as : when bld_env is "test" add another tag "Label=ec2start-stop"
So this Label has to be added only when bld_env is "test" since I am dealing with multiple env here ..
You can use any Jinja2 conditions inside expressions, for example (see end of line):
instance_tags: "Name={{ name }},bld_env={{ bld_env }},server={{ app_name }}-{{ bld_env }}{{ '' if bld_env != 'test' else ',other_tag='+other_tag_value }}"
This will append empty line if bld_env != 'test' or ,other_tag=<other_tag_value> otherwise.

Ansible - force a variable/fact to be undefined

I'm trying to run a playbook multiple times in a loop which creates AWS route53 records.
My task to create the route53 record looks like this:
- name: Create Public DNS record
route53:
profile: "{{ route53_profile_id }}"
command: "{{ dns_command }}"
zone: "{{ dns_zone }}"
record: "{{ dns_record_name }}.{{ dns_zone }}"
type: "{{ dns_type }}"
value: "{{ dns_value }}"
overwrite: "{{ dns_overwrite }}"
ttl: "{{ dns_ttl }}"
health_check: "{{ healthcheck.health_check.id | default(omit) }}"
failover: "{{ dns_setting.failover | default(omit) }}"
weight: "{{ dns_setting.weight | default(omit) }}"
region: "{{ region | default(omit) }}"
identifier: "{{ identifier | default(omit) }}"
My problem is that the health check isn't always defined every time.
Creation of the health check looks like this:
- name: Create healthcheck with IP address for EC2 instance
route53_health_check:
state: "{{ healthcheck.state | default( healthcheck_defaults.state ) }}"
profile: "{{ route53_profile_id }}"
region: "{{ vpc.region }}"
ip_address: "{{ dns_value }}"
type: "{{ healthcheck.type | default( healthcheck_defaults.type ) }}"
resource_path: "{{ healthcheck.resource_path | default( omit ) }}"
port: "{{ healthcheck.port | default( omit ) }}"
security_token: "{{ healthcheck.security_token | default( omit ) }}"
validate_certs: "{{ healthcheck.validate_certs | default( omit ) }}"
string_match: "{{ healthcheck.string_match | default( omit ) }}"
request_interval: "{{ healthcheck.request_interval | default( healthcheck_defaults.request_interval ) }}"
failure_threshold: "{{ healthcheck.failure_threshold | default( healthcheck_defaults.failure_threshold ) }}"
register: healthcheck
when:
- dns_type == "A"
- dns_setting.healthcheck is defined
If the loop runs 5 times, it may only be defined in one iteration. If the health check runs then the 'healthcheck' variable contains the details of the health check, e.g. the ID. If it does not run on a given loop, the 'healthcheck' variable contains the following:
"healthcheck": {
"changed": false,
"skip_reason": "Conditional check failed",
"skipped": true
}
In my route53 creation, the health check is omitted if the 'healthcheck' variable is undefined. However if it is defined, ansible attempts to dereference the id parameter of the health_check parameter of healthcheck, which doesn't exist.
If I try to set health check to a default value when not in use, e.g. {} then it is still defined, and my route53 creation fails.
Is there a way to force a variable or fact to be undefined? Something like:
- name: Undefine health check
set_fact:
healthcheck: undef
Try something like this:
- name: Create Public DNS record
route53:
... cut ...
health_check: "{{ healthcheck | skipped | ternary( omit, (healthcheck.health_check | default({})).id ) }}"
... cut ...
This will pass omit if healthcheck was skipped and healthcheck.health_check.id otherwise.
From my experience, default is not working properly with nested dicts of 2+ levels (i.e. works with mydict.myitem | default('ok') but fails with mydict.mysubdict.myitem | default('ok'), so I used the hack to default subdict to {} first to safely access id: (healthcheck.health_check | default({})).id