Is there a way to programmatically create secrets ...
# prefect-community
r
Is there a way to programmatically create secrets in prefect 2.0? It is easy to load secrets that were created via the UI but it would be useful to be able to write secrets from python.
Some additional questions as well: 1. is it possible to delete blocks after creating them? 2. I’ve tried defining a block with a type
List[secretStr]
but it ends up storing the secret as a list like
['*', '*', '*'...,'*']
. Is there a way to store secrets securely in a list object?
j
@Rio McMahon could you share how you defined your list of secrets? I would have expected that to work. I’ve written a unit test to check, is this different than what you’re seeing:
Copy code
async def test_block_with_list_of_secrets(self, session):
        class ListSecretBlock(Block):
            x: List[SecretStr]

        # save the block
        await ListSecretBlock(x=["a", "b"]).save(name="list-secret")

        # load the block
        block = await ListSecretBlock.load("list-secret")

        assert block.x[0].get_secret_value() == 'a'
        assert block.x[1].get_secret_value() == 'b'
r
Hi @Jeremiah thanks for the response. It works properly when creating the block but fails when editing the block. If I try this:
Copy code
from typing import Dict, List
from pydantic import SecretStr
from prefect.blocks.core import Block

class ApiKeys(Block):
    api_key_secret: List[SecretStr]
and register the block via:
prefect block register --file file.py
I can add the secret as shown in the attached vid. When I poll for the secrets using:
Copy code
import prefect
pc = prefect.get_client()
blocks = await pc.read_block_documents()
print(blocks)
The relevant returned bit shows:
Copy code
...name='stuff', data={'api_key_secret_list': ['*', '*', '*', '*', '*', '*', '*', '*', 'more_things']}...
I also tried doing this with a definition like
Copy code
from typing import Dict, List
from pydantic import SecretStr
from prefect.blocks.core import Block

class ApiKeys(Block):
    api_key_secret = List[Dict[str, SecretStr]]
but it was only prompting me for a list input; not a list of dict and strings. Basically I just want to store a list of secure data - if this is an antipattern/there is a better way to do this I am all ears.
Sorry for the text blast but one more thing I noticed that is relevant with the UI; I followed your example above and was able to implement the custom
List[Dict[str, SecretStr]]
block:
Copy code
from typing import Dict, List
from pydantic import SecretStr
from prefect.blocks.core import Block
    
class ApiKeys(Block):
    api_key_secret: List[Dict[str, SecretStr]]
await ApiKeys(api_key_secret=[{'a':'b'},{'c':'d'}]).save(name='testsecret')

secrets = await ApiKeys.load('testsecret')
print(secrets.api_key_secret[0])  # returns  `{'a': SecretStr('**********')}`
print(secrets.api_key_secret[0]['a'].get_secret_value())  # returns `'b'`
So you can see that the secret is properly obscured when printed to the console; however it is not obfuscated in the UI as seen in the attached screenshot:
j
Ah, thanks for pointing that out about the UI — this is going to be an issue, as I don’t think there’s a convention for discovering or declaring that elements are secret (only fields as a whole)
We may instead need to encourage json strings (or develop a
SecretJSON
type)
👍 2