Securely load environment variables from AWS SSM Parameter Store or Azure Key Vault.
Project description
Envilder Python SDK
Securely load environment variables from AWS SSM Parameter Store or Azure Key Vault directly into your Python application. Zero vendor lock-in — secrets stay in your cloud.
Part of the Envilder project.
Prerequisites
- Python 3.10+
- AWS provider: AWS credentials configured (CLI, environment variables, or IAM role)
- Azure provider: Azure credentials via
az login, managed identity, or environment variables
Install
uv add envilder
# or
pip install envilder
Quick Start
One-liner
from envilder import Envilder
# Resolve secrets and inject into os.environ
Envilder.load('secrets-map.json')
import os
print(os.environ['DB_PASSWORD'])
Resolve without injecting
from envilder import Envilder
secrets = Envilder.resolve_file('secrets-map.json')
print(secrets['DB_PASSWORD'])
Fluent builder (with overrides)
Override the map file's $config at runtime — useful for switching providers,
profiles, or vault URLs per environment:
from envilder import Envilder, SecretProviderType
# Override provider + vault URL
secrets = (
Envilder.from_map_file('secrets-map.json')
.with_provider(SecretProviderType.AZURE)
.with_vault_url('https://my-vault.vault.azure.net')
.resolve()
)
# Override AWS profile and inject
(
Envilder.from_map_file('secrets-map.json')
.with_profile('staging')
.inject()
)
Environment-based loading
Route secret loading based on your current environment. Each environment
maps to its own secrets file (or None to skip loading):
from envilder import Envilder
import os
env = os.getenv('APP_ENV', 'development')
# Resolve + inject into os.environ
Envilder.load(env, {
'production': 'prod-secrets.json',
'development': 'dev-secrets.json',
'test': None, # no secrets loaded
})
Resolve without injecting:
secrets = Envilder.resolve_file(env, {
'production': 'prod-secrets.json',
'development': 'dev-secrets.json',
'test': None,
})
Behaviour:
- If the environment maps to a file path, secrets are loaded from that file.
- If the environment maps to
Noneor is not in the mapping, an empty dict is returned silently — no errors, no output. - The environment name is stripped of leading/trailing whitespace before lookup.
- Empty or whitespace-only environment names raise
ValueError.
Secret validation
Opt-in validation ensures all resolved secrets have non-empty values:
from envilder import Envilder, validate_secrets
secrets = Envilder.resolve_file('secrets-map.json')
validate_secrets(secrets) # raises SecretValidationError if any value is empty
validate_secrets() checks that:
- The dictionary is not empty (raises
SecretValidationErrorwith emptymissing_keys) - Every value is non-None and non-whitespace (raises
SecretValidationErrorlisting the failing keys) - Passes silently when all values are present
from envilder import SecretValidationError, validate_secrets
try:
validate_secrets(secrets)
except SecretValidationError as e:
print(f"Missing: {', '.join(e.missing_keys)}")
Advanced usage
Implement the ISecretProvider protocol to plug in a custom backend
(e.g., HashiCorp Vault, GCP Secret Manager):
from envilder import EnvilderClient, ISecretProvider, MapFileParser
class MyCustomProvider(ISecretProvider):
def get_secret(self, name: str) -> str | None:
# fetch from your custom backend
...
with open('secrets-map.json', encoding='utf-8') as file:
map_file = MapFileParser().parse(file.read())
provider = MyCustomProvider()
secrets = EnvilderClient(provider).resolve_secrets(map_file)
EnvilderClient.inject_into_environment(secrets)
API Reference
Static facade (Envilder)
| Method | Description |
|---|---|
load(path) |
Resolve secrets and inject into os.environ |
resolve_file(path) |
Resolve secrets, return as dict |
load(env, mapping) |
Environment-based resolve + inject |
resolve_file(env, mapping) |
Environment-based resolve |
from_map_file(path) |
Returns fluent builder for configuration |
Fluent builder (via from_map_file())
| Method | Description |
|---|---|
with_provider(type) |
Override secret provider (AWS/Azure) |
with_profile(name) |
Override AWS named profile |
with_vault_url(url) |
Override Azure Key Vault URL |
resolve() |
Resolve secrets, return as dict |
inject() |
Resolve + inject into os.environ |
Validation
| Function | Description |
|---|---|
validate_secrets(dict) |
Raises SecretValidationError if any value is empty or dict is empty |
Map File Format
{
"$config": {
"provider": "aws",
"profile": "my-profile"
},
"DB_PASSWORD": "/app/prod/db-password",
"API_KEY": "/app/prod/api-key"
}
Supported providers: aws (default), azure.
For Azure, add vaultUrl:
{
"$config": {
"provider": "azure",
"vaultUrl": "https://my-vault.vault.azure.net"
},
"DB_PASSWORD": "db-password",
"API_KEY": "api-key"
}
Links
License
MIT
Development
Setup
# From the repo root
make install-sdk-python
Quality checks
make check-sdk-python # black + isort + mypy (no changes)
make format-sdk-python # auto-format
Running tests
Unit tests run without any external dependencies:
cd tests/sdks/python
python -m pytest -v -m "not acceptance"
Acceptance tests require Docker and a LocalStack auth token:
export LOCALSTACK_AUTH_TOKEN=<your-token>
cd tests/sdks/python
python -m pytest -v -m acceptance
All tests:
make test-sdk-python
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file envilder-0.3.2.tar.gz.
File metadata
- Download URL: envilder-0.3.2.tar.gz
- Upload date:
- Size: 76.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4b7834ef0e854cc997370a95cf3aeafc2cd0695aa2334cdbeca8eac77b0f7f2
|
|
| MD5 |
2ad05601f7a6e981a81e5d7d50ad93e8
|
|
| BLAKE2b-256 |
e934abeeb6f04b7b9da66933c692ed3912035030d59be498367eedf98f6bb213
|
Provenance
The following attestation bundles were made for envilder-0.3.2.tar.gz:
Publisher:
publish-pypi.yml on macalbert/envilder
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
envilder-0.3.2.tar.gz -
Subject digest:
a4b7834ef0e854cc997370a95cf3aeafc2cd0695aa2334cdbeca8eac77b0f7f2 - Sigstore transparency entry: 1339998337
- Sigstore integration time:
-
Permalink:
macalbert/envilder@7b2520ad0b3a04bb677d5fb09bbed033f451d298 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/macalbert
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@7b2520ad0b3a04bb677d5fb09bbed033f451d298 -
Trigger Event:
push
-
Statement type:
File details
Details for the file envilder-0.3.2-py3-none-any.whl.
File metadata
- Download URL: envilder-0.3.2-py3-none-any.whl
- Upload date:
- Size: 16.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
95543407f183781611c6cbd4afa6123879489daaf9c2bc7f5eabd3ff237c1f9c
|
|
| MD5 |
f3877e94180d322a4ab3cbe8fa9647ac
|
|
| BLAKE2b-256 |
c88e4f32a6fb04814dc4b282376cb3310cda962bafb59c31c92daa7fadc46877
|
Provenance
The following attestation bundles were made for envilder-0.3.2-py3-none-any.whl:
Publisher:
publish-pypi.yml on macalbert/envilder
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
envilder-0.3.2-py3-none-any.whl -
Subject digest:
95543407f183781611c6cbd4afa6123879489daaf9c2bc7f5eabd3ff237c1f9c - Sigstore transparency entry: 1339998338
- Sigstore integration time:
-
Permalink:
macalbert/envilder@7b2520ad0b3a04bb677d5fb09bbed033f451d298 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/macalbert
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@7b2520ad0b3a04bb677d5fb09bbed033f451d298 -
Trigger Event:
push
-
Statement type: