2026-01-27 17:35:45 -05:00
---
name : gcp-secret-manager
description : Secure secrets in Google Cloud Secret Manager. Configure IAM policies, integrate with GKE, and manage secret versions. Use when managing secrets in GCP environments.
license : MIT
metadata :
author : devops-skills
version : "1.0"
---
# GCP Secret Manager
Store and manage secrets securely in Google Cloud Platform.
## When to Use This Skill
Use this skill when:
2026-03-24 18:02:50 -04:00
- Managing secrets in GCP environments
- Integrating secrets with GKE workloads via Workload Identity
- Storing API keys, database credentials, or TLS certificates
- Implementing secret versioning and rotation
- Meeting compliance requirements for centralized secret management
2026-01-27 17:35:45 -05:00
## Prerequisites
2026-03-24 18:02:50 -04:00
- GCP project with billing enabled
- `gcloud` CLI installed and authenticated
- Secret Manager API enabled (`secretmanager.googleapis.com` )
- IAM permissions: `roles/secretmanager.admin` for management, `roles/secretmanager.secretAccessor` for reading
- For GKE: Workload Identity configured on the cluster
2026-01-27 17:35:45 -05:00
2026-03-24 18:02:50 -04:00
## Enable the API
2026-01-27 17:35:45 -05:00
```bash
2026-03-24 18:02:50 -04:00
# Enable Secret Manager API
gcloud services enable secretmanager.googleapis.com
2026-01-27 17:35:45 -05:00
2026-03-24 18:02:50 -04:00
# Verify it's enabled
gcloud services list --enabled --filter= "name:secretmanager"
```
## Secret Creation and Management
```bash
# Create a secret (creates the secret resource, not the value)
gcloud secrets create db-password \
--replication-policy= "automatic" \
--labels= "env=production,team=platform"
# Add the secret value (first version)
echo -n "S3cur3P@ssw0rd!" | gcloud secrets versions add db-password --data-file= -
# Create secret with value in one command
echo -n '{"username":"dbadmin","password":"S3cur3P@ss!","host":"10.0.1.5","port":5432}' | \
gcloud secrets create db-credentials --data-file= - \
--replication-policy= "automatic" \
--labels= "env=production,team=platform"
# Create with specific region replication
gcloud secrets create regional-secret \
--replication-policy= "user-managed" \
--locations= "us-central1,us-east1"
# Create with customer-managed encryption key (CMEK)
gcloud secrets create sensitive-secret \
--replication-policy= "user-managed" \
--locations= "us-central1" \
--kms-key-name= "projects/my-project/locations/us-central1/keyRings/my-ring/cryptoKeys/my-key"
# Access the latest version
2026-01-27 17:35:45 -05:00
gcloud secrets versions access latest --secret= db-password
2026-03-24 18:02:50 -04:00
# Access a specific version
gcloud secrets versions access 3 --secret= db-password
2026-01-27 17:35:45 -05:00
2026-03-24 18:02:50 -04:00
# Add a new version (rotation)
echo -n "N3wS3cur3P@ss!" | gcloud secrets versions add db-password --data-file= -
# List all secrets
gcloud secrets list --format= "table(name, createTime, labels)"
# List versions of a secret
gcloud secrets versions list db-password --format= "table(name, state, createTime)"
# Disable a version (makes it inaccessible but recoverable)
gcloud secrets versions disable 1 --secret= db-password
# Enable a disabled version
gcloud secrets versions enable 1 --secret= db-password
# Destroy a version (permanent)
gcloud secrets versions destroy 1 --secret= db-password
# Delete the entire secret
gcloud secrets delete db-password
# Set expiration on a secret
gcloud secrets update db-password \
--expire-time= "2026-06-01T00:00:00Z"
# Set TTL-based expiration
gcloud secrets update temp-token \
--ttl= "2592000s" # 30 days
# Update labels
gcloud secrets update db-password \
--update-labels= "rotation=enabled,last-rotated=2025-01-15"
# Add version aliases
gcloud secrets versions update 5 --secret= db-password --set-aliases= "production"
2026-01-27 17:35:45 -05:00
```
2026-03-24 18:02:50 -04:00
## IAM Bindings
2026-01-27 17:35:45 -05:00
2026-03-24 18:02:50 -04:00
```bash
# Grant secret accessor role to a service account
gcloud secrets add-iam-policy-binding db-password \
--member= "serviceAccount:myapp-sa@my-project.iam.gserviceaccount.com" \
--role= "roles/secretmanager.secretAccessor"
2026-01-27 17:35:45 -05:00
2026-03-24 18:02:50 -04:00
# Grant access to a specific secret version
gcloud secrets add-iam-policy-binding db-password \
--member= "serviceAccount:myapp-sa@my-project.iam.gserviceaccount.com" \
--role= "roles/secretmanager.secretVersionAccessor" \
--condition= 'expression=resource.name.endsWith("versions/latest"),title=latest-only'
# Grant admin to security team
gcloud secrets add-iam-policy-binding db-password \
--member= "group:security-team@example.com" \
--role= "roles/secretmanager.admin"
# View IAM policy for a secret
gcloud secrets get-iam-policy db-password
# Remove access
gcloud secrets remove-iam-policy-binding db-password \
--member= "serviceAccount:old-sa@my-project.iam.gserviceaccount.com" \
--role= "roles/secretmanager.secretAccessor"
# Project-level IAM for all secrets
gcloud projects add-iam-policy-binding my-project \
--member= "serviceAccount:myapp-sa@my-project.iam.gserviceaccount.com" \
--role= "roles/secretmanager.secretAccessor" \
--condition= 'expression=resource.name.startsWith("projects/my-project/secrets/myapp-"),title=myapp-secrets-only'
2026-01-27 17:35:45 -05:00
```
2026-03-24 18:02:50 -04:00
## Workload Identity for GKE
```bash
# Enable Workload Identity on cluster (if not already)
gcloud container clusters update my-cluster \
--zone us-central1-a \
--workload-pool= my-project.svc.id.goog
# Create GCP service account for the workload
gcloud iam service-accounts create myapp-gke-sa \
--display-name= "MyApp GKE Service Account"
# Grant secret accessor role
gcloud secrets add-iam-policy-binding db-password \
--member= "serviceAccount:myapp-gke-sa@my-project.iam.gserviceaccount.com" \
--role= "roles/secretmanager.secretAccessor"
# Bind Kubernetes SA to GCP SA
gcloud iam service-accounts add-iam-policy-binding \
myapp-gke-sa@my-project.iam.gserviceaccount.com \
--role= "roles/iam.workloadIdentityUser" \
--member= "serviceAccount:my-project.svc.id.goog[production/myapp-sa]"
```
### Kubernetes Manifests
2026-01-27 17:35:45 -05:00
```yaml
2026-03-24 18:02:50 -04:00
# Kubernetes service account annotated with GCP SA
apiVersion : v1
kind : ServiceAccount
metadata :
name : myapp-sa
namespace : production
annotations :
iam.gke.io/gcp-service-account : "myapp-gke-sa@my-project.iam.gserviceaccount.com"
---
# Secrets Store CSI Driver for GCP
2026-01-27 17:35:45 -05:00
apiVersion : secrets-store.csi.x-k8s.io/v1
kind : SecretProviderClass
metadata :
name : gcp-secrets
2026-03-24 18:02:50 -04:00
namespace : production
2026-01-27 17:35:45 -05:00
spec :
provider : gcp
parameters :
secrets : |
- resourceName: "projects/my-project/secrets/db-password/versions/latest"
path: "db-password"
2026-03-24 18:02:50 -04:00
- resourceName: "projects/my-project/secrets/db-credentials/versions/latest"
path: "db-credentials"
- resourceName: "projects/my-project/secrets/api-key/versions/latest"
path: "api-key"
secretObjects :
- secretName : myapp-secrets
type : Opaque
data :
- objectName : db-password
key : DB_PASSWORD
- objectName : api-key
key : API_KEY
---
# Deployment using the secrets
apiVersion : apps/v1
kind : Deployment
metadata :
name : myapp
namespace : production
spec :
replicas : 3
selector :
matchLabels :
app : myapp
template :
metadata :
labels :
app : myapp
spec :
serviceAccountName : myapp-sa
containers :
- name : myapp
image : gcr.io/my-project/myapp:v1.0.0
env :
- name : DB_PASSWORD
valueFrom :
secretKeyRef :
name : myapp-secrets
key : DB_PASSWORD
volumeMounts :
- name : secrets
mountPath : "/var/secrets"
readOnly : true
volumes :
- name : secrets
csi :
driver : secrets-store.csi.k8s.io
readOnly : true
volumeAttributes :
secretProviderClass : "gcp-secrets"
2026-01-27 17:35:45 -05:00
```
2026-03-24 18:02:50 -04:00
## Application SDK Examples
### Python
```python
from google.cloud import secretmanager
from google.api_core import exceptions
import json
def get_secret ( project_id : str , secret_id : str , version : str = "latest" ) -> str :
"""Access a secret version from GCP Secret Manager."""
client = secretmanager . SecretManagerServiceClient ()
name = f "projects/ { project_id } /secrets/ { secret_id } /versions/ { version } "
try :
response = client . access_secret_version ( request = { "name" : name })
return response . payload . data . decode ( "UTF-8" )
except exceptions . NotFound :
raise ValueError ( f "Secret { secret_id } version { version } not found" )
except exceptions . PermissionDenied :
raise PermissionError ( f "No access to secret { secret_id } " )
def get_json_secret ( project_id : str , secret_id : str ) -> dict :
"""Access and parse a JSON secret."""
raw = get_secret ( project_id , secret_id )
return json . loads ( raw )
def create_secret ( project_id : str , secret_id : str , value : str , labels : dict = None ) -> str :
"""Create a new secret with an initial version."""
client = secretmanager . SecretManagerServiceClient ()
parent = f "projects/ { project_id } "
secret_config = {
"replication" : { "automatic" : {}},
}
if labels :
secret_config [ "labels" ] = labels
secret = client . create_secret (
request = { "parent" : parent , "secret_id" : secret_id , "secret" : secret_config }
)
client . add_secret_version (
request = { "parent" : secret . name , "payload" : { "data" : value . encode ( "UTF-8" )}}
)
return secret . name
def rotate_secret ( project_id : str , secret_id : str , new_value : str ) -> str :
"""Add a new version to rotate the secret."""
client = secretmanager . SecretManagerServiceClient ()
parent = f "projects/ { project_id } /secrets/ { secret_id } "
version = client . add_secret_version (
request = { "parent" : parent , "payload" : { "data" : new_value . encode ( "UTF-8" )}}
)
return version . name
def list_secrets ( project_id : str , filter_str : str = "" ) -> list :
"""List all secrets in a project."""
client = secretmanager . SecretManagerServiceClient ()
parent = f "projects/ { project_id } "
secrets = []
for secret in client . list_secrets ( request = { "parent" : parent , "filter" : filter_str }):
secrets . append ({
"name" : secret . name . split ( "/" )[ - 1 ],
"created" : secret . create_time . isoformat (),
"labels" : dict ( secret . labels ),
})
return secrets
# Usage
creds = get_json_secret ( "my-project" , "db-credentials" )
connection_string = (
f "postgresql:// { creds [ 'username' ] } : { creds [ 'password' ] } "
f "@ { creds [ 'host' ] } : { creds [ 'port' ] } /mydb"
)
```
### Go
```go
package main
import (
"context"
"fmt"
"log"
secretmanager "cloud.google.com/go/secretmanager/apiv1"
secretmanagerpb "cloud.google.com/go/secretmanager/apiv1/secretmanagerpb"
)
func getSecret ( projectID , secretID , version string ) ( string , error ) {
ctx := context . Background ()
client , err := secretmanager . NewClient ( ctx )
if err != nil {
return "" , fmt . Errorf ( "failed to create client: %w" , err )
}
defer client . Close ()
name := fmt . Sprintf ( "projects/%s/secrets/%s/versions/%s" , projectID , secretID , version )
result , err := client . AccessSecretVersion ( ctx , & secretmanagerpb . AccessSecretVersionRequest {
Name : name ,
})
if err != nil {
return "" , fmt . Errorf ( "failed to access secret: %w" , err )
}
return string ( result . Payload . Data ), nil
}
func main () {
secret , err := getSecret ( "my-project" , "db-password" , "latest" )
if err != nil {
log . Fatalf ( "Error: %v" , err )
}
fmt . Printf ( "Secret: %s\n" , secret )
}
```
### Node.js
```javascript
const { SecretManagerServiceClient } = require ( '@google-cloud/secret-manager' );
const client = new SecretManagerServiceClient ();
async function getSecret ( projectId , secretId , version = 'latest' ) {
const name = `projects/ ${ projectId } /secrets/ ${ secretId } /versions/ ${ version } ` ;
const [ response ] = await client . accessSecretVersion ({ name });
return response . payload . data . toString ( 'utf8' );
}
async function main () {
const password = await getSecret ( 'my-project' , 'db-password' );
console . log ( `Secret retrieved, length: ${ password . length } ` );
}
main (). catch ( console . error );
```
## Secret Rotation with Cloud Functions
```python
"""cloud_function_rotation.py - Triggered by Pub/Sub on secret rotation events."""
import functions_framework
from google.cloud import secretmanager
import secrets
import string
@functions_framework.cloud_event
def rotate_secret ( cloud_event ):
"""Handle secret rotation events from Pub/Sub."""
data = cloud_event . data
secret_name = data . get ( "name" , "" )
if "db-password" not in secret_name :
print ( f "Skipping non-DB secret: { secret_name } " )
return
client = secretmanager . SecretManagerServiceClient ()
alphabet = string . ascii_letters + string . digits + "!@#$%^&*"
new_password = '' . join ( secrets . choice ( alphabet ) for _ in range ( 32 ))
parent = "/" . join ( secret_name . split ( "/" )[: 4 ])
client . add_secret_version (
request = {
"parent" : parent ,
"payload" : { "data" : new_password . encode ( "UTF-8" )},
}
)
print ( f "Rotated secret: { parent } " )
```
### Rotation Schedule with Cloud Scheduler
```bash
# Create a Pub/Sub topic for rotation events
gcloud pubsub topics create secret-rotation
# Configure secret to publish rotation events
gcloud secrets update db-password \
--add-topics= "projects/my-project/topics/secret-rotation" \
--event-types= "SECRET_ROTATE"
# Set up rotation schedule
gcloud secrets update db-password \
--next-rotation-time= "2025-04-01T00:00:00Z" \
--rotation-period= "2592000s" # 30 days
```
## Terraform Configuration
```hcl
resource "google_secret_manager_secret" "db_password" {
project = var . project_id
secret_id = "db-password"
replication {
auto {}
}
labels = {
env = "production"
team = "platform"
}
rotation {
next_rotation_time = "2025-04-01T00:00:00Z"
rotation_period = "2592000s"
}
topics {
name = google_pubsub_topic . secret_rotation . id
}
}
resource "google_secret_manager_secret_version" "db_password" {
secret = google_secret_manager_secret . db_password . id
secret_data = var . db_password
}
resource "google_secret_manager_secret_iam_member" "app_accessor" {
project = var . project_id
secret_id = google_secret_manager_secret . db_password . secret_id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.app.email}"
}
```
## Troubleshooting
| Problem | Cause | Solution |
|---------|-------|----------|
| "Secret Manager API not enabled" | API not activated in project | Run `gcloud services enable secretmanager.googleapis.com` |
| "Permission denied" on access | Missing `secretAccessor` role | Grant `roles/secretmanager.secretAccessor` on the specific secret |
| Workload Identity not working | K8s SA not bound to GCP SA | Verify annotation on K8s SA; check IAM binding with `workloadIdentityUser` |
| "Secret version is in DISABLED state" | Version was disabled | Enable with `gcloud secrets versions enable VERSION --secret=SECRET` |
| High latency on secret access | No client-side caching | Cache secrets in memory with TTL; use CSI driver for GKE |
| CMEK decrypt fails | KMS key permissions missing | Grant `roles/cloudkms.cryptoKeyEncrypterDecrypter` to Secret Manager SA |
| Rotation function not triggered | Pub/Sub topic not configured | Verify topic is attached to secret; check Cloud Function subscription |
2026-01-27 17:35:45 -05:00
## Best Practices
2026-03-24 18:02:50 -04:00
- Use Workload Identity for GKE instead of exported service account keys
- Implement IAM least-privilege at the individual secret level, not project level
- Enable audit logging for all secret access (Cloud Audit Logs)
- Use secret versions for safe rollback during rotation issues
- Set expiration dates or TTLs on temporary secrets
- Integrate with Cloud KMS for customer-managed encryption keys
- Use labels consistently for organization and automation
- Monitor secret access patterns with Cloud Monitoring
- Implement rotation schedules for all long-lived credentials
- Use conditional IAM bindings to restrict access by resource name pattern
2026-01-27 17:35:45 -05:00
## Related Skills
- [hashicorp-vault ](../hashicorp-vault/ ) - Multi-cloud secrets
- [gcp-gke ](../../../infrastructure/cloud-gcp/gcp-gke/ ) - GKE integration
2026-03-24 18:02:50 -04:00
- [aws-secrets-manager ](../aws-secrets-manager/ ) - AWS secret management
- [azure-keyvault ](../azure-keyvault/ ) - Azure secret management