constup-foss/symfony8-aws-secrets-bundle
Install the Bundle
composer require constup-foss/symfony8-aws-secrets-bundle
Ensure your project uses Symfony 5.4+ (or Symfony 8+ as implied by the package name).
Enable the Bundle
Add to config/bundles.php:
return [
// ...
Constup\Foss\Symfony8AwsSecretsBundle\Symfony8AwsSecretsBundle::class => ['all' => true],
];
Configure AWS Credentials
Add AWS credentials to .env:
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_REGION=your-region
AWS_SECRETS_MANAGER_ENDPOINT=optional-custom-endpoint
First Use Case: Fetch a Secret
Inject the AwsSecretsManagerClient service and retrieve a secret:
use Constup\Foss\Symfony8AwsSecretsBundle\Client\AwsSecretsManagerClient;
class MyService
{
public function __construct(private AwsSecretsManagerClient $secretsClient) {}
public function getDatabaseCredentials(): array
{
return $this->secretsClient->getSecret('my-db-credentials');
}
}
Use the bundle’s AwsSecretsManagerParameterBag to replace Symfony’s ParameterBag for environment variables:
# config/packages/framework.yaml
framework:
secret: '%env(AWS_SECRET)%' # Fetches from AWS Secrets Manager
Bind AWS secrets to services:
// src/Service/MyService.php
class MyService
{
public function __construct(
private string $apiKey, // Auto-injected from AWS Secrets
private AwsSecretsManagerClient $client
) {
// $apiKey resolves from AWS Secrets Manager via DI
}
}
Define in services.yaml:
services:
App\Service\MyService:
arguments:
$apiKey: '%env(AWS_API_KEY)%'
Cache secrets to reduce AWS API calls (default TTL: 300s):
$this->secretsClient->getSecret('my-secret', 600); // Custom TTL (600s)
Use the rotateSecret method to update secrets programmatically:
$this->secretsClient->rotateSecret('my-secret');
Extend the SecretParserInterface for non-JSON secrets:
use Constup\Foss\Symfony8AwsSecretsBundle\Parser\SecretParserInterface;
class CustomSecretParser implements SecretParserInterface
{
public function parse(string $secretString): array
{
return ['custom' => 'parsed', 'data' => json_decode($secretString, true)];
}
}
Register in services.yaml:
services:
App\Parser\CustomSecretParser:
tags: ['constup.aws_secrets.parser']
Environment-Specific Secrets
Use different secret names per environment (e.g., dev-db-credentials, prod-db-credentials).
$secret = $this->secretsClient->getSecret('{{ env("APP_ENV") }}-db-credentials');
Fallback to .env
Combine with Symfony’s %env% for local development:
# config/services.yaml
parameters:
db_password: '%env(AWS_DB_PASSWORD)%' # Falls back to .env if AWS fails
Logging Enable debug logging for AWS API calls:
# config/packages/monolog.yaml
monolog:
handlers:
aws_secrets:
type: stream
path: '%kernel.logs_dir%/aws_secrets.log'
level: debug
channels: ['aws_secrets']
IAM Permissions Ensure the AWS IAM role/user has:
secretsmanager:GetSecretValuesecretsmanager:DescribeSecret (for rotation)secretsmanager:ListSecrets (if using listSecrets()).Secret Not Found
Throws Constup\Foss\Symfony8AwsSecretsBundle\Exception\SecretNotFoundException.
Handle gracefully:
try {
$secret = $this->secretsClient->getSecret('nonexistent');
} catch (SecretNotFoundException $e) {
// Fallback logic
}
Caching Issues Clear cache after rotating secrets manually:
php bin/console cache:clear
Region Mismatch
Verify AWS_REGION matches the Secrets Manager region where secrets are stored.
Large Secrets AWS Secrets Manager has a 64KB limit for secret values. For larger data, use AWS Systems Manager (SSM) Parameter Store or S3.
Enable Verbose AWS SDK Logging
Add to .env:
AWS_DEBUG=true
Check SDK Errors The bundle wraps AWS SDK exceptions. Inspect the original exception:
try {
$this->secretsClient->getSecret('my-secret');
} catch (AwsException $e) {
error_log($e->getAwsErrorMessage());
}
Validate Secret JSON Ensure secrets are valid JSON. Use a custom parser for non-JSON formats (e.g., XML, YAML).
Custom Secret Storage
Implement Constup\Foss\Symfony8AwsSecretsBundle\Storage\SecretStorageInterface for alternative backends (e.g., DynamoDB).
Secret Validation Add validation rules via a compiler pass:
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class AddSecretValidatorPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$definition = $container->findDefinition('constup.aws_secrets.validator');
$definition->addMethodCall('addRule', ['required_keys', ['api_key', 'db_password']]);
}
}
Event Listeners Listen for secret rotation events:
use Constup\Foss\Symfony8AwsSecretsBundle\Event\SecretRotatedEvent;
class MyListener
{
public function onSecretRotated(SecretRotatedEvent $event)
{
// Notify Slack/email on rotation
}
}
Register in services.yaml:
services:
App\EventListener\MyListener:
tags:
- { name: 'kernel.event_listener', event: 'constup.aws_secrets.secret_rotated' }
Default TTL
Override the default 300s cache TTL in config/packages/constup_aws_secrets.yaml:
constup_aws_secrets:
cache_ttl: 900 # 15 minutes
Endpoint Overrides
Use AWS_SECRETS_MANAGER_ENDPOINT for custom endpoints (e.g., AWS GovCloud):
AWS_SECRETS_MANAGER_ENDPOINT=https://secretsmanager.us-gov-west-1.amazonaws.com
Profile Switching
Use named AWS profiles (via AWS_PROFILE env var) for multi-account setups.
How can I help you explore Laravel packages today?