In jedem Fall können Sie die JSON-Ausgabe mit JQ weiterverarbeiten, etwa um an einen in der tief verschachtelten Struktur versteckten Wert zu gelangen oder sich die Ausgabe formatiert und farblich markiert auszugeben (Bild 2). Der Code in Listing 1 liest die vorhandenen Security Groups einer Region aus. Auch hier bekommt der Boto-Client die Region als Keyword-Argument übergeben.
Listing 1: Security Groups
from __future__ import print_function import boto3 import json import sys ec2client = boto3.client('ec2', region_name='us-east-1') groups = ec2client.describe_security_groups() print(json.dumps(groups))
Mit dem folgenden Aufruf von JQ iterieren Sie über die als Liste gespeicherten "SecurityGroups" und geben dabei zu jeder den GroupName aus:
$ python3 sg.py | jq '.SecurityGroups[].GroupName'
Um das Layout der Ausgabe selber zu bestimmen, verwenden Sie die sogenannte String Interpolation, die JQ bietet. Dabei wird jede Variable mit einem Backslash und zwei Klammern umschlossen. Der Rest des Strings wird so dargestellt wie angegeben:
$ python3 sg.py | jq '.SecurityGroups[] | "\(.GroupName): \(.GroupId)"' "default: sg-0cba197b" "default_elb_3966756d-9722-3604-a7aa-aeb3277e84a3: sg-3f65ff48" "launch-wizard-1: sg-7f4af20b"
Bei der Verarbeitung per JQ befinden Sie sich natürlich dann auf Shell-Ebene, was in Ordnung ist, wenn Sie am Ende eines Skripts Daten ausgeben, die Sie sich anschauen oder mit einem anderen Skript weiterverarbeiten wollen. Weniger sinnvoll ist der Einsatz von JQ, wenn Sie innerhalb des Skripts die Daten verarbeiten möchten.
Dafür verwenden Sie besser ein Python-Modul, das sich auf die Suche in verschachtelten Listen und Dictionaries spezialisiert hat, wie etwa "nested lookup" oder "dpath", oder eins der Module, die eine Abfragesprache für JSON analog zu XPath für XML implementieren, etwa "jsonpath-ng". Ansonsten iterieren Sie wie üblich über die Liste und können dabei die Werte ausgeben:
for g in groups['SecurityGroups']: print(g['GroupName'], g['GroupId'])
Darüber hinaus unterstützen viele Boto-Methoden auch Filter, die es ermöglichen, Einschränkungen der gefragten Daten bereits auf dem Server anzuwenden und somit die übertragenen Datenmengen zu reduzieren, zum Beispiel hier bei der Suche nach laufenden AWS-Instanzen:
result = ec2.instances.filter(Filters=[{'Name': 'instance-state-name', 'Values': ['running']}])
Wenn Sie nun etwa eine Security Group mit der Methode "delete_security_group" löschen möchten, kann es sein, dass Sie mit einer Fehlermeldung wie der folgenden konfrontiert werden:
>> ec2client.delete_security_group(GroupID='sg-29b81b5e') ... botocore.exceptions.ClientError: An error occurred (DependencyViolation) when calling the DeleteSecurityGroup operation: resource sg-29b81b5e has a dependent object
Der Grund dafür ist, dass das zu löschende Objekt noch in einem anderen Objekt als Referenz vorkommt. Bevor dieser Verweis nicht gelöscht ist, dürfen Sie das Objekt nicht löschen. Im Fall von Security Groups können Sie zur Lösung des Problems alle Security Groups durchgehen und in den "UserIdGroupPairs" nachsehen, ob das fragliche Objekt vorhanden ist. Wenn ja, löschen Sie die Referenz, danach dürfen Sie auch das Objekt löschen, auf das verwiesen wird.