HashiCorp Vault — Secrets Engines Exercises
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.
- Enable KV v1 at
kv-v1/ - Enable KV v2 at
kv-v2/ - Write
username=admin password=secret env=prodto both at pathapp - Overwrite with only
password=new-passat both - 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.
- Write 5 versions to
secret/versioned - Read version 2
- Soft-delete version 3
- Verify version 3 is inaccessible
- Undelete version 3
- Permanently destroy version 1
- 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.
- Enable KV v2 at
team-alpha/,team-beta/, andshared/ - Write a secret to each
- List all secrets engines and verify all three are KV v2
- Tune
shared/to have a max of 3 versions and 30-day auto-delete - 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.
- Write a secret to
secret/payment-keyswithstripe_key=sk_live_abc - Add custom metadata:
owner=billing-team,compliance=pci,rotate-by=2024-06-01 - Enable CAS requirement on this secret
- Try to write without CAS — observe the error
- 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.
- Write a secret with 4 fields:
host,port,user,pass - Use
vault kv putto update onlypass— what happens to other fields? - Write all 4 fields again
- Use
vault kv patchto update onlypass— what happens now? - 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