Packer prepare error while building an image using VMware player as host - packer

I am new to packer.
I am trying to build a vmware VM using packer. I am using VMware player as host.
I set the variables and select the builders as following:
"builders": [
{
"boot_command": [" text net.ifnames=0 biosdevname=0 ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ks.cfg"],
"disk_size": "{{ user disk_size }}",
"cpus": "{{ user cpus }}",
"memory": "{{ user memory }}",
"guest_os_type": "{{ user guest_os_type }}",
"headless": "{{ user headless }}",
"iso_checksum": "{{ user iso_checksum }}",
"iso_checksum_type": "{{ user iso_checksum_type }}",
"iso_url": "{{ user iso_url }}",
"output_directory": "output-{{ user vm_name }}-cloud",
"shutdown_command": "{{ user shutdown_command }}",
"shutdown_timeout": "5m",
"ssh_password": "{{ user ssh_password }}",
"ssh_username": "{{ user ssh_username }}",
"ssh_wait_timeout": "{{ user ssh_wait_timeout }}",
"type": "vmware-iso",
"vm_name": "{{ user vm_name }}"
}
],
"variables": {
"cpus": "2",
"disk_size": "",
"headless": "false",
"iso_checksum": "",
"iso_checksum_type": "sha256",
"iso_url": "file://",
"kickstart": "ks.cfg",
"memory": "512",
"shutdown_command": "shutdown -P now",
"ssh_password": "packer",
"ssh_username": "root",
"ssh_wait_timeout" : "10000s",
"guest_os_type": "rhel7-64",
"vm_name": ""
}
The error I am receiving is "Build 'vmware-iso' prepare failure: 1 error occurred:
* unknown configuration key:" then it list all the parameters
After that I am receiving this --> and ctx data is map[interface {}]interface {}(nil)
Anyone can help?
Thanks in advance!!

After a I run the following commands:
"packer fix myfile.json > myfile-fixed.json"
"packer validate myfile-fixed.json"
issue resolved

Related

ansible match variable pattern to json output

I am using the Ansible vmware_vm_info module to generate json like this:
{
"virtual_machines": [
{
"guest_name": "serverx",
"datacenter": "datacenter",
"guest_fullname": "Red Hat Enterprise Linux 7 (64-bit)",
"esxi_hostname": "host1",
"tags": [
],
"cluster": "cluster1",
"vm_network": {
},
"mac_address": [
],
"attributes": {
},
"folder": "",
"power_state": "poweredOff",
"ip_address": "",
"uuid": "33234323oijdlk"
},
{
"guest_name": "server2_01112022",
"datacenter": "datacenter",
"guest_fullname": "Microsoft Windows Server 2012 (64-bit)",
"esxi_hostname": "host1",
"tags": [
],
"cluster": "cluster1",
"vm_network": {
},
"mac_address": [
],
"attributes": {
},
"folder": "",
"power_state": "poweredOff",
"ip_address": "",
"uuid": "287292lkqjjjjd"
},
I have a variable that reads a csv and sets the vmname like this:
vmname
server1
server2
I need set a new fact that will look for the vmname string existing in the vminfo json output. In the example above, server2 would match server2_01112022.
I have tried the following but I don't get a match:
- set_fact:
renamedvm: "{{ item.guest_name }}"
with_items:
- "{{ vminfo.virtual_machines }}"
when:
- item.guest_name |regex_search('^result_item.vmname.$')
For example, given the list
vmname: [server1, server2]
the task
- set_fact:
renamedvm: "{{ renamedvm|d({})|combine({item: _value}) }}"
loop: "{{ vmname|unique|sort }}"
vars:
_selection: "{{ virtual_machines|selectattr('guest_name', 'search', item) }}"
_value: "{{ _selection|map(attribute='guest_fullname')|list }}"
gives
renamedvm:
server1: []
server2:
- Microsoft Windows Server 2012 (64-bit)

Loop over multiple JSON arrays to filter the relevant JSON object based on a key value in Ansible

I am trying to loop over 2 different JSON array and filter only if the 2 key values are same.
I am comparing cidr from po-orig.json/po file and subnet from pocooling json. If the key values are same in both file, then print the pertaining details of pocooling json file as a list.
- name: Combine GP MGMT
vars:
pocidr: >-
{{
po
| json_query('ansible_facts.policyobject[].json[]')
}}
poname: >-
{{
pocidr
| selectattr('cidr', '==', item.subnet)
| map(attribute='name')
| list
}}
set_fact:
result: >-
{{
result | default([])
+
[poname]
}}
with_items: "{{ pocooling }}"
Updated code
- name: Compare the Current & Actual GP policy
set_fact:
pocidr: "{{ po | json_query(\"ansible_facts.policyobject[].json[]\") }}"
- name: Combine GP MGMT
vars:
poname:
"{{
pocidr
| selectattr('cidr', '==', item.subnet)
| map(attribute='name')
| first
}}"
set_fact:
result:
"{{
result | default([])
+
[poname]
}}"
with_items: "{{ pocooling }}"
pocooling.json
[
{
"applianceIp": "10.10.10.10",
"dhcpBootOptionsEnabled": false,
"dhcpHandling": "Run a DHCP server",
"dhcpLeaseTime": "1 day",
"dhcpOptions": [],
"dnsNameservers": "upstream_dns",
"fixedIpAssignments": {},
"id": 2222,
"name": "Cooling",
"networkId": "78984654989",
"reservedIpRanges": [],
"subnet": "10.10.10.0/28"
},
{
"applianceIp": "10.10.10.10",
"dhcpBootOptionsEnabled": false,
"dhcpHandling": "Run a DHCP server",
"dhcpLeaseTime": "1 day",
"dhcpOptions": [],
"dnsNameservers": "upstream_dns",
"fixedIpAssignments": {},
"id": 2222,
"name": "Cooling",
"networkId": "123456789",
"reservedIpRanges": [],
"subnet": "10.11.11.0/28"
}
]
po-orig.json(po)
{
"ansible_facts":{
"policyobject":[
{
"invocation":{
"module_args":{
"status_code":[
200,
201,
202
],
"validate_certs":false
}
},
"item":{
"id":"123456",
"name":"DC"
},
"json":[
{
"category":"network",
"cidr":"10.1.1.1/28",
"createdAt":"2021-11-23T19:48:21Z",
"groupIds":[
],
"id":"545649843651365",
"name":"JM-Privat",
"networkIds":[
"545649843651365"
],
"type":"cidr",
"updatedAt":"2021-11-23T19:48:21Z"
},
{
"category":"network",
"cidr":"10.10.10.0/28",
"createdAt":"2021-12-07T13:54:05Z",
"groupIds":[
"897987654689854"
],
"id":"564678984565465",
"name":"PO-L-DE-MCC-COOLING_DMZ_LAB",
"networkIds":[
],
"type":"cidr",
"updatedAt":"2021-12-07T13:54:05Z"
}
]
}
]
}
}
Error:
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: {{ pocidr\n | selectattr('cidr', '==', item.subnet)\n | map(attribute='name')\n | list\n}}: 'dict object' has no attribute 'cidr
Expected result
[
{
"applianceIp": "10.10.10.10",
"dhcpBootOptionsEnabled": false,
"dhcpHandling": "Run a DHCP server",
"dhcpLeaseTime": "1 day",
"dhcpOptions": [],
"dnsNameservers": "upstream_dns",
"fixedIpAssignments": {},
"id": 2222,
"name": "Cooling",
"networkId": "78984654989",
"reservedIpRanges": [],
"subnet": "10.10.10.0/28"
}
]
Complete Playbook
- name: Test
hosts: localhost
connection: local
gather_facts: false
vars:
pocooling: "{{ lookup('file', 'pocooling.json') | from_json }}"
tasks:
- include_vars:
file: po-orig.json
name: po
- name: Combine GP MGMT
vars:
poname: "{{ po|json_query('ansible_facts.policyobject[].json[]')|
selectattr('cidr', '==', item.subnet)|
map(attribute='name')|
list }}"
set_fact:
result: "{{ result|default([]) + [poname] }}"
loop: "{{ pocooling }}"
Given the list
pocooling:
- networkId: '78984654989'
subnet: 10.10.10.0/28
- networkId: '123456789'
subnet: 10.11.11.0/28
Select list of cidr
_cidr: "{{ ansible_facts.policyobject|json_query('[].json[].cidr') }}"
gives
_cidr:
- 10.1.1.1/28
- 10.10.10.0/28
Then select the items that fit the condition
- debug:
msg: "{{ pocooling|selectattr('subnet', 'in' , _cidr) }}"
vars:
_cidr: "{{ ansible_facts.policyobject|json_query('[].json[].cidr') }}"
gives
msg:
- networkId: '78984654989'
subnet: 10.10.10.0/28
Your code, fixed and simplified
- include_vars:
file: po-orig.json
name: po
- name: Combine GP MGMT
vars:
poname: "{{ po|json_query('ansible_facts.policyobject[].json[]')|
selectattr('cidr', '==', item.subnet)|
map(attribute='name')|
list }}"
set_fact:
result: "{{ result|default([]) + [poname] }}"
loop: "{{ pocooling }}"
gives
result:
- - PO-L-DE-MCC-COOLING_DMZ_LAB
- []

How to I access JSON value from ansible output?

I have this ansible playbook which will create and attach volumes to EC2 instances using ec2_vol module and I want to partition the same using parted module. Below is my ec2_vol module,
EC2_VOL Module:
ec2_vol:
aws_access_key: XXXXXXXX
aws_secret_key: XXXXXXXX
security_token: XXXXXXXX
instance: "{{ item.instance_id }}"
region: "{{ var_dict['REGION'] }}"
device_name: "{{ VOL_NAME }}"
volume_type: gp2
volume_size: '1000'
delete_on_termination: false
with_items: "{{ ec2_instances_list.instances}}"
register: ec2_volumes
- debug:
msg: "{{ ec2_volumes.results }}"
Result of ec2_volumes:
And the result that is printing from ec2_volumes.results. I want to loop through the device_name and partition the devices which is created
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": [
{
"_ansible_ignore_errors": null,
"_ansible_item_label": {
"ami_launch_index": 0,
"architecture": "x86_64",
"block_device_mappings": [
{
"device_name": "/dev/sda1",
"ebs": {
"attach_time": "2021-04-30T15:47:07+00:00",
"delete_on_termination": true,
"status": "attached",
"volume_id": "vol-XXXXXXXXXXXXXX"
}
},
{
"device_name": "/dev/sdb",
"ebs": {
"attach_time": "2021-04-30T15:47:07+00:00",
"delete_on_termination": false,
"status": "attached",
"volume_id": "vol-XXXXXXXXXXXXXXXX"
}
}
],
And here is my parted module code:
- name: Partition Additional volumes
parted:
device: "{{ item.block_device_mappings[0].device_name }}"
number: 1
part_type: 'primary'
state: present
when: ("{{ ec2_volumes.device_name }}" != "/dev/sda1")
with_items: "{{ ec2_volumes.results}}"
But still getting error that 'block_device_mappings' has no attribute.
fatal: [localhost]: FAILED! => {"msg": "'list object' has no attribute 'block_device_mappings'"}
What could be the cause? Can someone help me to resolve this issue?
Edit 1:
Facing another issue after updating the playbook in the parted module
- name: Partition Additional volumes
parted:
device: "{{ item._ansible_item_label.block_device_mappings[0].device_name }}"
number: 1
part_type: 'primary'
state: present
when: ("{{ ec2_volumes.device_name }}" != "/dev/sda1")
with_items: "{{ ec2_volumes.results}}"
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'device_name'
The error is clear:
FAILED! => {"msg": "'list object' has no attribute 'block_device_mappings'"}
When you take a look at the list you'll see that block_device_mappings is an attribute of _ansible_item_label
"msg": [
{
"_ansible_ignore_errors": null,
"_ansible_item_label": {
"ami_launch_index": 0,
"architecture": "x86_64",
"block_device_mappings": [
...
Fix the reference
device: "{{ item._ansible_item_label.block_device_mappings[0].device_name }}"

Access nested items on a dictionary ansible

I have a json file with server information and the applications related.
Something like this:
{
"Apps": [
{
"AppOwner": "Me",
"AppNm": "Vacations"
}
],
"Hostnm": "some_server",
"Environment": "Prod",
"OSnm": "Windows Server 2008",
"OSManu": "Microsoft",
"SerialNum": "VMware-42 14 e1 37 7a 63 9b 0e-43 07 15 46 64 9c 3c 12"
}
I want to create a list with all the applications and the servers related
For example
{
"Vacations": [server1, server2]
},
{
"other_app": [server2, server3]
},
For that, I have been trying the following.
- set_fact:
apps: "{{ apps }} + {{ item.Apps | map(attribute='AppNm') | list}}"
loop: "{{ server_changes }}"
- set_fact:
apps_revised: "{{apps | unique}}"
- set_fact:
apps_server: "{{ apps_revised | default([]) + [{item : []}] }}"
with_items: "{{ apps_revised }}"
The first part of the code, will obtain all the applications with the duplicates from the json.
The second part of the code, will clean the apps with unique.
The last part its going to create a list of application with a nested list (of servers).
The problem is that I don't understand how to navigate the nested server_changes application list and then add the server to my new list.
Probably there is an easier way to do it, so any help will be appreciated.
Thanks
Given a test data
server_changes:
- "Apps":
- {"AppOwner": "Me", "AppNm": "Vacations"}
"Hostnm": "server1"
- "Apps":
- {"AppOwner": "Me", "AppNm": "Vacations"}
- {"AppOwner": "Me", "AppNm": "other_app"}
"Hostnm": "server2"
- "Apps":
- {"AppOwner": "Me", "AppNm": "other_app"}
"Hostnm": "server3"
Let's simplify the data. For example
- set_fact:
my_server_changes: "{{ my_server_changes|default([]) +
[{'hostname': item.Hostnm,
'applications': item.Apps|
json_query('[].AppNm')}] }}"
loop: "{{ server_changes }}"
- debug:
var: my_server_changes
gives
"my_server_changes": [
{
"applications": [
"Vacations"
],
"hostname": "server1"
},
{
"applications": [
"Vacations",
"other_app"
],
"hostname": "server2"
},
{
"applications": [
"other_app"
],
"hostname": "server3"
}
]
Create the list of the applications
- set_fact:
my_apps: "{{ server_changes|json_query('[].Apps[].AppNm')|unique }}"
- debug:
var: my_apps
gives
"my_apps": [
"Vacations",
"other_app"
]
Then loop the list of the applications, select records that contains the item and combine the dictionary
- set_fact:
apps_server: "{{ apps_server|default({})|
combine({item: my_server_changes|json_query(query)}) }}"
loop: "{{ my_apps }}"
vars:
query: "[?applications.contains(#, '{{ item }}')].hostname"
- debug:
var: apps_server
gives
"apps_server": {
"Vacations": [
"server1",
"server2"
],
"other_app": [
"server2",
"server3"
]
}

best way to modify json in ansible

I have a variable (via set_fact) containing a json string:
{
"PolicyVersion": {
"CreateDate": "2017-08-07T02:48:05Z",
"Document": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Resource": [
"arn:aws:iam::123456789123:role/Root_Update_svcacct",
"arn:aws:iam::123456789123:role/Root_Delete_svcacct",
"arn:aws:iam::123456789123:role/Root_Create_svcacct",
"arn:aws:iam::123456789123:role/Root_Full_svcacct",
"arn:aws:iam::987654321987:role/Member1_Create_svcacct",
"arn:aws:iam::987654321987:role/Member1_Update_svcacct",
"arn:aws:iam::987654321987:role/Member1_Delete_svcacct",
"arn:aws:iam::987654321987:role/Member1_Full_svcacct"
]
}
],
"Version": "2012-10-17"
},
"IsDefaultVersion": true,
"VersionId": "v2"
}
}
What is the best way to insert more elements in the "Resource" array?
"arn:aws:iam::001122334455:role/Member1_Create_svcacct",
"arn:aws:iam::001122334455:role/Member1_Update_svcacct",
"arn:aws:iam::001122334455:role/Member1_Delete_svcacct",
"arn:aws:iam::001122334455:role/Member1_Full_svcacct"
I am exploring dumping the variable to a file and inserting the block I want with external shell tools, which does not seem to be elegant.
I don't know about the best way, but one option is to write a simple library module to handle the mechanics of the update for you. You could use the jsonpointer module as a way of locating the data you wish to modify, and then return the modified object to ansible. A starting point might look like:
#!/usr/bin/python
from ansible.module_utils.basic import AnsibleModule
import json
try:
import jsonpointer
except ImportError:
jsonpointer = None
def main():
module = AnsibleModule(
argument_spec=dict(
data=dict(required=True, type='dict'),
pointer=dict(required=True),
action=dict(required=True,
choices=['append', 'extend', 'update']),
update=dict(type='dict'),
extend=dict(type='list'),
append=dict(),
),
supports_check_mode=True,
)
if jsonpointer is None:
module.fail_json(msg='jsonpointer module is not available')
action = module.params['action']
data = module.params['data']
pointer = module.params['pointer']
if isinstance(data, str):
data = json.loads(str)
try:
res = jsonpointer.resolve_pointer(data, pointer)
except jsonpointer.JsonPointerException as err:
module.fail_json(msg=str(err))
if action == 'append':
res.append(module.params['append'])
if action == 'extend':
res.extend(module.params['extend'])
elif action == 'update':
res.update(module.params['update'])
module.exit_json(changed=True,
result=data)
if __name__ == '__main__':
main()
If you drop this into, e.g., library/json_modify.py, you can use it in a playbook like this:
- hosts: localhost
gather_facts: false
vars:
myvar: {
"PolicyVersion": {
"CreateDate": "2017-08-07T02:48:05Z",
"Document": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Resource": [
"arn:aws:iam::123456789123:role/Root_Update_svcacct",
"arn:aws:iam::123456789123:role/Root_Delete_svcacct",
"arn:aws:iam::123456789123:role/Root_Create_svcacct",
"arn:aws:iam::123456789123:role/Root_Full_svcacct",
"arn:aws:iam::987654321987:role/Member1_Create_svcacct",
"arn:aws:iam::987654321987:role/Member1_Update_svcacct",
"arn:aws:iam::987654321987:role/Member1_Delete_svcacct",
"arn:aws:iam::987654321987:role/Member1_Full_svcacct"
]
}
],
"Version": "2012-10-17"
},
"IsDefaultVersion": true,
"VersionId": "v2"
}
}
tasks:
- json_modify:
data: "{{ myvar }}"
pointer: "/PolicyVersion/Document/Statement/0/Resource"
action: extend
extend:
- "arn:aws:iam::001122334455:role/Member1_Create_svcacct"
- "arn:aws:iam::001122334455:role/Member1_Update_svcacct"
- "arn:aws:iam::001122334455:role/Member1_Delete_svcacct"
- "arn:aws:iam::001122334455:role/Member1_Full_svcacct"
register: result
- debug:
var: result.result
The result of running this playbook and the proposed module is:
TASK [debug] *******************************************************************
ok: [localhost] => {
"result.result": {
"PolicyVersion": {
"CreateDate": "2017-08-07T02:48:05Z",
"Document": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Resource": [
"arn:aws:iam::123456789123:role/Root_Update_svcacct",
"arn:aws:iam::123456789123:role/Root_Delete_svcacct",
"arn:aws:iam::123456789123:role/Root_Create_svcacct",
"arn:aws:iam::123456789123:role/Root_Full_svcacct",
"arn:aws:iam::987654321987:role/Member1_Create_svcacct",
"arn:aws:iam::987654321987:role/Member1_Update_svcacct",
"arn:aws:iam::987654321987:role/Member1_Delete_svcacct",
"arn:aws:iam::987654321987:role/Member1_Full_svcacct",
"arn:aws:iam::001122334455:role/Member1_Create_svcacct",
"arn:aws:iam::001122334455:role/Member1_Update_svcacct",
"arn:aws:iam::001122334455:role/Member1_Delete_svcacct",
"arn:aws:iam::001122334455:role/Member1_Full_svcacct"
]
}
],
"Version": "2012-10-17"
},
"IsDefaultVersion": true,
"VersionId": "v2"
}
}
}
Actually Ansible is natively able to read JSON files.
see this question:
add-a-new-key-value-to-a-json-file-using-ansible
This is a little bit old. I know but I felt it could be useful to many people so I'll post here my solution as "the best way to modify a json" that I could personally find.
First of all the JSON. In my use-case I had a bunch of aws snapshots that were encrypted with the wrong kms key and I had to recreate the AMI with the correct key.
So I had to :
get data of the old snapshots ( like size device_name etc.)
create the new snaps with the different key
re-register a new ami with the correct block_device_mapping
Here's the code
- name: get ami
amazon.aws.ec2_ami_info:
image_ids: ami-<id>
region: "{{ region }}"
register: ami
- name: save snapshot ids and device_name and volume_size
set_fact:
snapshot_ids: "{{ ami | json_query('images[].block_device_mappings[].ebs.snapshot_id') }}"
device_name: "{{ ami | json_query('images[].block_device_mappings[].device_name') }}"
volume_size: "{{ ami | json_query('images[].block_device_mappings[].ebs.volume_size') }}"
basically each of the above is a list of each of the 3 things (device_name, snap_id, volume_size) that I need (but it could be extended)
then:
- name: get kms arn
aws_kms_info:
filters:
alias: "{{ kms_keys.alias }}"
region: "{{ region }}"
register: aws_kms_facts_out
- debug:
var: aws_kms_facts_out
- set_fact:
kms_arn: "{{ aws_kms_facts_out['keys'][0].key_arn }}"
- name: copy snap with new encryption key
community.aws.ec2_snapshot_copy:
source_region: "{{ region }}"
region: "{{ region }}"
source_snapshot_id: "{{ item }}"
encrypted: yes
kms_key_id: "{{ kms_arn }}"
loop: "{{ snapshot_ids }}"
register: new_snapshots
and then here's the catch
- set_fact:
new_snapshot_ids: "{{ new_snapshots| json_query('snapshots[].snapshot_id') }}"
- name: creating the block_device_mappings structure (still a list of objects)
ansible.builtin.debug:
msg: '{
"device_name": "{{ item.2 }}",
"delete_on_termination": "true",
"snapshot_id": "{{ item.0 }}",
"volume_size": {{ item.1 }},
"volume_type": "gp2"
}'
loop: "{{ new_snapshot_ids|zip_longest(volume_size,device_name)|list }}"
register: block_device_mappings
- set_fact:
bdm: "{{ block_device_mappings | json_query('results[].msg') }}"
finally
- name: create new ami from newly created snapshots
amazon.aws.ec2_ami:
region: "{{ region }}"
name: "{{ instance.name }}-{{ ansible_date_time.date }}"
state: present
architecture: x86_64
virtualization_type: hvm
root_device_name: /dev/sda1
device_mapping: "{{ bdm }}"
This is how you can do it without requiring any additional trick.
Of course this is declined to my particular use case but you can adapt it to any circumstance, that do not require a complete disassemble and reassemble of the Json itself