Search code examples
jsonjq

Modify/delete nested/selected values with jq and output entire document


I'm migrating between Terraform providers, and to resolve a bug I need to change the tfstate files which are huge JSON blobs. I've distilled the file down to the most basic form, but I'd like to change from:

{
  "resources": [
    {
      "provider": "something_else",
      "type": "foo"
    },
    {
      "provider": "provider[\"registry.terraform.io/eddycharly/kops\"]",
      "type": "kops_cluster",
      "instances": [{
        "attributes": {
          "another_attr": "hello world",
          "config_base": "s3://foo-bucket/bar-env",
          "config_store": ""
        }
      }]
    },
    {
      "provider": "something_else",
      "type": "foo"
    }
  ]
}

To:

{
  "resources": [
    {
      "provider": "something_else",
      "type": "foo"
    },
    {
      "provider": "provider[\"registry.terraform.io/eddycharly/kops\"]",
      "type": "kops_cluster",
      "instances": [{
        "attributes": {
          "another_attr": "hello world",
          "config_store": [{
            "base": "s3://foo-bucket/bar-env"
          }]
        }
      }]
    },
    {
      "provider": "something_else",
      "type": "foo"
    }
  ]
}

To this end I've come up with the expression:

.resources[] | 
select(
  .provider == "provider[\"registry.terraform.io/eddycharly/kops\"]" and
  .type == "kops_cluster"
) |
.instances[].attributes |
.config_base as $config_base |
.config_store |= [{ "base":$config_base }] |
del(.config_base)

but the resulting output is only the modified .resources[].instances[].attributes block, not the entire document, eg:

{
  "another_attr": "hello world",
  "config_store": [
    {
      "base": "s3://foo-bucket/bar-env"
    }
  ]
}

How can I rearrange this expression to output the entire document?


Solution

  • You're looking for something like this:

    (
      .resources[] | 
      select(
        .provider == "provider[\"registry.terraform.io/eddycharly/kops\"]" and
        .type == "kops_cluster"
      ) |
      .instances[].attributes
    ) |= (
      .config_store = [{ base:.config_base }] |
      del(.config_base)
    )
    

    You need to parenthesize the lefthand side of |= to retain the original structure.