This page describes how to use manually-created customer-managed encryption keys (CMEK) for Spanner.
To learn more about CMEK, see the Customer-managed encryption keys (CMEK) overview.
Create a CMEK-enabled database
Create a key in Cloud Key Management Service (Cloud KMS). Spanner supports creating to following Cloud KMS types:
The key must be in the same location as your Spanner instance. For example, if your Spanner instance configuration is in
us-west1
, then your Cloud KMS key ring location must also beus-west1
.Not every Spanner multi-region instance configuration has a corresponding Cloud KMS key ring location. For Spanner databases in custom, dual-region, or multi-region instance configurations, you can use multiple regional (single-region) Cloud KMS keys to protect your database. For example:
- If your Spanner database is in the multi-region instance
configuration
nam14
, then you can create Cloud KMS keys inus-east4
,northamerica-northeast1
, andus-east1
. - If your database is in a custom instance configuration that uses
nam3
as the base instance configuration with an additional read-only replica inus-central2
, then you can create Cloud KMS keys inus-east4
,us-east1
,us-central1
, andus-central2
.
Optional: To see a list of the replica locations in your Spanner instance configuration, use the
gcloud spanner instances get-locations
command:gcloud spanner instances get-locations <var>INSTANCE_ID</var>
For more information, see the following resources:
- If your Spanner database is in the multi-region instance
configuration
Grant Spanner access to the key.
In Cloud Shell, create and display the service agent, or display it if the account already exists:
gcloud beta services identity create --service=spanner.googleapis.com \ --project=PROJECT_ID
If you're prompted to install the gcloud Beta Commands component, type
Y
. After installation, the command is automatically restarted.The
gcloud services identity
command creates or gets the service agent that Spanner can use to access the Cloud KMS key on your behalf.The service account ID is formatted like an email address:
Service identity created: service-xxx@gcp-sa-spanner.iam.gserviceaccount.com
Grant the Cloud KMS CryptoKey Encrypter/Decrypter(
cloudkms.cryptoKeyEncrypterDecrypter
) role to the service account for each region (--location
) in your Spanner instance configuration. To do so, run thegcloud kms keys add-iam-policybinding
command:gcloud kms keys add-iam-policy-binding KMS_KEY \ --location KMS_KEY_LOCATION \ --keyring KMS_KEY_RING \ --project=PROJECT_ID \ --member serviceAccount:service-xxx@gcp-sa-spanner.iam.gserviceaccount.com \ --role roles/cloudkms.cryptoKeyEncrypterDecrypter
Here's an example output:
Updated IAM policy for key [KMS_KEY]
If you're using multiple Cloud KMS keys to protect your database, run the
gcloud kms keys add-iam-policybinding
command for all the your keys.This role ensures that the service account has permission to both encrypt and decrypt with the Cloud KMS key. For more information, see Cloud KMS permissions and roles.
Create the database and specify your Cloud KMS key.
Console
Use the console to create databases in regional instance configurations.
In the Google Cloud console, go to the Instances page.
Click the instance where you want to create a database.
Click Create database and fill out the required fields.
Click Show encryption options.
Select Cloud KMS key.
Select a key from the drop-down list.
The list of keys is limited to the current Google Cloud project. To use a key from a different Google Cloud project, create the database using gcloud CLI instead of the Google Cloud console.
Once the database is created, you can verify that the database is CMEK-enabled by viewing the Database overview page.
gcloud
To create a CMEK-enabled database in a regional, custom, or multi-region
instance configuration, run the gcloud spanner databases create
command:
gcloud spanner databases create DATABASE \
--project=SPANNER_PROJECT_ID \
--instance=INSTANCE_ID \
--ddl="CREATE TABLE Users (Id INT64 NOT NULL, FirstName STRING(100) NOT NULL, LastName STRING(100) NOT NULL,) PRIMARY KEY (Id)" \
--kms-project=KMS_PROJECT_ID \
--kms-location=KMS_KEY_LOCATION \
--kms-keyring=KMS_KEYRING \
--kms-keys=KMS_KEY_1[, KMS_KEY_2 ... ]
To verify that a database is CMEK-enabled, run the
gcloud spanner databases describe
command:
gcloud spanner databases describe DATABASE \
--project=SPANNER_PROJECT_ID \
--instance=INSTANCE_ID
CMEK-enabled databases include a field for encryptionConfig
, as shown in the
following example output:
encryptionConfig:
kmsKeyNames:projects/my-kms-project/locations/eur5/keyRings/my-kms-key-ring/cryptoKeys/my-kms-key
name: projects/my-spanner-project/instances/my-instance/databases/my-db
state: READY
Client libraries
C#
To create a CMEK-enabled database in a regional instance configuration:
using Google.Cloud.Spanner.Admin.Database.V1;
using Google.Cloud.Spanner.Common.V1;
using System;
using System.Threading.Tasks;
public class CreateDatabaseWithEncryptionKeyAsyncSample
{
public async Task<Database> CreateDatabaseWithEncryptionKeyAsync(string projectId, string instanceId, string databaseId, CryptoKeyName kmsKeyName)
{
// Create a DatabaseAdminClient instance that can be used to execute a
// CreateDatabaseRequest with custom encryption configuration options.
DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.Create();
// Define create table statement for table #1.
var createSingersTable =
@"CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
ComposerInfo BYTES(MAX)
) PRIMARY KEY (SingerId)";
// Define create table statement for table #2.
var createAlbumsTable =
@"CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX)
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE";
// Create the CreateDatabase request with encryption configuration and execute it.
var request = new CreateDatabaseRequest
{
ParentAsInstanceName = InstanceName.FromProjectInstance(projectId, instanceId),
CreateStatement = $"CREATE DATABASE `{databaseId}`",
ExtraStatements = { createSingersTable, createAlbumsTable },
EncryptionConfig = new EncryptionConfig
{
KmsKeyNameAsCryptoKeyName = kmsKeyName,
},
};
var operation = await databaseAdminClient.CreateDatabaseAsync(request);
// Wait until the operation has finished.
Console.WriteLine("Waiting for the operation to finish.");
var completedResponse = await operation.PollUntilCompletedAsync();
if (completedResponse.IsFaulted)
{
Console.WriteLine($"Error while creating database: {completedResponse.Exception}");
throw completedResponse.Exception;
}
var database = completedResponse.Result;
Console.WriteLine($"Database {database.Name} created with encryption key {database.EncryptionConfig.KmsKeyName}");
return database;
}
}
To create a CMEK-enabled database in a multi-region instance configuration:
using Google.Cloud.Spanner.Admin.Database.V1;
using Google.Cloud.Spanner.Common.V1;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class CreateDatabaseWithMultiRegionEncryptionAsyncSample
{
public async Task<Database> CreateDatabaseWithMultiRegionEncryptionAsync(string projectId, string instanceId, string databaseId, IEnumerable<CryptoKeyName> kmsKeyNames)
{
// Create a DatabaseAdminClient instance that can be used to execute a
// CreateDatabaseRequest with custom encryption configuration options.
DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.Create();
// Define create table statement for table #1.
var createSingersTable =
@"CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
ComposerInfo BYTES(MAX)
) PRIMARY KEY (SingerId)";
// Define create table statement for table #2.
var createAlbumsTable =
@"CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX)
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE";
// Create the CreateDatabase request with encryption configuration and execute it.
var request = new CreateDatabaseRequest
{
ParentAsInstanceName = InstanceName.FromProjectInstance(projectId, instanceId),
CreateStatement = $"CREATE DATABASE `{databaseId}`",
ExtraStatements = { createSingersTable, createAlbumsTable },
EncryptionConfig = new EncryptionConfig
{
KmsKeyNamesAsCryptoKeyNames = { kmsKeyNames },
},
};
var operation = await databaseAdminClient.CreateDatabaseAsync(request);
// Wait until the operation has finished.
Console.WriteLine("Waiting for the operation to finish.");
var completedResponse = await operation.PollUntilCompletedAsync();
if (completedResponse.IsFaulted)
{
Console.WriteLine($"Error while creating database: {completedResponse.Exception}");
throw completedResponse.Exception;
}
var database = completedResponse.Result;
Console.WriteLine($"Database {database.Name} created with encryption keys {string.Join(", ", kmsKeyNames)}");
return database;
}
}
C++
To create a CMEK-enabled database in a regional instance configuration:
void CreateDatabaseWithEncryptionKey(
google::cloud::spanner_admin::DatabaseAdminClient client,
std::string const& project_id, std::string const& instance_id,
std::string const& database_id,
google::cloud::KmsKeyName const& encryption_key) {
google::cloud::spanner::Database database(project_id, instance_id,
database_id);
google::spanner::admin::database::v1::CreateDatabaseRequest request;
request.set_parent(database.instance().FullName());
request.set_create_statement("CREATE DATABASE `" + database.database_id() +
"`");
request.add_extra_statements(R"""(
CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX),
FullName STRING(2049)
AS (ARRAY_TO_STRING([FirstName, LastName], " ")) STORED
) PRIMARY KEY (SingerId))""");
request.add_extra_statements(R"""(
CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX)
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE)""");
request.mutable_encryption_config()->set_kms_key_name(
encryption_key.FullName());
auto db = client.CreateDatabase(request).get();
if (!db) throw std::move(db).status();
std::cout << "Database " << db->name() << " created";
std::cout << " using encryption key " << encryption_key.FullName();
std::cout << ".\n";
}
To create a CMEK-enabled database in a multi-region instance configuration:
void CreateDatabaseWithMRCMEK(
google::cloud::spanner_admin::DatabaseAdminClient client,
std::string const& project_id, std::string const& instance_id,
std::string const& database_id,
std::vector<google::cloud::KmsKeyName> const& encryption_keys) {
google::cloud::spanner::Database database(project_id, instance_id,
database_id);
google::spanner::admin::database::v1::CreateDatabaseRequest request;
request.set_parent(database.instance().FullName());
request.set_create_statement("CREATE DATABASE `" + database.database_id() +
"`");
request.add_extra_statements(R"""(
CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX),
FullName STRING(2049)
AS (ARRAY_TO_STRING([FirstName, LastName], " ")) STORED
) PRIMARY KEY (SingerId))""");
request.add_extra_statements(R"""(
CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX)
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE)""");
for (google::cloud::KmsKeyName const& encryption_key : encryption_keys) {
request.mutable_encryption_config()->add_kms_key_names(
encryption_key.FullName());
}
auto db = client.CreateDatabase(request).get();
if (!db) throw std::move(db).status();
std::cout << "Database " << db->name() << " created";
PrintKmsKeys(encryption_keys);
}
Go
To create a CMEK-enabled database in a regional instance configuration:
import (
"context"
"fmt"
"io"
"regexp"
database "cloud.google.com/go/spanner/admin/database/apiv1"
adminpb "cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
)
func createDatabaseWithCustomerManagedEncryptionKey(ctx context.Context, w io.Writer, db, kmsKeyName string) error {
// db = `projects/<project>/instances/<instance-id>/database/<database-id>`
// kmsKeyName = `projects/<project>/locations/<location>/keyRings/<key_ring>/cryptoKeys/<kms_key_name>`
matches := regexp.MustCompile("^(.+)/databases/(.+)$").FindStringSubmatch(db)
if matches == nil || len(matches) != 3 {
return fmt.Errorf("createDatabaseWithCustomerManagedEncryptionKey: invalid database id %q", db)
}
instanceName := matches[1]
databaseId := matches[2]
adminClient, err := database.NewDatabaseAdminClient(ctx)
if err != nil {
return fmt.Errorf("createDatabaseWithCustomerManagedEncryptionKey.NewDatabaseAdminClient: %w", err)
}
defer adminClient.Close()
// Create a database with tables using a Customer Managed Encryption Key
req := adminpb.CreateDatabaseRequest{
Parent: instanceName,
CreateStatement: "CREATE DATABASE `" + databaseId + "`",
ExtraStatements: []string{
`CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX)
) PRIMARY KEY (SingerId)`,
`CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX)
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE`,
},
EncryptionConfig: &adminpb.EncryptionConfig{KmsKeyName: kmsKeyName},
}
op, err := adminClient.CreateDatabase(ctx, &req)
if err != nil {
return fmt.Errorf("createDatabaseWithCustomerManagedEncryptionKey.CreateDatabase: %w", err)
}
dbObj, err := op.Wait(ctx)
if err != nil {
return fmt.Errorf("createDatabaseWithCustomerManagedEncryptionKey.Wait: %w", err)
}
fmt.Fprintf(w, "Created database [%s] using encryption key %q\n", dbObj.Name, dbObj.EncryptionConfig.KmsKeyName)
return nil
}
To create a CMEK-enabled database in a multi-region instance configuration:
import (
"context"
"fmt"
"io"
database "cloud.google.com/go/spanner/admin/database/apiv1"
adminpb "cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
)
// createDatabaseWithCustomerManagedMultiRegionEncryptionKey creates a new database with tables using a Customer Managed Multi-Region Encryption Key.
func createDatabaseWithCustomerManagedMultiRegionEncryptionKey(ctx context.Context, w io.Writer, projectID, instanceID, databaseID string, kmsKeyNames []string) error {
// projectID = `my-project`
// instanceID = `my-instance`
// databaseID = `my-database`
// kmsKeyNames := []string{"projects/my-project/locations/locations/<location1>/keyRings/<keyRing>/cryptoKeys/<keyId>",
// "projects/my-project/locations/locations/<location2>/keyRings/<keyRing>/cryptoKeys/<keyId>",
// "projects/my-project/locations/locations/<location3>/keyRings/<keyRing>/cryptoKeys/<keyId>",
// }
adminClient, err := database.NewDatabaseAdminClient(ctx)
if err != nil {
return fmt.Errorf("createDatabaseWithCustomerManagedMultiRegionEncryptionKey.NewDatabaseAdminClient: %w", err)
}
defer adminClient.Close()
// Create a database with tables using a Customer Managed Multi-Region Encryption Key
req := adminpb.CreateDatabaseRequest{
Parent: fmt.Sprintf("projects/%s/instances/%s", projectID, instanceID),
CreateStatement: "CREATE DATABASE `" + databaseID + "`",
ExtraStatements: []string{
`CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX)
) PRIMARY KEY (SingerId)`,
`CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX)
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE`,
},
EncryptionConfig: &adminpb.EncryptionConfig{KmsKeyNames: kmsKeyNames},
}
op, err := adminClient.CreateDatabase(ctx, &req)
if err != nil {
return fmt.Errorf("createDatabaseWithCustomerManagedMultiRegionEncryptionKey.CreateDatabase: %w", err)
}
dbObj, err := op.Wait(ctx)
if err != nil {
return fmt.Errorf("createDatabaseWithCustomerManagedMultiRegionEncryptionKey.Wait: %w", err)
}
fmt.Fprintf(w, "Created database [%s] using multi-region encryption keys %q\n", dbObj.Name, dbObj.EncryptionConfig.GetKmsKeyNames())
return nil
}
Java
To create a CMEK-enabled database in a regional instance configuration:
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.CreateDatabaseRequest;
import com.google.spanner.admin.database.v1.Database;
import com.google.spanner.admin.database.v1.EncryptionConfig;
import com.google.spanner.admin.database.v1.InstanceName;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class CreateDatabaseWithEncryptionKey {
static void createDatabaseWithEncryptionKey() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
String databaseId = "my-database";
String kmsKeyName =
"projects/" + projectId + "/locations/<location>/keyRings/<keyRing>/cryptoKeys/<keyId>";
try (Spanner spanner =
SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
DatabaseAdminClient adminClient = spanner.createDatabaseAdminClient()) {
createDatabaseWithEncryptionKey(
adminClient,
projectId,
instanceId,
databaseId,
kmsKeyName);
}
}
static void createDatabaseWithEncryptionKey(DatabaseAdminClient adminClient,
String projectId, String instanceId, String databaseId, String kmsKeyName) {
InstanceName instanceName = InstanceName.of(projectId, instanceId);
CreateDatabaseRequest request = CreateDatabaseRequest.newBuilder()
.setParent(instanceName.toString())
.setCreateStatement("CREATE DATABASE `" + databaseId + "`")
.setEncryptionConfig(EncryptionConfig.newBuilder().setKmsKeyName(kmsKeyName).build())
.addAllExtraStatements(
ImmutableList.of(
"CREATE TABLE Singers ("
+ " SingerId INT64 NOT NULL,"
+ " FirstName STRING(1024),"
+ " LastName STRING(1024),"
+ " SingerInfo BYTES(MAX)"
+ ") PRIMARY KEY (SingerId)",
"CREATE TABLE Albums ("
+ " SingerId INT64 NOT NULL,"
+ " AlbumId INT64 NOT NULL,"
+ " AlbumTitle STRING(MAX)"
+ ") PRIMARY KEY (SingerId, AlbumId),"
+ " INTERLEAVE IN PARENT Singers ON DELETE CASCADE"
))
.build();
try {
System.out.println("Waiting for operation to complete...");
Database createdDatabase =
adminClient.createDatabaseAsync(request).get(120, TimeUnit.SECONDS);
System.out.printf(
"Database %s created with encryption key %s%n",
createdDatabase.getName(),
createdDatabase.getEncryptionConfig().getKmsKeyName()
);
} catch (ExecutionException e) {
// If the operation failed during execution, expose the cause.
throw SpannerExceptionFactory.asSpannerException(e.getCause());
} catch (InterruptedException e) {
// Throw when a thread is waiting, sleeping, or otherwise occupied,
// and the thread is interrupted, either before or during the activity.
throw SpannerExceptionFactory.propagateInterrupt(e);
} catch (TimeoutException e) {
// If the operation timed out propagates the timeout
throw SpannerExceptionFactory.propagateTimeout(e);
}
}
}
To create a CMEK-enabled database in a multi-region instance configuration:
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.CreateDatabaseRequest;
import com.google.spanner.admin.database.v1.Database;
import com.google.spanner.admin.database.v1.EncryptionConfig;
import com.google.spanner.admin.database.v1.InstanceName;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class CreateDatabaseWithMultiRegionEncryptionKey {
static void createDatabaseWithEncryptionKey() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
String databaseId = "my-database";
String[] kmsKeyNames =
new String[] {
"projects/" + projectId + "/locations/<location1>/keyRings/<keyRing>/cryptoKeys/<keyId>",
"projects/" + projectId + "/locations/<location2>/keyRings/<keyRing>/cryptoKeys/<keyId>",
"projects/" + projectId + "/locations/<location3>/keyRings/<keyRing>/cryptoKeys/<keyId>"
};
try (Spanner spanner =
SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
DatabaseAdminClient adminClient = spanner.createDatabaseAdminClient()) {
createDatabaseWithMultiRegionEncryptionKey(
adminClient, projectId, instanceId, databaseId, kmsKeyNames);
}
}
static void createDatabaseWithMultiRegionEncryptionKey(
DatabaseAdminClient adminClient,
String projectId,
String instanceId,
String databaseId,
String[] kmsKeyNames) {
InstanceName instanceName = InstanceName.of(projectId, instanceId);
CreateDatabaseRequest request =
CreateDatabaseRequest.newBuilder()
.setParent(instanceName.toString())
.setCreateStatement("CREATE DATABASE `" + databaseId + "`")
.setEncryptionConfig(
EncryptionConfig.newBuilder()
.addAllKmsKeyNames(ImmutableList.copyOf(kmsKeyNames))
.build())
.addAllExtraStatements(
ImmutableList.of(
"CREATE TABLE Singers ("
+ " SingerId INT64 NOT NULL,"
+ " FirstName STRING(1024),"
+ " LastName STRING(1024),"
+ " SingerInfo BYTES(MAX)"
+ ") PRIMARY KEY (SingerId)",
"CREATE TABLE Albums ("
+ " SingerId INT64 NOT NULL,"
+ " AlbumId INT64 NOT NULL,"
+ " AlbumTitle STRING(MAX)"
+ ") PRIMARY KEY (SingerId, AlbumId),"
+ " INTERLEAVE IN PARENT Singers ON DELETE CASCADE"))
.build();
try {
System.out.println("Waiting for operation to complete...");
Database createdDatabase =
adminClient.createDatabaseAsync(request).get(120, TimeUnit.SECONDS);
System.out.printf(
"Database %s created with encryption keys %s%n",
createdDatabase.getName(), createdDatabase.getEncryptionConfig().getKmsKeyNamesList());
} catch (ExecutionException e) {
// If the operation failed during execution, expose the cause.
throw SpannerExceptionFactory.asSpannerException(e.getCause());
} catch (InterruptedException e) {
// Throw when a thread is waiting, sleeping, or otherwise occupied,
// and the thread is interrupted, either before or during the activity.
throw SpannerExceptionFactory.propagateInterrupt(e);
} catch (TimeoutException e) {
// If the operation timed out propagates the timeout
throw SpannerExceptionFactory.propagateTimeout(e);
}
}
}
Node.js
To create a CMEK-enabled database in a regional instance configuration:
// Imports the Google Cloud client library
const {Spanner, protos} = require('@google-cloud/spanner');
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const keyName =
// 'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key';
// creates a client
const spanner = new Spanner({
projectId: projectId,
});
// Gets a reference to a Cloud Spanner Database Admin Client object
const databaseAdminClient = spanner.getDatabaseAdminClient();
// Creates a database
const [operation] = await databaseAdminClient.createDatabase({
createStatement: 'CREATE DATABASE `' + databaseId + '`',
parent: databaseAdminClient.instancePath(projectId, instanceId),
encryptionConfig:
(protos.google.spanner.admin.database.v1.EncryptionConfig = {
kmsKeyName: keyName,
}),
});
console.log(`Waiting for operation on ${databaseId} to complete...`);
await operation.promise();
console.log(`Created database ${databaseId} on instance ${instanceId}.`);
// Get encryption key
const [metadata] = await databaseAdminClient.getDatabase({
name: databaseAdminClient.databasePath(projectId, instanceId, databaseId),
});
console.log(
`Database encrypted with key ${metadata.encryptionConfig.kmsKeyName}.`,
);
To create a CMEK-enabled database in a multi-region instance configuration:
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const kmsKeyNames =
// 'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key1,projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key2';
// Imports the Google Cloud client library
const {Spanner, protos} = require('@google-cloud/spanner');
// creates a client
const spanner = new Spanner({
projectId: projectId,
});
// Gets a reference to a Cloud Spanner Database Admin Client object
const databaseAdminClient = spanner.getDatabaseAdminClient();
async function createDatabaseWithMultipleKmsKeys() {
// Creates a database
const [operation] = await databaseAdminClient.createDatabase({
createStatement: 'CREATE DATABASE `' + databaseId + '`',
parent: databaseAdminClient.instancePath(projectId, instanceId),
encryptionConfig:
(protos.google.spanner.admin.database.v1.EncryptionConfig = {
kmsKeyNames: kmsKeyNames.split(','),
}),
});
console.log(`Waiting for operation on ${databaseId} to complete...`);
await operation.promise();
console.log(`Created database ${databaseId} on instance ${instanceId}.`);
// Get encryption key
const [metadata] = await databaseAdminClient.getDatabase({
name: databaseAdminClient.databasePath(projectId, instanceId, databaseId),
});
console.log(
`Database encrypted with keys ${metadata.encryptionConfig.kmsKeyNames}.`,
);
}
createDatabaseWithMultipleKmsKeys();
PHP
To create a CMEK-enabled database in a regional instance configuration:
use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient;
use Google\Cloud\Spanner\Admin\Database\V1\CreateDatabaseRequest;
use Google\Cloud\Spanner\Admin\Database\V1\EncryptionConfig;
/**
* Creates an encrypted database with tables for sample data.
* Example:
* ```
* create_database_with_encryption_key($projectId, $instanceId, $databaseId, $kmsKeyName);
* ```
*
* @param string $projectId The Google Cloud project ID.
* @param string $instanceId The Spanner instance ID.
* @param string $databaseId The Spanner database ID.
* @param string $kmsKeyName The KMS key used for encryption.
*/
function create_database_with_encryption_key(
string $projectId,
string $instanceId,
string $databaseId,
string $kmsKeyName
): void {
$databaseAdminClient = new DatabaseAdminClient();
$instanceName = DatabaseAdminClient::instanceName($projectId, $instanceId);
$createDatabaseRequest = new CreateDatabaseRequest();
$createDatabaseRequest->setParent($instanceName);
$createDatabaseRequest->setCreateStatement(sprintf('CREATE DATABASE `%s`', $databaseId));
$createDatabaseRequest->setExtraStatements([
'CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX)
) PRIMARY KEY (SingerId)',
'CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX)
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE'
]);
if (!empty($kmsKeyName)) {
$encryptionConfig = new EncryptionConfig();
$encryptionConfig->setKmsKeyName($kmsKeyName);
$createDatabaseRequest->setEncryptionConfig($encryptionConfig);
}
$operationResponse = $databaseAdminClient->createDatabase($createDatabaseRequest);
printf('Waiting for operation to complete...' . PHP_EOL);
$operationResponse->pollUntilComplete();
if ($operationResponse->operationSucceeded()) {
$database = $operationResponse->getResult();
printf(
'Created database %s on instance %s with encryption key %s' . PHP_EOL,
$databaseId,
$instanceId,
$database->getEncryptionConfig()->getKmsKeyName()
);
} else {
$error = $operationResponse->getError();
printf('Failed to create encrypted database: %s' . PHP_EOL, $error->getMessage());
}
}
To create a CMEK-enabled database in a multi-region instance configuration:
use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient;
use Google\Cloud\Spanner\Admin\Database\V1\CreateDatabaseRequest;
use Google\Cloud\Spanner\Admin\Database\V1\EncryptionConfig;
/**
* Creates a MR CMEK database with tables for sample data.
* Example:
* ```
* create_database_with_mr_cmek($projectId, $instanceId, $databaseId, $kmsKeyNames);
* ```
*
* @param string $projectId The Google Cloud project ID.
* @param string $instanceId The Spanner instance ID.
* @param string $databaseId The Spanner database ID.
* @param string[] $kmsKeyNames The KMS keys used for encryption.
*/
function create_database_with_mr_cmek(
string $projectId,
string $instanceId,
string $databaseId,
array $kmsKeyNames
): void {
$databaseAdminClient = new DatabaseAdminClient();
$instanceName = DatabaseAdminClient::instanceName($projectId, $instanceId);
$createDatabaseRequest = new CreateDatabaseRequest();
$createDatabaseRequest->setParent($instanceName);
$createDatabaseRequest->setCreateStatement(sprintf('CREATE DATABASE `%s`', $databaseId));
$createDatabaseRequest->setExtraStatements([
'CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX)
) PRIMARY KEY (SingerId)',
'CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX)
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE'
]);
if (!empty($kmsKeyNames)) {
$encryptionConfig = new EncryptionConfig();
$encryptionConfig->setKmsKeyNames($kmsKeyNames);
$createDatabaseRequest->setEncryptionConfig($encryptionConfig);
}
$operationResponse = $databaseAdminClient->createDatabase($createDatabaseRequest);
printf('Waiting for operation to complete...' . PHP_EOL);
$operationResponse->pollUntilComplete();
if ($operationResponse->operationSucceeded()) {
$database = $operationResponse->getResult();
printf(
'Created database %s on instance %s with encryption keys %s' . PHP_EOL,
$databaseId,
$instanceId,
print_r($database->getEncryptionConfig()->getKmsKeyNames(), true)
);
} else {
$error = $operationResponse->getError();
printf('Failed to create encrypted database: %s' . PHP_EOL, $error->getMessage());
}
}
Python
To create a CMEK-enabled database in a regional instance configuration:
def create_database_with_encryption_key(instance_id, database_id, kms_key_name):
"""Creates a database with tables using a Customer Managed Encryption Key (CMEK)."""
from google.cloud.spanner_admin_database_v1 import EncryptionConfig
from google.cloud.spanner_admin_database_v1.types import spanner_database_admin
spanner_client = spanner.Client()
database_admin_api = spanner_client.database_admin_api
request = spanner_database_admin.CreateDatabaseRequest(
parent=database_admin_api.instance_path(spanner_client.project, instance_id),
create_statement=f"CREATE DATABASE `{database_id}`",
extra_statements=[
"""CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX)
) PRIMARY KEY (SingerId)""",
"""CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX)
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE""",
],
encryption_config=EncryptionConfig(kms_key_name=kms_key_name),
)
operation = database_admin_api.create_database(request=request)
print("Waiting for operation to complete...")
database = operation.result(OPERATION_TIMEOUT_SECONDS)
print(
"Database {} created with encryption key {}".format(
database.name, database.encryption_config.kms_key_name
)
)
To create a CMEK-enabled database in a multi-region instance configuration:
def create_database_with_multiple_kms_keys(instance_id, database_id, kms_key_names):
"""Creates a database with tables using multiple KMS keys(CMEK)."""
from google.cloud.spanner_admin_database_v1 import EncryptionConfig
from google.cloud.spanner_admin_database_v1.types import spanner_database_admin
spanner_client = spanner.Client()
database_admin_api = spanner_client.database_admin_api
request = spanner_database_admin.CreateDatabaseRequest(
parent=database_admin_api.instance_path(spanner_client.project, instance_id),
create_statement=f"CREATE DATABASE `{database_id}`",
extra_statements=[
"""CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX)
) PRIMARY KEY (SingerId)""",
"""CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX)
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE""",
],
encryption_config=EncryptionConfig(kms_key_names=kms_key_names),
)
operation = database_admin_api.create_database(request=request)
print("Waiting for operation to complete...")
database = operation.result(OPERATION_TIMEOUT_SECONDS)
print(
"Database {} created with multiple KMS keys {}".format(
database.name, database.encryption_config.kms_key_names
)
)
Ruby
To create a CMEK-enabled database in a regional instance configuration:
# project_id = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"
# kms_key_name = "Database eencryption KMS key"
require "google/cloud/spanner"
require "google/cloud/spanner/admin/database"
database_admin_client = Google::Cloud::Spanner::Admin::Database.database_admin
instance_path = database_admin_client.instance_path project: project_id, instance: instance_id
db_path = database_admin_client.database_path project: project_id,
instance: instance_id,
database: database_id
job = database_admin_client.create_database parent: instance_path,
create_statement: "CREATE DATABASE `#{database_id}`",
extra_statements: [
"CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX)
) PRIMARY KEY (SingerId)",
"CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX)
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE"
],
encryption_config: { kms_key_name: kms_key_name }
puts "Waiting for create database operation to complete"
job.wait_until_done!
database = database_admin_client.get_database name: db_path
puts "Database #{database_id} created with encryption key #{database.encryption_config.kms_key_name}"
To create a CMEK-enabled database in a multi-region instance configuration:
# project_id = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"
# kms_key_names = ["key1", "key2", "key3"]
require "google/cloud/spanner"
require "google/cloud/spanner/admin/database"
database_admin_client = Google::Cloud::Spanner::Admin::Database.database_admin
instance_path = database_admin_client.instance_path(
project: project_id, instance: instance_id
)
encryption_config = {
kms_key_names: kms_key_names
}
db_path = database_admin_client.database_path(
project: project_id,
instance: instance_id,
database: database_id
)
job = database_admin_client.create_database(
parent: instance_path,
create_statement: "CREATE DATABASE `#{database_id}`",
extra_statements: [
<<~STATEMENT,
CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX)
) PRIMARY KEY (SingerId)
STATEMENT
<<~STATEMENT
CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX)
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers
ON DELETE CASCADE
STATEMENT
],
encryption_config: encryption_config
)
puts "Waiting for create database operation to complete"
job.wait_until_done!
database = database_admin_client.get_database name: db_path
puts "Database #{database_id} created with encryption key " \
"#{database.encryption_config.kms_key_names}"
View the key versions in use
The database's encryption_info
field shows information about key versions.
When a database's key version changes, the change isn't immediately propagated
to encryption_info
. There might be a delay before the change is reflected in
this field.
Console
In the Google Cloud console, go to the Instances page.
Click the instance containing the database you want to view.
Click the database.
Encryption information is displayed on the Database details page.
gcloud
You can get a database's encryption_info
by running the
gcloud spanner databases describe
or gcloud spanner databases list
command. For example:
gcloud spanner databases describe DATABASE \
--project=SPANNER_PROJECT_ID \
--instance=INSTANCE_ID
Here's an example output:
name: projects/my-project/instances/test-instance/databases/example-db
encryptionInfo:
- encryptionType: CUSTOMER_MANAGED_ENCRYPTION
kmsKeyVersion: projects/my-kms-project/locations/my-kms-key1-location/keyRings/my-kms-key-ring1/cryptoKeys/my-kms-key1/cryptoKeyVersions/1
- encryptionType: CUSTOMER_MANAGED_ENCRYPTION
kmsKeyVersion: projects/my-kms-project/locations/my-kms-key2-location/keyRings/my-kms-key-ring2/cryptoKeys/my-kms-key2/cryptoKeyVersions/1
Disable the key
Disable the key version(s) that are in use by following these instructions for each key version.
Wait for the change to take effect. Disabling a key can take up to three hours to propagate.
To confirm that the database is no longer accessible, execute a query in the CMEK-disabled database:
gcloud spanner databases execute-sql DATABASE \ --project=SPANNER_PROJECT_ID \ --instance=INSTANCE_ID \ --sql='SELECT * FROM Users'
The following error message appears:
KMS key required by the Spanner resource is not accessible.
Enable the key
Enable the key versions that are in use by the database by following these instructions for each key version.
Wait for the change to take effect. Enabling a key can take up to three hours to propagate.
To confirm that the database is no longer accessible, execute a query in the CMEK-enabled database:
gcloud spanner databases execute-sql DATABASE \ --project=SPANNER_PROJECT_ID \ --instance=INSTANCE_ID \ --sql='SELECT * FROM Users'
If the change has taken effect, the command executes successfully.
Back up a database
You can use Spanner backups to create backups of your databases. By default, Spanner backups created from a database use the same encryption configuration as the database itself. You can optionally specify a different encryption configuration for a backup.
Console
Use the console to create backups in regional instance configurations.
In the Google Cloud console, go to the Instances page.
Click the instance name that contains the database that you want to back up.
Click the database.
In the navigation pane, click Backup/Restore.
In the Backups tab, click Create backup.
Enter a backup name and select an expiration date.
Optional: Click Show encryption options.
a. If you want to use a different encryption configuration for your backup, click the slider next to Use existing encryption.
a. Select Cloud KMS key.
a. Select a key from the drop-down list.
The list of keys is limited to the current Google Cloud project. To use a key from a different Google Cloud project, create the database using gcloud CLI instead of the Google Cloud console.
Click Create.
The Backups table displays encryption information for each backup.
gcloud
To create a CMEK-enabled backup in a regional, custom, or multi-region
instance configuration, run the gcloud spanner backups create
command:
gcloud spanner backups create BACKUP \
--project=SPANNER_PROJECT_ID \
--instance=INSTANCE_ID \
--database=DATABASE \
--retention-period=RETENTION_PERIOD \
--encryption-type=customer_managed_encryption \
--kms-project=KMS_PROJECT_ID \
--kms-location=KMS_KEY_LOCATION \
--kms-keyring=KMS_KEY_RING \
--kms-keys=KMS_KEY_1[, KMS_KEY_2 ... ]
--async
To verify that the backup created is CMEK encrypted:
gcloud spanner backups describe BACKUP \
--project=SPANNER_PROJECT_ID \
--instance=INSTANCE_ID
Client libraries
C#
To create a CMEK-enabled backup in a regional instance configuration:
using Google.Cloud.Spanner.Admin.Database.V1;
using Google.Cloud.Spanner.Common.V1;
using Google.Protobuf.WellKnownTypes;
using System;
using System.Threading.Tasks;
public class CreateBackupWithEncryptionKeyAsyncSample
{
public async Task<Backup> CreateBackupWithEncryptionKeyAsync(string projectId, string instanceId, string databaseId, string backupId, CryptoKeyName kmsKeyName)
{
// Create a DatabaseAdminClient instance.
DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.Create();
// Create the CreateBackupRequest with encryption configuration.
CreateBackupRequest request = new CreateBackupRequest
{
ParentAsInstanceName = InstanceName.FromProjectInstance(projectId, instanceId),
BackupId = backupId,
Backup = new Backup
{
DatabaseAsDatabaseName = DatabaseName.FromProjectInstanceDatabase(projectId, instanceId, databaseId),
ExpireTime = DateTime.UtcNow.AddDays(14).ToTimestamp(),
},
EncryptionConfig = new CreateBackupEncryptionConfig
{
EncryptionType = CreateBackupEncryptionConfig.Types.EncryptionType.CustomerManagedEncryption,
KmsKeyNameAsCryptoKeyName = kmsKeyName,
},
};
// Execute the CreateBackup request.
var operation = await databaseAdminClient.CreateBackupAsync(request);
Console.WriteLine("Waiting for the operation to finish.");
// Poll until the returned long-running operation is complete.
var completedResponse = await operation.PollUntilCompletedAsync();
if (completedResponse.IsFaulted)
{
Console.WriteLine($"Error while creating backup: {completedResponse.Exception}");
throw completedResponse.Exception;
}
var backup = completedResponse.Result;
Console.WriteLine($"Backup {backup.Name} of size {backup.SizeBytes} bytes " +
$"was created at {backup.CreateTime} " +
$"using encryption key {kmsKeyName}");
return backup;
}
}
To create a CMEK-enabled backup in a multi-region instance configuration:
using Google.Cloud.Spanner.Admin.Database.V1;
using Google.Cloud.Spanner.Common.V1;
using Google.Protobuf.WellKnownTypes;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class CreateBackupWithMultiRegionEncryptionAsyncSample
{
public async Task<Backup> CreateBackupWithMultiRegionEncryptionAsync(string projectId, string instanceId, string databaseId, string backupId, IEnumerable<CryptoKeyName> kmsKeyNames)
{
// Create a DatabaseAdminClient instance.
DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.Create();
// Create the CreateBackupRequest with encryption configuration.
CreateBackupRequest request = new CreateBackupRequest
{
ParentAsInstanceName = InstanceName.FromProjectInstance(projectId, instanceId),
BackupId = backupId,
Backup = new Backup
{
DatabaseAsDatabaseName = DatabaseName.FromProjectInstanceDatabase(projectId, instanceId, databaseId),
ExpireTime = DateTime.UtcNow.AddDays(14).ToTimestamp(),
},
EncryptionConfig = new CreateBackupEncryptionConfig
{
EncryptionType = CreateBackupEncryptionConfig.Types.EncryptionType.CustomerManagedEncryption,
KmsKeyNamesAsCryptoKeyNames = { kmsKeyNames },
},
};
// Execute the CreateBackup request.
var operation = await databaseAdminClient.CreateBackupAsync(request);
Console.WriteLine("Waiting for the operation to finish.");
// Poll until the returned long-running operation is complete.
var completedResponse = await operation.PollUntilCompletedAsync();
if (completedResponse.IsFaulted)
{
Console.WriteLine($"Error while creating backup: {completedResponse.Exception}");
throw completedResponse.Exception;
}
var backup = completedResponse.Result;
Console.WriteLine($"Backup {backup.Name} of size {backup.SizeBytes} bytes was created with encryption keys {string.Join(", ", kmsKeyNames)} at {backup.CreateTime}");
return backup;
}
}
C++
To create a CMEK-enabled backup in a regional instance configuration:
void CreateBackupWithEncryptionKey(
google::cloud::spanner_admin::DatabaseAdminClient client,
std::string const& project_id, std::string const& instance_id,
std::string const& database_id, std::string const& backup_id,
google::cloud::spanner::Timestamp expire_time,
google::cloud::spanner::Timestamp version_time,
google::cloud::KmsKeyName const& encryption_key) {
google::cloud::spanner::Database database(project_id, instance_id,
database_id);
google::spanner::admin::database::v1::CreateBackupRequest request;
request.set_parent(database.instance().FullName());
request.set_backup_id(backup_id);
request.mutable_backup()->set_database(database.FullName());
*request.mutable_backup()->mutable_expire_time() =
expire_time.get<google::protobuf::Timestamp>().value();
*request.mutable_backup()->mutable_version_time() =
version_time.get<google::protobuf::Timestamp>().value();
request.mutable_encryption_config()->set_encryption_type(
google::spanner::admin::database::v1::CreateBackupEncryptionConfig::
CUSTOMER_MANAGED_ENCRYPTION);
request.mutable_encryption_config()->set_kms_key_name(
encryption_key.FullName());
auto backup = client.CreateBackup(request).get();
if (!backup) throw std::move(backup).status();
std::cout
<< "Backup " << backup->name() << " of " << backup->database()
<< " of size " << backup->size_bytes() << " bytes as of "
<< google::cloud::spanner::MakeTimestamp(backup->version_time()).value()
<< " was created at "
<< google::cloud::spanner::MakeTimestamp(backup->create_time()).value()
<< " using encryption key " << encryption_key.FullName() << ".\n";
}
To create a CMEK-enabled backup in a multi-region instance configuration:
void CreateBackupWithMRCMEK(
google::cloud::spanner_admin::DatabaseAdminClient client,
BackupIdentifier dst, std::string const& database_id,
google::cloud::spanner::Timestamp expire_time,
google::cloud::spanner::Timestamp version_time,
std::vector<google::cloud::KmsKeyName> const& encryption_keys) {
google::cloud::spanner::Database database(dst.project_id, dst.instance_id,
database_id);
google::spanner::admin::database::v1::CreateBackupRequest request;
request.set_parent(database.instance().FullName());
request.set_backup_id(dst.backup_id);
request.mutable_backup()->set_database(database.FullName());
*request.mutable_backup()->mutable_expire_time() =
expire_time.get<google::protobuf::Timestamp>().value();
*request.mutable_backup()->mutable_version_time() =
version_time.get<google::protobuf::Timestamp>().value();
request.mutable_encryption_config()->set_encryption_type(
google::spanner::admin::database::v1::CreateBackupEncryptionConfig::
CUSTOMER_MANAGED_ENCRYPTION);
for (google::cloud::KmsKeyName const& encryption_key : encryption_keys) {
request.mutable_encryption_config()->add_kms_key_names(
encryption_key.FullName());
}
auto backup = client.CreateBackup(request).get();
if (!backup) throw std::move(backup).status();
std::cout
<< "Backup " << backup->name() << " of " << backup->database()
<< " of size " << backup->size_bytes() << " bytes as of "
<< google::cloud::spanner::MakeTimestamp(backup->version_time()).value()
<< " was created at "
<< google::cloud::spanner::MakeTimestamp(backup->create_time()).value();
PrintKmsKeys(encryption_keys);
}
Go
To create a CMEK-enabled backup in a regional instance configuration:
import (
"context"
"fmt"
"io"
"regexp"
"time"
database "cloud.google.com/go/spanner/admin/database/apiv1"
adminpb "cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
pbt "github.com/golang/protobuf/ptypes/timestamp"
)
func createBackupWithCustomerManagedEncryptionKey(ctx context.Context, w io.Writer, db, backupID, kmsKeyName string) error {
// db = `projects/<project>/instances/<instance-id>/database/<database-id>`
// backupID = `my-backup-id`
// kmsKeyName = `projects/<project>/locations/<location>/keyRings/<key_ring>/cryptoKeys/<kms_key_name>`
matches := regexp.MustCompile("^(.+)/databases/(.+)$").FindStringSubmatch(db)
if matches == nil || len(matches) != 3 {
return fmt.Errorf("createBackupWithCustomerManagedEncryptionKey: invalid database id %q", db)
}
instanceName := matches[1]
adminClient, err := database.NewDatabaseAdminClient(ctx)
if err != nil {
return fmt.Errorf("createBackupWithCustomerManagedEncryptionKey.NewDatabaseAdminClient: %w", err)
}
defer adminClient.Close()
expireTime := time.Now().AddDate(0, 0, 14)
// Create a backup for a database using a Customer Managed Encryption Key
req := adminpb.CreateBackupRequest{
Parent: instanceName,
BackupId: backupID,
Backup: &adminpb.Backup{
Database: db,
ExpireTime: &pbt.Timestamp{Seconds: expireTime.Unix(), Nanos: int32(expireTime.Nanosecond())},
},
EncryptionConfig: &adminpb.CreateBackupEncryptionConfig{
KmsKeyName: kmsKeyName,
EncryptionType: adminpb.CreateBackupEncryptionConfig_CUSTOMER_MANAGED_ENCRYPTION,
},
}
op, err := adminClient.CreateBackup(ctx, &req)
if err != nil {
return fmt.Errorf("createBackupWithCustomerManagedEncryptionKey.CreateBackup: %w", err)
}
// Wait for backup operation to complete.
backup, err := op.Wait(ctx)
if err != nil {
return fmt.Errorf("createBackupWithCustomerManagedEncryptionKey.Wait: %w", err)
}
// Get the name, create time, backup size and encryption key from the backup.
backupCreateTime := time.Unix(backup.CreateTime.Seconds, int64(backup.CreateTime.Nanos))
fmt.Fprintf(w,
"Backup %s of size %d bytes was created at %s using encryption key %s\n",
backup.Name,
backup.SizeBytes,
backupCreateTime.Format(time.RFC3339),
kmsKeyName)
return nil
}
To create a CMEK-enabled backup in a multi-region instance configuration:
import (
"context"
"fmt"
"io"
"time"
database "cloud.google.com/go/spanner/admin/database/apiv1"
adminpb "cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
pbt "github.com/golang/protobuf/ptypes/timestamp"
)
// createBackupWithCustomerManagedMultiRegionEncryptionKey creates a backup for a database using a Customer Managed Multi-Region Encryption Key.
func createBackupWithCustomerManagedMultiRegionEncryptionKey(ctx context.Context, w io.Writer, projectID, instanceID, databaseID, backupID string, kmsKeyNames []string) error {
// projectID = `my-project`
// instanceID = `my-instance`
// databaseID = `my-database`
// backupID = `my-backup-id`
// kmsKeyNames := []string{"projects/my-project/locations/locations/<location1>/keyRings/<keyRing>/cryptoKeys/<keyId>",
// "projects/my-project/locations/locations/<location2>/keyRings/<keyRing>/cryptoKeys/<keyId>",
// "projects/my-project/locations/locations/<location3>/keyRings/<keyRing>/cryptoKeys/<keyId>",
// }
adminClient, err := database.NewDatabaseAdminClient(ctx)
if err != nil {
return fmt.Errorf("createBackupWithCustomerManagedMultiRegionEncryptionKey.NewDatabaseAdminClient: %w", err)
}
defer adminClient.Close()
expireTime := time.Now().AddDate(0, 0, 14)
// Create a backup for a database using a Customer Managed Encryption Key
req := adminpb.CreateBackupRequest{
Parent: fmt.Sprintf("projects/%s/instances/%s", projectID, instanceID),
BackupId: backupID,
Backup: &adminpb.Backup{
Database: fmt.Sprintf("projects/%s/instances/%s/databases/%s", projectID, instanceID, databaseID),
ExpireTime: &pbt.Timestamp{Seconds: expireTime.Unix(), Nanos: int32(expireTime.Nanosecond())},
},
EncryptionConfig: &adminpb.CreateBackupEncryptionConfig{
KmsKeyNames: kmsKeyNames,
EncryptionType: adminpb.CreateBackupEncryptionConfig_CUSTOMER_MANAGED_ENCRYPTION,
},
}
op, err := adminClient.CreateBackup(ctx, &req)
if err != nil {
return fmt.Errorf("createBackupWithCustomerManagedMultiRegionEncryptionKey.CreateBackup: %w", err)
}
// Wait for backup operation to complete.
backup, err := op.Wait(ctx)
if err != nil {
return fmt.Errorf("createBackupWithCustomerManagedMultiRegionEncryptionKey.Wait: %w", err)
}
// Get the name, create time, backup size and encryption key from the backup.
backupCreateTime := time.Unix(backup.CreateTime.Seconds, int64(backup.CreateTime.Nanos))
fmt.Fprintf(w,
"Backup %s of size %d bytes was created at %s using multi-region encryption keys %q\n",
backup.Name,
backup.SizeBytes,
backupCreateTime.Format(time.RFC3339),
kmsKeyNames)
return nil
}
Java
To create a CMEK-enabled backup in a regional instance configuration:
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.protobuf.Timestamp;
import com.google.spanner.admin.database.v1.Backup;
import com.google.spanner.admin.database.v1.BackupName;
import com.google.spanner.admin.database.v1.CreateBackupEncryptionConfig;
import com.google.spanner.admin.database.v1.CreateBackupEncryptionConfig.EncryptionType;
import com.google.spanner.admin.database.v1.CreateBackupRequest;
import com.google.spanner.admin.database.v1.DatabaseName;
import com.google.spanner.admin.database.v1.InstanceName;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.threeten.bp.LocalDateTime;
import org.threeten.bp.OffsetDateTime;
public class CreateBackupWithEncryptionKey {
static void createBackupWithEncryptionKey() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
String databaseId = "my-database";
String backupId = "my-backup";
String kmsKeyName =
"projects/" + projectId + "/locations/<location>/keyRings/<keyRing>/cryptoKeys/<keyId>";
try (Spanner spanner =
SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
DatabaseAdminClient adminClient = spanner.createDatabaseAdminClient()) {
createBackupWithEncryptionKey(
adminClient,
projectId,
instanceId,
databaseId,
backupId,
kmsKeyName);
}
}
static Void createBackupWithEncryptionKey(DatabaseAdminClient adminClient,
String projectId, String instanceId, String databaseId, String backupId, String kmsKeyName) {
// Set expire time to 14 days from now.
final Timestamp expireTime =
Timestamp.newBuilder().setSeconds(TimeUnit.MILLISECONDS.toSeconds((
System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14)))).build();
final BackupName backupName = BackupName.of(projectId, instanceId, backupId);
Backup backup = Backup.newBuilder()
.setName(backupName.toString())
.setDatabase(DatabaseName.of(projectId, instanceId, databaseId).toString())
.setExpireTime(expireTime).build();
final CreateBackupRequest request =
CreateBackupRequest.newBuilder()
.setParent(InstanceName.of(projectId, instanceId).toString())
.setBackupId(backupId)
.setBackup(backup)
.setEncryptionConfig(
CreateBackupEncryptionConfig.newBuilder()
.setEncryptionType(EncryptionType.CUSTOMER_MANAGED_ENCRYPTION)
.setKmsKeyName(kmsKeyName).build()).build();
try {
System.out.println("Waiting for operation to complete...");
backup = adminClient.createBackupAsync(request).get(1200, TimeUnit.SECONDS);
} catch (ExecutionException e) {
// If the operation failed during execution, expose the cause.
throw SpannerExceptionFactory.asSpannerException(e.getCause());
} catch (InterruptedException e) {
// Throw when a thread is waiting, sleeping, or otherwise occupied,
// and the thread is interrupted, either before or during the activity.
throw SpannerExceptionFactory.propagateInterrupt(e);
} catch (TimeoutException e) {
// If the operation timed out propagates the timeout
throw SpannerExceptionFactory.propagateTimeout(e);
}
System.out.printf(
"Backup %s of size %d bytes was created at %s using encryption key %s%n",
backup.getName(),
backup.getSizeBytes(),
LocalDateTime.ofEpochSecond(
backup.getCreateTime().getSeconds(),
backup.getCreateTime().getNanos(),
OffsetDateTime.now().getOffset()),
kmsKeyName
);
return null;
}
}
To create a CMEK-enabled backup in a multi-region instance configuration:
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.Timestamp;
import com.google.spanner.admin.database.v1.Backup;
import com.google.spanner.admin.database.v1.BackupName;
import com.google.spanner.admin.database.v1.CreateBackupEncryptionConfig;
import com.google.spanner.admin.database.v1.CreateBackupEncryptionConfig.EncryptionType;
import com.google.spanner.admin.database.v1.CreateBackupRequest;
import com.google.spanner.admin.database.v1.DatabaseName;
import com.google.spanner.admin.database.v1.InstanceName;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.threeten.bp.LocalDateTime;
import org.threeten.bp.OffsetDateTime;
public class CreateBackupWithMultiRegionEncryptionKey {
static void createBackupWithMultiRegionEncryptionKey() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
String databaseId = "my-database";
String backupId = "my-backup";
String[] kmsKeyNames =
new String[] {
"projects/" + projectId + "/locations/<location1>/keyRings/<keyRing>/cryptoKeys/<keyId>",
"projects/" + projectId + "/locations/<location2>/keyRings/<keyRing>/cryptoKeys/<keyId>",
"projects/" + projectId + "/locations/<location3>/keyRings/<keyRing>/cryptoKeys/<keyId>"
};
try (Spanner spanner =
SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
DatabaseAdminClient adminClient = spanner.createDatabaseAdminClient()) {
createBackupWithMultiRegionEncryptionKey(
adminClient, projectId, instanceId, databaseId, backupId, kmsKeyNames);
}
}
static Void createBackupWithMultiRegionEncryptionKey(
DatabaseAdminClient adminClient,
String projectId,
String instanceId,
String databaseId,
String backupId,
String[] kmsKeyNames) {
// Set expire time to 14 days from now.
final Timestamp expireTime =
Timestamp.newBuilder()
.setSeconds(
TimeUnit.MILLISECONDS.toSeconds(
(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14))))
.build();
final BackupName backupName = BackupName.of(projectId, instanceId, backupId);
Backup backup =
Backup.newBuilder()
.setName(backupName.toString())
.setDatabase(DatabaseName.of(projectId, instanceId, databaseId).toString())
.setExpireTime(expireTime)
.build();
final CreateBackupRequest request =
CreateBackupRequest.newBuilder()
.setParent(InstanceName.of(projectId, instanceId).toString())
.setBackupId(backupId)
.setBackup(backup)
.setEncryptionConfig(
CreateBackupEncryptionConfig.newBuilder()
.setEncryptionType(EncryptionType.CUSTOMER_MANAGED_ENCRYPTION)
.addAllKmsKeyNames(ImmutableList.copyOf(kmsKeyNames))
.build())
.build();
try {
System.out.println("Waiting for operation to complete...");
backup = adminClient.createBackupAsync(request).get(1200, TimeUnit.SECONDS);
} catch (ExecutionException e) {
// If the operation failed during execution, expose the cause.
throw SpannerExceptionFactory.asSpannerException(e.getCause());
} catch (InterruptedException e) {
// Throw when a thread is waiting, sleeping, or otherwise occupied,
// and the thread is interrupted, either before or during the activity.
throw SpannerExceptionFactory.propagateInterrupt(e);
} catch (TimeoutException e) {
// If the operation timed out propagates the timeout
throw SpannerExceptionFactory.propagateTimeout(e);
}
System.out.printf(
"Backup %s of size %d bytes was created at %s using encryption keys %s%n",
backup.getName(),
backup.getSizeBytes(),
LocalDateTime.ofEpochSecond(
backup.getCreateTime().getSeconds(),
backup.getCreateTime().getNanos(),
OffsetDateTime.now().getOffset()),
ImmutableList.copyOf(kmsKeyNames));
return null;
}
}
Node.js
To create a CMEK-enabled backup in a regional instance configuration:
// Imports the Google Cloud client library
const {Spanner, protos} = require('@google-cloud/spanner');
const {PreciseDate} = require('@google-cloud/precise-date');
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const backupId = 'my-backup';
// const keyName =
// 'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key';
// Creates a client
const spanner = new Spanner({
projectId: projectId,
});
// Gets a reference to a Cloud Spanner Database Admin Client object
const databaseAdminClient = spanner.getDatabaseAdminClient();
// Creates a new backup of the database
try {
console.log(
`Creating backup of database ${databaseAdminClient.databasePath(
projectId,
instanceId,
databaseId,
)}.`,
);
// Expire backup 14 days in the future
const expireTime = Date.now() + 1000 * 60 * 60 * 24 * 14;
// Create a backup of the state of the database at the current time.
const [operation] = await databaseAdminClient.createBackup({
parent: databaseAdminClient.instancePath(projectId, instanceId),
backupId: backupId,
backup: (protos.google.spanner.admin.database.v1.Backup = {
database: databaseAdminClient.databasePath(
projectId,
instanceId,
databaseId,
),
expireTime: Spanner.timestamp(expireTime).toStruct(),
name: databaseAdminClient.backupPath(projectId, instanceId, backupId),
}),
encryptionConfig: {
encryptionType: 'CUSTOMER_MANAGED_ENCRYPTION',
kmsKeyName: keyName,
},
});
console.log(
`Waiting for backup ${databaseAdminClient.backupPath(
projectId,
instanceId,
backupId,
)} to complete...`,
);
await operation.promise();
// Verify backup is ready
const [backupInfo] = await databaseAdminClient.getBackup({
name: databaseAdminClient.backupPath(projectId, instanceId, backupId),
});
if (backupInfo.state === 'READY') {
console.log(
`Backup ${backupInfo.name} of size ` +
`${backupInfo.sizeBytes} bytes was created at ` +
`${new PreciseDate(backupInfo.createTime).toISOString()} ` +
`using encryption key ${backupInfo.encryptionInfo.kmsKeyVersion}`,
);
} else {
console.error('ERROR: Backup is not ready.');
}
} catch (err) {
console.error('ERROR:', err);
} finally {
// Close the spanner client when finished.
// The databaseAdminClient does not require explicit closure. The closure of the Spanner client will automatically close the databaseAdminClient.
spanner.close();
}
To create a CMEK-enabled backup in a multi-region instance configuration:
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const backupId = 'my-backup';
// const kmsKeyNames =
// 'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key1,
// 'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key2';
// Imports the Google Cloud client library
const {Spanner, protos} = require('@google-cloud/spanner');
const {PreciseDate} = require('@google-cloud/precise-date');
// Creates a client
const spanner = new Spanner({
projectId: projectId,
});
// Gets a reference to a Cloud Spanner Database Admin Client object
const databaseAdminClient = spanner.getDatabaseAdminClient();
async function createBackupWithMultipleKmsKeys() {
// Creates a new backup of the database
try {
console.log(
`Creating backup of database ${databaseAdminClient.databasePath(
projectId,
instanceId,
databaseId,
)}.`,
);
// Expire backup 14 days in the future
const expireTime = Date.now() + 1000 * 60 * 60 * 24 * 14;
// Create a backup of the state of the database at the current time.
const [operation] = await databaseAdminClient.createBackup({
parent: databaseAdminClient.instancePath(projectId, instanceId),
backupId: backupId,
backup: (protos.google.spanner.admin.database.v1.Backup = {
database: databaseAdminClient.databasePath(
projectId,
instanceId,
databaseId,
),
expireTime: Spanner.timestamp(expireTime).toStruct(),
name: databaseAdminClient.backupPath(projectId, instanceId, backupId),
}),
encryptionConfig: {
encryptionType: 'CUSTOMER_MANAGED_ENCRYPTION',
kmsKeyNames: kmsKeyNames.split(','),
},
});
console.log(
`Waiting for backup ${databaseAdminClient.backupPath(
projectId,
instanceId,
backupId,
)} to complete...`,
);
await operation.promise();
// Verify backup is ready
const [backupInfo] = await databaseAdminClient.getBackup({
name: databaseAdminClient.backupPath(projectId, instanceId, backupId),
});
const kmsKeyVersions = backupInfo.encryptionInformation
.map(encryptionInfo => encryptionInfo.kmsKeyVersion)
.join(', ');
if (backupInfo.state === 'READY') {
console.log(
`Backup ${backupInfo.name} of size ` +
`${backupInfo.sizeBytes} bytes was created at ` +
`${new PreciseDate(backupInfo.createTime).toISOString()} ` +
`using encryption key ${kmsKeyVersions}`,
);
} else {
console.error('ERROR: Backup is not ready.');
}
} catch (err) {
console.error('ERROR:', err);
} finally {
// Close the spanner client when finished.
// The databaseAdminClient does not require explicit closure. The closure of the Spanner client will automatically close the databaseAdminClient.
spanner.close();
}
}
createBackupWithMultipleKmsKeys();
PHP
To create a CMEK-enabled backup in a regional instance configuration:
use Google\Cloud\Spanner\Admin\Database\V1\Backup;
use \Google\Cloud\Spanner\Admin\Database\V1\Backup\State;
use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient;
use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupEncryptionConfig;
use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupRequest;
use Google\Cloud\Spanner\Admin\Database\V1\GetBackupRequest;
use Google\Protobuf\Timestamp;
/**
* Create an encrypted backup.
* Example:
* ```
* create_backup_with_encryption_key($projectId, $instanceId, $databaseId, $backupId, $kmsKeyName);
* ```
*
* @param string $projectId The Google Cloud project ID.
* @param string $instanceId The Spanner instance ID.
* @param string $databaseId The Spanner database ID.
* @param string $backupId The Spanner backup ID.
* @param string $kmsKeyName The KMS key used for encryption.
*/
function create_backup_with_encryption_key(
string $projectId,
string $instanceId,
string $databaseId,
string $backupId,
string $kmsKeyName
): void {
$databaseAdminClient = new DatabaseAdminClient();
$instanceFullName = DatabaseAdminClient::instanceName($projectId, $instanceId);
$databaseFullName = DatabaseAdminClient::databaseName($projectId, $instanceId, $databaseId);
$expireTime = new Timestamp();
$expireTime->setSeconds((new \DateTime('+14 days'))->getTimestamp());
$request = new CreateBackupRequest([
'parent' => $instanceFullName,
'backup_id' => $backupId,
'encryption_config' => new CreateBackupEncryptionConfig([
'kms_key_name' => $kmsKeyName,
'encryption_type' => CreateBackupEncryptionConfig\EncryptionType::CUSTOMER_MANAGED_ENCRYPTION
]),
'backup' => new Backup([
'database' => $databaseFullName,
'expire_time' => $expireTime
])
]);
$operation = $databaseAdminClient->createBackup($request);
print('Waiting for operation to complete...' . PHP_EOL);
$operation->pollUntilComplete();
$request = new GetBackupRequest();
$request->setName($databaseAdminClient->backupName($projectId, $instanceId, $backupId));
$info = $databaseAdminClient->getBackup($request);
if (State::name($info->getState()) == 'READY') {
printf(
'Backup %s of size %d bytes was created at %d using encryption key %s' . PHP_EOL,
basename($info->getName()),
$info->getSizeBytes(),
$info->getCreateTime()->getSeconds(),
$info->getEncryptionInfo()->getKmsKeyVersion()
);
} else {
print('Backup is not ready!' . PHP_EOL);
}
}
To create a CMEK-enabled backup in a multi-region instance configuration:
use Google\Cloud\Spanner\Admin\Database\V1\Backup;
use \Google\Cloud\Spanner\Admin\Database\V1\Backup\State;
use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient;
use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupEncryptionConfig;
use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupRequest;
use Google\Cloud\Spanner\Admin\Database\V1\GetBackupRequest;
use Google\Protobuf\Timestamp;
/**
* Create a CMEK backup.
* Example:
* ```
* create_backup_with_mr_cmek($projectId, $instanceId, $databaseId, $backupId, $kmsKeyNames);
* ```
*
* @param string $projectId The Google Cloud project ID.
* @param string $instanceId The Spanner instance ID.
* @param string $databaseId The Spanner database ID.
* @param string $backupId The Spanner backup ID.
* @param string[] $kmsKeyNames The KMS keys used for encryption.
*/
function create_backup_with_mr_cmek(
string $projectId,
string $instanceId,
string $databaseId,
string $backupId,
array $kmsKeyNames
): void {
$databaseAdminClient = new DatabaseAdminClient();
$instanceFullName = DatabaseAdminClient::instanceName($projectId, $instanceId);
$databaseFullName = DatabaseAdminClient::databaseName($projectId, $instanceId, $databaseId);
$expireTime = new Timestamp();
$expireTime->setSeconds((new \DateTime('+14 days'))->getTimestamp());
$request = new CreateBackupRequest([
'parent' => $instanceFullName,
'backup_id' => $backupId,
'encryption_config' => new CreateBackupEncryptionConfig([
'kms_key_names' => $kmsKeyNames,
'encryption_type' => CreateBackupEncryptionConfig\EncryptionType::CUSTOMER_MANAGED_ENCRYPTION
]),
'backup' => new Backup([
'database' => $databaseFullName,
'expire_time' => $expireTime
])
]);
$operation = $databaseAdminClient->createBackup($request);
print('Waiting for operation to complete...' . PHP_EOL);
$operation->pollUntilComplete();
$request = new GetBackupRequest();
$request->setName($databaseAdminClient->backupName($projectId, $instanceId, $backupId));
$info = $databaseAdminClient->getBackup($request);
if (State::name($info->getState()) == 'READY') {
$kmsKeyVersions = [];
foreach ($info->getEncryptionInformation() as $encryptionInfo) {
$kmsKeyVersions[] = $encryptionInfo->getKmsKeyVersion();
}
printf(
'Backup %s of size %d bytes was created at %d using encryption keys %s' . PHP_EOL,
basename($info->getName()),
$info->getSizeBytes(),
$info->getCreateTime()->getSeconds(),
print_r($kmsKeyVersions, true)
);
} else {
print('Backup is not ready!' . PHP_EOL);
}
}
Python
To create a CMEK-enabled backup in a regional instance configuration:
def create_backup_with_encryption_key(
instance_id, database_id, backup_id, kms_key_name
):
"""Creates a backup for a database using a Customer Managed Encryption Key (CMEK)."""
from google.cloud.spanner_admin_database_v1 import CreateBackupEncryptionConfig
from google.cloud.spanner_admin_database_v1.types import backup as backup_pb
spanner_client = spanner.Client()
database_admin_api = spanner_client.database_admin_api
# Create a backup
expire_time = datetime.utcnow() + timedelta(days=14)
encryption_config = {
"encryption_type": CreateBackupEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION,
"kms_key_name": kms_key_name,
}
request = backup_pb.CreateBackupRequest(
parent=database_admin_api.instance_path(spanner_client.project, instance_id),
backup_id=backup_id,
backup=backup_pb.Backup(
database=database_admin_api.database_path(
spanner_client.project, instance_id, database_id
),
expire_time=expire_time,
),
encryption_config=encryption_config,
)
operation = database_admin_api.create_backup(request)
# Wait for backup operation to complete.
backup = operation.result(2100)
# Verify that the backup is ready.
assert backup.state == backup_pb.Backup.State.READY
# Get the name, create time, backup size and encryption key.
print(
"Backup {} of size {} bytes was created at {} using encryption key {}".format(
backup.name, backup.size_bytes, backup.create_time, kms_key_name
)
)
To create a CMEK-enabled backup in a multi-region instance configuration:
def create_backup_with_multiple_kms_keys(
instance_id, database_id, backup_id, kms_key_names
):
"""Creates a backup for a database using multiple KMS keys(CMEK)."""
from google.cloud.spanner_admin_database_v1 import CreateBackupEncryptionConfig
from google.cloud.spanner_admin_database_v1.types import backup as backup_pb
spanner_client = spanner.Client()
database_admin_api = spanner_client.database_admin_api
# Create a backup
expire_time = datetime.utcnow() + timedelta(days=14)
encryption_config = {
"encryption_type": CreateBackupEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION,
"kms_key_names": kms_key_names,
}
request = backup_pb.CreateBackupRequest(
parent=database_admin_api.instance_path(spanner_client.project, instance_id),
backup_id=backup_id,
backup=backup_pb.Backup(
database=database_admin_api.database_path(
spanner_client.project, instance_id, database_id
),
expire_time=expire_time,
),
encryption_config=encryption_config,
)
operation = database_admin_api.create_backup(request)
# Wait for backup operation to complete.
backup = operation.result(2100)
# Verify that the backup is ready.
assert backup.state == backup_pb.Backup.State.READY
# Get the name, create time, backup size and encryption key.
print(
"Backup {} of size {} bytes was created at {} using encryption key {}".format(
backup.name, backup.size_bytes, backup.create_time, kms_key_names
)
)
Ruby
To create a CMEK-enabled backup in a regional instance configuration:
# project_id = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"
# backup_id = "Your Spanner backup ID"
# kms_key_name = "Your backup encryption database KMS key"
require "google/cloud/spanner"
require "google/cloud/spanner/admin/database"
database_admin_client = Google::Cloud::Spanner::Admin::Database.database_admin
instance_path = database_admin_client.instance_path project: project_id, instance: instance_id
db_path = database_admin_client.database_path project: project_id,
instance: instance_id,
database: database_id
backup_path = database_admin_client.backup_path project: project_id,
instance: instance_id,
backup: backup_id
expire_time = Time.now + (14 * 24 * 3600) # 14 days from now
encryption_config = {
encryption_type: :CUSTOMER_MANAGED_ENCRYPTION,
kms_key_name: kms_key_name
}
job = database_admin_client.create_backup parent: instance_path,
backup_id: backup_id,
backup: {
database: db_path,
expire_time: expire_time
},
encryption_config: encryption_config
puts "Backup operation in progress"
job.wait_until_done!
backup = database_admin_client.get_backup name: backup_path
puts "Backup #{backup_id} of size #{backup.size_bytes} bytes was created at #{backup.create_time} using encryption key #{kms_key_name}"
To create a CMEK-enabled backup in a multi-region instance configuration:
# project_id = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"
# backup_id = "Your Spanner backup ID"
# kms_key_names = ["key1", "key2", "key3"]
require "google/cloud/spanner"
require "google/cloud/spanner/admin/database"
database_admin_client = Google::Cloud::Spanner::Admin::Database.database_admin
instance_path = database_admin_client.instance_path(
project: project_id, instance: instance_id
)
db_path = database_admin_client.database_path project: project_id,
instance: instance_id,
database: database_id
backup_path = database_admin_client.backup_path project: project_id,
instance: instance_id,
backup: backup_id
expire_time = Time.now + (14 * 24 * 3600) # 14 days from now
encryption_config = {
encryption_type: :CUSTOMER_MANAGED_ENCRYPTION,
kms_key_names: kms_key_names
}
job = database_admin_client.create_backup parent: instance_path,
backup_id: backup_id,
backup: {
database: db_path,
expire_time: expire_time
},
encryption_config: encryption_config
puts "Backup operation in progress"
job.wait_until_done!
backup = database_admin_client.get_backup name: backup_path
puts "Backup #{backup_id} of size #{backup.size_bytes} bytes was created " \
"at #{backup.create_time} using encryption key #{kms_key_names}"
Copy a backup
You can copy a backup of your Spanner database from one instance to another instance in a different region or project. By default, a copied backup uses the same encryption configuration, either Google-managed or customer-managed, as its source backup encryption. You can override this behavior by specifying a different encryption configuration when copying the backup. If you want the copied backup to be encrypted with CMEK when copying across regions, specify the Cloud KMS keys corresponding to the destination regions.
Console
Use the console to copy a backup in a regional instance configuration.
In the Google Cloud console, go to the Instances page.
Click the instance name that contains the database that you want to back up.
Click the database.
In the navigation pane, click Backup/Restore.
In the Backups table, select Actions for your backup and click Copy.
Fill out the form by choosing a destination instance, providing a name, and selecting an expiration date for the backup copy.
Optional: If you want to use a different encryption configuration for your backup, click Show encryption options.
a. Select Cloud KMS key.
a. Select a key from the drop-down list.
The list of keys is limited to the current Google Cloud project. To use a key from a different Google Cloud project, create the database using gcloud CLI instead of the Google Cloud console.
Click Copy.
gcloud
To copy a backup, with a new encryption configuration, to a different instance
in the same project, run the following gcloud spanner backups copy
command:
gcloud spanner backups copy --async \
--source-instance=INSTANCE_ID \
--source-backup=SOURCE_BACKUP_NAME \
--destination-instance=DESTINATION_INSTANCE_ID \
--destination-backup=DESTINATION_BACKUP_NAME \
--expiration-date=EXPIRATION_DATE \
--encryption-type=CUSTOMER_MANAGED_ENCRYPTION \
--kms-keys=KMS_KEY_1[, KMS_KEY_2 ... ]
To copy a backup, with a new encryption configuration, to a different instance
in a different project, run the following gcloud spanner backups copy
command:
gcloud spanner backups copy --async \
--source-backup=SOURCE_BACKUP_NAME \
--destination-backup=DESTINATION_BACKUP_NAME \
--encryption-type=CUSTOMER_MANAGED_ENCRYPTION \
--kms-keys=KMS_KEY_1[, KMS_KEY_2 ... ]
To verify that the copied backup is CMEK encrypted:
gcloud spanner backups describe BACKUP \
--project=SPANNER_PROJECT_ID \
--instance=INSTANCE_ID
Client libraries
C#
To copy a CMEK-enabled backup in a multi-region instance configuration:
using Google.Cloud.Spanner.Admin.Database.V1;
using Google.Cloud.Spanner.Common.V1;
using Google.Protobuf.WellKnownTypes;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class CopyBackupWithMultiRegionEncryptionAsyncSample
{
public async Task<Backup> CopyBackupWithMultiRegionEncryptionAsync(
string sourceProjectId, string sourceInstanceId, string sourceBackupId,
string targetProjectId, string targetInstanceId, string targetBackupId,
DateTimeOffset expireTime, IEnumerable<CryptoKeyName> kmsKeyNames)
{
DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.Create();
var request = new CopyBackupRequest
{
SourceBackupAsBackupName = new BackupName(sourceProjectId, sourceInstanceId, sourceBackupId),
ParentAsInstanceName = new InstanceName(targetProjectId, targetInstanceId),
BackupId = targetBackupId,
ExpireTime = Timestamp.FromDateTimeOffset(expireTime),
EncryptionConfig = new CopyBackupEncryptionConfig
{
EncryptionType = CopyBackupEncryptionConfig.Types.EncryptionType.CustomerManagedEncryption,
KmsKeyNamesAsCryptoKeyNames = { kmsKeyNames },
}
};
// Execute the CopyBackup request.
var operation = await databaseAdminClient.CopyBackupAsync(request);
Console.WriteLine("Waiting for the operation to finish.");
// Poll until the returned long-running operation is complete.
var completedResponse = await operation.PollUntilCompletedAsync();
if (completedResponse.IsFaulted)
{
Console.WriteLine($"Error while copying backup: {completedResponse.Exception}");
throw completedResponse.Exception;
}
Backup backup = completedResponse.Result;
Console.WriteLine($"Backup copied successfully.");
Console.WriteLine($"Backup with Id {sourceBackupId} has been copied from {sourceProjectId}/{sourceInstanceId} to {targetProjectId}/{targetInstanceId} Backup {targetBackupId}");
Console.WriteLine($"Backup {backup.Name} of size {backup.SizeBytes} bytes was created with encryption keys {string.Join(", ", kmsKeyNames)} at {backup.CreateTime} from {backup.Database} and is in state {backup.State} and has version time {backup.VersionTime}");
return backup;
}
}
C++
To copy a CMEK-enabled backup in a multi-region instance configuration:
struct BackupIdentifier {
std::string project_id;
std::string instance_id;
std::string backup_id;
};
void PrintKmsKeys(
std::vector<google::cloud::KmsKeyName> const& encryption_keys) {
std::cout << " using encryption keys ";
for (std::size_t i = 0; i < encryption_keys.size(); ++i) {
std::cout << encryption_keys[i].FullName();
if (i != encryption_keys.size() - 1) {
std::cout << ", ";
}
}
std::cout << ".\n";
}
void CopyBackupWithMRCMEK(
google::cloud::spanner_admin::DatabaseAdminClient client,
BackupIdentifier const& src, BackupIdentifier const& dst,
google::cloud::spanner::Timestamp expire_time,
std::vector<google::cloud::KmsKeyName> const& encryption_keys) {
google::cloud::spanner::Backup source(
google::cloud::spanner::Instance(src.project_id, src.instance_id),
src.backup_id);
google::cloud::spanner::Instance dst_in(dst.project_id, dst.instance_id);
google::spanner::admin::database::v1::CopyBackupRequest request;
request.set_backup_id(dst.backup_id);
request.set_parent(dst_in.FullName());
request.set_source_backup(source.FullName());
*request.mutable_expire_time() =
expire_time.get<google::protobuf::Timestamp>().value();
request.mutable_encryption_config()->set_encryption_type(
google::spanner::admin::database::v1::CopyBackupEncryptionConfig::
CUSTOMER_MANAGED_ENCRYPTION);
for (google::cloud::KmsKeyName const& encryption_key : encryption_keys) {
request.mutable_encryption_config()->add_kms_key_names(
encryption_key.FullName());
}
auto copy_backup = client.CopyBackup(request).get();
if (!copy_backup) throw std::move(copy_backup).status();
std::cout << "Copy Backup " << copy_backup->name() //
<< " of " << source.FullName() //
<< " of size " << copy_backup->size_bytes() << " bytes as of "
<< google::cloud::spanner::MakeTimestamp(
copy_backup->version_time())
.value()
<< " was created at "
<< google::cloud::spanner::MakeTimestamp(copy_backup->create_time())
.value();
PrintKmsKeys(encryption_keys);
}
Go
To copy a CMEK-enabled backup in a multi-region instance configuration:
import (
"context"
"fmt"
"io"
"time"
database "cloud.google.com/go/spanner/admin/database/apiv1"
adminpb "cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
pbt "github.com/golang/protobuf/ptypes/timestamp"
)
// copyBackupWithMultiRegionEncryptionKey copies an existing backup to a given instance in same or different region, or in same or different project with multiple encryption keys.
func copyBackupWithMultiRegionEncryptionKey(w io.Writer, instancePath string, copyBackupId string, sourceBackupPath string, kmsKeyNames []string) error {
// instancePath := "projects/my-project/instances/my-instance"
// copyBackupId := "my-copy-backup"
// sourceBackupPath := "projects/my-project/instances/my-instance/backups/my-source-backup"
// kmsKeyNames := []string{"projects/my-project/locations/locations/<location1>/keyRings/<keyRing>/cryptoKeys/<keyId>",
// "projects/my-project/locations/locations/<location2>/keyRings/<keyRing>/cryptoKeys/<keyId>",
// "projects/my-project/locations/locations/<location3>/keyRings/<keyRing>/cryptoKeys/<keyId>",
// }
// Add timeout to context.
ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
defer cancel()
// Instantiate database admin client.
adminClient, err := database.NewDatabaseAdminClient(ctx)
if err != nil {
return fmt.Errorf("database.NewDatabaseAdminClient: %w", err)
}
defer adminClient.Close()
expireTime := time.Now().AddDate(0, 0, 14)
// Instantiate the request for performing copy backup operation.
copyBackupReq := adminpb.CopyBackupRequest{
Parent: instancePath,
BackupId: copyBackupId,
SourceBackup: sourceBackupPath,
ExpireTime: &pbt.Timestamp{Seconds: expireTime.Unix(), Nanos: int32(expireTime.Nanosecond())},
EncryptionConfig: &adminpb.CopyBackupEncryptionConfig{
EncryptionType: adminpb.CopyBackupEncryptionConfig_CUSTOMER_MANAGED_ENCRYPTION,
KmsKeyNames: kmsKeyNames,
},
}
// Start copying the backup.
copyBackupOp, err := adminClient.CopyBackup(ctx, ©BackupReq)
if err != nil {
return fmt.Errorf("adminClient.CopyBackup: %w", err)
}
// Wait for copy backup operation to complete.
fmt.Fprintf(w, "Waiting for backup copy %s/backups/%s to complete...\n", instancePath, copyBackupId)
copyBackup, err := copyBackupOp.Wait(ctx)
if err != nil {
return fmt.Errorf("copyBackup.Wait: %w", err)
}
// Check if long-running copyBackup operation is completed.
if !copyBackupOp.Done() {
return fmt.Errorf("backup %v could not be copied to %v", sourceBackupPath, copyBackupId)
}
// Get the name, create time, version time and backup size.
copyBackupCreateTime := time.Unix(copyBackup.CreateTime.Seconds, int64(copyBackup.CreateTime.Nanos))
copyBackupVersionTime := time.Unix(copyBackup.VersionTime.Seconds, int64(copyBackup.VersionTime.Nanos))
fmt.Fprintf(w,
"Backup %s of size %d bytes was created at %s with version time %s using multi-region encryption keys\n",
copyBackup.Name,
copyBackup.SizeBytes,
copyBackupCreateTime.Format(time.RFC3339),
copyBackupVersionTime.Format(time.RFC3339))
return nil
}
Java
To copy a CMEK-enabled backup in a multi-region instance configuration:
import com.google.cloud.Timestamp;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.Backup;
import com.google.spanner.admin.database.v1.BackupName;
import com.google.spanner.admin.database.v1.CopyBackupEncryptionConfig;
import com.google.spanner.admin.database.v1.CopyBackupEncryptionConfig.EncryptionType;
import com.google.spanner.admin.database.v1.CopyBackupRequest;
import com.google.spanner.admin.database.v1.InstanceName;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class CopyBackupWithMultiRegionEncryptionKey {
static void copyBackupWithMultiRegionEncryptionKey() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
String sourceBackupId = "my-backup";
String destinationBackupId = "my-destination-backup";
String[] kmsKeyNames =
new String[] {
"projects/" + projectId + "/locations/<location1>/keyRings/<keyRing>/cryptoKeys/<keyId>",
"projects/" + projectId + "/locations/<location2>/keyRings/<keyRing>/cryptoKeys/<keyId>",
"projects/" + projectId + "/locations/<location3>/keyRings/<keyRing>/cryptoKeys/<keyId>"
};
try (Spanner spanner =
SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
copyBackupWithMultiRegionEncryptionKey(
databaseAdminClient,
projectId,
instanceId,
sourceBackupId,
destinationBackupId,
kmsKeyNames);
}
}
static void copyBackupWithMultiRegionEncryptionKey(
DatabaseAdminClient databaseAdminClient,
String projectId,
String instanceId,
String sourceBackupId,
String destinationBackupId,
String[] kmsKeyNames) {
Timestamp expireTime =
Timestamp.ofTimeMicroseconds(
TimeUnit.MICROSECONDS.convert(
System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14), TimeUnit.MILLISECONDS));
// Initiate the request which returns an OperationFuture.
System.out.println("Copying backup [" + destinationBackupId + "]...");
CopyBackupRequest request =
CopyBackupRequest.newBuilder()
.setParent(InstanceName.of(projectId, instanceId).toString())
.setBackupId(destinationBackupId)
.setSourceBackup(BackupName.of(projectId, instanceId, sourceBackupId).toString())
.setExpireTime(expireTime.toProto())
.setEncryptionConfig(
CopyBackupEncryptionConfig.newBuilder()
.setEncryptionType(EncryptionType.CUSTOMER_MANAGED_ENCRYPTION)
.addAllKmsKeyNames(ImmutableList.copyOf(kmsKeyNames))
.build())
.build();
Backup destinationBackup;
try {
// Creates a copy of an existing backup.
// Wait for the backup operation to complete.
destinationBackup = databaseAdminClient.copyBackupAsync(request).get();
System.out.println("Copied backup [" + destinationBackup.getName() + "]");
} catch (ExecutionException e) {
throw (SpannerException) e.getCause();
} catch (InterruptedException e) {
throw SpannerExceptionFactory.propagateInterrupt(e);
}
// Load the metadata of the new backup from the server.
destinationBackup = databaseAdminClient.getBackup(destinationBackup.getName());
System.out.println(
String.format(
"Backup %s of size %d bytes was copied at %s for version of database at %s",
destinationBackup.getName(),
destinationBackup.getSizeBytes(),
OffsetDateTime.ofInstant(
Instant.ofEpochSecond(
destinationBackup.getCreateTime().getSeconds(),
destinationBackup.getCreateTime().getNanos()),
ZoneId.systemDefault()),
OffsetDateTime.ofInstant(
Instant.ofEpochSecond(
destinationBackup.getVersionTime().getSeconds(),
destinationBackup.getVersionTime().getNanos()),
ZoneId.systemDefault())));
}
}
Node.js
To copy a CMEK-enabled backup in a multi-region instance configuration:
/**
* TODO(developer): Uncomment these variables before running the sample.
*/
// const instanceId = 'my-instance';
// const backupId = 'my-backup',
// const sourceBackupPath = 'projects/my-project-id/instances/my-source-instance/backups/my-source-backup',
// const projectId = 'my-project-id';
// const kmsKeyNames =
// 'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key1,
// projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key2';
// Imports the Google Cloud Spanner client library
const {Spanner} = require('@google-cloud/spanner');
const {PreciseDate} = require('@google-cloud/precise-date');
// Creates a client
const spanner = new Spanner({
projectId: projectId,
});
// Gets a reference to a Cloud Spanner Database Admin Client object
const databaseAdminClient = spanner.getDatabaseAdminClient();
async function spannerCopyBackupWithMultipleKmsKeys() {
// Expire copy backup 14 days in the future
const expireTime = Spanner.timestamp(
Date.now() + 1000 * 60 * 60 * 24 * 14,
).toStruct();
// Copy the source backup
try {
console.log(`Creating copy of the source backup ${sourceBackupPath}.`);
const [operation] = await databaseAdminClient.copyBackup({
parent: databaseAdminClient.instancePath(projectId, instanceId),
sourceBackup: sourceBackupPath,
backupId: backupId,
expireTime: expireTime,
kmsKeyNames: kmsKeyNames.split(','),
});
console.log(
`Waiting for backup copy ${databaseAdminClient.backupPath(
projectId,
instanceId,
backupId,
)} to complete...`,
);
await operation.promise();
// Verify the copy backup is ready
const [copyBackup] = await databaseAdminClient.getBackup({
name: databaseAdminClient.backupPath(projectId, instanceId, backupId),
});
if (copyBackup.state === 'READY') {
console.log(
`Backup copy ${copyBackup.name} of size ` +
`${copyBackup.sizeBytes} bytes was created at ` +
`${new PreciseDate(copyBackup.createTime).toISOString()} ` +
'with version time ' +
`${new PreciseDate(copyBackup.versionTime).toISOString()}`,
);
} else {
console.error('ERROR: Copy of backup is not ready.');
}
} catch (err) {
console.error('ERROR:', err);
}
}
spannerCopyBackupWithMultipleKmsKeys();
PHP
To copy a CMEK-enabled backup in a multi-region instance configuration:
use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient;
use Google\Cloud\Spanner\Admin\Database\V1\CopyBackupRequest;
use Google\Cloud\Spanner\Admin\Database\V1\CopyBackupEncryptionConfig;
use Google\Protobuf\Timestamp;
/**
* Copy a MR CMEK backup.
* Example:
* ```
* copy_backup_with_mr_cmek($projectId, $instanceId, $sourceBackupId, $backupId, $kmsKeyNames);
* ```
* @param string $projectId The Google Cloud project ID.
* @param string $instanceId The Spanner instance ID.
* @param string $sourceBackupId The Spanner source backup ID.
* @param string $backupId The Spanner backup ID.
* @param string[] $kmsKeyNames The KMS keys used for encryption.
*/
/**
* Create a copy MR CMEK backup from another source backup.
* Example:
* ```
* copy_backup_with_mr_cmek($projectId, $destInstanceId, $destBackupId, $sourceInstanceId, $sourceBackupId, $kmsKeyNames);
* ```
*
* @param string $projectId The Google Cloud project ID.
* @param string $destInstanceId The Spanner instance ID where the copy backup will reside.
* @param string $destBackupId The Spanner backup ID of the new backup to be created.
* @param string $sourceInstanceId The Spanner instance ID of the source backup.
* @param string $sourceBackupId The Spanner backup ID of the source.
* @param string[] $kmsKeyNames The KMS keys used for encryption.
*/
function copy_backup_with_mr_cmek(
string $projectId,
string $destInstanceId,
string $destBackupId,
string $sourceInstanceId,
string $sourceBackupId,
array $kmsKeyNames
): void {
$databaseAdminClient = new DatabaseAdminClient();
$destInstanceFullName = DatabaseAdminClient::instanceName($projectId, $destInstanceId);
$expireTime = new Timestamp();
$expireTime->setSeconds((new \DateTime('+8 hours'))->getTimestamp());
$sourceBackupFullName = DatabaseAdminClient::backupName($projectId, $sourceInstanceId, $sourceBackupId);
$request = new CopyBackupRequest([
'source_backup' => $sourceBackupFullName,
'parent' => $destInstanceFullName,
'backup_id' => $destBackupId,
'expire_time' => $expireTime,
'encryption_config' => new CopyBackupEncryptionConfig([
'kms_key_names' => $kmsKeyNames,
'encryption_type' => CopyBackupEncryptionConfig\EncryptionType::CUSTOMER_MANAGED_ENCRYPTION
])
]);
$operationResponse = $databaseAdminClient->copyBackup($request);
$operationResponse->pollUntilComplete();
if (!$operationResponse->operationSucceeded()) {
$error = $operationResponse->getError();
printf('Backup not created due to error: %s.' . PHP_EOL, $error->getMessage());
return;
}
$destBackupInfo = $operationResponse->getResult();
$kmsKeyVersions = [];
foreach ($destBackupInfo->getEncryptionInformation() as $encryptionInfo) {
$kmsKeyVersions[] = $encryptionInfo->getKmsKeyVersion();
}
printf(
'Backup %s of size %d bytes was copied at %d from the source backup %s using encryption keys %s' . PHP_EOL,
basename($destBackupInfo->getName()),
$destBackupInfo->getSizeBytes(),
$destBackupInfo->getCreateTime()->getSeconds(),
$sourceBackupId,
print_r($kmsKeyVersions, true)
);
printf('Version time of the copied backup: %d' . PHP_EOL, $destBackupInfo->getVersionTime()->getSeconds());
}
Python
To copy a CMEK-enabled backup in a multi-region instance configuration:
def copy_backup_with_multiple_kms_keys(
instance_id, backup_id, source_backup_path, kms_key_names
):
"""Copies a backup."""
from google.cloud.spanner_admin_database_v1.types import backup as backup_pb
from google.cloud.spanner_admin_database_v1 import CopyBackupEncryptionConfig
spanner_client = spanner.Client()
database_admin_api = spanner_client.database_admin_api
encryption_config = {
"encryption_type": CopyBackupEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION,
"kms_key_names": kms_key_names,
}
# Create a backup object and wait for copy backup operation to complete.
expire_time = datetime.utcnow() + timedelta(days=14)
request = backup_pb.CopyBackupRequest(
parent=database_admin_api.instance_path(spanner_client.project, instance_id),
backup_id=backup_id,
source_backup=source_backup_path,
expire_time=expire_time,
encryption_config=encryption_config,
)
operation = database_admin_api.copy_backup(request)
# Wait for backup operation to complete.
copy_backup = operation.result(2100)
# Verify that the copy backup is ready.
assert copy_backup.state == backup_pb.Backup.State.READY
print(
"Backup {} of size {} bytes was created at {} with version time {} using encryption keys {}".format(
copy_backup.name,
copy_backup.size_bytes,
copy_backup.create_time,
copy_backup.version_time,
copy_backup.encryption_information,
)
)
Ruby
To copy a CMEK-enabled backup in a multi-region instance configuration:
# project_id = "Your Google Cloud project ID"
# instance_id = "The ID of the destination instance that will contain the backup copy"
# backup_id = "The ID of the backup copy"
# source_backup = "The source backup to be copied"
# kms_key_names = ["key1", "key2", "key3"]
require "google/cloud/spanner"
require "google/cloud/spanner/admin/database"
database_admin_client = Google::Cloud::Spanner::Admin::Database.database_admin
instance_path = database_admin_client.instance_path(
project: project_id, instance: instance_id
)
backup_path = database_admin_client.backup_path project: project_id,
instance: instance_id,
backup: backup_id
source_backup = database_admin_client.backup_path project: project_id,
instance: instance_id,
backup: source_backup_id
expire_time = Time.now + (14 * 24 * 3600) # 14 days from now
encryption_config = {
encryption_type: :CUSTOMER_MANAGED_ENCRYPTION,
kms_key_names: kms_key_names
}
job = database_admin_client.copy_backup parent: instance_path,
backup_id: backup_id,
source_backup: source_backup,
expire_time: expire_time,
encryption_config: encryption_config
puts "Copy backup operation in progress"
job.wait_until_done!
backup = database_admin_client.get_backup name: backup_path
puts "Backup #{backup_id} of size #{backup.size_bytes} bytes was copied at " \
"#{backup.create_time} from #{source_backup} for version " \
"#{backup.version_time} using encryption keys #{kms_key_names}"
Restore from a backup
You can restore a backup of a Spanner database into a new database. By default, databases that are restored from a backup use the same encryption configuration as the backup itself, but you can override this behavior by specifying a different encryption configuration for the restored database. If the backup is protected by CMEK, the key version that was used to create the backup must be available so that it can be decrypted.
Console
Use the console to restore a backup in a regional instance configuration.
In the Google Cloud console, go to the Instances page.
Click the instance containing the database you want to restore.
Click the database.
In the navigation pane, click Backup/Restore.
In the Backups table, select Actions for your backup and click Restore.
Select the instance to restore and name the restored database.
Optional: If you want to use a different encryption configuration with your restored database, click the slider next to Use existing encryption.
a. Select Cloud KMS key.
a. Select a key from the drop-down list.
The list of keys is limited to the current Google Cloud project. To use a key from a different Google Cloud project, create the database using gcloud CLI instead of the Google Cloud console.
Click Restore.
gcloud
To restore a backup, with a new encryption configuration, run the following
gcloud spanner databases restore
command:
gcloud spanner databases restore --async \
--project=SPANNER_PROJECT_ID \
--destination-instance=DESTINATION_INSTANCE_ID \
--destination-database=DESTINATION_DATABASE_ID \
--source-instance=SOURCE_INSTANCE_ID \
--source-backup=SOURCE_BACKUP_NAME
To verify that the restored database is CMEK encrypted:
gcloud spanner databases describe DATABASE \
--project=SPANNER_PROJECT_ID \
--instance=INSTANCE_ID
For more information, see Restore from a backup.
Client libraries
C#
To restore a CMEK-enabled backup in a regional instance configuration:
using Google.Cloud.Spanner.Admin.Database.V1;
using Google.Cloud.Spanner.Common.V1;
using System;
using System.Threading.Tasks;
public class RestoreDatabaseWithEncryptionAsyncSample
{
public async Task<Database> RestoreDatabaseWithEncryptionAsync(string projectId, string instanceId, string databaseId, string backupId, CryptoKeyName kmsKeyName)
{
// Create a DatabaseAdminClient instance.
DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.Create();
// Create the RestoreDatabaseRequest with encryption configuration.
RestoreDatabaseRequest request = new RestoreDatabaseRequest
{
ParentAsInstanceName = InstanceName.FromProjectInstance(projectId, instanceId),
DatabaseId = databaseId,
BackupAsBackupName = BackupName.FromProjectInstanceBackup(projectId, instanceId, backupId),
EncryptionConfig = new RestoreDatabaseEncryptionConfig
{
EncryptionType = RestoreDatabaseEncryptionConfig.Types.EncryptionType.CustomerManagedEncryption,
KmsKeyNameAsCryptoKeyName = kmsKeyName,
}
};
// Execute the RestoreDatabase request.
var operation = await databaseAdminClient.RestoreDatabaseAsync(request);
Console.WriteLine("Waiting for the operation to finish.");
// Poll until the returned long-running operation is complete.
var completedResponse = await operation.PollUntilCompletedAsync();
if (completedResponse.IsFaulted)
{
Console.WriteLine($"Error while restoring database: {completedResponse.Exception}");
throw completedResponse.Exception;
}
var database = completedResponse.Result;
var restoreInfo = database.RestoreInfo;
Console.WriteLine($"Database {restoreInfo.BackupInfo.SourceDatabase} " +
$"restored to {database.Name} " +
$"from backup {restoreInfo.BackupInfo.Backup} " +
$"using encryption key {database.EncryptionConfig.KmsKeyName}");
return database;
}
}
To restore a CMEK-enabled backup in a multi-region instance configuration:
using Google.Cloud.Spanner.Admin.Database.V1;
using Google.Cloud.Spanner.Common.V1;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class RestoreDatabaseWithMultiRegionEncryptionAsyncSample
{
public async Task<Database> RestoreDatabaseWithMultiRegionEncryptionAsync(string projectId, string instanceId, string databaseId, string backupId, IEnumerable<CryptoKeyName> kmsKeyNames)
{
// Create a DatabaseAdminClient instance.
DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.Create();
// Create the RestoreDatabaseRequest with encryption configuration.
RestoreDatabaseRequest request = new RestoreDatabaseRequest
{
ParentAsInstanceName = InstanceName.FromProjectInstance(projectId, instanceId),
DatabaseId = databaseId,
BackupAsBackupName = BackupName.FromProjectInstanceBackup(projectId, instanceId, backupId),
EncryptionConfig = new RestoreDatabaseEncryptionConfig
{
EncryptionType = RestoreDatabaseEncryptionConfig.Types.EncryptionType.CustomerManagedEncryption,
KmsKeyNamesAsCryptoKeyNames = { kmsKeyNames },
}
};
// Execute the RestoreDatabase request.
var operation = await databaseAdminClient.RestoreDatabaseAsync(request);
Console.WriteLine("Waiting for the operation to finish.");
// Poll until the returned long-running operation is complete.
var completedResponse = await operation.PollUntilCompletedAsync();
if (completedResponse.IsFaulted)
{
Console.WriteLine($"Error while restoring database: {completedResponse.Exception}");
throw completedResponse.Exception;
}
var database = completedResponse.Result;
var restoreInfo = database.RestoreInfo;
Console.WriteLine($"Database {restoreInfo.BackupInfo.SourceDatabase} restored to {database.Name} from backup {restoreInfo.BackupInfo.Backup} using encryption keys {string.Join(", ", kmsKeyNames)}");
return database;
}
}
C++
To restore a CMEK-enabled backup in a regional instance configuration:
void RestoreDatabaseWithEncryptionKey(
google::cloud::spanner_admin::DatabaseAdminClient client,
std::string const& project_id, std::string const& instance_id,
std::string const& database_id, std::string const& backup_id,
google::cloud::KmsKeyName const& encryption_key) {
google::cloud::spanner::Database database(project_id, instance_id,
database_id);
google::cloud::spanner::Backup backup(database.instance(), backup_id);
google::spanner::admin::database::v1::RestoreDatabaseRequest request;
request.set_parent(database.instance().FullName());
request.set_database_id(database.database_id());
request.set_backup(backup.FullName());
request.mutable_encryption_config()->set_encryption_type(
google::spanner::admin::database::v1::RestoreDatabaseEncryptionConfig::
CUSTOMER_MANAGED_ENCRYPTION);
request.mutable_encryption_config()->set_kms_key_name(
encryption_key.FullName());
auto restored_db = client.RestoreDatabase(request).get();
if (!restored_db) throw std::move(restored_db).status();
std::cout << "Database";
if (restored_db->restore_info().source_type() ==
google::spanner::admin::database::v1::BACKUP) {
auto const& backup_info = restored_db->restore_info().backup_info();
std::cout << " " << backup_info.source_database() << " as of "
<< google::cloud::spanner::MakeTimestamp(
backup_info.version_time())
.value();
}
std::cout << " restored to " << restored_db->name();
std::cout << " from backup " << backup.FullName();
std::cout << " using encryption key " << encryption_key.FullName();
std::cout << ".\n";
}
To restore a CMEK-enabled backup in a multi-region instance configuration:
void RestoreDatabaseWithMRCMEK(
google::cloud::spanner_admin::DatabaseAdminClient client,
BackupIdentifier const& src, std::string const& database_id,
std::vector<google::cloud::KmsKeyName> const& encryption_keys) {
google::cloud::spanner::Database database(src.project_id, src.instance_id,
database_id);
google::cloud::spanner::Backup backup(database.instance(), src.backup_id);
google::spanner::admin::database::v1::RestoreDatabaseRequest request;
request.set_parent(database.instance().FullName());
request.set_database_id(database.database_id());
request.set_backup(backup.FullName());
request.mutable_encryption_config()->set_encryption_type(
google::spanner::admin::database::v1::RestoreDatabaseEncryptionConfig::
CUSTOMER_MANAGED_ENCRYPTION);
for (google::cloud::KmsKeyName const& encryption_key : encryption_keys) {
request.mutable_encryption_config()->add_kms_key_names(
encryption_key.FullName());
}
auto restored_db = client.RestoreDatabase(request).get();
if (!restored_db) throw std::move(restored_db).status();
std::cout << "Database";
if (restored_db->restore_info().source_type() ==
google::spanner::admin::database::v1::BACKUP) {
auto const& backup_info = restored_db->restore_info().backup_info();
std::cout << " " << backup_info.source_database() << " as of "
<< google::cloud::spanner::MakeTimestamp(
backup_info.version_time())
.value();
}
std::cout << " restored to " << restored_db->name();
std::cout << " from backup " << backup.FullName();
PrintKmsKeys(encryption_keys);
}
Go
To restore a CMEK-enabled backup in a regional instance configuration:
import (
"context"
"fmt"
"io"
"regexp"
database "cloud.google.com/go/spanner/admin/database/apiv1"
adminpb "cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
)
func restoreBackupWithCustomerManagedEncryptionKey(ctx context.Context, w io.Writer, db, backupID, kmsKeyName string) error {
// db = `projects/<project>/instances/<instance-id>/database/<database-id>`
// backupID = `my-backup-id`
// kmsKeyName = `projects/<project>/locations/<location>/keyRings/<key_ring>/cryptoKeys/<kms_key_name>`
matches := regexp.MustCompile("^(.*)/databases/(.*)$").FindStringSubmatch(db)
if matches == nil || len(matches) != 3 {
return fmt.Errorf("restoreBackupWithCustomerManagedEncryptionKey: invalid database id %q", db)
}
instanceName := matches[1]
databaseID := matches[2]
backupName := instanceName + "/backups/" + backupID
adminClient, err := database.NewDatabaseAdminClient(ctx)
if err != nil {
return fmt.Errorf("restoreBackupWithCustomerManagedEncryptionKey.NewDatabaseAdminClient: %w", err)
}
defer adminClient.Close()
// Restore a database from a backup using a Customer Managed Encryption Key.
restoreOp, err := adminClient.RestoreDatabase(ctx, &adminpb.RestoreDatabaseRequest{
Parent: instanceName,
DatabaseId: databaseID,
Source: &adminpb.RestoreDatabaseRequest_Backup{
Backup: backupName,
},
EncryptionConfig: &adminpb.RestoreDatabaseEncryptionConfig{
EncryptionType: adminpb.RestoreDatabaseEncryptionConfig_CUSTOMER_MANAGED_ENCRYPTION,
KmsKeyName: kmsKeyName,
},
})
if err != nil {
return fmt.Errorf("restoreBackupWithCustomerManagedEncryptionKey.RestoreDatabase: %w", err)
}
// Wait for restore operation to complete.
restoredDatabase, err := restoreOp.Wait(ctx)
if err != nil {
return fmt.Errorf("restoreBackupWithCustomerManagedEncryptionKey.Wait: %w", err)
}
// Get the information from the newly restored database.
backupInfo := restoredDatabase.RestoreInfo.GetBackupInfo()
fmt.Fprintf(w, "Database %s restored from backup %s using encryption key %s\n",
backupInfo.SourceDatabase,
backupInfo.Backup,
restoredDatabase.EncryptionConfig.KmsKeyName)
return nil
}
To restore a CMEK-enabled backup in a multi-region instance configuration:
import (
"context"
"fmt"
"io"
database "cloud.google.com/go/spanner/admin/database/apiv1"
adminpb "cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
)
// restoreBackupWithCustomerManagedMultiRegionEncryptionKey restores a database from a backup using a Customer Managed Multi-Region Encryption Key.
func restoreBackupWithCustomerManagedMultiRegionEncryptionKey(ctx context.Context, w io.Writer, instName, databaseID string, backupID string, kmsKeyNames []string) error {
// instName = `projects/my-project/instances/my-instance`
// databaseID = `my-database`
// backupID = `my-backup-id`
// kmsKeyNames := []string{"projects/my-project/locations/locations/<location1>/keyRings/<keyRing>/cryptoKeys/<keyId>",
// "projects/my-project/locations/locations/<location2>/keyRings/<keyRing>/cryptoKeys/<keyId>",
// "projects/my-project/locations/locations/<location3>/keyRings/<keyRing>/cryptoKeys/<keyId>",
// }
adminClient, err := database.NewDatabaseAdminClient(ctx)
if err != nil {
return fmt.Errorf("restoreBackupWithCustomerManagedMultiRegionEncryptionKey.NewDatabaseAdminClient: %w", err)
}
defer adminClient.Close()
// Restore a database from a backup using a Customer Managed Encryption Key.
restoreOp, err := adminClient.RestoreDatabase(ctx, &adminpb.RestoreDatabaseRequest{
Parent: instName,
DatabaseId: databaseID,
Source: &adminpb.RestoreDatabaseRequest_Backup{
Backup: fmt.Sprintf("%s/backups/%s", instName, backupID),
},
EncryptionConfig: &adminpb.RestoreDatabaseEncryptionConfig{
EncryptionType: adminpb.RestoreDatabaseEncryptionConfig_CUSTOMER_MANAGED_ENCRYPTION,
KmsKeyNames: kmsKeyNames,
},
})
if err != nil {
return fmt.Errorf("restoreBackupWithCustomerManagedMultiRegionEncryptionKey.RestoreDatabase: %w", err)
}
// Wait for restore operation to complete.
restoredDatabase, err := restoreOp.Wait(ctx)
if err != nil {
return fmt.Errorf("restoreBackupWithCustomerManagedMultiRegionEncryptionKey.Wait: %w", err)
}
// Get the information from the newly restored database.
backupInfo := restoredDatabase.RestoreInfo.GetBackupInfo()
fmt.Fprintf(w, "Database %s restored from backup %s using multi-region encryption keys %q\n",
backupInfo.SourceDatabase,
backupInfo.Backup,
restoredDatabase.EncryptionConfig.GetKmsKeyNames())
return nil
}
Java
To restore a CMEK-enabled backup in a regional instance configuration:
import static com.google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.spanner.admin.database.v1.BackupName;
import com.google.spanner.admin.database.v1.Database;
import com.google.spanner.admin.database.v1.InstanceName;
import com.google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig;
import com.google.spanner.admin.database.v1.RestoreDatabaseRequest;
import java.util.concurrent.ExecutionException;
public class RestoreBackupWithEncryptionKey {
static void restoreBackupWithEncryptionKey() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
String databaseId = "my-database";
String backupId = "my-backup";
String kmsKeyName =
"projects/" + projectId + "/locations/<location>/keyRings/<keyRing>/cryptoKeys/<keyId>";
try (Spanner spanner =
SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
DatabaseAdminClient adminClient = spanner.createDatabaseAdminClient()) {
restoreBackupWithEncryptionKey(
adminClient,
projectId,
instanceId,
backupId,
databaseId,
kmsKeyName);
}
}
static Void restoreBackupWithEncryptionKey(DatabaseAdminClient adminClient,
String projectId, String instanceId, String backupId, String restoreId, String kmsKeyName) {
RestoreDatabaseRequest request =
RestoreDatabaseRequest.newBuilder()
.setParent(InstanceName.of(projectId, instanceId).toString())
.setDatabaseId(restoreId)
.setBackup(BackupName.of(projectId, instanceId, backupId).toString())
.setEncryptionConfig(RestoreDatabaseEncryptionConfig.newBuilder()
.setEncryptionType(CUSTOMER_MANAGED_ENCRYPTION).setKmsKeyName(kmsKeyName)).build();
Database database;
try {
System.out.println("Waiting for operation to complete...");
database = adminClient.restoreDatabaseAsync(request).get();
;
} catch (ExecutionException e) {
// If the operation failed during execution, expose the cause.
throw SpannerExceptionFactory.asSpannerException(e.getCause());
} catch (InterruptedException e) {
// Throw when a thread is waiting, sleeping, or otherwise occupied,
// and the thread is interrupted, either before or during the activity.
throw SpannerExceptionFactory.propagateInterrupt(e);
}
System.out.printf(
"Database %s restored to %s from backup %s using encryption key %s%n",
database.getRestoreInfo().getBackupInfo().getSourceDatabase(),
database.getName(),
database.getRestoreInfo().getBackupInfo().getBackup(),
database.getEncryptionConfig().getKmsKeyName()
);
return null;
}
}
To restore a CMEK-enabled backup in a multi-region instance configuration:
import static com.google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.BackupName;
import com.google.spanner.admin.database.v1.Database;
import com.google.spanner.admin.database.v1.InstanceName;
import com.google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig;
import com.google.spanner.admin.database.v1.RestoreDatabaseRequest;
import java.util.concurrent.ExecutionException;
public class RestoreBackupWithMultiRegionEncryptionKey {
static void restoreBackupWithMultiRegionEncryptionKey() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
String databaseId = "my-database";
String backupId = "my-backup";
String[] kmsKeyNames =
new String[] {
"projects/" + projectId + "/locations/<location1>/keyRings/<keyRing>/cryptoKeys/<keyId>",
"projects/" + projectId + "/locations/<location2>/keyRings/<keyRing>/cryptoKeys/<keyId>",
"projects/" + projectId + "/locations/<location3>/keyRings/<keyRing>/cryptoKeys/<keyId>"
};
try (Spanner spanner =
SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
DatabaseAdminClient adminClient = spanner.createDatabaseAdminClient()) {
restoreBackupWithMultiRegionEncryptionKey(
adminClient, projectId, instanceId, backupId, databaseId, kmsKeyNames);
}
}
static Void restoreBackupWithMultiRegionEncryptionKey(
DatabaseAdminClient adminClient,
String projectId,
String instanceId,
String backupId,
String restoreId,
String[] kmsKeyNames) {
RestoreDatabaseRequest request =
RestoreDatabaseRequest.newBuilder()
.setParent(InstanceName.of(projectId, instanceId).toString())
.setDatabaseId(restoreId)
.setBackup(BackupName.of(projectId, instanceId, backupId).toString())
.setEncryptionConfig(
RestoreDatabaseEncryptionConfig.newBuilder()
.setEncryptionType(CUSTOMER_MANAGED_ENCRYPTION)
.addAllKmsKeyNames(ImmutableList.copyOf(kmsKeyNames)))
.build();
Database database;
try {
System.out.println("Waiting for operation to complete...");
database = adminClient.restoreDatabaseAsync(request).get();
;
} catch (ExecutionException e) {
// If the operation failed during execution, expose the cause.
throw SpannerExceptionFactory.asSpannerException(e.getCause());
} catch (InterruptedException e) {
// Throw when a thread is waiting, sleeping, or otherwise occupied,
// and the thread is interrupted, either before or during the activity.
throw SpannerExceptionFactory.propagateInterrupt(e);
}
System.out.printf(
"Database %s restored to %s from backup %s using encryption keys %s%n",
database.getRestoreInfo().getBackupInfo().getSourceDatabase(),
database.getName(),
database.getRestoreInfo().getBackupInfo().getBackup(),
database.getEncryptionConfig().getKmsKeyNamesList());
return null;
}
}
Node.js
To restore a CMEK-enabled backup in a regional instance configuration:
// Imports the Google Cloud client library and precise date library
const {Spanner} = require('@google-cloud/spanner');
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const backupId = 'my-backup';
// const keyName =
// 'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key';
// Creates a client
const spanner = new Spanner({
projectId: projectId,
});
// Gets a reference to a Cloud Spanner Database Admin Client object
const databaseAdminClient = spanner.getDatabaseAdminClient();
// Restore the database
console.log(
`Restoring database ${databaseAdminClient.databasePath(
projectId,
instanceId,
databaseId,
)} from backup ${backupId}.`,
);
const [restoreOperation] = await databaseAdminClient.restoreDatabase({
parent: databaseAdminClient.instancePath(projectId, instanceId),
databaseId: databaseId,
backup: databaseAdminClient.backupPath(projectId, instanceId, backupId),
encryptionConfig: {
encryptionType: 'CUSTOMER_MANAGED_ENCRYPTION',
kmsKeyName: keyName,
},
});
// Wait for restore to complete
console.log('Waiting for database restore to complete...');
await restoreOperation.promise();
console.log('Database restored from backup.');
const [metadata] = await databaseAdminClient.getDatabase({
name: databaseAdminClient.databasePath(projectId, instanceId, databaseId),
});
console.log(
`Database ${metadata.restoreInfo.backupInfo.sourceDatabase} was restored ` +
`to ${databaseId} from backup ${metadata.restoreInfo.backupInfo.backup} ` +
`using encryption key ${metadata.encryptionConfig.kmsKeyName}.`,
);
To restore a CMEK-enabled backup in a multi-region instance configuration:
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const backupId = 'my-backup';
// const kmsKeyNames =
// 'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key1,
// projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key2';
// Imports the Google Cloud client library and precise date library
const {Spanner} = require('@google-cloud/spanner');
// Creates a client
const spanner = new Spanner({
projectId: projectId,
});
// Gets a reference to a Cloud Spanner Database Admin Client object
const databaseAdminClient = spanner.getDatabaseAdminClient();
async function restoreBackupWithMultipleKmsKeys() {
// Restore the database
console.log(
`Restoring database ${databaseAdminClient.databasePath(
projectId,
instanceId,
databaseId,
)} from backup ${backupId}.`,
);
const [restoreOperation] = await databaseAdminClient.restoreDatabase({
parent: databaseAdminClient.instancePath(projectId, instanceId),
databaseId: databaseId,
backup: databaseAdminClient.backupPath(projectId, instanceId, backupId),
encryptionConfig: {
encryptionType: 'CUSTOMER_MANAGED_ENCRYPTION',
kmsKeyNames: kmsKeyNames.split(','),
},
});
// Wait for restore to complete
console.log('Waiting for database restore to complete...');
await restoreOperation.promise();
console.log('Database restored from backup.');
const [metadata] = await databaseAdminClient.getDatabase({
name: databaseAdminClient.databasePath(projectId, instanceId, databaseId),
});
console.log(
`Database ${metadata.restoreInfo.backupInfo.sourceDatabase} was restored ` +
`to ${databaseId} from backup ${metadata.restoreInfo.backupInfo.backup} ` +
`using encryption key ${metadata.encryptionConfig.kmsKeyNames}.`,
);
}
restoreBackupWithMultipleKmsKeys();
PHP
To restore a CMEK-enabled backup in a regional instance configuration:
use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient;
use Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseEncryptionConfig;
use Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseRequest;
/**
* Restore a database from a backup.
* Example:
* ```
* restore_backup_with_encryption_key($projectId, $instanceId, $databaseId, $backupId, $kmsKeyName);
* ```
* @param string $projectId The Google Cloud project ID.
* @param string $instanceId The Spanner instance ID.
* @param string $databaseId The Spanner database ID.
* @param string $backupId The Spanner backup ID.
* @param string $kmsKeyName The KMS key used for encryption.
*/
function restore_backup_with_encryption_key(
string $projectId,
string $instanceId,
string $databaseId,
string $backupId,
string $kmsKeyName
): void {
$databaseAdminClient = new DatabaseAdminClient();
$instanceFullName = DatabaseAdminClient::instanceName($projectId, $instanceId);
$backupFullName = DatabaseAdminClient::backupName($projectId, $instanceId, $backupId);
$request = new RestoreDatabaseRequest([
'parent' => $instanceFullName,
'database_id' => $databaseId,
'backup' => $backupFullName,
'encryption_config' => new RestoreDatabaseEncryptionConfig([
'kms_key_name' => $kmsKeyName,
'encryption_type' => RestoreDatabaseEncryptionConfig\EncryptionType::CUSTOMER_MANAGED_ENCRYPTION
])
]);
// Create restore operation
$operation = $databaseAdminClient->restoreDatabase($request);
print('Waiting for operation to complete...' . PHP_EOL);
$operation->pollUntilComplete();
// Reload new database and get restore info
$database = $operation->operationSucceeded() ? $operation->getResult() : null;
$restoreInfo = $database->getRestoreInfo();
$backupInfo = $restoreInfo->getBackupInfo();
$sourceDatabase = $backupInfo->getSourceDatabase();
$sourceBackup = $backupInfo->getBackup();
$encryptionConfig = $database->getEncryptionConfig();
printf(
'Database %s restored from backup %s using encryption key %s' . PHP_EOL,
$sourceDatabase, $sourceBackup, $encryptionConfig->getKmsKeyName()
);
}
To restore a CMEK-enabled backup in a multi-region instance configuration:
use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient;
use Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseEncryptionConfig;
use Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseRequest;
/**
* Restore a MR CMEK database from a backup.
* Example:
* ```
* restore_backup_with_mr_cmek($projectId, $instanceId, $databaseId, $backupId, $kmsKeyNames);
* ```
* @param string $projectId The Google Cloud project ID.
* @param string $instanceId The Spanner instance ID.
* @param string $databaseId The Spanner database ID.
* @param string $backupId The Spanner backup ID.
* @param string[] $kmsKeyNames The KMS keys used for encryption.
*/
function restore_backup_with_mr_cmek(
string $projectId,
string $instanceId,
string $databaseId,
string $backupId,
array $kmsKeyNames
): void {
$databaseAdminClient = new DatabaseAdminClient();
$instanceFullName = DatabaseAdminClient::instanceName($projectId, $instanceId);
$backupFullName = DatabaseAdminClient::backupName($projectId, $instanceId, $backupId);
$request = new RestoreDatabaseRequest([
'parent' => $instanceFullName,
'database_id' => $databaseId,
'backup' => $backupFullName,
'encryption_config' => new RestoreDatabaseEncryptionConfig([
'kms_key_names' => $kmsKeyNames,
'encryption_type' => RestoreDatabaseEncryptionConfig\EncryptionType::CUSTOMER_MANAGED_ENCRYPTION
])
]);
// Create restore operation
$operation = $databaseAdminClient->restoreDatabase($request);
print('Waiting for operation to complete...' . PHP_EOL);
$operation->pollUntilComplete();
// Reload new database and get restore info
$database = $operation->operationSucceeded() ? $operation->getResult() : null;
$restoreInfo = $database->getRestoreInfo();
$backupInfo = $restoreInfo->getBackupInfo();
$sourceDatabase = $backupInfo->getSourceDatabase();
$sourceBackup = $backupInfo->getBackup();
$encryptionConfig = $database->getEncryptionConfig();
printf(
'Database %s restored from backup %s using encryption keys %s' . PHP_EOL,
$sourceDatabase, $sourceBackup, print_r($encryptionConfig->getKmsKeyNames(), true)
);
}
Python
To restore a CMEK-enabled backup in a regional instance configuration:
def restore_database_with_encryption_key(
instance_id, new_database_id, backup_id, kms_key_name
):
"""Restores a database from a backup using a Customer Managed Encryption Key (CMEK)."""
from google.cloud.spanner_admin_database_v1 import (
RestoreDatabaseEncryptionConfig,
RestoreDatabaseRequest,
)
spanner_client = spanner.Client()
database_admin_api = spanner_client.database_admin_api
# Start restoring an existing backup to a new database.
encryption_config = {
"encryption_type": RestoreDatabaseEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION,
"kms_key_name": kms_key_name,
}
request = RestoreDatabaseRequest(
parent=database_admin_api.instance_path(spanner_client.project, instance_id),
database_id=new_database_id,
backup=database_admin_api.backup_path(
spanner_client.project, instance_id, backup_id
),
encryption_config=encryption_config,
)
operation = database_admin_api.restore_database(request)
# Wait for restore operation to complete.
db = operation.result(1600)
# Newly created database has restore information.
restore_info = db.restore_info
print(
"Database {} restored to {} from backup {} with using encryption key {}.".format(
restore_info.backup_info.source_database,
new_database_id,
restore_info.backup_info.backup,
db.encryption_config.kms_key_name,
)
)
To restore a CMEK-enabled backup in a multi-region instance configuration:
def restore_database_with_multiple_kms_keys(
instance_id, new_database_id, backup_id, kms_key_names
):
"""Restores a database from a backup using a Customer Managed Encryption Key (CMEK)."""
from google.cloud.spanner_admin_database_v1 import (
RestoreDatabaseEncryptionConfig,
RestoreDatabaseRequest,
)
spanner_client = spanner.Client()
database_admin_api = spanner_client.database_admin_api
# Start restoring an existing backup to a new database.
encryption_config = {
"encryption_type": RestoreDatabaseEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION,
"kms_key_names": kms_key_names,
}
request = RestoreDatabaseRequest(
parent=database_admin_api.instance_path(spanner_client.project, instance_id),
database_id=new_database_id,
backup=database_admin_api.backup_path(
spanner_client.project, instance_id, backup_id
),
encryption_config=encryption_config,
)
operation = database_admin_api.restore_database(request)
# Wait for restore operation to complete.
db = operation.result(1600)
# Newly created database has restore information.
restore_info = db.restore_info
print(
"Database {} restored to {} from backup {} with using encryption key {}.".format(
restore_info.backup_info.source_database,
new_database_id,
restore_info.backup_info.backup,
db.encryption_config.kms_key_names,
)
)
Ruby
To restore a CMEK-enabled backup in a regional instance configuration:
# project_id = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID of where to restore"
# backup_id = "Your Spanner backup ID"
# kms_key_name = "Your backup encryption database KMS key"
require "google/cloud/spanner"
require "google/cloud/spanner/admin/database"
database_admin_client = Google::Cloud::Spanner::Admin::Database.database_admin
instance_path = database_admin_client.instance_path project: project_id, instance: instance_id
db_path = database_admin_client.database_path project: project_id,
instance: instance_id,
database: database_id
backup_path = database_admin_client.backup_path project: project_id,
instance: instance_id,
backup: backup_id
encryption_config = {
encryption_type: :CUSTOMER_MANAGED_ENCRYPTION,
kms_key_name: kms_key_name
}
job = database_admin_client.restore_database parent: instance_path,
database_id: database_id,
backup: backup_path,
encryption_config: encryption_config
puts "Waiting for restore backup operation to complete"
job.wait_until_done!
database = database_admin_client.get_database name: db_path
restore_info = database.restore_info
puts "Database #{restore_info.backup_info.source_database} was restored to #{database_id} from backup #{restore_info.backup_info.backup} using encryption key #{database.encryption_config.kms_key_name}"
To restore a CMEK-enabled backup in a multi-region instance configuration:
# project_id = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID of where to restore"
# backup_id = "Your Spanner backup ID"
# kms_key_names = ["key1", "key2", "key3"]
require "google/cloud/spanner"
require "google/cloud/spanner/admin/database"
database_admin_client = Google::Cloud::Spanner::Admin::Database.database_admin
instance_path = database_admin_client.instance_path(
project: project_id, instance: instance_id
)
db_path = database_admin_client.database_path project: project_id,
instance: instance_id,
database: database_id
backup_path = database_admin_client.backup_path project: project_id,
instance: instance_id,
backup: backup_id
encryption_config = {
encryption_type: :CUSTOMER_MANAGED_ENCRYPTION,
kms_key_names: kms_key_names
}
job = database_admin_client.restore_database(
parent: instance_path,
database_id: database_id,
backup: backup_path,
encryption_config: encryption_config
)
puts "Waiting for restore backup operation to complete"
job.wait_until_done!
database = database_admin_client.get_database name: db_path
restore_info = database.restore_info
puts "Database #{restore_info.backup_info.source_database} was restored " \
"to #{database_id} from backup #{restore_info.backup_info.backup} " \
"using encryption key #{database.encryption_config.kms_key_names}"
View audit logs for the Cloud KMS key
Make sure that logging is enabled for the Cloud KMS API in your project.
In the Google Cloud console, go to Logs Explorer.
Limit the log entries to your Cloud KMS key by adding the following lines to the Query builder:
resource.type="cloudkms_cryptokey" resource.labels.location="KMS_KEY_LOCATION" resource.labels.key_ring_id="KMS_KEY_RING_ID" resource.labels.crypto_key_id="KMS_KEY_ID"
Under normal operations, encrypt and decrypt actions are logged with
INFO
severity. These entries are logged as the zones in your Spanner instance poll the Cloud KMS key about every five minutes.If Spanner fails to access the key, the operations are logged as
ERROR
.