HashiCorp Vault — Secrets Engines Exercises

4 minute read

Practice working with secrets engines, KV versions, and metadata management.


Exercise 1 — KV v1 vs v2 Behavior

Task: Observe the difference in overwrite behavior between KV v1 and KV v2.

  1. Enable KV v1 at kv-v1/
  2. Enable KV v2 at kv-v2/
  3. Write username=admin password=secret env=prod to both at path app
  4. Overwrite with only password=new-pass at both
  5. Read both secrets — what’s different?
Show Answer
# Enable both
vault secrets enable -path=kv-v1 -version=1 kv
vault secrets enable -path=kv-v2 kv-v2

# Initial write
vault kv put kv-v1/app username=admin password=secret env=prod
vault kv put kv-v2/app username=admin password=secret env=prod

# Overwrite
vault kv put kv-v1/app password=new-pass
vault kv put kv-v2/app password=new-pass

# Read KV v1
vault kv get kv-v1/app
# Only shows: password=new-pass
# username and env are GONE (replaced entirely)

# Read KV v2
vault kv get kv-v2/app
# Only shows: password=new-pass for current version
# BUT version 1 still has all three fields:
vault kv get -version=1 kv-v2/app
# Shows: username=admin, password=secret, env=prod

Key difference: KV v1 permanently loses the old data. KV v2 preserves it as a previous version.


Exercise 2 — Version Management

Task: Practice version lifecycle operations.

  1. Write 5 versions to secret/versioned
  2. Read version 2
  3. Soft-delete version 3
  4. Verify version 3 is inaccessible
  5. Undelete version 3
  6. Permanently destroy version 1
  7. Set max-versions to 3 and write version 6 — what happens to old versions?
Show Answer
# Write 5 versions
for i in 1 2 3 4 5; do
  vault kv put secret/versioned data="version-$i"
done

# Read version 2
vault kv get -version=2 secret/versioned
# data=version-2

# Soft-delete version 3
vault kv delete -versions=3 secret/versioned

# Try to read version 3
vault kv get -version=3 secret/versioned
# No data returned (deleted)

# Check metadata
vault kv metadata get secret/versioned
# Version 3 shows deletion_time

# Undelete version 3
vault kv undelete -versions=3 secret/versioned

# Read version 3 again
vault kv get -version=3 secret/versioned
# data=version-3 (restored!)

# Permanently destroy version 1
vault kv destroy -versions=1 secret/versioned

# Try to read version 1
vault kv get -version=1 secret/versioned
# No data (destroyed, cannot recover)

# Set max-versions to 3
vault kv metadata put -max-versions=3 secret/versioned

# Write version 6
vault kv put secret/versioned data="version-6"

# Check metadata — only versions 4, 5, 6 remain
vault kv metadata get secret/versioned
# oldest_version: 4

Exercise 3 — Multiple Mount Points

Task: Set up an engine-per-team architecture.

  1. Enable KV v2 at team-alpha/, team-beta/, and shared/
  2. Write a secret to each
  3. List all secrets engines and verify all three are KV v2
  4. Tune shared/ to have a max of 3 versions and 30-day auto-delete
  5. Disable team-beta/
Show Answer
# Enable engines
vault secrets enable -path=team-alpha -description="Team Alpha secrets" kv-v2
vault secrets enable -path=team-beta -description="Team Beta secrets" kv-v2
vault secrets enable -path=shared -description="Shared secrets" kv-v2

# Write secrets
vault kv put team-alpha/db-creds host=alpha-db.local user=alpha pass=abc
vault kv put team-beta/api-keys stripe=sk_test_123 twilio=AC_456
vault kv put shared/config region=us-east-1 env=production

# List all engines
vault secrets list -format=json | jq 'to_entries[] | select(.value.type=="kv") | .key'

# Tune shared/
vault secrets tune -options=max_versions=3 -options=delete_version_after=720h shared/

# Disable team-beta (WARNING: destroys all data!)
vault secrets disable team-beta/

Exercise 4 — Custom Metadata and CAS

Task: Use custom metadata and check-and-set.

  1. Write a secret to secret/payment-keys with stripe_key=sk_live_abc
  2. Add custom metadata: owner=billing-team, compliance=pci, rotate-by=2024-06-01
  3. Enable CAS requirement on this secret
  4. Try to write without CAS — observe the error
  5. Write correctly with CAS
Show Answer
# Write the secret
vault kv put secret/payment-keys stripe_key=sk_live_abc

# Add custom metadata and enable CAS
vault kv metadata put \
    -custom-metadata=owner="billing-team" \
    -custom-metadata=compliance="pci" \
    -custom-metadata=rotate-by="2024-06-01" \
    -cas-required=true \
    secret/payment-keys

# Try writing without CAS — this fails
vault kv put secret/payment-keys stripe_key=sk_live_xyz
# Error: check-and-set parameter required for this path

# Write with CAS (current version is 1)
vault kv put -cas=1 secret/payment-keys stripe_key=sk_live_xyz
# Success! Now version 2

# Next write needs cas=2
vault kv put -cas=2 secret/payment-keys stripe_key=sk_live_new
# Success! Version 3

Exercise 5 — Patch vs Put

Task: Compare patch and put operations.

  1. Write a secret with 4 fields: host, port, user, pass
  2. Use vault kv put to update only pass — what happens to other fields?
  3. Write all 4 fields again
  4. Use vault kv patch to update only pass — what happens now?
  5. Verify all 4 fields are present after patch
Show Answer
# Write initial secret
vault kv put secret/db host="db.local" port="5432" user="admin" pass="old-pass"

# PUT with only pass — replaces entire secret
vault kv put secret/db pass="new-pass"
vault kv get secret/db
# Only shows: pass=new-pass (other fields GONE!)

# Restore all fields
vault kv put secret/db host="db.local" port="5432" user="admin" pass="old-pass"

# PATCH with only pass — updates just that field
vault kv patch secret/db pass="patched-pass"
vault kv get secret/db
# Shows all fields:
#   host=db.local
#   pass=patched-pass   ← updated
#   port=5432
#   user=admin

# Key takeaway:
# PUT = replace entire secret
# PATCH = update specific fields, preserve others

What’s Next?

You’ve mastered secrets engines, KV versioning, and metadata. Next, we’ll learn how clients authenticate to Vault.

Next Chapter: Authentication