エスケープ処理されたJSON文字列を整形して必要な情報を抽出してみた

JSONの中にJSONが埋まったデータから必要な情報を抽出
2023.09.30

こんにちは!コンサル部のinomaso(@inomasosan)です。

前回、AWS Config アグリゲータで、Advanced Queryに対応していない起動テンプレートの情報を取得するためのサンプルコードを作成してみました。

ただ、出力結果の一部にエスケープ処理されたJSON文字列がありました。
そこから必要な情報を取得しようとしたところ、なかなか時間がかかってしまったので備忘として残しておきます。

エスケープ処理されたJSON文字列とは?

以下は起動テンプレートの出力結果となります。

出力結果

{
    "ConfigurationItem": {
        "version": "1.3",
        "accountId": "1234567890",
        "configurationItemCaptureTime": "2023-09-14T08:53:54.339000+00:00",
        "configurationItemStatus": "OK",
        "configurationStateId": "1694681634339",
        "configurationItemMD5Hash": "",
        "arn": "arn:aws:ec2:ap-northeast-1:1234567890:launch-template/lt-07cae1e5cb08b09f7",
        "resourceType": "AWS::EC2::LaunchTemplate",
        "resourceId": "lt-07cae1e5cb08b09f7",
        "resourceName": "asg-web-template",
        "awsRegion": "ap-northeast-1",
        "availabilityZone": "Regional",
        "tags": {},
        "relatedEvents": [],
        "relationships": [],
        "configuration": "{\"LaunchTemplateName\":\"asg-web-template\",\"Id\":\"lt-07cae1e5cb08b09f7\",\"LaunchTemplateData\":{\"EbsOptimized\":false,\"BlockDeviceMappings\":[{\"DeviceName\":\"/dev/xvda\",\"Ebs\":{\"Encrypted\":false,\"DeleteOnTermination\":true,\"VolumeSize\":8,\"VolumeType\":\"gp2\"}}],\"NetworkInterfaces\":[{\"AssociatePublicIpAddress\":true,\"DeleteOnTermination\":true,\"Description\":\"Primary network interface\",\"DeviceIndex\":0,\"Groups\":[\"sg-07b90410018cd1533\"],\"Ipv6Addresses\":[],\"PrivateIpAddresses\":[],\"Ipv4Prefixes\":[],\"Ipv6Prefixes\":[]}],\"ImageId\":\"ami-09ebacdc178ae23b7\",\"InstanceType\":\"t2.micro\",\"KeyName\":\"aws-ssh-key\",\"Monitoring\":{\"Enabled\":false},\"Placement\":{\"Tenancy\":\"default\"},\"DisableApiTermination\":false,\"UserData\":\"IyEvYmluL2Jhc2gKeXVtIHVwZGF0ZSAteQp5dW0gaW5zdGFsbCAteSBodHRwZApzeXN0ZW1jdGwgc3RhcnQgaHR0cGQuc2VydmljZQpzeXN0ZW1jdGwgZW5hYmxlIGh0dHBkLnNlcnZpY2UKZWNobyDigJxIZWxsbyBXb3JsZCBmcm9tICQoaG9zdG5hbWUgLWYp4oCdID4gL3Zhci93d3cvaHRtbC9pbmRleC5odG1s\",\"TagSpecifications\":[],\"ElasticGpuSpecifications\":[],\"ElasticInferenceAccelerators\":[],\"SecurityGroupIds\":[],\"SecurityGroups\":[],\"CreditSpecification\":{\"CpuCredits\":\"standard\"},\"CapacityReservationSpecification\":{\"CapacityReservationPreference\":\"open\"},\"LicenseSpecifications\":[],\"MetadataOptions\":{\"HttpTokens\":\"optional\",\"HttpPutResponseHopLimit\":1,\"HttpEndpoint\":\"enabled\"}},\"DefaultVersionNumber\":\"1\",\"LatestVersionNumber\":\"2\",\"TagSpecifications\":[]}",
        "supplementaryConfiguration": {}
    }
}

18行目の"configuration"を見て頂けると分かる通り、JSONの中にJSONが埋まる形でエスケープ処理されたJSON文字列が出力されています。 ここから特定の値を取得するためには、前処理として整形から実施していく必要があります。

エスケープ処理された文字列を整形

まずは、エスケープ処理されたjson文字列を、fromjsonで見やすく整形していきます。

コマンド

aws configservice list-aggregate-discovered-resources \
  --configuration-aggregator-name hoge \
  --resource-type AWS::EC2::LaunchTemplate \
  --query "ResourceIdentifiers[*].[SourceAccountId, SourceRegion, ResourceId, ResourceType]" \
  --output text | while read line
  do
    SourceAccountId=$(echo $line | awk '{print $1}')
    SourceRegion=$(echo $line | awk '{print $2}')
    ResourceId=$(echo $line | awk '{print $3}')
    ResourceType=$(echo $line | awk '{print $4}')
#EOSの行はスペースを省略しないとエラーになる。ただし一つ目のEOSの行はスペースがあっても良い
json=$(tr -d ' |\n' << EOS
      {
        "SourceAccountId" : "$SourceAccountId",
        "SourceRegion" : "$SourceRegion",
        "ResourceId" : "$ResourceId",
        "ResourceType" : "$ResourceType"
      }
EOS
    )
    aws configservice get-aggregate-resource-config \
      --configuration-aggregator-name hoge \
      --resource-identifier $json \
      | jq '.ConfigurationItem.configuration | fromjson'
  done

エスケープ処理された出力結果と比較して、だいぶ見やすくなったことがわかります。

出力結果

{
  "LaunchTemplateName": "asg-web-template",
  "Id": "lt-07cae1e5cb08b09f7",
  "LaunchTemplateData": {
    "EbsOptimized": false,
    "BlockDeviceMappings": [
      {
        "DeviceName": "/dev/xvda",
        "Ebs": {
          "Encrypted": false,
          "DeleteOnTermination": true,
          "VolumeSize": 8,
          "VolumeType": "gp2"
        }
      }
    ],
    "NetworkInterfaces": [
      {
        "AssociatePublicIpAddress": true,
        "DeleteOnTermination": true,
        "Description": "Primary network interface",
        "DeviceIndex": 0,
        "Groups": [
          "sg-07b90410018cd1533"
        ],
        "Ipv6Addresses": [],
        "PrivateIpAddresses": [],
        "Ipv4Prefixes": [],
        "Ipv6Prefixes": []
      }
    ],
    "ImageId": "ami-09ebacdc178ae23b7",
    "InstanceType": "t2.micro",
    "KeyName": "aws-ssh-key",
    "Monitoring": {
      "Enabled": false
    },
    "Placement": {
      "Tenancy": "default"
    },
    "DisableApiTermination": false,
    "UserData": "IyEvYmluL2Jhc2gKeXVtIHVwZGF0ZSAteQp5dW0gaW5zdGFsbCAteSBodHRwZApzeXN0ZW1jdGwgc3RhcnQgaHR0cGQuc2VydmljZQpzeXN0ZW1jdGwgZW5hYmxlIGh0dHBkLnNlcnZpY2UKZWNobyDigJxIZWxsbyBXb3JsZCBmcm9tICQoaG9zdG5hbWUgLWYp4oCdID4gL3Zhci93d3cvaHRtbC9pbmRleC5odG1s",
    "TagSpecifications": [],
    "ElasticGpuSpecifications": [],
    "ElasticInferenceAccelerators": [],
    "SecurityGroupIds": [],
    "SecurityGroups": [],
    "CreditSpecification": {
      "CpuCredits": "standard"
    },
    "CapacityReservationSpecification": {
      "CapacityReservationPreference": "open"
    },
    "LicenseSpecifications": [],
    "MetadataOptions": {
      "HttpTokens": "optional",
      "HttpPutResponseHopLimit": 1,
      "HttpEndpoint": "enabled"
    }
  },
  "DefaultVersionNumber": "1",
  "LatestVersionNumber": "2",
  "TagSpecifications": []
}

整形した文字列から必要な情報を抽出

先ほど整形した文字列から、JSON形式でセキュリティグループID関連の情報を取得してみます。

コマンド

aws configservice list-aggregate-discovered-resources \
  --configuration-aggregator-name hoge \
  --resource-type AWS::EC2::LaunchTemplate \
  --query "ResourceIdentifiers[*].[SourceAccountId, SourceRegion, ResourceId, ResourceType]" \
  --output text | while read line
  do
    SourceAccountId=$(echo $line | awk '{print $1}')
    SourceRegion=$(echo $line | awk '{print $2}')
    ResourceId=$(echo $line | awk '{print $3}')
    ResourceType=$(echo $line | awk '{print $4}')
#EOSの行はスペースを省略しないとエラーになる。ただし一つ目のEOSの行はスペースがあっても良い
json=$(tr -d ' |\n' << EOS
      {
        "SourceAccountId" : "$SourceAccountId",
        "SourceRegion" : "$SourceRegion",
        "ResourceId" : "$ResourceId",
        "ResourceType" : "$ResourceType"
      }
EOS
    )
    aws configservice get-aggregate-resource-config \
      --configuration-aggregator-name hoge \
      --resource-identifier $json \
      | jq '.ConfigurationItem.configuration | fromjson' | jq '{LaunchTemplateName: .LaunchTemplateName, Id: .Id,Groups: .LaunchTemplateData.NetworkInterfaces[].Groups[]}'
  done

出力結果

{
  "LaunchTemplateName": "asg-web-template",
  "Id": "lt-07cae1e5cb08b09f7",
  "Groups": "sg-07b90410018cd1533"
}

まとめ

エスケープ処理されたJSON文字列を最初に見たときは面食らいました。
jqでの必要な情報の抽出は、インターネット上での情報収集に苦労したため今回まとめてみました。

この記事が、どなたかのお役に立てば幸いです。それでは!