google/cloud-secret-manager
Idiomatic PHP client for Google Cloud Secret Manager. Install via Composer, authenticate with Google Cloud credentials, then use SecretManagerServiceClient to create, access, and manage secrets over REST or gRPC with robust error handling.
composer require google/cloud-secret-manager
putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json');
AppServiceProvider:
use Google\Cloud\SecretManager\V1\SecretManagerServiceClient;
use Google\Cloud\SecretManager\V1\GetSecretRequest;
public function boot()
{
$client = new SecretManagerServiceClient();
$request = (new GetSecretRequest())
->setName('projects/YOUR_PROJECT_ID/secrets/YOUR_SECRET_ID/versions/latest');
$secret = $client->getSecret($request);
config(['services.db.password' => $secret->getPayload()->getData()]);
}
SecretManagerServiceClient::secretName()).addSecretVersion).Pattern: Replace .env values with dynamic Secret Manager fetches.
Example:
// config/services.php
return [
'db' => [
'password' => fn() => app(SecretManagerService::class)->get('database_password'),
],
'mail' => [
'password' => fn() => app(SecretManagerService::class)->get('smtp_password'),
],
];
Service Provider:
// app/Providers/SecretManagerServiceProvider.php
public function register()
{
$this->app->singleton(SecretManagerService::class, function ($app) {
$client = new SecretManagerServiceClient();
return new SecretManagerService($client);
});
}
Pattern: Use tags to fetch environment-specific secrets (e.g., env:production).
Example:
public function getSecretByTag(string $tag, string $secretName)
{
$filter = sprintf('tags.%s=%s', $tag, app()->environment());
$request = (new ListSecretsRequest())
->setParent('projects/YOUR_PROJECT_ID')
->setFilter($filter);
$secrets = $this->client->listSecrets($request);
foreach ($secrets->getSecrets() as $secret) {
if ($secret->getName() === $secretName) {
return $this->client->accessSecretVersion($secret->getName(), 'latest');
}
}
throw new \RuntimeException("Secret not found");
}
Pattern: Schedule secret rotations via Laravel’s scheduler. Example:
// app/Console/Commands/RotateDatabasePassword.php
public function handle()
{
$newPassword = Str::random(32);
$secretName = 'projects/YOUR_PROJECT_ID/secrets/db_password';
$this->client->addSecretVersion(
$secretName,
(new AddSecretVersionRequest())
->setPayload((new SecretPayload())
->setData($newPassword)
)
);
// Update Laravel config (e.g., via database or another secret)
$this->updateConfig($newPassword);
}
Schedule:
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
$schedule->command(RotateDatabasePassword::class)
->dailyAt('03:00')
->when(function () {
return app()->environment('production');
});
}
Pattern: Inject secrets into pipelines using GCP Workload Identity. GitHub Actions Example:
- name: Authenticate with GCP
uses: google-github-actions/auth@v1
with:
workload_identity_provider: 'projects/YOUR_PROJECT_ID/locations/global/workloadIdentityPools/my-pool/providers/my-provider'
service_account: 'my-service-account@YOUR_PROJECT_ID.iam.gserviceaccount.com'
- name: Fetch secrets
run: |
SECRET_VALUE=$(gcloud secrets versions access latest --secret=my-secret)
echo "SECRET_VALUE=$SECRET_VALUE" >> $GITHUB_ENV
Pattern: Cache secrets in Redis with TTL-based invalidation. Example:
public function getCachedSecret(string $secretName, int $ttl = 3600)
{
$cacheKey = "secret:{$secretName}";
if (cache()->has($cacheKey)) {
return cache()->get($cacheKey);
}
$secret = $this->client->accessSecretVersion($secretName, 'latest');
cache()->put($cacheKey, $secret->getPayload()->getData(), $ttl);
return $secret->getPayload()->getData();
}
Pattern: Use Laravel’s exception handling for API errors. Example:
try {
$secret = $this->client->accessSecretVersion($secretName, 'latest');
return $secret->getPayload()->getData();
} catch (ApiException $e) {
if ($e->getStatus()->getCode() === 5) { // Permission denied
Log::error("Access denied to secret {$secretName}", ['error' => $e->getMessage()]);
throw new \RuntimeException("Unauthorized access to secret");
}
throw $e;
}
Pattern: Store feature flags as secrets and toggle them dynamically. Example:
// config/feature-flags.php
return [
'new_checkout_flow' => fn() => app(SecretManagerService::class)->get('feature_flags_new_checkout'),
];
// In a controller:
if (config('feature-flags.new_checkout_flow') === 'enabled') {
// Enable new flow
}
Authentication Failures:
GOOGLE_APPLICATION_CREDENTIALS or misconfiguring IAM roles.putenv('GOOGLE_APPLICATION_CREDENTIALS=' . __DIR__ . '/service-account.json');
ApiException with status code 403 (Permission Denied).Resource Name Format:
versions/latest).$secretName = SecretManagerServiceClient::secretVersionName(
'projects/YOUR_PROJECT_ID',
'secrets/YOUR_SECRET_ID',
'versions/latest'
);
Large Secrets:
Delayed Destruction:
$request = (new DestroySecretRequest())
->setName($secretName)
->setForceDestroy(false) // Enable delayed destruction
->setRetentionPeriod('30d');
gRPC vs REST:
composer require google/cloud-common grpc/grpc
Then configure the client:
$client = new SecretManagerServiceClient([
'grpc' => [
'settings' => [
'default_metadata' => [
'x-goog-api-client' => 'laravel-app/1.0.0',
],
],
],
]);
Caching Invalidation:
$this->client->addSecretVersion($secretName, $payload);
cache()->forget("secret:{$secretName}");
Cross-Project Access:
roles/secretmanager.secretAccessor on the target project.8
How can I help you explore Laravel packages today?