Anthony Kong
Anthony Kong

Reputation: 40664

How to use jq to extract a particular field from a terraform state file?

Here is a simplified json file of a terraform state file (let's call it dev.ftstate)

{
  "version": 4,
  "terraform_version": "0.12.9",
  "serial": 2,
  "lineage": "ba56cc3e-71fd-1488-e6fb-3136f4630e70",
  "outputs": {},
  "resources": [
    {
      "module": "module.rds.module.reports_cpu_warning",
      "mode": "managed",
      "type": "datadog_monitor",
      "name": "alert",
      "each": "list",
      "provider": "module.rds.provider.datadog",
      "instances": []
    },
    {
      "module": "module.rds.module.reports_lag_warning",
      "mode": "managed",
      "type": "datadog_monitor",
      "name": "alert",
      "each": "list",
      "provider": "module.rds.provider.datadog",
      "instances": []
    },
    {
      "module": "module.rds.module.cross_region_replica_lag_alert",
      "mode": "managed",
      "type": "datadog_monitor",
      "name": "alert",
      "each": "list",
      "provider": "module.rds.provider.datadog",
      "instances": []
    },
    {
      "module": "module.rds",
      "mode": "managed",
      "type": "aws_db_instance",
      "name": "master",
      "provider": "provider.aws",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "address": "dev-database.123456.us-east-8.rds.amazonaws.com",
            "allocated_storage": 10,            
            "password": "",
            "performance_insights_enabled": false,
            "tags": {
              "env": "development"
            },
            "timeouts": {
              "create": "6h",
              "delete": "6h",
              "update": "6h"
            },
            "timezone": "",
            "username": "admin",
            "vpc_security_group_ids": [
              "sg-1234"
            ]
          },
          "private": ""
        }
      ]
    }
  ]
}

There are many modules at the same level of module.rds inside the instances. I took out many of them to create the simplified version of the raw data. The key takeway: do not assume the array index will be constant in all cases.

I wanted to extract the password field in the above example.

My first attempt is to use equality check to extract the relevant modules

` jq '.resources[].module == "module.rds"' dev.tfstate`

but it actually just produced a list of boolean values. I don't see any mention of builtin functions like filter in jq's manual

then I tried to just access the field:

> jq '.resources[].module[].attributes[].password?' dev.tfstate

then it throws the following error

jq: error (at dev.tfstate:1116): Cannot iterate over string ("module.rds")

So what is the best way to extract the value? Hopefully it can only focus on the password attribute in module.rds module only.

Edit:

My purpose is to detect if a password is left inside a state file. I want to ensure the passwords are exclusively stored in AWS secret manager.

Upvotes: 3

Views: 9358

Answers (2)

ydaetskcoR
ydaetskcoR

Reputation: 56877

You could also get the password value out of the state file without jq by using Terraform outputs. Your module should define an output with the value you want to output and you should also output this at the root module.

Without seeing your Terraform code you'd want something like this:

modules/rds/main.tf

resource "aws_db_instance" "master" {
  # ...
}

output "password" {
  value = aws_db_instance.master.password

  sensitive = true
}

example/main.tf

module "rds" {
  source = "../modules/rds"
  # ...
}

output "rds_password" {
  value = module.rds.password

  sensitive = true
}

The sensitive = true parameter means that Terraform won't print the output to stdout when running terraform apply but it's still held in plain text in the state file.

To then access this value without jq you can use the terraform output command which will retrieve the output from the state file and print it to stdout. From there you can use it however you want.

Upvotes: 2

user197693
user197693

Reputation: 2045

You can extract the module you want like this.

jq '.resources[] | select(.module == "module.rds")'

I'm not confident that I understand the requirements for the rest of the solution. So this might not only not be the best way of doing what you want; it might not do what you want at all!

If you know where password will be, you can do this.

jq '.resources[] | select(.module == "module.rds") | .instances[].attributes.password'

If you don't know exactly where password will be, this is a way of finding it.

jq '.resources[] | select(.module == "module.rds") | .. | .password? | values'

According to the manual under the heading "Recursive Descent," ..|.a? will "find all the values of object keys “a” in any object found “below” ."

values filters out the null results.

Upvotes: 5

Related Questions