Get specific list of dict from json content using Ansible - json

I spend a lot of times to finally didn't get what I need, my request is so complicated then I prefer share with you my concern::
I have this json.content to pars it:
"json": {
"content": [
{
"name": "machine1",
"hasChildren": false,
"resourceData": {
"entries": [
{
"value": {
"type": "string",
"value": "Red Hat Enterprise Linux 7 (64-bit)"
},
"key": "MachineGuestOperatingSystem"
},
{
"value": {
"type": "string",
"value": "Folder1"
},
"key": "VMware.VirtualCenter.Folder"
},
{
"value": {
"type": "boolean",
"value": true
},
"key": "Destroy"
}
]
}
},
{
"name": "machine2",
"hasChildren": false,
"resourceData": {
"entries": [
{
"value": {
"type": "string",
"value": "Red Hat Enterprise Linux 7 (64-bit)"
},
"key": "MachineGuestOperatingSystem"
},
{
"value": {
"type": "string",
"value": "Folder2"
},
"key": "VMware.VirtualCenter.Folder"
},
{
"value": {
"type": "boolean",
"value": false
},
"key": "Destroy"
}
]
}
},
{
"name": "machine3",
"hasChildren": false,
"resourceData": {
"entries": [
{
"value": {
"type": "string",
"value": "Windows Server 2016 or later (64-bit)"
},
"key": "MachineGuestOperatingSystem"
},
{
"value": {
"type": "string",
"value": "Folder3"
},
"key": "VMware.VirtualCenter.Folder"
},
{
"value": {
"type": "boolean",
"value": true
},
"key": "Destroy"
}
]
}
}
]
}
and I'd like to have as output Something like:
[{"name":"machine1", "OsName": "Red Hat Enterprise Linux 7 (64-bit)", "Folder": "Folder1"}, {"name":"machine2", "OsName": "Red Hat Enterprise Linux 7 (64-bit)", "Folder": "Folder2"},
{"name":"machine3", "OsName": "Windows Server 2016 or later (64-bit)", "Folder": "Folder3"}]
I tried all the solutions but no way, all result not what I want to expect. someone can help me to find a solution to get this attended result

The task below does the job
- set_fact:
_list: "{{ json.content|json_query(query) }}"
vars:
query: "[].{name: name,
OsName: resourceData.entries[0].value.value,
Folder: resourceData.entries[1].value.value}"
gives
_list:
- Folder: Folder1
OsName: Red Hat Enterprise Linux 7 (64-bit)
name: machine1
- Folder: Folder2
OsName: Red Hat Enterprise Linux 7 (64-bit)
name: machine2
- Folder: Folder3
OsName: Windows Server 2016 or later (64-bit)
name: machine
Q: "It changes the order of (key, value), so referencing by [0] or [1] will be not the same."
A: Select the items by the keys. You'll have to pipe the results and select the first items of the selected lists, e.g. the task below gives the same results
- set_fact:
_list: "{{ json.content|json_query(query) }}"
vars:
query: "[].{name: name,
OsName: resourceData.entries[?key==`MachineGuestOperatingSystem`].value.value|[0],
Folder: resourceData.entries[?key==`VMware.VirtualCenter.Folder`].value.value|[0]}"
Q: "Filter 'Red Hat ...' Get output"
- Folder: Folder1
OsName: Red Hat Enterprise Linux 7 (64-bit)
name: machine1
- Folder: Folder2
OsName: Red Hat Enterprise Linux 7 (64-bit)
name: machine2
A: Use selectattr, e.g. the task below does the job
- debug:
msg: "{{ _list|
selectattr('OsName', 'eq', 'Red Hat Enterprise Linux 7 (64-bit)')|
list }}"
Q: "Where I can learn this kind of search?"
A: There are examples in JMESPath Specification. As a hint, see the code below on how to translate these examples into Ansible json_query
Example1
- name: 'search(foo, {"foo": "value"}) -> "value"'
debug:
msg: "{{ _dict|json_query(_query) }}"
vars:
_dict: {"foo": "value"}
_query: "foo"
Example2
- name: 'search(foo[?bar==`10`], {"foo": [{"bar": 1}, {"bar": 10}]}) -> [{"bar": 10}]'
debug:
msg: "{{ _dict|json_query(_query) }}"
vars:
_dict: {"foo": [{"bar": 1}, {"bar": 10}]}
_query: "foo[?bar==`10`]"
Example3
- name: 'search(foo | bar, {"foo": {"bar": "baz"}}) -> "baz"'
debug:
msg: "{{ _dict|json_query(_query) }}"
vars:
_dict: {"foo": {"bar": "baz"}}
_query: "foo | bar"

Related

Merge List of Dictionary using ansible

I am trying to merge two list of dictionaries.
"my_name":
[ {
"name": "xyz",
"number": "123" },
{
"name": "abc",
"number": "456"
}
]
"my_address":
[ {
"name": "abc",
"address": "smith street" },
{
"name": "xyz",
"number": "ray street"
}
]
Required Output:
merged:
[ {
"name": "abc",
"address": "smith street",
"number": "456" },
{
"name": "xyz",
"address": "ray street",
"number": "123"
}
]
I tried using combine but it would just combine the last item.
set_fact:
merged: "{{ my_name | combine(my_address) }}"
"merged":
[
{
"name": "xyz",
"number": "ray street",
"number": "123"
}
]
i used the debug module. debug: msg: "{{ my_name| list_mergeby(my_address,'name')}}" The error was: template error while templating string: no filter named 'lists_mergby,
For example, given the fixed my_address
- hosts: localhost
vars:
my_name:
- name: xyz
number: '123'
- name: abc
number: '456'
my_address:
- name: abc
address: smith street
- name: xyz
address: ray street
merged: "{{ [my_name, my_address]|community.general.lists_mergeby('name') }}"
tasks:
- debug:
var: merged
gives (abridged)
merged:
- address: smith street
name: abc
number: '456'
- address: ray street
name: xyz
number: '123'
See: the filter community.general.lists_mergeby

json_query filter error - template error while templating string: expected token ','

I am trying to use the json_query filter to parse the desired JSON data.
Ansible playbook task:
- name: Print items in option
ansible.builtin.debug:
msg: "{{ clust_opts | community.general.json_query('defaultReplicaSet.topology.\"dc1-indb-tst1:3306\" | [].option['memberWeight']')' }}"
I have also tried the following in my playbook - which resulted in the same error:
- name: Print items in option
ansible.builtin.debug:
msg: "{{ clust_opts | community.general.json_query('defaultReplicaSet.topology.[?option == 'memberWeight']')' }}"
The error message:
TASK [Print items in option] **************************************************************************************************
fatal: [dc1-indb-tst1]: FAILED! => {"msg": "template error while templating string: expected token ',', got 'memberWeight'. String: {{ clust_opts | json_query('defaultReplicaSet.topology.[?option == 'memberWeight']')' }}"}
JSON to parse data from:
{
"clusterName": "idb_test",
"defaultReplicaSet": {
"globalOptions": [
{
"option": "groupName",
"value": "8f9e6040-F10s-11ec-b244-005056a4645c",
"variable": "group_replication_group_name"
},
{
"option": "memberSslMode",
"value": "REQUIRED",
"variable": "group_replication_ssl_mode"
},
{
"option": "disableClone",
"value": false
}
],
"tags": {
"dc1-indb-tst1:3306": [],
"dc1-indb-tst2:3306": [],
"dc1-indb-tst3:3306": [],
"global": []
},
"topology": {
"dc1-indb-tst1:3306": [
{
"option": "autoRejoinTries",
"value": "3",
"variable": "group_replication_autorejoin_tries"
},
{
"option": "consistency",
"value": "EVENTUAL",
"variable": "group_replication_consistency"
},
{
"option": "exitStateAction",
"value": "READ_ONLY",
"variable": "group_replication_exit_state_action"
},
{
"option": "expelTimeout",
"value": "5",
"variable": "group_replication_member_expel_timeout"
},
{
"option": "groupSeeds",
"value": "dc1-indb-tst2:33061,dc1-indb-tst3:33061",
"variable": "group_replication_group_seeds"
},
{
"option": "ipAllowlist",
"value": "AUTOMATIC",
"variable": "group_replication_ip_allowlist"
},
{
"option": "ipWhitelist",
"value": "AUTOMATIC",
"variable": "group_replication_ip_whitelist"
},
{
"option": "localAddress",
"value": "dc1-indb-tst1:33061",
"variable": "group_replication_local_address"
},
{
"option": "memberWeight",
"value": "50",
"variable": "group_replication_member_weight"
},
{
"value": "WRITESET",
"variable": "binlog_transaction_dependency_tracking"
},
{
"value": "LOGICAL_CLOCK",
"variable": "replica_parallel_type"
},
{
"value": "4",
"variable": "replica_parallel_workers"
},
{
"value": "ON",
"variable": "replica_preserve_commit_order"
},
{
"value": "XXHASH64",
"variable": "transaction_write_set_extraction"
}
],
"dc1-indb-tst2:3306": [
{
"option": "autoRejoinTries",
"value": "3",
"variable": "group_replication_autorejoin_tries"
},
{
"option": "consistency",
"value": "EVENTUAL",
"variable": "group_replication_consistency"
},
{
"option": "exitStateAction",
"value": "READ_ONLY",
"variable": "group_replication_exit_state_action"
},
{
"option": "expelTimeout",
"value": "5",
"variable": "group_replication_member_expel_timeout"
},
{
"option": "groupSeeds",
"value": "dc1-indb-tst1:33061,dc1-indb-tst3:33061",
"variable": "group_replication_group_seeds"
},
{
"option": "ipAllowlist",
"value": "AUTOMATIC",
"variable": "group_replication_ip_allowlist"
},
{
"option": "ipWhitelist",
"value": "AUTOMATIC",
"variable": "group_replication_ip_whitelist"
},
{
"option": "localAddress",
"value": "dc1-indb-tst2:33061",
"variable": "group_replication_local_address"
},
{
"option": "memberWeight",
"value": "50",
"variable": "group_replication_member_weight"
},
{
"value": "WRITESET",
"variable": "binlog_transaction_dependency_tracking"
},
{
"value": "LOGICAL_CLOCK",
"variable": "replica_parallel_type"
},
{
"value": "4",
"variable": "replica_parallel_workers"
},
{
"value": "ON",
"variable": "replica_preserve_commit_order"
},
{
"value": "XXHASH64",
"variable": "transaction_write_set_extraction"
}
],
"dc1-indb-tst3:3306": [
{
"option": "autoRejoinTries",
"value": "3",
"variable": "group_replication_autorejoin_tries"
},
{
"option": "consistency",
"value": "EVENTUAL",
"variable": "group_replication_consistency"
},
{
"option": "exitStateAction",
"value": "READ_ONLY",
"variable": "group_replication_exit_state_action"
},
{
"option": "expelTimeout",
"value": "5",
"variable": "group_replication_member_expel_timeout"
},
{
"option": "groupSeeds",
"value": "dc1-indb-tst1:33061,dc1-indb-tst2:33061",
"variable": "group_replication_group_seeds"
},
{
"option": "ipAllowlist",
"value": "AUTOMATIC",
"variable": "group_replication_ip_allowlist"
},
{
"option": "ipWhitelist",
"value": "AUTOMATIC",
"variable": "group_replication_ip_whitelist"
},
{
"option": "localAddress",
"value": "dc1-indb-tst3:33061",
"variable": "group_replication_local_address"
},
{
"option": "memberWeight",
"value": "50",
"variable": "group_replication_member_weight"
},
{
"value": "WRITESET",
"variable": "binlog_transaction_dependency_tracking"
},
{
"value": "LOGICAL_CLOCK",
"variable": "replica_parallel_type"
},
{
"value": "4",
"variable": "replica_parallel_workers"
},
{
"value": "ON",
"variable": "replica_preserve_commit_order"
},
{
"value": "XXHASH64",
"variable": "transaction_write_set_extraction"
}
]
}
}
}
The end goal is to be able to look for the data in the option fields and return the value associated with it.
So, in my example I want to find the memberWeight for a specific system and return the value.
I am looking at dc1-indb-tst1:3306, specifically, for memberWeight which should return 50 - but I am getting the above error instead.
TL;DR;
A working debug task, in this use case would be:
- ansible.builtin.debug:
msg: >-
{{
clust_opts | community.general.json_query('
defaultReplicaSet
.topology
."dc1-indb-tst1:3306"[?
option == `memberWeight`
].value | [0]
')
}}
Which gives:
ok: [localhost] =>
msg: '50'
The error you are getting is because you forgot to escape the single quote ' in your JMESPath query.
An idea to ease your own life with quote escaping would be to use the YAML folded style block notation:
- ansible.builtin.debug:
msg: >-
I don't have to escape single (')
nor double (") quote in this string,
it is delimited only by the fact that it is further indented
than the block where the folded block starts.
Now, for your JMESPath query, you have part of the solution in both of them so, if you mix them both, you get nearly what you expect.
A little word of explanation, properties of map are queried with the dot . notation. While elements of an array are queried via the square bracket notation [].
So from your first attempts, this part is correct:
defaultReplicaSet.topology."dc1-indb-tst1:3306"
And in the second one, this part is correct:
[?option == 'memberWeight']
Together you have
defaultReplicaSet
.topology
."dc1-indb-tst1:3306"[?
option == 'memberWeight'
]
Which gives you the whole object:
[
{
"option": "memberWeight",
"value": "50",
"variable": "group_replication_member_weight"
}
]
Now you just need to query the value, and return the first result of it, so adding:
.value | [0]
The pipe expression is there to stop the projection, more on that can be found in the relevant documentation.
So, your query ends up being:
defaultReplicaSet
.topology
."dc1-indb-tst1:3306"[?
option == `memberWeight`
].value | [0]
Please mind: I swapped single quote ' for backticks here `, as proposed in one of the notes of the Ansible documentation regarding json_query:
In the example above, quoting literals using backticks avoids escaping quotes and maintains readability.
Indeed, in JMESPath, they are both represent literals. There is a slight difference between the two, where the single quote is a literal expression and the backpacks represent a raw literal string, but in the case at hand, is will act the same.
And your debug task should be:
- ansible.builtin.debug:
msg: >-
{{
clust_opts | community.general.json_query('
defaultReplicaSet
.topology
."dc1-indb-tst1:3306"[?
option == `memberWeight`
].value | [0]
')
}}
Which will yield:
TASK [ansible.builtin.debug] *********************************************
ok: [localhost] =>
msg: '50'

Filtering the Ansible Output and print it

I am trying to filter the ansible below ansible output in JSON to retrieve the value of Key. Please fnd the below output.
New KEY value to be extracted.
"msg": {
"allow": "GET, POST",
"cache_control": "no-cache, no-store",
"changed": false,
"connection": "close",
"content_disposition": "attachment; filename=response",
"content_language": "en",
"content_length": "994",
"content_type": "application/json",
"cookies": {
"session_id": "cd67b9ff4a9e35c7949b03ce5afde96de9d7147b"
},
"cookies_string": "session_id=cd67b9ff4a9e35c7949b03ce5afde96de9d7147b",
"date": "Fri, 12 Feb 2021 07:59:55 GMT",
"elapsed": 0,
"expires": "0",
"failed": false,
"json": {
"items": [
{
"body": {
"name": "admin",
"password": {
"key": "a1c20753-4ce7-4d8b-aef8-611f0ded49c5",
"meta": {
"href": "/api/configuration/passwords#a1c20753-4ce7-4d8b-aef8-611f0ded49c5"
}
}
},
"key": "12372718655d3883ec89c09",
"meta": {
"href": "/api/configuration/aaa/local_database/users/12372718655d3883ec89c09"
}
}
],
"meta": {
"first": "/api/configuration/aaa/local_database/groups",
"href": "/api/configuration/aaa/local_database/users",
"last": "/api/configuration/aaa/local_database/users",
"next": null,
"parent": "/api/configuration/aaa/local_database",
"previous": "/api/configuration/aaa/local_database/groups",
"remaining_seconds": 600,
"transaction": "/api/transaction"
}
},
The square braces after the items is causing the issue and i am not able to reach until the key. Can you please help me out. I have filter the parameter Key and pass this to a differnt task in the playbook. I tried using the debug as below but it didn't work out.
- debug:
msg: "{{ json.items[0].key }}"
Its not clear from the question whether you already have the JSON or you get it from some other ansible task.
Assuming that we have the JSON ready, you can get the `key` as shown in the below playbook.
Note: I have updated the key items as it conflicts with the ansible
functions, as I noticed the same while using items to get the key
value.
---
- name: extract key from json
hosts: localhost
gather_facts: no
vars:
- json: {
"abc": [
{
"body": {
"name": "admin",
"password": {
"key": "26b2fd50-a853-4a8d-a35e-8cfbe98cea5f",
"meta": {
"href": "/api/configuration/passwords#26b2fd50-a853-4a8d-a35e-8cfbe98cea5f"
}
}
},
"key": "12372718655d3883ec89c09",
"meta": {
"href": "/api/configuration/aaa/local_database/users/12372718655d3883ec89c09"
}
}
],
"meta": {
"first": "/api/configuration/aaa/local_database/groups",
"href": "/api/configuration/aaa/local_database/users",
"last": "/api/configuration/aaa/local_database/users",
"next": null,
"parent": "/api/configuration/aaa/local_database",
"previous": "/api/configuration/aaa/local_database/groups",
"remaining_seconds": 600,
"transaction": "/api/transaction"
}
}
tasks:
- name: print_json
debug:
msg: "{{ json.abc[0].key }}"

CI pipeline fails even though it is doing what its supposed to

Let me start by saying I just started Azure and by no means a pro and been following guides here and there. So, I'm following this article by deploying this repo in my own Azure DevOps subscription, after I changed the resource group, subscription, and function name, I build and run the CI pipeline, then at the "Deploy Azure Resources" I'd get this error even though after the pipeline exits, the resources are created the way its supposed to, I also notice if I re run the same pipeline the 2nd time, it works perfectly which is confusing. Here is the modified the YAML template that I have
name: DeployAzureFunction
variables:
FunctionAppName: 'test'
AzureConnection: 'DevOps-Test'
ResourcegGroupName: 'test'
trigger:
branches:
include:
- '*' # must quote since "*" is a YAML reserved character; we want a string
stages:
- stage: Build
jobs:
- job: Test_FA
pool:
vmImage: windows-2019
steps:
- task: AzureResourceGroupDeployment#2
displayName: 'Test ARM Deployment'
inputs:
azureSubscription: $(AzureConnection)
resourceGroupName: $(ResourcegGroupName)
location: 'East US 2'
csmFile: Deployment/azuredeploy.json
deploymentMode: Validation
- powershell: |
Install-PackageProvider -Name NuGet -Force -Scope CurrentUser
Install-Module -Name Pester -Force -Scope CurrentUser -SkipPublisherCheck
Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser
displayName: 'Install Pester and import module'
- job: Build_FA
pool:
vmImage: windows-2019
steps:
- task: ArchiveFiles#2
displayName: 'Archive FunctionApp'
inputs:
rootFolderOrFile: FunctionApp
includeRootFolder: false
archiveFile: '$(System.DefaultWorkingDirectory)/zip/FunctionApp.zip'
- task: PublishPipelineArtifact#0
inputs:
artifactName: 'zip'
targetPath: '$(System.DefaultWorkingDirectory)/zip'
- stage: Deploy
jobs:
- job: Deploy_ARM
pool:
vmImage: windows-2019
steps:
- task: AzureResourceGroupDeployment#2
displayName: 'Deploy Azure Resources'
continueOnError: true
inputs:
azureSubscription: $(AzureConnection)
resourceGroupName: $(ResourcegGroupName)
location: 'East US 2'
csmFile: Deployment/azuredeploy.json
csmParametersFile: Deployment/azuredeploy.parameters.json
overrideParameters: '-functionAppName $(FunctionAppName)'
deploymentMode: Incremental
deploymentOutputs: DeploymentOutput
And this is the unmodified azuredeploy.json that I think is causing the issue but not sure why
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"functionAppName": {
"type": "string",
"defaultValue": "[uniqueString(resourceGroup().id)]",
"metadata": {
"description": "Specify the name of the function application"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Specify the location for the function application resources"
}
}
},
"variables": {
"hostingPlanName": "[parameters('functionAppName')]",
"storageacccount": "[toLower(parameters('functionAppName'))]",
"storageAccountName": "[concat('storage', variables('storageacccount'))]"
},
"resources": [
{
"apiVersion": "2017-06-01",
"type": "Microsoft.Storage/storageAccounts",
"name": "[variables('storageAccountName')]",
"location": "[parameters('location')]",
"kind": "Storage",
"sku": {
"name": "Standard_LRS"
}
},
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2018-11-01",
"name": "[variables('hostingPlanName')]",
"location": "[parameters('location')]",
"sku": {
"name": "Y1",
"tier": "Dynamic",
"size": "Y1",
"family": "Y",
"capacity": 0
},
"properties": {
"name": "[variables('hostingPlanName')]"
}
},
{
"name": "[parameters('functionAppName')]",
"type": "Microsoft.Web/sites",
"apiVersion": "2018-02-01",
"location": "[parameters('location')]",
"kind": "functionapp",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms/', variables('hostingPlanName'))]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]",
"[resourceId('microsoft.insights/components/', parameters('functionAppName'))]"
],
"identity": {
"type": "SystemAssigned"
},
"properties": {
"siteConfig": {
"appSettings": [
{
"name": "FUNCTIONS_WORKER_RUNTIME",
"value": "powershell"
},
{
"name": "AzureWebJobsStorage",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2017-06-01').keys[0].value)]"
},
{
"name": "FUNCTIONS_EXTENSION_VERSION",
"value": "~2"
},
{
"name": "APPINSIGHTS_INSTRUMENTATIONKEY",
"value": "[reference(resourceId('microsoft.insights/components/', parameters('functionAppName')), '2018-05-01-preview').InstrumentationKey]"
},
{
"name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')),'2017-06-01').keys[0].value)]"
},
{
"name": "WEBSITE_CONTENTSHARE",
"value": "[toLower(parameters('functionAppName'))]"
}
]
},
"name": "[parameters('functionAppName')]",
"clientAffinityEnabled": false,
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms/', variables('hostingPlanName'))]"
}
},
{
"apiVersion": "2015-05-01",
"name": "[parameters('functionAppName')]",
"type": "Microsoft.Insights/components",
"kind": "Web",
"location": "[parameters('location')]",
"tags": {
"[concat('hidden-link:', resourceId('Microsoft.Web/sites/', parameters('functionAppName')))]": "Resource"
},
"properties": {
"ApplicationId": "[parameters('functionAppName')]",
"Application_Type": "web"
}
}
],
"outputs": {
"principalId": {
"type": "string",
"value": "[reference(concat(resourceId('Microsoft.Web/sites/', parameters('functionAppName')), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2015-08-31-PREVIEW').principalId]"
}
}
}
If anyone could help me figure out why I get that error in the first run, I'd appreciate it, thanks!
The Resources are created yet the Deployment operation fails because there is a problem in caclulating the output variable.
Most likely it is because of the way you are trying to access the Service Principal Id of the App Service. The '/providers/Microsoft.ManagedIdentity/Identities/default' part.
You probably want something like this:
[reference(resourceId('Microsoft.Web/sites', parameters('functionAppName')), '2016-08-01', 'Full').identity.principalId]

JSON query for finding the newest snapshot for host and device?

I am trying to find the newest snapshot for a device and host in AWS with the aws ec2 command. I am getting the following output from aws ec2 describe-snapshots.
As you can see I can have several snapshots for the same host (see Tags with Keys hostname and devicename) and device. The start time differs.
{
"Snapshots": [
{
"Description": "My desc.",
"Encrypted": false,
"VolumeId": "vol-aaa",
"State": "completed",
"VolumeSize": 8,
"StartTime": "2018-02-02T19:27:56.000Z",
"Progress": "100%",
"OwnerId": "5674567",
"SnapshotId": "snap-xxx"
},
{
"Description": "host1.domain.com - sdc",
"Tags": [
{
"Value": "SNAP1",
"Key": "Name"
},
{
"Value": "sdc",
"Key": "devicename"
},
{
"Value": "host1.domain.com",
"Key": "hostname"
}
],
"Encrypted": false,
"VolumeId": "vol-xxx",
"State": "completed",
"VolumeSize": 140,
"StartTime": "2018-09-21T08:39:58.000Z",
"Progress": "100%",
"OwnerId": "345634563456",
"SnapshotId": "snap-xxx"
},
{
"Description": "host1.domain.com - sdc",
"Tags": [
{
"Value": "SNAP2",
"Key": "Name"
},
{
"Value": "sdc",
"Key": "devicename"
},
{
"Value": "host1.domain.com",
"Key": "hostname"
}
],
"Encrypted": false,
"VolumeId": "vol-xxx",
"State": "completed",
"VolumeSize": 140,
"StartTime": "2018-09-22T08:39:58.000Z",
"Progress": "100%",
"OwnerId": "345634563456",
"SnapshotId": "snap-xxx"
}
}
How would I query this JSON in Ansible to get the newest snapshot for the hostname and device? I don't do this often so struggle with the query syntax.
Until now I have the following.
- shell: "aws ec2 describe-snapshots"
register: snap
delegate_to: localhost
- debug:
msg: "{{ snap.stdout | from_json | json_query(query) }}"
vars:
query: "Snapshots[].Tags[?Key=='hostname'].Value"
But how do I select all snapshot elements where Tags.Value is equal to a certain value where Key is "hostname"? And how do I then select the newest from the list I get?
According to the fine manual, JMESPath supports nested bracket specifier expressions:
vars:
snap: |
{
"Snapshots": [
{"Id": "aaa", "Tags": [{"Key": "hostname", "Value": "alpha"}]},
{"Id": "bbb", "Tags": [{"Key": "hostname", "Value": "beta"}]}
]
}
jq: "Snapshots[? Tags[? Key=='hostname' && Value=='alpha']].Id"
tasks:
- debug:
msg: "{{ snap | from_json | json_query(jq) }}"
As for the "for a certain host" part, that vars: query: is subject to jinja2 interpolation just like every other ansible string, thus:
vars:
query: ... [? Value=='{{ the_hostname }}' ] ...
Just be careful to ensure the value is correctly escaped -- which likely won't be a problem with a hostname, but I mean in general.
Then, as for the "newest from the list" part, ISO8601 has the very pleasing side benefit of sorting lexigraphically, so:
vars:
jq: "Snapshots[? Tags[? Key=='hostname' && Value=='alpha']]"
tasks:
- debug:
msg: "{{ snap | from_json | json_query(jq) | sort(attribute='StartTime', reverse=True) }}"