The gcp:serviceaccount/key:Key resource, part of the Pulumi GCP provider, generates authentication keys for GCP service accounts, producing downloadable credential files that applications use to authenticate with Google Cloud APIs. This guide focuses on two capabilities: key generation with format selection and automatic rotation policies.
Service account keys require an existing service account and produce private keys in JSON or PEM format. The examples are intentionally small. Combine them with your own service account provisioning and secret management.
Generate a service account key for authentication
Applications running outside GCP need credentials to authenticate with Google Cloud APIs.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const myaccount = new gcp.serviceaccount.Account("myaccount", {
accountId: "myaccount",
displayName: "My Service Account",
});
const mykey = new gcp.serviceaccount.Key("mykey", {
serviceAccountId: myaccount.name,
publicKeyType: "TYPE_X509_PEM_FILE",
});
import pulumi
import pulumi_gcp as gcp
myaccount = gcp.serviceaccount.Account("myaccount",
account_id="myaccount",
display_name="My Service Account")
mykey = gcp.serviceaccount.Key("mykey",
service_account_id=myaccount.name,
public_key_type="TYPE_X509_PEM_FILE")
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/serviceaccount"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
myaccount, err := serviceaccount.NewAccount(ctx, "myaccount", &serviceaccount.AccountArgs{
AccountId: pulumi.String("myaccount"),
DisplayName: pulumi.String("My Service Account"),
})
if err != nil {
return err
}
_, err = serviceaccount.NewKey(ctx, "mykey", &serviceaccount.KeyArgs{
ServiceAccountId: myaccount.Name,
PublicKeyType: pulumi.String("TYPE_X509_PEM_FILE"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var myaccount = new Gcp.ServiceAccount.Account("myaccount", new()
{
AccountId = "myaccount",
DisplayName = "My Service Account",
});
var mykey = new Gcp.ServiceAccount.Key("mykey", new()
{
ServiceAccountId = myaccount.Name,
PublicKeyType = "TYPE_X509_PEM_FILE",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.serviceaccount.Account;
import com.pulumi.gcp.serviceaccount.AccountArgs;
import com.pulumi.gcp.serviceaccount.Key;
import com.pulumi.gcp.serviceaccount.KeyArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var myaccount = new Account("myaccount", AccountArgs.builder()
.accountId("myaccount")
.displayName("My Service Account")
.build());
var mykey = new Key("mykey", KeyArgs.builder()
.serviceAccountId(myaccount.name())
.publicKeyType("TYPE_X509_PEM_FILE")
.build());
}
}
resources:
myaccount:
type: gcp:serviceaccount:Account
properties:
accountId: myaccount
displayName: My Service Account
mykey:
type: gcp:serviceaccount:Key
properties:
serviceAccountId: ${myaccount.name}
publicKeyType: TYPE_X509_PEM_FILE
When you create a key, GCP generates a private key and returns it in the privateKey output property as a base64-encoded JSON credential file. The serviceAccountId references the service account that owns this key. The publicKeyType controls the format of the public key portion; TYPE_X509_PEM_FILE produces an X.509 certificate suitable for verification workflows.
Rotate keys automatically on a schedule
Security policies often require periodic key rotation to limit exposure from compromised credentials.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
import * as time from "@pulumiverse/time";
const myaccount = new gcp.serviceaccount.Account("myaccount", {
accountId: "myaccount",
displayName: "My Service Account",
});
// note this requires the terraform to be run regularly
const mykeyRotation = new time.Rotating("mykey_rotation", {rotationDays: 30});
const mykey = new gcp.serviceaccount.Key("mykey", {
serviceAccountId: myaccount.name,
keepers: {
rotation_time: mykeyRotation.rotationRfc3339,
},
});
import pulumi
import pulumi_gcp as gcp
import pulumiverse_time as time
myaccount = gcp.serviceaccount.Account("myaccount",
account_id="myaccount",
display_name="My Service Account")
# note this requires the terraform to be run regularly
mykey_rotation = time.Rotating("mykey_rotation", rotation_days=30)
mykey = gcp.serviceaccount.Key("mykey",
service_account_id=myaccount.name,
keepers={
"rotation_time": mykey_rotation.rotation_rfc3339,
})
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/serviceaccount"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
"github.com/pulumiverse/pulumi-time/sdk/go/time"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
myaccount, err := serviceaccount.NewAccount(ctx, "myaccount", &serviceaccount.AccountArgs{
AccountId: pulumi.String("myaccount"),
DisplayName: pulumi.String("My Service Account"),
})
if err != nil {
return err
}
// note this requires the terraform to be run regularly
mykeyRotation, err := time.NewRotating(ctx, "mykey_rotation", &time.RotatingArgs{
RotationDays: pulumi.Int(30),
})
if err != nil {
return err
}
_, err = serviceaccount.NewKey(ctx, "mykey", &serviceaccount.KeyArgs{
ServiceAccountId: myaccount.Name,
Keepers: pulumi.StringMap{
"rotation_time": mykeyRotation.RotationRfc3339,
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
using Time = Pulumiverse.Time;
return await Deployment.RunAsync(() =>
{
var myaccount = new Gcp.ServiceAccount.Account("myaccount", new()
{
AccountId = "myaccount",
DisplayName = "My Service Account",
});
// note this requires the terraform to be run regularly
var mykeyRotation = new Time.Rotating("mykey_rotation", new()
{
RotationDays = 30,
});
var mykey = new Gcp.ServiceAccount.Key("mykey", new()
{
ServiceAccountId = myaccount.Name,
Keepers =
{
{ "rotation_time", mykeyRotation.RotationRfc3339 },
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.serviceaccount.Account;
import com.pulumi.gcp.serviceaccount.AccountArgs;
import com.pulumiverse.time.Rotating;
import com.pulumiverse.time.RotatingArgs;
import com.pulumi.gcp.serviceaccount.Key;
import com.pulumi.gcp.serviceaccount.KeyArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var myaccount = new Account("myaccount", AccountArgs.builder()
.accountId("myaccount")
.displayName("My Service Account")
.build());
// note this requires the terraform to be run regularly
var mykeyRotation = new Rotating("mykeyRotation", RotatingArgs.builder()
.rotationDays(30)
.build());
var mykey = new Key("mykey", KeyArgs.builder()
.serviceAccountId(myaccount.name())
.keepers(Map.of("rotation_time", mykeyRotation.rotationRfc3339()))
.build());
}
}
resources:
myaccount:
type: gcp:serviceaccount:Account
properties:
accountId: myaccount
displayName: My Service Account
# note this requires the terraform to be run regularly
mykeyRotation:
type: time:Rotating
name: mykey_rotation
properties:
rotationDays: 30
mykey:
type: gcp:serviceaccount:Key
properties:
serviceAccountId: ${myaccount.name}
keepers:
rotation_time: ${mykeyRotation.rotationRfc3339}
The keepers property accepts arbitrary key-value pairs that trigger new key generation when changed. Here, a time.Rotating resource updates rotationRfc3339 every 30 days, causing Pulumi to destroy the old key and create a new one. This requires running Pulumi regularly to detect when rotation is due.
Beyond these examples
These snippets focus on specific key-level features: key generation and format selection, and automatic rotation with time-based triggers. They’re intentionally minimal rather than complete credential management solutions.
The examples reference pre-existing infrastructure such as GCP service accounts. They focus on key configuration rather than provisioning service accounts or managing credential distribution.
To keep things focused, common key patterns are omitted, including:
- Custom key algorithms (keyAlgorithm)
- Public key upload (publicKeyData)
- Private key format selection (privateKeyType)
- Key lifecycle management and revocation
These omissions are intentional: the goal is to illustrate how each key feature is wired, not provide drop-in credential modules. See the Service Account Key resource reference for all available configuration options.
Let's create GCP Service Account Keys
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Key Creation & Configuration
{ACCOUNT} (email or name, project inferred), projects/{PROJECT_ID}/serviceAccounts/{ACCOUNT} (email or unique ID), or projects/-/serviceAccounts/{ACCOUNT} (wildcard for project inference).KEY_ALG_RSA_2048. Private keys default to TYPE_GOOGLE_CREDENTIALS_FILE format, and public keys default to TYPE_X509_PEM_FILE.publicKeyData when you want to provide your own base64-encoded X509_PEM public key. Note that publicKeyData conflicts with publicKeyType and privateKeyType, so you can’t use them together.Key Rotation & Lifecycle
keepers property with a time.Rotating resource. Set rotation_time to the rotation RFC3339 timestamp, which triggers new key generation when changed. Note that this requires Pulumi to be run regularly (via CI/CD or scheduled jobs) to detect rotation changes.privateKey output is only populated when creating a new key, not on updates or subsequent reads. Store the private key securely after initial creation.keepers map triggers the creation of a new service account key, allowing you to implement custom rotation logic.Limitations & Constraints
serviceAccountId, keepers, keyAlgorithm, privateKeyType, publicKeyData, and publicKeyType. Changing any of these requires creating a new key.