here we have an ec2 module in a playbook:
---
# This playbook creates dev-test instances at AWS.
- name: test creation
hosts: localhost
gather_facts: False
tasks:
- name: instance creation
ec2:
key_name: dev-key
group_id: sg-55667788
instance_type: t2.micro
image: ami-cd0f5cb6
wait: yes
wait_timeout: 300
volumes:
- device_name: /dev/sda1
volume_type: gp2
volume_size: 32
delete_on_termination: True
- device_name: /dev/xvdb
volume_type: gp2
volume_size: 1
delete_on_termination: True
- device_name: /dev/xvdc
volume_type: gp2
volume_size: 200
delete_on_termination: True
vpc_subnet_id: "{{ item.vpc_subnet_id }}"
zone: "{{ item.zone }}"
region: us-east-1
assign_public_ip: no
private_ip: "{{ item.private_ip }}"
instance_tags:
Name: "{{ item.tag_name }}"
user_data: |
#!/bin/bash
mkswap /dev/xvdb
swapon /dev/xvdb
echo "/dev/xvdb none swap sw 0 0" >> /etc/fstab
echo "n
e
1
n
l
w
" | fdisk /dev/xvdc
with_items:
- { vpc_subnet_id: 'subnet-11223344', zone: 'us-east-1d', private_ip: '172.31.5.15', tag_name: 'dev-test.vpc-01' }
- { vpc_subnet_id: 'subnet-44332211', zone: 'us-east-1e', private_ip: '172.31.7.13', tag_name: 'dev-test.vpc-01' }
And here the error output:
test#test:~$ ansible-playbook --check create-ec2.yml
[WARNING]: Could not match supplied host pattern, ignoring: all
[WARNING]: provided hosts list is empty, only localhost is available
PLAY [instance creation] *********************************************************************************************************
TASK [instance creation] ************************************************************************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "The task includes an option with an undefined variable. The error was: 'item' is undefined\n\nThe error appears to have been in 'create-ec2.yml': line 8, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: cria instancia\n ^ here\n\nexception type: <class 'ansible.errors.AnsibleUndefinedVariable'>\nexception: 'item' is undefined"}
to retry, use: --limit #create-ec2.retry
PLAY RECAP ***********************************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1
I don't see where the mistake is. Where is this item undefined?
with_items is wrongly indented. It should be on a task level, not on task parameters level.
In result the directive for with-loop is ignored and the variable item is not defined.
Related
When I run my playbook I see the following error:
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: list object has no element 0\n\nThe e
rror appears to be in '/etc/ansible/loyalty/tasks/create_ec2_stage.yaml': line 63, column 7, but may\nbe elsewhere in the file depending on the exac
t syntax problem.\n\nThe offending line appears to be:\n\n register: ec2_metadata\n - name: Parse < JSON >\n ^ here\n"}
I run the playbook this way:
/usr/bin/ansible-playbook -i hosts --extra-vars "CARRIER=xx" tasks/create_ec2_stage.yaml
Here is my playbook:
---
- name: New EC2 instances
hosts: localhost
gather_facts: no
vars_files:
- /etc/ansible/loyalty/vars/vars.yaml
tasks:
- name: Run EC2 Instances
amazon.aws.ec2_instance:
name: "new-{{ CARRIER }}.test"
aws_secret_key: "{{ ec2_secret_key }}"
aws_access_key: "{{ ec2_access_key }}"
region: us-east-1
key_name: Kiu
instance_type: t2.medium
image_id: xxxxxxxxxxxxx
wait: yes
wait_timeout: 500
volumes:
- device_name: /dev/xvda
ebs:
volume_type: gp3
volume_size: 20
delete_on_termination: yes
vpc_subnet_id: xxxxxxxxxxxx
network:
assign_public_ip: no
security_groups: ["xxxxxxxxxx", "xxxxxxxxxxx", "xxxxxxxxxxxxxx"]
tags:
Enviroment: TEST
count: 1
- name: Pause Few Seconds
pause:
seconds: 20
prompt: "Please wait"
- name: Get Information for EC2 Instances
ec2_instance_info:
region: us-east-1
filters:
"tag:Name": new-{{ CARRIER }}.test
register: ec2_metadata
- name: Parse JSON
set_fact:
ip_addr: "{{ ec2_metadata.instances[0].network_interfaces[0].private_ip_address }}"
If I create a slightly smaller playbook to query the private IP address of an existing instance, I don’t see any error.
---
- name: New EC2 Instances
hosts: localhost
gather_facts: no
vars_files:
- /etc/ansible/loyalty/vars/vars.yaml
vars:
pwd_alias: "{{ lookup('password', '/dev/null length=15 chars=ascii_letters') }}"
CARRIER_UPPERCASE: "{{ CARRIER | upper }}"
tasks:
- set_fact:
MY_PASS: "{{ pwd_alias }}"
- name: Get EC2 info
ec2_instance_info:
region: us-east-1
filters:
"tag:Name": new-{{ CARRIER }}.stage
register: ec2_metadata
- name: Parsing JSON
set_fact:
ip_addr: "{{ ec2_metadata.instances[0].network_interfaces[0].private_ip_address }}"
- name: Show Result
debug:
msg: "{{ ip_addr }}"
Results in
TASK [Show Result] ******************************************************
ok: [localhost] => {
"msg": "172.31.x.x"
}
I am creating an EC2 instance on Amazon and querying the private IP to use that IP on other services like router53 and Cloudflare, other tasks do not add them because the error is in the fact.
You don't have to query AWS with the ec2_instance_info module since the module amazon.aws.ec2_instance already returns you the newly created EC2 when the parameter wait is set to yes, as it is in your case.
You just need to register the return of this task.
So, given the two tasks:
- name: Run EC2 Instances
amazon.aws.ec2_instance:
name: "new-{{ CARRIER }}.test"
aws_secret_key: "{{ ec2_secret_key }}"
aws_access_key: "{{ ec2_access_key }}"
region: us-east-1
key_name: Kiu
instance_type: t2.medium
image_id: xxxxxxxxxxxxx
wait: yes
wait_timeout: 500
volumes:
- device_name: /dev/xvda
ebs:
volume_type: gp3
volume_size: 20
delete_on_termination: yes
vpc_subnet_id: xxxxxxxxxxxx
network:
assign_public_ip: no
security_groups: ["xxxxxxxxxx", "xxxxxxxxxxx", "xxxxxxxxxxxxxx"]
tags:
Enviroment: TEST
count: 1
register: ec2
- set_fact:
ip_addr: "{{ ec2.instance[0].private_ip_address }}"
You should have the private IP address of the newly created EC2 instance.
I want to be able to reference the variable vpc_info registered by file create-public-vpc from file create-public-sunbet
/etc/ansible/roles/ec2/tasks/main.yml
# tasks file for ec2-provision
- name:
import_tasks: create-vpc.yml
import_tasks: create-public-subnet.yml
/etc/ansible/roles/ec2/vars/main.yml
---
# vars file for ec2-provision
################################### designate python interpreter ########################
ansible_python_interpreter: /usr/local/bin/python3.8
############################## VPC INFO #########################################
vpc_name: "My VPC"
vpc_cidr_block: "10.0.0.0/16"
aws_region: "us-east-1"
################################### VPC Subnet ###############################################
aws_zone: "us-east-1a"
# Subnets
vpc_public_subnet_cidr: "10.0.0.0/24"
# Subnet
vpc_private_subnet_cidr: "10.0.1.0/24"
create-vpc.yml
- name: Create AWS VPC
ec2_vpc_net:
name: "{{ vpc_name }}"
cidr_block: "{{ vpc_cidr_block }}"
region: "{{ aws_region }}"
aws_access_key: "{{ access_key }}"
aws_secret_key: "{{ secret_key }}"
state: present
register: vpc_info
- name: Set vpc_info as fact
set_fact:
vpc_info_fact: "{{ vpc_info }}"
create-public-sunbet.yml
- name: print vpc_info_fact
debug:
msg: "{{ hostvars['localhost']['vpc_info_fact'] }}"
- name: Create Public Subnet in VPC
ec2_vpc_subnet:
vpc_id: "{{ vpc_info['vpc']['id'] }}"
cidr: "{{ vpc_public_subnet_cidr }}"
region: "{{ aws_region }}"
az: "{{ aws_zone }}"
aws_access_key: "{{ access_key }}"
aws_secret_key: "{{ secret_key }}"
state: present
tags:
Name: Public Subnet
register: public_subnet_info
When I run ansible-playbook ec2-provision.yml, the error message is as follows:
[root#VM-0-14-centos tasks]# ansible-playbook ec2-provision.yml
[WARNING]: While constructing a mapping from /etc/ansible/roles/EC2/tasks/main.yml, line 4, column 3, found a duplicate dict key (import_tasks). Using last defined value
only.
PLAY [localhost] ************************************************************************************************************************************************************
TASK [Gathering Facts] ******************************************************************************************************************************************************
ok: [localhost]
TASK [EC2 : print vpc_info_fact] ********************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'vpc_info_fact' is undefined\n\nThe error appears to be in '/etc/ansible/roles/EC2/tasks/create-public-subnet.yml': line 3, 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: print vpc_info_fact\n ^ here\n"}
PLAY RECAP ******************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Try to set the variable value as a fact once the variable is created and then you'd access the corresponding fact via hostvars.
For example:
- name: Create AWS VPC
ec2_vpc_net:
name: "{{ vpc_name }}"
cidr_block: "{{ vpc_cidr_block }}"
region: "{{ aws_region }}"
aws_access_key: "{{ access_key }}"
aws_secret_key: "{{ secret_key }}"
state: present
register: vpc_info
- name: Set vpc_info as fact
set_fact: vpc_info_fact="{{ vpc_info }}"
To access it from a different file, we have the following task:
- name: Create Public Subnet in VPC
ec2_vpc_subnet:
vpc_id: "{{ hostvars['localhost']['vpc_info']['vpc']['id'] }}"
cidr: "{{ vpc_public_subnet_cidr }}"
region: "{{ aws_region }}"
az: "{{ aws_zone }}"
aws_access_key: "{{ access_key }}"
aws_secret_key: "{{ secret_key }}"
state: present
tags:
Name: Public Subnet
register: public_subnet_info
Your main.yml contains a tasks with two modules. It should be
- name: Create VPC
import_tasks: create-vpc.yml
- name: Create Public Subnets
import_tasks: create-public-subnet.yml
The running playbooks prints the warning about that issue.
[WARNING]: While constructing a mapping from /etc/ansible/roles/EC2/tasks/main.yml, line 4, column 3, found a duplicate dict key (import_tasks). Using last defined value
only.
Ansible cannot have more then one module per task - and include_tasks is a module. It picks the last module, if there are multiple modules in one task (after writing the warning message and not exiting).
This is the main problem of all your issues. Everything else looks ok to me.
I am trying to automate ACI with ansible, very new to ansible, as a start point, I started to create simple Tenant and VRF and I am using a variable with my playbook, here it is my playbook like below.
---
- name: Playbook
hosts: APIC
connection: local
gather_facts: no
tasks:
- name: Create new tenant
aci_tenant:
host: "{{ apic_host }}"
username: "{{ apic_username }}"
password: "{{ apic_password }}"
tenant: "{{ tenant_name }}"
validate_certs: False
description: "{{ tenant_description }}"
state: present
# - pause:
# seconds: 10
- name: Add a new VRF
aci_vrf:
host: "{{ apic_host }}"
username: "{{ apic_username }}"
password: "{{ apic_password }}"
tenant: "{{ tenant_name }}"
validate_certs: false
vrf: "{{ vrf_name }}"
policy_control_preference: enforced
policy_control_direction: ingress
state: present
# - pause:
# seconds: 30
and here is my variable file
---
apic_host: sandboxapicdc.cisco.com
apic_username: admin
apic_password: ciscopsdt
tenant_name: zhajili_TN
tenant_description: 'zhajili_first_ansible_tenant'
ap_name: 'AP'
ap_description: 'AP created with Ansible'
vrf_name: 'zhajili_VRF'
When I execute the playbook I receive the following error.
$ ansible-playbook tenants_vrfs.yml -i inventory -v
No config file found; using defaults
PLAY [Playbook] ************************************************************************************************************************************************************************************************
TASK [Create new tenant] ***************************************************************************************************************************************************************************************
fatal: [apicsim_sandbox]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'apic_host' is undefined\n\nThe error appears to be in '/Users/zhajili/Desktop/ansible/ACI/Cisco Sandbox/Tenants_and_VRFs/tasks/tenants_vrfs.yml': line 8, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: Create new tenant\n ^ here\n"}
PLAY RECAP *****************************************************************************************************************************************************************************************************
apicsim_sandbox : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Have task which copies each user's key
- name: SSH Keys
authorized_key:
user: "{{ item.0.name }}"
key: "{{ item.0.ssh_key.0.key }}"
state: "{{ item.0.ssh_key.0.state }}"
when:
- item.1 == 'all' or item.1 in group_names or item.1 == inventory_hostname
with_subelements:
- "{{ users }}"
- servers
Var list:
users:
- name: user1
ssh_key:
- key:
- "key1.user1"
- "key2.user1"
- "key3.user1"
state: present
servers:
- server1
- name: user2
ssh_key:
- key:
- "key1.user2"
- "key2.user2"
state: present
servers:
- all
QUESTION: How can we allow users to copy multiple keys? Without deleting servers from with_subelements.
When starting the task, either the last key or an array with keys is copied, depending on how we write it in var list.
In this format copied last key.
- key: "key1.user1"
- key: "key2.user1"
- key: "key3.user1"
In this array.
- key:
- "key1"
- "key2"
Let's fit the structure of the data to this purpose. For example,
users:
- name: user1
ssh_key:
- "key1.user1"
- "key2.user1"
- "key3.user1"
state: present
servers:
- server1
...
It's possible to loop include_tasks. For example, create the task (test it with debug first)
shell> cat conf_authorized_key.yml
- name: SSH Keys
# authorized_key:
debug:
msg:
- "user: {{ item.0.name }}"
- "state: {{ item.0.state }}"
- "key: {{ iitem }}"
loop: "{{ item.0.ssh_key }}"
loop_control:
loop_var: iitem
Then include it in the playbook
shell> cat playbook.yml
- hosts: localhost
vars:
users:
- name: user1
ssh_key:
- "key1.user1"
- "key2.user1"
- "key3.user1"
state: present
servers:
- server1
- name: user2
ssh_key:
- "key1.user2"
- "key2.user2"
state: present
servers:
- all
tasks:
- name: Loop include_task
include_tasks: conf_authorized_key.yml
loop: "{{ users|subelements('servers') }}"
loop_control:
label: "{{ item.1 }}"
when: (item.1 == 'all') or
(item.1 in group_names) or
(item.1 == inventory_hostname)
gives
shell> ansible-playbook playbook.yml
PLAY [localhost] ****
TASK [Loop include_task] ****
skipping: [localhost] => (item=server1)
included: /export/scratch/tmp/conf_authorized_key.yml for localhost
TASK [SSH Keys] ****
ok: [localhost] => (item=key1.user2) => {
"msg": [
"user: user2",
"state: present",
"key: key1.user2"
]
}
ok: [localhost] => (item=key2.user2) => {
"msg": [
"user: user2",
"state: present",
"key: key2.user2"
]
}
PLAY RECAP ****
localhost: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
I am iterating over yml file and filtering and keeping those microservice metadata in the list which is in the command line argument passed
ansible-playbook -i inventory/inventory sp-deployer.yml --ask-vault-pass --extra-vars '{"microservices_list":[iwan,csrservice]}'
Finally I need these three value from the yml file based on the criteria mentioned above. I have created ansible sp-deployer.yml for this purpose. I have used set_facts for creating dynamic list. First list works fine but the moment I create second one it fails.
name: "ms_service"
port: "830"
streams: "noti,jk-noti"
vars.yml
version: 1
name: user
jobs:
ns1:
ip: "1.1.1.1"
ns_version: "4.2"
f_packs:
- f-pack-1:
name: "pack1"
microservices:
- microservice-1:
name: "ms_service"
port: "830"
streams: "noti,jk-noti"
- microservice-2:
name: "ms_service1"
port: "830"
streams: "noti1,jk-noti1"
- f-pack-2:
name: "pack4"
microservices:
- microservice-1:
name: "ms_service3"
port: "830"
streams: "noti,jk-noti3"
- microservice-2:
name: "ms_service4"
port: "830"
streams: "noti,jk-noti4"
- microservice-3:
name: "ms_service5"
port: "830"
streams: "noti,jk-noti5"
Script:sp-deployer.yml
---
- hosts: localhost
vars_files:
- ./vars.yml
sudo: yes
tasks:
- name: Reading vars.yml file and preparing list of microservice with its metadata
set_fact: foo_item="{{ item.1 }}"
register: result
with_subelements:
- "{{ jobs.ns1.f_packs }}" ###item.0
- microservices ###item.1
- name: make first list
set_fact: foo="{{ result.results | map(attribute='ansible_facts.foo_item') | list }}"
- name: print register
debug: msg="{{ item }}" verbosity=3
with_items:
- "{{ foo }}"
- name: Filtering micro service list with match found from command line input
when: item[0].name == item[1]
set_fact: foo_item1="{{ item.0 }}"
register: result_final
with_nested:
- "{{ foo }}"
- "{{ microservices_list }}"
- name: make a list
set_fact: foo1="{{ result_final.results | map(attribute='ansible_facts.foo_item1') | list }}"
ERROR
TASK [make a list] *************************************************************
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: 'dict object' has no attribute 'ansible_facts'\n\nThe error appears to have been in '/home/user/ansible/sp-deployer1.yml': line 40, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: make a list\n ^ here\n"}
to retry, use: --limit #/home/user/ansible/sp-deployer1.retry
PLAY RECAP *********************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=1
Friendly advice: always check registered variables with debug if you bump on such errors.
The reason for your error is that set_fact don't yield ansible_facts dict if the loop iteration is skipped.
And I see you have when statement in your loop.
To overcome this error, you should select only those loop iterations, that have ansible_facts dict defined:
- name: make a list
set_fact: foo1="{{ result_final.results | selectattr('ansible_facts','defined') | map(attribute='ansible_facts.foo_item1') | list }}"