Skip to content

Export

vkv export requires an engine path (--path or --engine-path) and supports the following export formats (specify via --format flag).

See the CLI Reference for more details on the supported flags and env vars.

Warning

Vault allows / in the name of a KV engine. This makes it difficult for vkv to distinguish between directories and the KV engine name..

If your KV engine name/mount contains a / you have to specify it using --engine-path|-e, otherwise vkv will output the secrets wrong.

This also applies for any vkv import ... operations.

Info

vkv handles 3 different path arguments, specified using -e|-p

  1. root path: any normal KV mount. Use -p.
  2. engine-path: in case your KV mount contains a /. Use -e.
  3. sub path: the path to the corresponding directory within a KV mount. When using -p this is everything after the first /: e.g: kv/prod/db/; root path=kv, subpath=prod/db. In conjunction with a -e you can specify a sub-path by using -p: -e=kv/prod -p=db.

base

Each secret shows its current version ([v=N]), when that version was created ((created X ago)) and any custom metadata ([key=value]):

> vkv export -p secret -f=base
secret/ [desc=key/value secret storage] [type=kv2]
├── admin [v=1] (created 5 minutes ago) [key=value]
   └── sub=********
├── demo [v=1] (created 5 minutes ago)
   └── foo=***
└── sub
    ├── demo [v=1] (created 5 minutes ago)
       ├── demo=***********
       ├── password=*******
       └── user=*****
    └── sub2
        └── demo [v=2] (created 5 minutes ago) [admin=false key=value]
            ├── foo=***
            ├── password=********
            └── user=****

Info

In a terminal, path elements are shown in bold and the version/age annotation in cyan. Colors are disabled automatically when the output is piped or redirected. Use --show-version=false to hide the [v=N] (created X ago) annotation.

yaml

Info

yaml and json always export real values (no masking) using flat, full secret-path keys. This keeps the output easy to consume programmatically and lets it be piped straight back into vkv import.

> vkv export -p secret -f=yaml
admin:
  sub: password
demo:
  foo: bar
sub/demo:
  demo: hello world
  password: s3cre5<
  user: admin
sub/sub2/demo:
  foo: bar
  password: password
  user: user

json

> vkv export -p secret -f=json
{
  "admin": {
    "sub": "password"
  },
  "demo": {
    "foo": "bar"
  },
  "sub/demo": {
    "demo": "hello world",
    "password": "s3cre5<",
    "user": "admin"
  },
  "sub/sub2/demo": {
    "foo": "bar",
    "password": "password",
    "user": "user"
  }
}

all-versions

The --all-versions flag exports every version of each KVv2 secret instead of only the latest one. It is supported by the base, yaml and json formats.

In the base (tree) format each version is shown with its creation time (and deleted/destroyed status), the key/value pairs it contained, and any custom metadata next to the secret name:

> vkv export -p secret --all-versions
secret/ [kv2] (key/value secret storage)
├── admin {key=value}
   └── [Version 1 created 4 minutes ago]
       └── sub=********
├── demo
   └── [Version 1 created 4 minutes ago]
       └── foo=***
└── sub
    ├── demo
       └── [Version 1 created 4 minutes ago]
           ├── demo=***********
           ├── password=*******
           └── user=*****
    └── sub2
        └── demo {admin=false key=value}
            ├── [Version 2 created 4 minutes ago]
               ├── foo=***
               ├── password=********
               └── user=****
            └── [Version 1 created 4 minutes ago]
                ├── foo=***
                ├── password=********
                └── user=****

Info

In a terminal, path elements are shown in bold, version headers in cyan and metadata dimmed. Colors are automatically disabled when the output is piped or redirected.

The yaml and json formats emit the full version history per secret, including timestamps, destroyed/deletion_time status and custom metadata:

> vkv export -p secret/sub/sub2 --all-versions -f=json
{
  "sub/sub2/demo": {
    "custom_metadata": {
      "admin": "false",
      "key": "value"
    },
    "versions": [
      {
        "version": 2,
        "created_time": "2024-05-28T05:57:52Z",
        "destroyed": false,
        "data": {
          "foo": "bar",
          "password": "password",
          "user": "user"
        }
      },
      {
        "version": 1,
        "created_time": "2024-05-28T04:47:54Z",
        "destroyed": false,
        "data": {
          "foo": "bar",
          "password": "password",
          "user": "user"
        }
      }
    ]
  }
}

export

> vkv export -p secret -f=export
export admin='key'
export demo='hello world'
export foo='bar'
export password='password'
export sub='password'

eval $(vkv export -p secret -f=export)
echo $admin
key

policy

> vkv export -p secret -f=policy
PATH                    CREATE  READ    UPDATE  DELETE  LIST    ROOT
secret/sub/sub2/demo                                       secret/admin                                               secret/demo                                                secret/sub/demo                                            

markdown

> vkv export -p secret -f=markdown
|         PATH         |   KEY    |    VALUE    | VERSION |       METADATA        |
|----------------------|----------|-------------|---------|-----------------------|
| secret/admin         | sub      | ********    |       1 | key=value             |
| secret/demo          | foo      | ***         |       1 |                       |
| secret/sub/demo      | demo     | *********** |       1 |                       |
|                      | password | ******      |         |                       |
|                      | user     | *****       |         |                       |
| secret/sub/sub2/demo | admin    | ***         |       2 | admin=false key=value |
|                      | foo      | ***         |         |                       |
|                      | password | ********    |         |                       |
|                      | user     | ****        |         |                       |

template

template is a special output format that allows you, render the output using Golangs template engine. Format template requires either a --template-file or a --template-string flag or the equivalent env vars.

The secrets are passed as map with the secret path as the key and the actual secrets as values:

# <PATH>              <SECRETS>
secret/admin          map[sub:password]
secret/demo           map[foo:bar]
secret/sub/demo       map[demo:hello world password:s3cre5< user:admin]
secret/sub/sub2/demo  map[foo:bar password:password user:user]

Here is an advanced template that renders the secrets in a special env var export format. Note that within a --template-file or a --template-string the following functions are available: http://masterminds.github.io/sprig/:

# export.tmpl
{{- range $path, $secrets := . }}
{{- range $key, $value := $secrets }}
export {{ list $path $key | join "/" | replace "/" "_" | upper | trimPrefix "SECRET_" }}={{ $value | squote -}}
{{ end -}}
{{- end }}

This would result in the following output:

> vkv export -p secret -f=template --template-file=export.tmpl
export ADMIN_SUB='password'
export DEMO_FOO='bar'
export SUB_DEMO_DEMO='hello world'
export SUB_DEMO_PASSWORD='s3cre5<'
export SUB_DEMO_USER='admin'
export SUB_SUB2_DEMO_FOO='bar'
export SUB_SUB2_DEMO_PASSWORD='password'
export SUB_SUB2_DEMO_USER='user'

The yaml and json formats already emit flat, non-nested paths (see above). For the template format the secrets are passed to the template nested by default; enable --merge-paths to receive them as flat, full-path keys instead:

# merged.tmpl
{{- range $path, $secrets := . }}
{{ $path }}: {{ $secrets }}
{{- end }}
> vkv export -p secret --merge-paths -f=template --template-file=merged.tmpl
secret/admin: map[sub:password]
secret/demo: map[foo:bar]
secret/sub/demo: map[demo:hello world password:s3cre5< user:admin]
secret/sub/sub2/demo: map[foo:bar password:password user:user]