This assumes you are familiar with the Python hvac client. If not, check out my article Hashicorp Vault - Getting Started with Python hvac.
This assumes the following has already been done.
- Hashicorp Vault has been installed
- Hashicorp Vault has been initialized
- Hashicorp Vault has been unsealed
Let's say the secrets engine has been enabled with -path=secret/
~]# vault secrets enable -path=secret/ kv
Success! Enabled the kv secrets engine at: secret/
And let's say approle has been enabled and there is a role named "my-role" and contains a policy named "my-policy".
~]$ vault read auth/approle/role/my-role
Key Value
--- -----
policies [my-policy]
In this example, since the secrets engine has been enabled with -path=secret/ the policy path will need to begin with secret/
Let's say "my-policy" permits the following capabilities to "secret/my_path/*".
~]$ vault policy read my-policy
path "secret/my_path/*" {
capabilities = ["create", "delete", "list", "patch", "read", "update"]
}
Creating or Updating a secret uses the same exact method, client.secrets.kv.v2.create_or_update_secret. The method behavior doesn't change based on whether you are creating a new secret or updating a secret that already exists. This isn't problematic when creating a new secret, but it can be an issue when updating a secret that already exists. Check out my article update a secret using Python hvac.
In this scenario, you would first use approle login with the role ID and secret ID for my-role and then use client.secrets.kv.v2.create_or_update_secret to create a new secret.
- mount_path='my_path' is used here since my-policy has secret/my_path/*
- the name of the secret will be my_secret and the path to the secret will be secret/my_path/my_secret
- the secret will contain a single key/value pair (foo: bar)
Check out my article Hashicorp Vault - Error Handling using Python hvac for details on how to include Error Handling.
#!/usr/bin/python3
import hvac
client = hvac.Client(url='http://vault.example.com:8200')
client.auth.approle.login(
role_id="b4a68549-1464-7aac-b0cd-d22954985aa8",
secret_id="6039e2e2-6017-8db9-2e1b-dd6bd449f901"
)
client.secrets.kv.v2.create_or_update_secret(
mount_point="my_path",
path='my_secret',
secret=dict(foo='bar')
)
client.logout()
Or like this.
client.secrets.kv.v2.create_or_update_secret(
mount_point="my_path",
path='my_secret',
secret={"foo": "bar"}
)
Or like this, to create a secret with multiple key/value pairs.
client.secrets.kv.v2.create_or_update_secret(
mount_point="my_path",
path='my_secret',
secret={"foo": "hello", "bar": "world"}
)
If the secret is successfully created or updated, something like this should be returned.
{
'request_id': 'bb772050-6722-33f2-4419-efa6c1886d82',
'lease_id': '',
'renewable': False,
'lease_duration': 0,
'data': {
'created_time': '2024-03-20T08:29:09.697056103Z',
'custom_metadata': None,
'deletion_time': '',
'destroyed': False,
'version': 3},
'wrap_info': None,
'warnings': None,
'auth': None
}
The response dictionary can be used to run a basic test to determine if the secret was created or updated.
try:
response = client.secrets.kv.v2.create_or_update_secret(
mount_point="my_path",
path='my_secret',
secret={"foo": "bar"}
)
except hvac.exceptions.InvalidPath:
print("my_path appears to be invalid")
except hvac.exceptions.Forbidden:
print("you are forbidden access to my_path")
try:
response['data']['created_time']
except KeyError:
print(f"got KeyError")
else:
print(f"Successfully created key foo in secret my_secret at {response['data']['created_time']}")
Did you find this article helpful?
If so, consider buying me a coffee over at