Multi-Cloud Secret Management¶
Problem Statement¶
Managing secrets across multiple cloud providers creates significant operational challenges:
- Secrets scattered across AWS, Azure, GCP, and HashiCorp Vault with no unified management
- Difficult to maintain consistency across different secret storage systems
- Manual synchronization between cloud providers is error-prone and time-consuming
- No standardized way to handle cloud provider-specific secret formats and APIs
- Cross-cloud disaster recovery and backup strategies are complex
- Auditing and compliance tracking across multiple platforms is challenging
- Different authentication mechanisms and permission models for each provider
SecretZero solves this by providing a unified interface for managing secrets across all major cloud providers with consistent configuration, automated synchronization, and comprehensive audit trails.
Prerequisites¶
- SecretZero installed with cloud provider support:
pip install secretzero[aws,azure,vault] - AWS credentials configured (IAM role, environment variables, or AWS CLI profile)
- Azure credentials configured (managed identity, service principal, or Azure CLI)
- HashiCorp Vault access token and endpoint URL
- Appropriate permissions in each cloud provider for secret management
- Optional: Google Cloud credentials for GCP Secret Manager
Authentication Setup¶
AWS Authentication¶
SecretZero uses the standard AWS credential chain:
# Option 1: Environment variables
export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
export AWS_DEFAULT_REGION=us-east-1
# Option 2: AWS CLI profile
aws configure --profile production
export AWS_PROFILE=production
# Option 3: IAM role (recommended for EC2/ECS/Lambda)
# No configuration needed - uses instance metadata
# Verify AWS credentials
aws sts get-caller-identity
Configure in Secretfile.yml:
providers:
aws:
kind: aws
auth:
kind: ambient # Uses AWS credential chain
config:
region: us-east-1
# Optional: specific profile
# profile: production
Azure Authentication¶
SecretZero uses Azure's DefaultAzureCredential chain:
# Option 1: Azure CLI (for local development)
az login
az account set --subscription "Production Subscription"
# Option 2: Service Principal (for automation)
export AZURE_CLIENT_ID=your-client-id
export AZURE_CLIENT_SECRET=your-client-secret
export AZURE_TENANT_ID=your-tenant-id
# Option 3: Managed Identity (for Azure VMs/Functions)
# No configuration needed - uses instance metadata
# Verify Azure credentials
az account show
Configure in Secretfile.yml:
providers:
azure:
kind: azure
auth:
kind: default # Uses DefaultAzureCredential chain
config:
# Optional: specific tenant
# tenant_id: your-tenant-id
HashiCorp Vault Authentication¶
# Set Vault address and token
export VAULT_ADDR=https://vault.example.com:8200
export VAULT_TOKEN=hvs.CAESIJ1...
# Verify Vault connectivity
vault status
vault token lookup
Configure in Secretfile.yml:
providers:
vault:
kind: vault
auth:
kind: token
config:
token: ${VAULT_TOKEN}
url: ${VAULT_ADDR}
# Optional: TLS configuration
# ca_cert: /path/to/ca.pem
# client_cert: /path/to/client.pem
# client_key: /path/to/client-key.pem
Google Cloud Authentication¶
# Authenticate with gcloud
gcloud auth application-default login
# Or use service account
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account-key.json
# Verify credentials
gcloud auth list
gcloud projects list
Configure in Secretfile.yml:
providers:
gcp:
kind: gcp
auth:
kind: ambient
config:
project_id: my-production-project
# Optional: service account key file
# credentials_file: /path/to/service-account-key.json
Configuration¶
Basic Multi-Cloud Setup¶
Create Secretfile.yml:
version: '1.0'
metadata:
project: multi-cloud-app
owner: platform-team
description: Multi-cloud secret distribution
variables:
environment: production
aws_region: us-east-1
app_name: myapp
providers:
# AWS Secrets Manager & SSM Parameter Store
aws:
kind: aws
auth:
kind: ambient
config:
region: ${aws_region}
# Azure Key Vault
azure:
kind: azure
auth:
kind: default
# HashiCorp Vault
vault:
kind: vault
auth:
kind: token
config:
token: ${VAULT_TOKEN}
url: ${VAULT_ADDR}
# Local development
local:
kind: local
secrets:
# Database password distributed to all providers
- name: database_password
kind: random_password
one_time: true # Generate once, sync everywhere
rotation_period: 90d
config:
length: 32
special: true
exclude_characters: '"@/\`'
targets:
# Local development
- provider: local
kind: file
config:
path: .env
format: dotenv
merge: true
# AWS Secrets Manager (primary)
- provider: aws
kind: secrets_manager
config:
name: ${environment}/${app_name}/database/password
description: Database password for ${app_name}
tags:
Environment: ${environment}
Application: ${app_name}
ManagedBy: secretzero
# AWS SSM Parameter Store (backup)
- provider: aws
kind: ssm_parameter
config:
name: /${environment}/${app_name}/database/password
type: SecureString
description: Database password for ${app_name}
tags:
Environment: ${environment}
Application: ${app_name}
# Azure Key Vault (DR region)
- provider: azure
kind: key_vault
config:
vault_url: https://myapp-prod-secrets.vault.azure.net
secret_name: ${app_name}-database-password
tags:
environment: ${environment}
application: ${app_name}
# HashiCorp Vault (multi-cloud reference)
- provider: vault
kind: kv
config:
path: ${environment}/${app_name}/database/password
mount_point: secret
version: 2 # KV v2
metadata:
application: ${app_name}
environment: ${environment}
# API Key distributed across clouds
- name: external_api_key
kind: random_string
rotation_period: 90d
config:
length: 32
charset: alphanumeric
targets:
# AWS Secrets Manager
- provider: aws
kind: secrets_manager
config:
name: ${environment}/${app_name}/api-key
description: External API key
# Azure Key Vault
- provider: azure
kind: key_vault
config:
vault_url: https://myapp-prod-secrets.vault.azure.net
secret_name: ${app_name}-api-key
# Vault KV
- provider: vault
kind: kv
config:
path: ${environment}/${app_name}/api-key
mount_point: secret
version: 2
# Service account credentials with multiple fields
- name: service_account
kind: templates.service_credentials
templates:
service_credentials:
description: Service account credentials
fields:
username:
description: Service account username
generator:
kind: static
config:
default: svc_${app_name}_${environment}
password:
description: Service account password
generator:
kind: random_password
config:
length: 32
special: true
api_endpoint:
description: API endpoint URL
generator:
kind: static
config:
default: https://api.example.com
api_version:
description: API version
generator:
kind: static
config:
default: v1
targets:
# Store as JSON in AWS Secrets Manager
- provider: aws
kind: secrets_manager
config:
name: ${environment}/${app_name}/service-account
description: Service account credentials
# Store in Azure Key Vault (as JSON string)
- provider: azure
kind: key_vault
config:
vault_url: https://myapp-prod-secrets.vault.azure.net
secret_name: ${app_name}-service-account
# Store as separate fields in Vault
- provider: vault
kind: kv
config:
path: ${environment}/${app_name}/service-account
mount_point: secret
version: 2
# Store locally as JSON file
- provider: local
kind: file
config:
path: .service-account.json
format: json
AWS-Specific Configuration¶
providers:
aws:
kind: aws
auth:
kind: ambient
config:
region: us-east-1
secrets:
# AWS Secrets Manager secret with automatic rotation
- name: rds_master_password
kind: random_password
rotation_period: 90d
config:
length: 32
special: true
targets:
- provider: aws
kind: secrets_manager
config:
name: /production/rds/master-password
description: RDS master password
kms_key_id: alias/aws/secretsmanager # Use custom KMS key
replica_regions:
- us-west-2 # Replicate to DR region
- eu-west-1
tags:
Environment: production
Compliance: soc2
DataClassification: sensitive
# SSM Parameter with tier and policies
- name: app_config_param
kind: random_string
config:
length: 64
targets:
- provider: aws
kind: ssm_parameter
config:
name: /production/app/config/secret-key
type: SecureString
tier: Advanced # Standard or Advanced
data_type: text
policies: |
{
"Type": "Expiration",
"Version": "1.0",
"Attributes": {
"Timestamp": "2025-12-31T23:59:59.000Z"
}
}
tags:
Environment: production
Application: myapp
# Cross-region AWS secrets
- name: multi_region_secret
kind: random_password
config:
length: 32
targets:
# Primary region
- provider: aws
kind: secrets_manager
config:
name: /production/multi-region-secret
description: Multi-region secret
replica_regions:
- us-west-2
- eu-west-1
- ap-southeast-1
Azure-Specific Configuration¶
providers:
azure:
kind: azure
auth:
kind: default
secrets:
# Azure Key Vault secret with expiration
- name: azure_storage_key
kind: random_string
rotation_period: 90d
config:
length: 44
charset: base64
targets:
- provider: azure
kind: key_vault
config:
vault_url: https://myapp-prod-kv.vault.azure.net
secret_name: storage-account-key
content_type: text/plain
enabled: true
expires_on: "2025-12-31T23:59:59Z"
not_before: "2024-01-01T00:00:00Z"
tags:
environment: production
application: myapp
compliance: iso27001
# Certificate in Azure Key Vault
- name: application_certificate
kind: templates.azure_certificate
templates:
azure_certificate:
description: Application certificate
fields:
certificate:
generator:
kind: static
config:
# PFX/PKCS12 certificate
default: ${CERTIFICATE_PFX_BASE64}
password:
generator:
kind: static
config:
default: ${CERTIFICATE_PASSWORD}
targets:
- provider: azure
kind: key_vault_certificate
config:
vault_url: https://myapp-prod-kv.vault.azure.net
certificate_name: app-certificate
content_type: application/x-pkcs12
tags:
environment: production
type: certificate
Vault-Specific Configuration¶
providers:
vault:
kind: vault
auth:
kind: token
config:
token: ${VAULT_TOKEN}
url: ${VAULT_ADDR}
secrets:
# KV v2 secret with metadata
- name: vault_app_secret
kind: random_password
rotation_period: 90d
config:
length: 32
targets:
- provider: vault
kind: kv
config:
path: production/app/secret-key
mount_point: secret
version: 2 # KV v2 enables versioning
cas: 0 # Check-and-Set for version control
metadata:
application: myapp
environment: production
owner: platform-team
rotation_period: 90d
# Dynamic database credentials
- name: vault_dynamic_db
kind: templates.vault_database
templates:
vault_database:
description: Vault dynamic database credentials
fields:
role:
generator:
kind: static
config:
default: myapp-readonly
connection:
generator:
kind: static
config:
default: postgres-prod
targets:
- provider: vault
kind: database_role
config:
database_mount: database
role_name: myapp-readonly
db_name: postgres-prod
creation_statements:
- "CREATE USER '{{name}}' WITH PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';"
- "GRANT SELECT ON ALL TABLES IN SCHEMA public TO '{{name}}';"
default_ttl: 1h
max_ttl: 24h
# Transit encryption key
- name: vault_encryption_key
kind: static
config:
default: placeholder
targets:
- provider: vault
kind: transit_key
config:
mount_point: transit
key_name: myapp-encryption-key
key_type: aes256-gcm96
derived: false
exportable: false
allow_plaintext_backup: false
Step-by-Step Instructions¶
1. Configure Cloud Provider Credentials¶
# AWS
export AWS_PROFILE=production
aws sts get-caller-identity
# Azure
az login
az account show
# Vault
export VAULT_ADDR=https://vault.example.com:8200
export VAULT_TOKEN=hvs.CAESIJ1...
vault status
# Verify all providers
secretzero test
2. Validate Configuration¶
secretzero validate -f Secretfile.yml
# Expected output:
# ✓ Configuration is valid
# ✓ 4 providers configured (aws, azure, vault, local)
# ✓ 3 secrets defined
# ✓ 12 targets configured
# ✓ All provider credentials valid
3. Preview Multi-Cloud Sync¶
secretzero sync --dry-run
# Expected output:
# [DRY RUN] Would create:
# ✓ database_password → Local file (.env)
# ✓ database_password → AWS Secrets Manager (production/myapp/database/password)
# ✓ database_password → AWS SSM Parameter (/production/myapp/database/password)
# ✓ database_password → Azure Key Vault (myapp-database-password)
# ✓ database_password → Vault KV (production/myapp/database/password)
# ✓ external_api_key → AWS Secrets Manager (production/myapp/api-key)
# ✓ external_api_key → Azure Key Vault (myapp-api-key)
# ✓ external_api_key → Vault KV (production/myapp/api-key)
# ✓ service_account → AWS Secrets Manager (production/myapp/service-account)
# ✓ service_account → Azure Key Vault (myapp-service-account)
# ✓ service_account → Vault KV (production/myapp/service-account)
# ✓ service_account → Local file (.service-account.json)
4. Sync Secrets Across Cloud Providers¶
secretzero sync
# Expected output:
# ✓ Generated database_password
# ✓ Created local file: .env
# ✓ Created AWS Secrets Manager secret: production/myapp/database/password
# ✓ Created AWS SSM Parameter: /production/myapp/database/password
# ✓ Created Azure Key Vault secret: myapp-database-password
# ✓ Created Vault KV secret: production/myapp/database/password
# ✓ Generated external_api_key
# ✓ Created AWS Secrets Manager secret: production/myapp/api-key
# ✓ Created Azure Key Vault secret: myapp-api-key
# ✓ Created Vault KV secret: production/myapp/api-key
# ✓ Generated service_account
# ✓ Created AWS Secrets Manager secret: production/myapp/service-account
# ✓ Created Azure Key Vault secret: myapp-service-account
# ✓ Created Vault KV secret: production/myapp/service-account
# ✓ Created local file: .service-account.json
# ✓ Updated .gitsecrets.lock
5. Verify Secrets in Each Provider¶
# AWS Secrets Manager
aws secretsmanager get-secret-value \
--secret-id production/myapp/database/password \
--query 'SecretString' --output text
# AWS SSM Parameter Store
aws ssm get-parameter \
--name /production/myapp/database/password \
--with-decryption \
--query 'Parameter.Value' --output text
# Azure Key Vault
az keyvault secret show \
--vault-name myapp-prod-secrets \
--name myapp-database-password \
--query 'value' --output tsv
# Vault KV
vault kv get -field=password secret/production/myapp/database/password
# Local files
cat .env
cat .service-account.json
Using Secrets in Different Clouds¶
AWS Lambda Function¶
import boto3
import json
# Using AWS Secrets Manager
def get_secret_from_secretsmanager():
client = boto3.client('secretsmanager', region_name='us-east-1')
response = client.get_secret_value(SecretId='production/myapp/database/password')
return response['SecretString']
# Using AWS SSM Parameter Store
def get_secret_from_ssm():
client = boto3.client('ssm', region_name='us-east-1')
response = client.get_parameter(
Name='/production/myapp/database/password',
WithDecryption=True
)
return response['Parameter']['Value']
# Using service account JSON
def get_service_account():
client = boto3.client('secretsmanager')
response = client.get_secret_value(SecretId='production/myapp/service-account')
return json.loads(response['SecretString'])
# Lambda handler
def lambda_handler(event, context):
db_password = get_secret_from_secretsmanager()
api_key = get_secret_from_ssm()
svc_account = get_service_account()
# Use secrets
return {
'statusCode': 200,
'body': json.dumps('Secrets loaded successfully')
}
Azure Function¶
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
import json
# Initialize Key Vault client
credential = DefaultAzureCredential()
vault_url = "https://myapp-prod-secrets.vault.azure.net"
client = SecretClient(vault_url=vault_url, credential=credential)
def get_database_password():
secret = client.get_secret("myapp-database-password")
return secret.value
def get_api_key():
secret = client.get_secret("myapp-api-key")
return secret.value
def get_service_account():
secret = client.get_secret("myapp-service-account")
return json.loads(secret.value)
# Azure Function main
def main(req):
db_password = get_database_password()
api_key = get_api_key()
svc_account = get_service_account()
# Use secrets
return {
"statusCode": 200,
"body": "Secrets loaded successfully"
}
HashiCorp Vault Integration¶
import hvac
import os
# Initialize Vault client
vault_url = os.environ.get('VAULT_ADDR', 'https://vault.example.com:8200')
vault_token = os.environ['VAULT_TOKEN']
client = hvac.Client(url=vault_url, token=vault_token)
def get_database_password():
secret = client.secrets.kv.v2.read_secret_version(
path='production/myapp/database/password',
mount_point='secret'
)
return secret['data']['data']['password']
def get_api_key():
secret = client.secrets.kv.v2.read_secret_version(
path='production/myapp/api-key',
mount_point='secret'
)
return secret['data']['data']
def get_service_account():
secret = client.secrets.kv.v2.read_secret_version(
path='production/myapp/service-account',
mount_point='secret'
)
return secret['data']['data']
# Use secrets
db_password = get_database_password()
api_key = get_api_key()
svc_account = get_service_account()
Multi-Cloud Application (Python)¶
import os
import boto3
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
import hvac
class MultiCloudSecretManager:
"""Unified secret manager for multi-cloud deployments"""
def __init__(self, cloud_provider=None):
self.cloud_provider = cloud_provider or self._detect_cloud_provider()
self._init_clients()
def _detect_cloud_provider(self):
"""Auto-detect cloud provider from environment"""
if os.environ.get('AWS_EXECUTION_ENV'):
return 'aws'
elif os.environ.get('AZURE_FUNCTIONS_ENVIRONMENT'):
return 'azure'
elif os.environ.get('VAULT_ADDR'):
return 'vault'
else:
return 'local'
def _init_clients(self):
"""Initialize cloud-specific clients"""
if self.cloud_provider == 'aws':
self.secrets_client = boto3.client('secretsmanager')
self.ssm_client = boto3.client('ssm')
elif self.cloud_provider == 'azure':
credential = DefaultAzureCredential()
vault_url = os.environ['AZURE_KEYVAULT_URL']
self.kv_client = SecretClient(vault_url=vault_url, credential=credential)
elif self.cloud_provider == 'vault':
self.vault_client = hvac.Client(
url=os.environ['VAULT_ADDR'],
token=os.environ['VAULT_TOKEN']
)
def get_secret(self, secret_path):
"""Get secret from appropriate cloud provider"""
if self.cloud_provider == 'aws':
response = self.secrets_client.get_secret_value(SecretId=secret_path)
return response['SecretString']
elif self.cloud_provider == 'azure':
secret = self.kv_client.get_secret(secret_path)
return secret.value
elif self.cloud_provider == 'vault':
secret = self.vault_client.secrets.kv.v2.read_secret_version(
path=secret_path,
mount_point='secret'
)
return secret['data']['data']
elif self.cloud_provider == 'local':
# Read from environment or .env file
return os.environ.get(secret_path)
# Usage
secrets = MultiCloudSecretManager()
db_password = secrets.get_secret('production/myapp/database/password')
api_key = secrets.get_secret('production/myapp/api-key')
Advanced Scenarios¶
Cross-Region Disaster Recovery¶
Replicate secrets across multiple regions for high availability:
version: '1.0'
variables:
environment: production
primary_region: us-east-1
dr_region: us-west-2
providers:
aws_primary:
kind: aws
auth:
kind: ambient
config:
region: ${primary_region}
aws_dr:
kind: aws
auth:
kind: ambient
config:
region: ${dr_region}
azure_primary:
kind: azure
auth:
kind: default
azure_dr:
kind: azure
auth:
kind: default
secrets:
- name: critical_database_password
kind: random_password
one_time: true
rotation_period: 90d
config:
length: 32
special: true
targets:
# AWS primary region
- provider: aws_primary
kind: secrets_manager
config:
name: ${environment}/database/password
description: Critical database password (primary)
replica_regions:
- ${dr_region} # AWS native replication
tags:
Region: primary
DR: enabled
# AWS DR region (explicit)
- provider: aws_dr
kind: secrets_manager
config:
name: ${environment}/database/password
description: Critical database password (DR)
tags:
Region: dr
DR: enabled
# Azure primary Key Vault
- provider: azure_primary
kind: key_vault
config:
vault_url: https://myapp-primary.vault.azure.net
secret_name: database-password
tags:
region: primary
dr: enabled
# Azure DR Key Vault
- provider: azure_dr
kind: key_vault
config:
vault_url: https://myapp-dr.vault.azure.net
secret_name: database-password
tags:
region: dr
dr: enabled
metadata:
project: multi-region-dr
owner: platform-team
compliance:
- soc2
- iso27001
Hybrid Cloud with On-Premises Vault¶
Bridge cloud secrets with on-premises HashiCorp Vault:
version: '1.0'
providers:
aws:
kind: aws
auth:
kind: ambient
config:
region: us-east-1
azure:
kind: azure
auth:
kind: default
# On-premises Vault cluster
vault_onprem:
kind: vault
auth:
kind: token
config:
token: ${VAULT_TOKEN}
url: https://vault.internal.company.com:8200
ca_cert: /etc/vault/ca.pem
# Cloud Vault cluster
vault_cloud:
kind: vault
auth:
kind: token
config:
token: ${VAULT_CLOUD_TOKEN}
url: https://vault.cloud.company.com:8200
secrets:
# Hybrid cloud application credentials
- name: app_hybrid_credentials
kind: templates.hybrid_credentials
templates:
hybrid_credentials:
description: Hybrid cloud application credentials
fields:
cloud_api_key:
generator:
kind: random_string
config:
length: 32
charset: hex
onprem_api_key:
generator:
kind: random_string
config:
length: 32
charset: hex
shared_secret:
generator:
kind: random_password
config:
length: 48
special: true
targets:
# Cloud secrets in AWS
- provider: aws
kind: secrets_manager
config:
name: production/hybrid/credentials
# Cloud secrets in Azure
- provider: azure
kind: key_vault
config:
vault_url: https://myapp-cloud.vault.azure.net
secret_name: hybrid-credentials
# On-premises Vault
- provider: vault_onprem
kind: kv
config:
path: production/hybrid/credentials
mount_point: secret
version: 2
# Cloud Vault (for cloud-native apps)
- provider: vault_cloud
kind: kv
config:
path: production/hybrid/credentials
mount_point: secret
version: 2
Compliance-Driven Multi-Cloud¶
Implement strict compliance requirements across cloud providers:
version: '1.0'
metadata:
project: compliance-multi-cloud
owner: compliance-team
compliance:
- soc2
- iso27001
- pci-dss
classification: highly-sensitive
variables:
environment: production
providers:
aws:
kind: aws
auth:
kind: ambient
config:
region: us-east-1
azure:
kind: azure
auth:
kind: default
vault:
kind: vault
auth:
kind: token
config:
token: ${VAULT_TOKEN}
url: ${VAULT_ADDR}
policies:
# Strict rotation requirements
compliance_rotation:
kind: rotation
require_rotation_period: true
max_age: 60d # PCI-DSS requires 90 days max
severity: error
enabled: true
# Encryption at rest required
encryption_required:
kind: encryption
require_kms: true
severity: error
enabled: true
# Audit trail requirements
audit_required:
kind: audit
require_tags: true
required_tags:
- DataClassification
- Owner
- Compliance
severity: error
enabled: true
secrets:
# PCI-DSS compliant payment processing secret
- name: payment_gateway_key
kind: random_password
rotation_period: 60d
config:
length: 48
special: true
targets:
# AWS with KMS encryption
- provider: aws
kind: secrets_manager
config:
name: ${environment}/payment/gateway-key
description: Payment gateway API key
kms_key_id: arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012
tags:
DataClassification: highly-sensitive
Owner: payments-team
Compliance: pci-dss,soc2
Environment: ${environment}
# Azure with managed encryption
- provider: azure
kind: key_vault
config:
vault_url: https://myapp-compliance.vault.azure.net
secret_name: payment-gateway-key
tags:
data-classification: highly-sensitive
owner: payments-team
compliance: pci-dss,soc2
# Vault with audit enabled
- provider: vault
kind: kv
config:
path: ${environment}/payment/gateway-key
mount_point: secret
version: 2
metadata:
data_classification: highly-sensitive
owner: payments-team
compliance: pci-dss,soc2
audit_required: "true"
# SOC2 compliant database credentials
- name: database_admin_password
kind: random_password
rotation_period: 90d
config:
length: 32
special: true
exclude_characters: '"@/\`'
targets:
- provider: aws
kind: secrets_manager
config:
name: ${environment}/database/admin-password
description: Database administrator password
kms_key_id: arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012
replica_regions:
- us-west-2 # DR region
tags:
DataClassification: sensitive
Owner: database-team
Compliance: soc2,iso27001
RotationPeriod: 90d
- provider: azure
kind: key_vault
config:
vault_url: https://myapp-compliance.vault.azure.net
secret_name: database-admin-password
expires_on: "{{ 90 days from now }}"
tags:
data-classification: sensitive
owner: database-team
compliance: soc2,iso27001
- provider: vault
kind: kv
config:
path: ${environment}/database/admin-password
mount_point: secret
version: 2
metadata:
data_classification: sensitive
owner: database-team
compliance: soc2,iso27001
rotation_period: 90d
Cost Optimization Strategy¶
Optimize costs by using appropriate secret storage for different use cases:
version: '1.0'
variables:
environment: production
providers:
aws:
kind: aws
auth:
kind: ambient
secrets:
# High-value secrets → AWS Secrets Manager ($0.40/month + $0.05/10k API calls)
- name: database_master_password
kind: random_password
rotation_period: 90d
config:
length: 32
special: true
targets:
- provider: aws
kind: secrets_manager
config:
name: ${environment}/database/master-password
description: RDS master password (high-value, rotation enabled)
tags:
Cost-Optimization: secrets-manager
Rotation: enabled
# Standard secrets → SSM Parameter Store ($0.05/month for Standard tier)
- name: api_key
kind: random_string
config:
length: 32
targets:
- provider: aws
kind: ssm_parameter
config:
name: /${environment}/api/key
type: SecureString
tier: Standard # More cost-effective than Secrets Manager
description: API key (standard access pattern)
tags:
Cost-Optimization: ssm-standard
# Configuration values → SSM Parameter Store String (Free)
- name: app_version
kind: static
config:
default: "1.0.0"
targets:
- provider: aws
kind: ssm_parameter
config:
name: /${environment}/app/version
type: String # Free tier
tier: Standard
description: Application version (non-sensitive)
tags:
Cost-Optimization: ssm-string-free
# Large secrets → SSM Parameter Store Advanced tier ($0.05/month)
- name: large_certificate
kind: static
config:
default: ${CERTIFICATE_CONTENT} # Up to 8KB
targets:
- provider: aws
kind: ssm_parameter
config:
name: /${environment}/certificates/large-cert
type: SecureString
tier: Advanced # Required for >4KB parameters
description: Large certificate (Advanced tier required)
tags:
Cost-Optimization: ssm-advanced
Best Practices¶
-
Use Provider-Native Features - Leverage cloud-specific features like AWS Secrets Manager automatic rotation, Azure Key Vault soft delete, and Vault's dynamic secrets. Don't try to replicate provider features with custom code - use what each platform does best.
-
Implement Geographic Redundancy - Store critical secrets in multiple regions and cloud providers for disaster recovery. Use AWS Secrets Manager replica regions, Azure geo-replication, and Vault replication clusters. Test failover procedures regularly.
-
Tag Everything Consistently - Apply consistent tagging across all cloud providers for cost tracking, compliance, and automation. Use tags for: DataClassification, Owner, Environment, Application, Compliance, CostCenter, and ManagedBy. Enforce tag policies.
-
Minimize Cross-Cloud API Calls - Cache secrets locally when possible to reduce API calls and costs. Use AWS Secrets Manager caching, Azure Key Vault caching, or implement application-level caching with appropriate TTLs (5-60 minutes depending on sensitivity).
-
Implement Least Privilege Access - Grant minimum necessary permissions in each cloud provider: AWS IAM policies with specific secret ARNs, Azure RBAC with specific Key Vault permissions, and Vault policies with path-based access. Avoid wildcard permissions.
-
Monitor and Alert on Secret Access - Enable CloudTrail (AWS), Azure Monitor, and Vault audit logs to track secret access. Set up alerts for unusual access patterns, failed authentication attempts, and secret modifications. Integrate with SIEM systems.
-
Use Cost-Effective Storage - Choose appropriate secret storage based on access patterns and requirements: Secrets Manager for auto-rotation, SSM Parameter Store for simple secrets, Key Vault for Azure-native apps, and Vault for dynamic secrets. Balance cost vs. features.
-
Encrypt with Customer-Managed Keys - Use AWS KMS customer-managed keys (CMK), Azure Key Vault keys, or Vault transit encryption for additional control. Enable key rotation policies. Separate keys by environment and data classification level.
-
Implement Secret Versioning - Enable versioning in AWS Secrets Manager, Azure Key Vault, and Vault KV v2 to support rollback during incidents. Retain previous versions for audit and recovery purposes (typically 90 days minimum for compliance).
-
Automate Compliance Validation - Use SecretZero policies to enforce rotation requirements, tagging standards, and access controls across all cloud providers. Run compliance checks in CI/CD pipelines. Generate compliance reports for auditors showing consistent controls across clouds.
Troubleshooting¶
AWS Authentication Failed¶
Problem: Error: Unable to authenticate with AWS
Solutions:
# Verify AWS credentials
aws sts get-caller-identity
# Check IAM permissions
aws iam get-user
aws iam list-attached-user-policies --user-name your-username
# Verify required permissions
aws secretsmanager list-secrets --max-items 1
aws ssm describe-parameters --max-items 1
# Check region configuration
echo $AWS_DEFAULT_REGION
aws configure get region
# Use specific profile
export AWS_PROFILE=production
secretzero sync
# Check credential chain
aws configure list
# For EC2/ECS, verify IAM role
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
Required AWS IAM permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:CreateSecret",
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue",
"secretsmanager:UpdateSecret",
"secretsmanager:TagResource"
],
"Resource": "arn:aws:secretsmanager:*:*:secret:production/*"
},
{
"Effect": "Allow",
"Action": [
"ssm:PutParameter",
"ssm:GetParameter",
"ssm:AddTagsToResource"
],
"Resource": "arn:aws:ssm:*:*:parameter/production/*"
},
{
"Effect": "Allow",
"Action": [
"kms:Decrypt",
"kms:Encrypt",
"kms:GenerateDataKey"
],
"Resource": "arn:aws:kms:*:*:key/*"
}
]
}
Azure Authentication Failed¶
Problem: Error: Azure authentication failed
Solutions:
# Verify Azure login
az account show
# Check subscription
az account list --output table
# Set correct subscription
az account set --subscription "Production Subscription"
# Verify Key Vault access
az keyvault list --output table
az keyvault secret list --vault-name myapp-prod-secrets
# Check service principal (if using)
az ad sp show --id $AZURE_CLIENT_ID
# Verify Key Vault permissions
az keyvault set-policy \
--name myapp-prod-secrets \
--object-id $(az ad signed-in-user show --query objectId -o tsv) \
--secret-permissions get list set delete
# For managed identity (in Azure VM/Functions)
curl 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net' -H Metadata:true
# Check environment variables
echo $AZURE_CLIENT_ID
echo $AZURE_TENANT_ID
Required Azure Key Vault permissions:
- Secret: Get, List, Set, Delete
- Certificate: Get, List, Import (if using certificates)
- Storage: Get, List, Set (if using storage account keys)
Vault Connection Failed¶
Problem: Error: Unable to connect to Vault
Solutions:
# Verify Vault address
echo $VAULT_ADDR
curl -k $VAULT_ADDR/v1/sys/health
# Check Vault token
echo $VAULT_TOKEN
vault token lookup
# Verify token permissions
vault token capabilities secret/production/myapp
# Check Vault status
vault status
# Test authentication
vault auth list
vault login $VAULT_TOKEN
# Verify KV mount
vault secrets list
vault kv list secret/
# Check network connectivity
nc -zv vault.example.com 8200
curl -v https://vault.example.com:8200/v1/sys/health
# TLS certificate issues
vault status -tls-skip-verify
# Or configure CA certificate
export VAULT_CACERT=/path/to/ca.pem
# Check Vault policies
vault policy read myapp-policy
vault token lookup -format=json | jq -r .data.policies
Secret Not Syncing to All Providers¶
Problem: Secret syncs to some providers but not others
Solutions:
# Enable verbose logging
secretzero sync --verbose
# Test each provider individually
secretzero test
# Check provider configuration
secretzero validate
# Force sync to specific provider
secretzero sync --provider aws
secretzero sync --provider azure
secretzero sync --provider vault
# Check lockfile for sync status
cat .gitsecrets.lock | jq '.secrets[] | select(.name=="database_password")'
# Verify permissions for each provider
# AWS
aws secretsmanager describe-secret --secret-id production/myapp/database/password
# Azure
az keyvault secret show --vault-name myapp-prod-secrets --name myapp-database-password
# Vault
vault kv get secret/production/myapp/database/password
# Check for provider-specific errors in logs
secretzero sync 2>&1 | grep ERROR
Cross-Region Replication Issues¶
Problem: AWS Secrets Manager replication not working
Solutions:
# Verify replica regions configuration
aws secretsmanager describe-secret \
--secret-id production/myapp/database/password \
--query 'ReplicationStatus' --output json
# Check KMS key permissions in replica regions
aws kms describe-key \
--key-id alias/aws/secretsmanager \
--region us-west-2
# Enable replication manually
aws secretsmanager replicate-secret-to-regions \
--secret-id production/myapp/database/password \
--add-replica-regions Region=us-west-2 \
--region us-east-1
# Verify replication status
aws secretsmanager describe-secret \
--secret-id production/myapp/database/password \
--region us-west-2
# Check for replication errors
aws secretsmanager list-secrets \
--filter Key=name,Values=production/myapp/database/password \
--region us-east-1
# Remove and re-add replica if stuck
aws secretsmanager remove-regions-from-replication \
--secret-id production/myapp/database/password \
--remove-replica-regions us-west-2 \
--region us-east-1
# Wait a few seconds, then re-add
aws secretsmanager replicate-secret-to-regions \
--secret-id production/myapp/database/password \
--add-replica-regions Region=us-west-2 \
--region us-east-1
Cost Overruns¶
Problem: Unexpected high costs from secret storage
Solutions:
# Analyze AWS costs
# Secrets Manager: $0.40/month per secret + $0.05/10k API calls
aws secretsmanager list-secrets --query 'SecretList | length'
# SSM Parameter Store: $0.05/month (Standard), $0.05/month (Advanced)
aws ssm describe-parameters --query 'Parameters | length'
# Check API call metrics
aws cloudwatch get-metric-statistics \
--namespace AWS/SecretsManager \
--metric-name SecretAccessed \
--dimensions Name=SecretName,Value=production/myapp/database/password \
--start-time 2024-01-01T00:00:00Z \
--end-time 2024-01-31T23:59:59Z \
--period 86400 \
--statistics Sum
# Optimize by migrating to SSM Parameter Store
# For secrets without rotation needs
secretzero sync --target-type ssm_parameter
# Implement caching to reduce API calls
# Use AWS Secrets Manager caching client
pip install aws-secretsmanager-caching
# Azure Key Vault costs
# $0.03 per 10,000 operations
az monitor metrics list \
--resource /subscriptions/{subscription-id}/resourceGroups/{rg}/providers/Microsoft.KeyVault/vaults/{vault-name} \
--metric ServiceApiHit \
--start-time 2024-01-01T00:00:00Z \
--end-time 2024-01-31T23:59:59Z
# Vault costs depend on deployment model
# Self-hosted: infrastructure costs
# HCP Vault: per-client pricing
Provider API Rate Limiting¶
Problem: Error: Rate limit exceeded
Solutions:
# AWS rate limits
# Secrets Manager: 1,500 TPS per account per region
# SSM: 3,000 TPS for GetParameter
# Implement exponential backoff
secretzero sync --retry-max-attempts 5 --retry-backoff exponential
# Batch operations when possible
# Use AWS SDK batch operations
# Azure rate limits
# Key Vault: 2,000 GET requests per 10 seconds per vault
# Spread across multiple Key Vaults if needed
# Vault rate limits
# Configured via rate limit quotas
vault read sys/quotas/rate-limit
# Implement client-side caching
# Cache secrets for 5-60 minutes depending on sensitivity
export SECRETZERO_CACHE_TTL=300 # 5 minutes
# Monitor rate limit metrics
# AWS CloudWatch
aws cloudwatch get-metric-statistics \
--namespace AWS/SecretsManager \
--metric-name ThrottledRequests \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
--period 300 \
--statistics Sum
# Use provider SDKs with built-in retry logic
Secret Value Corruption¶
Problem: Secret value appears corrupted or unreadable
Solutions:
# Check encoding
# AWS and Azure use UTF-8, Vault uses bytes
# Verify secret value
# AWS
aws secretsmanager get-secret-value \
--secret-id production/myapp/database/password \
--query 'SecretString' --output text | od -c
# Azure
az keyvault secret show \
--vault-name myapp-prod-secrets \
--name myapp-database-password \
--query 'value' --output tsv | od -c
# Vault
vault kv get -field=password secret/production/myapp/database/password | od -c
# Check for base64 encoding issues
# SecretZero handles base64 encoding automatically
# Don't manually encode values
# Verify character set
secretzero validate
# Check special characters in secret
# Some providers have restrictions on special characters
# Re-generate and sync
secretzero sync --force --secret database_password
# Check lockfile for original value hash
cat .gitsecrets.lock | jq '.secrets[] | select(.name=="database_password")'
Complete Example¶
Full production multi-cloud secret management:
version: '1.0'
metadata:
project: multi-cloud-production
owner: platform-team
compliance:
- soc2
- iso27001
classification: sensitive
description: |
Production multi-cloud secret management across AWS, Azure, and Vault.
Implements geographic redundancy, compliance requirements, and cost optimization.
variables:
environment: production
app_name: myapp
aws_primary_region: us-east-1
aws_dr_region: us-west-2
azure_primary_region: eastus
azure_dr_region: westus2
providers:
# AWS Primary Region
aws_primary:
kind: aws
auth:
kind: ambient
config:
region: ${aws_primary_region}
# AWS DR Region
aws_dr:
kind: aws
auth:
kind: ambient
config:
region: ${aws_dr_region}
# Azure Primary
azure_primary:
kind: azure
auth:
kind: default
# HashiCorp Vault
vault:
kind: vault
auth:
kind: token
config:
token: ${VAULT_TOKEN}
url: ${VAULT_ADDR}
# Local development
local:
kind: local
policies:
# Rotation requirements
compliance_rotation:
kind: rotation
require_rotation_period: true
max_age: 90d
severity: error
enabled: true
# Tagging requirements
audit_trail:
kind: audit
require_tags: true
required_tags:
- DataClassification
- Owner
- Environment
- Application
severity: warning
enabled: true
secrets:
# 1. Database credentials (multi-cloud, auto-rotating)
- name: database_credentials
kind: templates.database_full
rotation_period: 90d
# 2. API keys (distributed across providers)
- name: external_api_keys
kind: templates.api_credentials
rotation_period: 90d
# 3. Service account (JSON format)
- name: service_account_credentials
kind: templates.service_account
rotation_period: 180d
# 4. Encryption keys
- name: application_encryption_key
kind: random_string
one_time: true
rotation_period: 365d
config:
length: 32
charset: base64
targets:
# AWS with KMS encryption
- provider: aws_primary
kind: secrets_manager
config:
name: ${environment}/${app_name}/encryption-key
description: Application encryption key
kms_key_id: alias/myapp-encryption
replica_regions:
- ${aws_dr_region}
tags:
DataClassification: highly-sensitive
Owner: security-team
Environment: ${environment}
Application: ${app_name}
Compliance: soc2,iso27001
# Azure Key Vault
- provider: azure_primary
kind: key_vault
config:
vault_url: https://myapp-prod-primary.vault.azure.net
secret_name: ${app_name}-encryption-key
tags:
data-classification: highly-sensitive
owner: security-team
environment: ${environment}
# Vault
- provider: vault
kind: kv
config:
path: ${environment}/${app_name}/encryption-key
mount_point: secret
version: 2
metadata:
data_classification: highly-sensitive
owner: security-team
templates:
# Complete database credentials
database_full:
description: Complete database credentials with connection strings
fields:
host:
generator:
kind: static
config:
default: ${DB_HOST}
port:
generator:
kind: static
config:
default: "5432"
database:
generator:
kind: static
config:
default: ${app_name}
username:
generator:
kind: static
config:
default: ${app_name}_user
password:
generator:
kind: random_password
config:
length: 32
special: true
exclude_characters: '"@/\`'
connection_string:
generator:
kind: static
config:
default: postgresql://${app_name}_user:{{password}}@${DB_HOST}:5432/${app_name}
targets:
# AWS Secrets Manager (primary)
- provider: aws_primary
kind: secrets_manager
config:
name: ${environment}/${app_name}/database/credentials
description: Database credentials with auto-rotation
replica_regions:
- ${aws_dr_region}
tags:
DataClassification: sensitive
Owner: database-team
Environment: ${environment}
Application: ${app_name}
RotationEnabled: "true"
# Azure Key Vault (DR)
- provider: azure_primary
kind: key_vault
config:
vault_url: https://myapp-prod-primary.vault.azure.net
secret_name: ${app_name}-database-credentials
content_type: application/json
tags:
data-classification: sensitive
owner: database-team
environment: ${environment}
# Vault (multi-cloud reference)
- provider: vault
kind: kv
config:
path: ${environment}/${app_name}/database/credentials
mount_point: secret
version: 2
metadata:
data_classification: sensitive
owner: database-team
rotation_period: 90d
# Local development
- provider: local
kind: file
config:
path: .env
format: dotenv
merge: true
# API credentials
api_credentials:
description: External API credentials
fields:
stripe_api_key:
generator:
kind: static
config:
default: ${STRIPE_API_KEY}
sendgrid_api_key:
generator:
kind: static
config:
default: ${SENDGRID_API_KEY}
twilio_auth_token:
generator:
kind: static
config:
default: ${TWILIO_AUTH_TOKEN}
targets:
# AWS Secrets Manager
- provider: aws_primary
kind: secrets_manager
config:
name: ${environment}/${app_name}/api-keys
description: Third-party API keys
tags:
DataClassification: sensitive
Owner: platform-team
Environment: ${environment}
Application: ${app_name}
# Azure Key Vault
- provider: azure_primary
kind: key_vault
config:
vault_url: https://myapp-prod-primary.vault.azure.net
secret_name: ${app_name}-api-keys
content_type: application/json
tags:
data-classification: sensitive
owner: platform-team
# Vault
- provider: vault
kind: kv
config:
path: ${environment}/${app_name}/api-keys
mount_point: secret
version: 2
# Service account
service_account:
description: Service account credentials for automation
fields:
account_id:
generator:
kind: static
config:
default: svc_${app_name}_${environment}
api_key:
generator:
kind: random_string
config:
length: 32
charset: hex
api_secret:
generator:
kind: random_password
config:
length: 48
special: true
created_at:
generator:
kind: static
config:
default: "2024-01-15T10:30:00Z" # ISO 8601 timestamp
targets:
# AWS Secrets Manager
- provider: aws_primary
kind: secrets_manager
config:
name: ${environment}/${app_name}/service-account
description: Service account credentials
tags:
DataClassification: sensitive
Owner: automation-team
Environment: ${environment}
# Azure Key Vault
- provider: azure_primary
kind: key_vault
config:
vault_url: https://myapp-prod-primary.vault.azure.net
secret_name: ${app_name}-service-account
content_type: application/json
# Vault
- provider: vault
kind: kv
config:
path: ${environment}/${app_name}/service-account
mount_point: secret
version: 2
# Local file
- provider: local
kind: file
config:
path: .service-account.json
format: json
Deploy:
# 1. Set environment variables
export AWS_PROFILE=production
export AZURE_SUBSCRIPTION_ID=your-subscription-id
export VAULT_ADDR=https://vault.example.com:8200
export VAULT_TOKEN=hvs.CAESIJ1...
export DB_HOST=postgres.prod.example.com
export STRIPE_API_KEY=sk_live_...
export SENDGRID_API_KEY=SG....
export TWILIO_AUTH_TOKEN=your-token
# 2. Validate configuration
secretzero validate
# 3. Test all provider connections
secretzero test
# 4. Check compliance policies
secretzero policy --fail-on-warning
# 5. Preview multi-cloud sync
secretzero sync --dry-run
# 6. Sync secrets to all providers
secretzero sync
# 7. Verify in each provider
# AWS
aws secretsmanager list-secrets --query 'SecretList[?starts_with(Name, `production/myapp/`)].Name'
# Azure
az keyvault secret list --vault-name myapp-prod-primary --query '[].name'
# Vault
vault kv list secret/production/myapp/
# Local
ls -la .env .service-account.json
# 8. Test secret retrieval
# AWS
aws secretsmanager get-secret-value --secret-id production/myapp/database/credentials
# Azure
az keyvault secret show --vault-name myapp-prod-primary --name myapp-database-credentials
# Vault
vault kv get secret/production/myapp/database/credentials
# 9. Set up automated rotation
# Add to CI/CD pipeline or scheduled job
secretzero rotate --dry-run
secretzero rotate
Next Steps¶
- Kubernetes Integration - Sync multi-cloud secrets to Kubernetes
- Compliance Scenarios - Implement SOC2/ISO27001 across clouds
- Migration Guide - Migrate from single-cloud to multi-cloud
- AWS Secrets Manager - AWS-specific features
- Azure Key Vault - Azure-specific features
- HashiCorp Vault - Vault-specific features