Why does this task (from Best way to launch aws ec2 instances with ansible):
- name: Add the newly created EC2 instance(s) to the local host group (located inside the directory)
local_action: lineinfile
dest="./hosts"
regexp={{ item.public_ip }}
insertafter="[webserver]" line={{ item.public_ip }}
with_items: ec2.instances
create this error?
TASK [Add the newly created EC2 instance(s) to the local host group (located inside the directory)] ********************************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'ansible.vars.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'public_ip'\n\nThe error appears to have been in '/Users/snowcrash/ansible-ec2/ec2_launch.yml': line 55, column 9, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Add the newly created EC2 instance(s) to the local host group (located inside the directory)\n ^ here\n"}
the issue is here
with_items: ec2.instances
It should be:
with_items: '{{ ec2.instances }}'
ec2 is variable referencing a dictionary so you will need to reference it with the proper syntax
Put:
- debug: msg="{{ ec2.instances }}"
before that code and inspect what are the contents of that variable. It should be a list of dictionaries that each have a member public_ip, otherwise you'd get the message that you're getting.
Related
I'm currently trying to further automate VM automation by not having to include the IP address in the variables file. I found nslookup module with dig, but feel I'm going about this the wrong way, for example here is variables file which is read upon creation for details:
# VMware Launch Variables
# If this is a test deployment you must ensure the vm is terminated after use.
vmname: agent5
esxi_datacenter: Datacenter
esxi_cluster: Cluster
esxi_datastore: ds1 # Do not change value.
esxi_template: template-v2
esxi_folder: agents # Folder must be pre-created
# Static IP Addresses
esxi_static_ip: "{{ lookup('dig', '{{ vmname }}.example.com.') }}"
esxi_netmask: 255.255.252.0
esxi_gateway: 10.0.0.1
What I was hoping to do with these was just to have the "esxi_static_ip" but pulled on the fly from a lookup with dig. This, however, in its current state does not work.
What is happening is either the VM launches without an ipv4 address or more often it fails with the following error:
fatal: [localhost -> localhost]: FAILED! => {"changed": false, "msg": "Failed to create a virtual machine : A specified parameter was not correct: spec.nicSettingMap.adapter.ip.ipAddress"}
I get the IP and pass it along, which works when I hard code the esxi_static_ip: in my vmware-lanch-vars.yml file. However, if I use (including the examples) it fails.
The newvm is registered when I run my vmware_guest playbook.
- name: Make virtual machine IP persistant
set_fact:
newvm_ip_address: '{{ newvm.instance.ipv4 }}'
- name: Add host to in memory inventory
add_host:
hostname: "{{ newvm_ip_address }}"
groups: just_created
newvm_ip_address: "{{ newvm.instance.ipv4 }}"
When I run with -vvvv I can see no IP is being attached:
"networks": [
{
"device_type": "vmxnet3",
"gateway": "0.0.0.01",
"ip": "",
"name": "Network",
"netmask": "255.255.252.0",
"type": "static"
}
],
UPDATE 3
When I created a simple playbook it works, just not when I put it into my regular flow, this below works:
---
- hosts: localhost
vars:
vmname: "apim-sb-ng1-agent2"
vm_dig_fqdn: "{{ vmname }}.example.com."
esxi_static_ip: "{{ lookup('dig', vm_dig_fqdn) }}"
tasks:
- debug: msg="{{ esxi_static_ip }}"
I am not sure this is the first problem your are facing (see my comment above), but your jinja2 template expression is wrong.
You cannot use jinja2 expression expansion while already inside a jinja2 expression expansion.
In this case, you have to concatenate your variable and string with the + operator:
esxi_static_ip: "{{ lookup('dig', vmname + '.example.com.') }}"
If your prefer to use jinja2 expansion everywhere, you can separate this in different variables, e.g.:
vm_dig_fqdn: "{{ vmname }}.example.com."
esxi_static_ip: "{{ lookup('dig', vm_dig_fqdn) }}"
I have a previously working piece of Ansible that I've inherited for a previous contractor, and I'm getting an error message that doesn't lead me in the right direction. I have tried searching for a few days now, with no joy, and my colleagues can't figure it out either.
The Ansible in question is :-
- name: Routes | Set up NAT-protected route table
ec2_vpc_route_table:
vpc_id: "{{ ec2_vpc_net_reg.vpc.id }}"
region: "{{ vpc_region }}"
tags:
Name: "Internal {{ item.subnet_id }}"
subnets:
- "{{ az_to_private_sub[public_subnets_to_az[item.subnet_id]] }}"
- "{{ az_to_private_data_sub[public_subnets_to_az[item.subnet_id]] }}"
routes:
- dest: 0.0.0.0/0
gateway_id: "{{ item.nat_gateway_id }}"
loop: "{{ existing_nat_gateways.result|flatten(levels=1) }}"
#with_items: "{{ existing_nat_gateways.result }}"
register: nat_route_table
retry: 2
delay: 10
And the error message is :-
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute u'subnet-0facefaceface9'\n\nThe error appears to have been in '/cup/core-kubernetes-linux/ansible/roles/aws_vpc/tasks/routes.yml': line 62, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: Routes | Set up NAT-protected route table\n ^ here\n"}
I have tried adding extra debug, for az_to_private_sub and public_subnet_to_az, and these look OK. I've tried reading the docs
Can anyone suggest where I should look next?
Thanks!
After speaking to a different colleague, they pointed out that the version which works uses Ansible 2.5.5 and I was trying with 2.5.1 also the boto python libraries need to be using the correct version.
I have used Ansible to create 1 AWS EC2 instance using the examples in the Ansible ec2 documentation. I can successfully create the instance with a tag. Then I temporarily add it to my local inventory group using add_host.
After doing this, I am having trouble when I try to configure the newly created instance. In my Ansible play, I would like to specify the instance by its tag name. eg. hosts: <tag_name_here>, but I am getting an error.
Here is what I have done so far:
My directory layout is
inventory/
staging/
hosts
group_vars/
all/
all.yml
site.yml
My inventory/staging/hosts file is
[local]
localhost ansible_connection=local ansible_python_interpreter=/home/localuser/ansible_ec2/.venv/bin/python
My inventory/staging/group_vars/all/all.yml file is
---
ami_image: xxxxx
subnet_id: xxxx
region: xxxxx
launched_tag: tag_Name_NginxDemo
Here is my Ansible playbook site.yml
- name: Launch instance
hosts: localhost
gather_facts: no
tasks:
- ec2:
key_name: key-nginx
group: web_sg
instance_type: t2.micro
image: "{{ ami_image }}"
wait: true
region: "{{ region }}"
vpc_subnet_id: "{{ subnet_id }}"
assign_public_ip: yes
instance_tags:
Name: NginxDemo
exact_count: 1
count_tag:
Name: NginxDemo
exact_count: 1
register: ec2
- name: Add EC2 instance to inventory group
add_host:
hostname: "{{ item.public_ip }}"
groupname: tag_Name_NginxDemo
ansible_user: centos_user
ansible_become: yes
with_items: "{{ ec2.instances }}"
- name: Configure EC2 instance in launched group
hosts: tag_Name_NginxDemo
become: True
gather_facts: no
tasks:
- ping:
I run this playbook with
$ cd /home/localuser/ansible_ec2
$ source .venv/bin/activate
$ ansible-playbook -i inventory/staging site.yml -vvv`
and this creates the EC2 instance - the 1st play works correctly. However, the 2nd play gives the following error
TASK [.....] ******************************************************************
The authenticity of host 'xx.xxx.xxx.xx (xx.xxx.xxx.xx)' can't be established.
ECDSA key fingerprint is XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.
Are you sure you want to continue connecting (yes/no)? yes
fatal: [xx.xxx.xxx.xx]: FAILED! => {"changed": false, "module_stderr":
"Shared connection to xx.xxx.xxx.xx closed.\r\n", "module_stdout": "/bin/sh:
1: /usr/bin/python: not found\r\n", "msg": "MODULE FAILURE", "rc": 127}
I followed the instructions from
this SO question to create the task with add_hosts
here to set gather_facts: False, but this still does not allow the play to run correctly.
How can I target the EC2 host using the tag name?
EDIT:
Additional info
This is the only playbook I have run to this point. I see this message requires Python but I cannot install Python on the instance as I cannot connect to it in my play Configure EC2 instance in launched group...if I could make that connection, then I could install Python (if this is the problem). Though, I'm not sure how to connect to the instance.
EDIT 2:
Here is my Python info on the localhost where I am running Ansible
I am running Ansible inside a Python venv.
Here is my python inside the venv
$ python --version
Python 2.7.15rc1
$ which python
~/ansible_ec2/.venv/bin/python
Here are my details about Ansible that I installed inside the Python venv
ansible 2.6.2
config file = /home/localuser/ansible_ec2/ansible.cfg
configured module search path = [u'/home/localuser/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /home/localuser/ansible_ec2/.venv/local/lib/python2.7/site-packages/ansible
executable location = /home/localuser/ansible_ec2/.venv/bin/ansible
python version = 2.7.15rc1 (default, xxxx, xxxx) [GCC 7.3.0]
Ok, so after a lot of searching, I found 1 possible workaround here. Basically, this workaround uses the lineinfile module and adds the new EC2 instance details to the hosts file permanently....not just for the in-memory plays following the add_host task. I followed this suggestion very closely and this approach worked for me. I did not need to use the add_host module.
EDIT:
The line I added in the lineinfile module was
- name: Add EC2 instance to inventory group
- lineinfile: line="{{ item.public_ip }} ansible_python_interpreter=/usr/bin/python3" insertafter=EOF dest=./inventory/staging/hosts
with_items: "{{ ec2.instances }}"
From: Best way to launch aws ec2 instances with ansible
- name: Add the newly created EC2 instance(s) to the local host group (located inside the directory)
local_action: lineinfile
dest="/etc/ansibles/aws/hosts"
regexp={{ item.private_ip }}
insertafter="[webserver]" line={{ item.private_ip }}
with_items: "{{ ec2.instances }}"
creates this error:
fatal: [localhost]: FAILED! => {
"failed": true,
"msg": "the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'ansible.vars.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'private_ip'\n\n
I have defined variable private_ip: under vars , with a value
I think that the private_ip property in the code above references to the property of the ec2 variable that's used to catch the returned values from the ec2 module (from the last step), no the one that you defined elsewhere.
- name: Launch the new EC2 Instance
local_action: ec2
group={{ security_group }}
instance_type={{ instance_type}}
image={{ image }}
wait=true
region={{ region }}
keypair={{ keypair }}
count={{count}}
register: ec2 (this is where the variable is defined!!!)
Essentially Ansible is complaining that the ec2 variable hasen't got the
attribute 'private_ip'
, so check the preceding code and see how that variable gets defined.
In the example above you're trying to get the private_ip address from aws. Is that really what you want? Most of the time you want the public ip address since that's what you will use to connect to the ec2 machine, provision it, deploy your app etc...
This question already has answers here:
How to use Ansible's with_item with a variable?
(2 answers)
Closed 5 years ago.
I wrote an ansible task to create ec2 instance and add the host as dynamic host. The task is working perfectly and created the instance, But Am not able to retrieve instance information.
My Ansible version : 2.2.0.0 / Ubuntu 14.04
Here is my code
- name: launch ec2 instance for QA
local_action:
module: ec2
key_name: "{{ ec2_keypair }}"
group: "{{ ec2_security_group }}"
instance_type: "{{ ec2_instance_type }}"
image: "{{ ec2_image }}"
vpc_subnet_id: "{{ ec2_subnet_ids }}"
region: "{{ ec2_region }}"
instance_tags: '{"Name":"{{ec2_tag_Name}}","Type":"{{ec2_tag_Type}}","Environment":"{{ec2_tag_Environment}}"}'
assign_public_ip: yes
wait: true
count: 1
register: ec2
- debug: var=item
with_items: ec2.instances
- add_host: name={{ item.public_ip }} >
groups=dynamically_created_hosts
with_items: ec2.instances
- name: Wait for the instances to boot by checking the ssh port
wait_for: host={{item.public_ip}} port=22 delay=60 timeout=320 state=started
with_items: ec2.instances
The output what am getting is:
TASK [launch ec2 instance for QA] **********************************************
changed: [localhost -> localhost]
TASK [debug] *******************************************************************
ok: [localhost] => (item=ec2.instances) => {
"item": "ec2.instances"
}
TASK [add_host] ****************************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'unicode object' has no attribute 'public_ip'\n\nThe error appears to have been in '/var/lib/jenkins/jobs/QA/workspace/dynamic-ec2.yml': line 37, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - add_host: name={{ item.public_ip }} >\n ^ here\nWe could be wrong, but this one looks like it might be an issue with\nmissing quotes. Always quote template expression brackets when they\nstart a value. For instance:\n\n with_items:\n - {{ foo }}\n\nShould be written as:\n\n with_items:\n - \"{{ foo }}\"\n"}
Is there any other way to do this?
You cannot use bare variables in 2.2. The syntax has been deprecated and users were warned since version 2.0.
You should read the error message you have pasted, and although it suggests a different reason, you should follow the example given:
Should be written as:
with_items:
- "{{ foo }}"
In your case it's enough to replace all with_items: ec2.instances with:
with_items: "{{ ec2.instances }}"