I'm trying to query AWS EC2 AMIs from Ansible but keep running into an error when looping through the results:
- hosts: localhost
tasks:
- name: Get AMI
ec2_ami_facts:
owner: amazon
filters:
architecture: x86_64
root-device-type: ebs
register: amis
- name: return filtered data
debug:
msg: "{{ item }}"
loop: " {{ amis \
| json_query( 'Images[?Description!=`null`] \
| [?starts_with(Description,`Amazon Linux`)]' ) \
}} "
The idea is to return the image documents, and later just the image IDs with more filtering (end goal is to get the most recent ami id for a given description). But with the current example, and anything else I try I get this error:
TASK [return filtered data] ****************************************************
fatal: [localhost]: FAILED! => {"msg": "Invalid data passed to 'loop',
it requires a list, got this instead: . Hint: If you passed a
list/dict of just one element, try adding wantlist=True to your lookup
invocation or use q/query instead of lookup."}
I can look at the 'amis' in its entirety and it looks good, but any filtering I try fails. What is the correct method?
This works, thanks for the folks at #ansible on freenode.
- hosts: localhost
tasks:
- name: Get AMI
ec2_ami_facts:
owner: amazon
filters:
architecture: x86_64
root-device-type: ebs
register: amis
- name: return latest AMI
set_fact:
my_ami: "{{ amis.images \
| selectattr('description', 'defined') \
| selectattr('description', 'match', '^Amazon Linux.*GP2$') \
| selectattr('description', 'match', '[^(Candidate)]') \
| sort(attribute='creation_date') \
| last }} "
- debug:
msg: "ami = {{ my_ami | to_nice_yaml }}"
Also see here: https://bitbucket.org/its-application-delivery/ansible-aws/src/master/ansible/task_find_ami.yml?fileviewer=file-view-default
Use following to dynamically fetch the latest AMI.
---
- name: Find latest AMI
ec2_ami_facts:
owners: 099720109477
region: "{{ AWS_REGION }}"
filters:
name: "ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"
register: findami
- name: Sort the latest AMI
set_fact:
latest_ami: >
{{ findami.images | sort(attribute='creation_date') | last }}
- name: Launch Instance with latest AMI
ec2:
instance_type: "{{ INSTANCE_TYPE }}"
image: "{{ latest_ami.image_id }}"
key_name: "{{ KEY_NAME }}"
region: "{{ AWS_REGION }}"
group_id: "{{ sg.group_id }}"
wait: yes
count: "{{ INSTANCES_COUNT }}"
vpc_subnet_id: "{{ subnet.subnet.id }}"
assign_public_ip: no
Related
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'
I'm doing "aws rds describe-db-instances":
- name: list recovery points by resource
shell: aws rds describe-db-instances --db-instance-identifier {{ item }} --query "DBInstances[?(LatestRestorableTime>'{{ time }}')].[DBInstanceIdentifier,LatestRestorableTime]" --output text
loop: "{{ db_identifier }}"
register: recovery_points
environment:
AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
AWS_SESSION_TOKEN: "{{ security_token }}"
-->until: (recovery_points.results|map(attribute='stdout')|list|length) > 0
-->retries: 20
-->delay: 30
when: db_identifier is defined
That should check for recovery points older than "time", also db_identifier|lenght = 2.
- debug:
msg: "{{ recovery_points.results | map(attribute='stdout') | list }}"
result:
['', '']
The issue I'm facing is that when there are no recovery points and it returns this empty list, it says the length of this list is 2.
- debug:
msg: "{{ recovery_points.results | map(attribute='stdout') | list | length }}"
result:
2
Having this, I wanted to include the "until" condition in the shell command (that you can see preceded by -->) but I cannot do it because of this length.
How can I work around it?
I am running several Ansible playbooks in AWS and I am having difficulty with a test yaml file. The purpose of the yaml file is to query AWS for a list of servers using a filter and set_fact the instance name, the instance ID the instance size and the private IP.
The code I have is returning only data for the first node in the list and repeats the debug line every 12 lines. All the other lines show no data returned. I am using the ec2_instance_info to get the various data about the instances.
Here is the Ansible yaml file
---
# This script gathers the Instance ID's et. al.
- name: Get EC2 Info
ec2_instance_info:
region: '{{ aws_region }}'
aws_access_key: "{{ lookup('ini', 'aws_access_key_id section=saml file=~/.aws/credentials') }}"
aws_secret_key: "{{ lookup('ini', 'aws_secret_access_key section=saml file=~/.aws/credentials') }}"
security_token: "{{ lookup('ini', 'aws_session_token section=saml file=~/.aws/credentials') }}"
filters:
"tag:Name": "test-envMan*"
register: Instance_ID
- name: Get Instance ID
debug:
msg: "{{ item.0 }} | {{ item.1 }} | {{ item.2 }} | {{ item.3 }}"
with_together:
- "{{ Instance_ID.instances | map(attribute='tags.Name') | list }}"
- "{{ Instance_ID.instances[0].instance_id }}"
- "{{ Instance_ID.instances[1].instance_type }}"
- "{{ Instance_ID.instances[2].private_ip_address }}"
- name: Gather and Save info
set_fact:
Tag_Name: "{{ Instance_ID.instances | map(attribute='tags.Name') | list }}"
Instance_ID: "{{ Instance_ID.instances[0].instance_id }}"
Instance_Size: "{{ Instance_ID.instances[1].instance_type }}"
Instance_PrivIP: "{{ Instance_ID.instances[2].private_ip_address }}"
The output shows 12 lines of Ansible "ok" output for each server. The first line of which includes the debug output of the expected fields for the first node.
So 1 line of "ok" log output, then the debug line. Then 11 lines of "ok" log output of the same node. Then 1 line of "ok" output for the second node, the the debug line for the first node. etc.
I need to discover what I am doing incorrectly and how to make it behave.
Any comments, suggestions or pointers are appreciated.
Thanks.
I'm trying to retrieve password from aws secret manager using ansible 2.8 using lookup.
Below things are not working for me:
In .bashrc, I have exported region
Ansible Environment Variables in task
Setting up ansible variables in pre_tasks
- hosts: StagingApps
remote_user: staging
gather_facts: false
tasks:
- debug:
var: "{{ lookup('aws_secret', 'staging_mongodb_pass', region='us-east-1') }}"
msg: "{{ query('aws_secret', 'staging_mongodb_pass', region='us-east-1') }}"
environment:
region: 'us-east-1'
Error Message:
FAILED! => {"msg": "An unhandled exception occurred while running the lookup plugin 'aws_secret'. Error was a , original message: 'Requested entry (plugin_type: lookup plugin: aws_secret setting: region ) was not defined in configuration.'"}
below playbook has worked for me
- name: "register mongodb from secretsmanager"
shell: "aws secretsmanager get-secret-value --secret-id staging_mongodb"
register: mongodb_pass
delegate_to: 127.0.0.1
- set_fact:
mongodb_pass_dict: "{{ mongodb_pass.stdout | from_json | json_query('SecretString') }}"
- set_fact:
mongodb_pass_list: "{{ ['staging_mongodb'] | map('extract', mongodb_pass_dict) | list }}"
- set_fact:
mongodb_pass: "{{ mongodb_pass_list[0] }}"
- template:
src: application.properties.j2
dest: application.properties
mode: 0644
backup: yes
It looks like Ansible released this lookup plugin in a broken state. They have an issue and a PR open to fix it:
https://github.com/ansible/ansible/issues/54790
https://github.com/ansible/ansible/pull/54792
Very disappointing, as I've been waiting for this plugin for many months.
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.