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.
Install the package:
composer require google/cloud-secret-manager
Authenticate (choose one):
putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json');
First use case: Fetch a secret in Laravel’s 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/secrets/YOUR_SECRET/versions/latest');
$secret = $client->getSecret($request);
$payload = $secret->getPayload()->getData();
config(['services.database.password' => $payload]);
}
SecretManagerServiceClient::secretName()).env() with runtime secret fetching.$client = new SecretManagerServiceClient();
$secret = $client->accessSecretVersion(
$client->secretVersionName('projects/PROJECT/secrets/DB_PASSWORD')
);
return $secret->getPayload()->getData();
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->bind('db.password', function () {
return app(SecretManagerServiceClient::class)
->accessSecretVersion('projects/.../secrets/DB_PASSWORD')
->getPayload()
->getData();
});
}
$filter = 'tags.environment="production" AND tags.tenant="acme"';
$listRequest = (new ListSecretsRequest())
->setParent('projects/PROJECT')
->setFilter($filter);
$secrets = $client->listSecrets($listRequest);
foreach ($secrets as $secret) {
// Process secrets matching tags
}
// Rotate a secret every 30 days
$rotation = (new AddSecretVersionRequest())
->setPayload((new Payload())->setData('new_password_here'))
->setSecretId('DB_PASSWORD');
$client->addSecretVersion($rotation);
- name: Fetch secrets
run: |
SECRET=$(gcloud secrets versions access latest --secret=DB_PASSWORD)
echo "DB_PASSWORD=$SECRET" >> $GITHUB_ENV
.env.$cacheKey = 'secret:db_password';
if (!cache()->has($cacheKey)) {
$secret = $client->accessSecretVersion('projects/.../secrets/DB_PASSWORD');
cache()->put($cacheKey, $secret->getPayload()->getData(), now()->addHours(1));
}
return cache()->get($cacheKey);
config(['services.stripe.key' => $client->accessSecretVersion('projects/.../secrets/STRIPE_KEY')->getPayload()->getData()]);
Horizon::configureUsing(function () {
$redisPassword = $client->accessSecretVersion('projects/.../secrets/REDIS_PASSWORD')->getPayload()->getData();
return ['redis' => ['password' => $redisPassword]];
});
Nova::serving(function () {
$novaKey = $client->accessSecretVersion('projects/.../secrets/NOVA_KEY')->getPayload()->getData();
config(['nova.key' => $novaKey]);
});
# In Envoyer post-deploy script
gcloud secrets versions access latest --secret=APP_KEY > /var/www/.env
Authentication Failures:
ApiException with 403 Permission Denied.roles/secretmanager.secretAccessor (or higher) and the secret’s IAM policy allows access.GOOGLE_APPLICATION_CREDENTIALS_DEBUG=true to log auth details.Resource Name Format:
InvalidArgumentException for malformed secret names.$name = $client->secretName('projects/PROJECT/secrets/SECRET');
Payload Decoding:
getData() returns binary data (not a string).$payload = base64_decode($secret->getPayload()->getData());
Rate Limiting:
429 Too Many Requests during peak loads.Delayed Destruction:
destroySecret().disableSecretVersion() for soft deletion or set a retention period:
$request = (new DestroySecretRequest())
->setName('projects/PROJECT/secrets/SECRET')
->setForceDestroy(false); // Enables delayed destruction
PHP Version Mismatch:
Class not found errors on PHP <8.1.1.15.x).gRPC vs. REST:
pecl install grpc
Or fall back to REST:
$client = new SecretManagerServiceClient(['grpc' => false]);
$client = new SecretManagerServiceClient([
'logger' => new \Google\ApiCore\Logging\Logger(
new \Monolog\Logger('secret-manager', [new \Monolog\Handler\StreamHandler('php://stderr')])
)
]);
$mockClient = $this->createMock(SecretManagerServiceClient::class);
$mockClient->method('accessSecretVersion')
->willReturn((new SecretVersion())->setPayload((new Payload())->setData('mock_password')));
Custom Secret Classes:
Extend Secret or SecretVersion for domain-specific logic:
class DatabaseSecret extends Secret
{
public function getHost(): string
{
return $this->getMetadata()['host'] ?? '';
}
}
Event Listeners: Trigger actions on secret updates (e.g., restart workers):
$client->addSecretVersion($request)->wait();
// Dispatch event to
How can I help you explore Laravel packages today?