The gcp:looker/instance:Instance resource, part of the Pulumi GCP provider, provisions a managed Looker instance: its edition, region, networking mode, and authentication settings. This guide focuses on three capabilities: public and private networking configurations, maintenance window scheduling, and FIPS and customer-managed encryption.
Looker instances require OAuth credentials from Google Cloud Console and may reference VPC networks, KMS keys, or service networking connections. The examples are intentionally small. Combine them with your own VPC infrastructure, encryption keys, and access policies.
Create a public Looker instance with OAuth
Most deployments start with a public instance that users access via OAuth authentication, providing a managed analytics platform without custom networking.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const looker_instance = new gcp.looker.Instance("looker-instance", {
name: "my-instance",
platformEdition: "LOOKER_CORE_STANDARD_ANNUAL",
region: "us-central1",
oauthConfig: {
clientId: "my-client-id",
clientSecret: "my-client-secret",
},
deletionPolicy: "DEFAULT",
});
import pulumi
import pulumi_gcp as gcp
looker_instance = gcp.looker.Instance("looker-instance",
name="my-instance",
platform_edition="LOOKER_CORE_STANDARD_ANNUAL",
region="us-central1",
oauth_config={
"client_id": "my-client-id",
"client_secret": "my-client-secret",
},
deletion_policy="DEFAULT")
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/looker"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := looker.NewInstance(ctx, "looker-instance", &looker.InstanceArgs{
Name: pulumi.String("my-instance"),
PlatformEdition: pulumi.String("LOOKER_CORE_STANDARD_ANNUAL"),
Region: pulumi.String("us-central1"),
OauthConfig: &looker.InstanceOauthConfigArgs{
ClientId: pulumi.String("my-client-id"),
ClientSecret: pulumi.String("my-client-secret"),
},
DeletionPolicy: pulumi.String("DEFAULT"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var looker_instance = new Gcp.Looker.Instance("looker-instance", new()
{
Name = "my-instance",
PlatformEdition = "LOOKER_CORE_STANDARD_ANNUAL",
Region = "us-central1",
OauthConfig = new Gcp.Looker.Inputs.InstanceOauthConfigArgs
{
ClientId = "my-client-id",
ClientSecret = "my-client-secret",
},
DeletionPolicy = "DEFAULT",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.looker.Instance;
import com.pulumi.gcp.looker.InstanceArgs;
import com.pulumi.gcp.looker.inputs.InstanceOauthConfigArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var looker_instance = new Instance("looker-instance", InstanceArgs.builder()
.name("my-instance")
.platformEdition("LOOKER_CORE_STANDARD_ANNUAL")
.region("us-central1")
.oauthConfig(InstanceOauthConfigArgs.builder()
.clientId("my-client-id")
.clientSecret("my-client-secret")
.build())
.deletionPolicy("DEFAULT")
.build());
}
}
resources:
looker-instance:
type: gcp:looker:Instance
properties:
name: my-instance
platformEdition: LOOKER_CORE_STANDARD_ANNUAL
region: us-central1
oauthConfig:
clientId: my-client-id
clientSecret: my-client-secret
deletionPolicy: DEFAULT
The platformEdition determines instance size and features; subscription editions like LOOKER_CORE_STANDARD_ANNUAL provide production capacity. The oauthConfig block requires a client ID and secret from your Google Cloud Console OAuth credentials. Without explicit publicIpEnabled, the instance defaults to public access.
Configure maintenance windows and access controls
Production deployments schedule maintenance windows to minimize disruption and restrict access by email domain.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const looker_instance = new gcp.looker.Instance("looker-instance", {
name: "my-instance",
platformEdition: "LOOKER_CORE_STANDARD_ANNUAL",
region: "us-central1",
publicIpEnabled: true,
geminiEnabled: true,
adminSettings: {
allowedEmailDomains: ["google.com"],
},
maintenanceWindow: {
dayOfWeek: "THURSDAY",
startTime: {
hours: 22,
minutes: 0,
seconds: 0,
nanos: 0,
},
},
denyMaintenancePeriod: {
startDate: {
year: 2050,
month: 1,
day: 1,
},
endDate: {
year: 2050,
month: 2,
day: 1,
},
time: {
hours: 10,
minutes: 0,
seconds: 0,
nanos: 0,
},
},
oauthConfig: {
clientId: "my-client-id",
clientSecret: "my-client-secret",
},
});
import pulumi
import pulumi_gcp as gcp
looker_instance = gcp.looker.Instance("looker-instance",
name="my-instance",
platform_edition="LOOKER_CORE_STANDARD_ANNUAL",
region="us-central1",
public_ip_enabled=True,
gemini_enabled=True,
admin_settings={
"allowed_email_domains": ["google.com"],
},
maintenance_window={
"day_of_week": "THURSDAY",
"start_time": {
"hours": 22,
"minutes": 0,
"seconds": 0,
"nanos": 0,
},
},
deny_maintenance_period={
"start_date": {
"year": 2050,
"month": 1,
"day": 1,
},
"end_date": {
"year": 2050,
"month": 2,
"day": 1,
},
"time": {
"hours": 10,
"minutes": 0,
"seconds": 0,
"nanos": 0,
},
},
oauth_config={
"client_id": "my-client-id",
"client_secret": "my-client-secret",
})
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/looker"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := looker.NewInstance(ctx, "looker-instance", &looker.InstanceArgs{
Name: pulumi.String("my-instance"),
PlatformEdition: pulumi.String("LOOKER_CORE_STANDARD_ANNUAL"),
Region: pulumi.String("us-central1"),
PublicIpEnabled: pulumi.Bool(true),
GeminiEnabled: pulumi.Bool(true),
AdminSettings: &looker.InstanceAdminSettingsArgs{
AllowedEmailDomains: pulumi.StringArray{
pulumi.String("google.com"),
},
},
MaintenanceWindow: &looker.InstanceMaintenanceWindowArgs{
DayOfWeek: pulumi.String("THURSDAY"),
StartTime: &looker.InstanceMaintenanceWindowStartTimeArgs{
Hours: pulumi.Int(22),
Minutes: pulumi.Int(0),
Seconds: pulumi.Int(0),
Nanos: pulumi.Int(0),
},
},
DenyMaintenancePeriod: &looker.InstanceDenyMaintenancePeriodArgs{
StartDate: &looker.InstanceDenyMaintenancePeriodStartDateArgs{
Year: pulumi.Int(2050),
Month: pulumi.Int(1),
Day: pulumi.Int(1),
},
EndDate: &looker.InstanceDenyMaintenancePeriodEndDateArgs{
Year: pulumi.Int(2050),
Month: pulumi.Int(2),
Day: pulumi.Int(1),
},
Time: &looker.InstanceDenyMaintenancePeriodTimeArgs{
Hours: pulumi.Int(10),
Minutes: pulumi.Int(0),
Seconds: pulumi.Int(0),
Nanos: pulumi.Int(0),
},
},
OauthConfig: &looker.InstanceOauthConfigArgs{
ClientId: pulumi.String("my-client-id"),
ClientSecret: pulumi.String("my-client-secret"),
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var looker_instance = new Gcp.Looker.Instance("looker-instance", new()
{
Name = "my-instance",
PlatformEdition = "LOOKER_CORE_STANDARD_ANNUAL",
Region = "us-central1",
PublicIpEnabled = true,
GeminiEnabled = true,
AdminSettings = new Gcp.Looker.Inputs.InstanceAdminSettingsArgs
{
AllowedEmailDomains = new[]
{
"google.com",
},
},
MaintenanceWindow = new Gcp.Looker.Inputs.InstanceMaintenanceWindowArgs
{
DayOfWeek = "THURSDAY",
StartTime = new Gcp.Looker.Inputs.InstanceMaintenanceWindowStartTimeArgs
{
Hours = 22,
Minutes = 0,
Seconds = 0,
Nanos = 0,
},
},
DenyMaintenancePeriod = new Gcp.Looker.Inputs.InstanceDenyMaintenancePeriodArgs
{
StartDate = new Gcp.Looker.Inputs.InstanceDenyMaintenancePeriodStartDateArgs
{
Year = 2050,
Month = 1,
Day = 1,
},
EndDate = new Gcp.Looker.Inputs.InstanceDenyMaintenancePeriodEndDateArgs
{
Year = 2050,
Month = 2,
Day = 1,
},
Time = new Gcp.Looker.Inputs.InstanceDenyMaintenancePeriodTimeArgs
{
Hours = 10,
Minutes = 0,
Seconds = 0,
Nanos = 0,
},
},
OauthConfig = new Gcp.Looker.Inputs.InstanceOauthConfigArgs
{
ClientId = "my-client-id",
ClientSecret = "my-client-secret",
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.looker.Instance;
import com.pulumi.gcp.looker.InstanceArgs;
import com.pulumi.gcp.looker.inputs.InstanceAdminSettingsArgs;
import com.pulumi.gcp.looker.inputs.InstanceMaintenanceWindowArgs;
import com.pulumi.gcp.looker.inputs.InstanceMaintenanceWindowStartTimeArgs;
import com.pulumi.gcp.looker.inputs.InstanceDenyMaintenancePeriodArgs;
import com.pulumi.gcp.looker.inputs.InstanceDenyMaintenancePeriodStartDateArgs;
import com.pulumi.gcp.looker.inputs.InstanceDenyMaintenancePeriodEndDateArgs;
import com.pulumi.gcp.looker.inputs.InstanceDenyMaintenancePeriodTimeArgs;
import com.pulumi.gcp.looker.inputs.InstanceOauthConfigArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var looker_instance = new Instance("looker-instance", InstanceArgs.builder()
.name("my-instance")
.platformEdition("LOOKER_CORE_STANDARD_ANNUAL")
.region("us-central1")
.publicIpEnabled(true)
.geminiEnabled(true)
.adminSettings(InstanceAdminSettingsArgs.builder()
.allowedEmailDomains("google.com")
.build())
.maintenanceWindow(InstanceMaintenanceWindowArgs.builder()
.dayOfWeek("THURSDAY")
.startTime(InstanceMaintenanceWindowStartTimeArgs.builder()
.hours(22)
.minutes(0)
.seconds(0)
.nanos(0)
.build())
.build())
.denyMaintenancePeriod(InstanceDenyMaintenancePeriodArgs.builder()
.startDate(InstanceDenyMaintenancePeriodStartDateArgs.builder()
.year(2050)
.month(1)
.day(1)
.build())
.endDate(InstanceDenyMaintenancePeriodEndDateArgs.builder()
.year(2050)
.month(2)
.day(1)
.build())
.time(InstanceDenyMaintenancePeriodTimeArgs.builder()
.hours(10)
.minutes(0)
.seconds(0)
.nanos(0)
.build())
.build())
.oauthConfig(InstanceOauthConfigArgs.builder()
.clientId("my-client-id")
.clientSecret("my-client-secret")
.build())
.build());
}
}
resources:
looker-instance:
type: gcp:looker:Instance
properties:
name: my-instance
platformEdition: LOOKER_CORE_STANDARD_ANNUAL
region: us-central1
publicIpEnabled: true
geminiEnabled: true
adminSettings:
allowedEmailDomains:
- google.com
maintenanceWindow:
dayOfWeek: THURSDAY
startTime:
hours: 22
minutes: 0
seconds: 0
nanos: 0
denyMaintenancePeriod:
startDate:
year: 2050
month: 1
day: 1
endDate:
year: 2050
month: 2
day: 1
time:
hours: 10
minutes: 0
seconds: 0
nanos: 0
oauthConfig:
clientId: my-client-id
clientSecret: my-client-secret
The maintenanceWindow property schedules monthly updates on a specific day and time. The denyMaintenancePeriod blocks maintenance during critical periods like year-end. The adminSettings.allowedEmailDomains restricts login to specific domains. Gemini AI features are enabled via geminiEnabled.
Enable FIPS 140-2 encryption for compliance
Organizations with regulatory requirements use FIPS 140-2 validated cryptographic modules for data protection.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const looker_instance = new gcp.looker.Instance("looker-instance", {
name: "my-instance-fips",
platformEdition: "LOOKER_CORE_ENTERPRISE_ANNUAL",
region: "us-central1",
publicIpEnabled: true,
fipsEnabled: true,
oauthConfig: {
clientId: "my-client-id",
clientSecret: "my-client-secret",
},
});
import pulumi
import pulumi_gcp as gcp
looker_instance = gcp.looker.Instance("looker-instance",
name="my-instance-fips",
platform_edition="LOOKER_CORE_ENTERPRISE_ANNUAL",
region="us-central1",
public_ip_enabled=True,
fips_enabled=True,
oauth_config={
"client_id": "my-client-id",
"client_secret": "my-client-secret",
})
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/looker"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := looker.NewInstance(ctx, "looker-instance", &looker.InstanceArgs{
Name: pulumi.String("my-instance-fips"),
PlatformEdition: pulumi.String("LOOKER_CORE_ENTERPRISE_ANNUAL"),
Region: pulumi.String("us-central1"),
PublicIpEnabled: pulumi.Bool(true),
FipsEnabled: pulumi.Bool(true),
OauthConfig: &looker.InstanceOauthConfigArgs{
ClientId: pulumi.String("my-client-id"),
ClientSecret: pulumi.String("my-client-secret"),
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var looker_instance = new Gcp.Looker.Instance("looker-instance", new()
{
Name = "my-instance-fips",
PlatformEdition = "LOOKER_CORE_ENTERPRISE_ANNUAL",
Region = "us-central1",
PublicIpEnabled = true,
FipsEnabled = true,
OauthConfig = new Gcp.Looker.Inputs.InstanceOauthConfigArgs
{
ClientId = "my-client-id",
ClientSecret = "my-client-secret",
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.looker.Instance;
import com.pulumi.gcp.looker.InstanceArgs;
import com.pulumi.gcp.looker.inputs.InstanceOauthConfigArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var looker_instance = new Instance("looker-instance", InstanceArgs.builder()
.name("my-instance-fips")
.platformEdition("LOOKER_CORE_ENTERPRISE_ANNUAL")
.region("us-central1")
.publicIpEnabled(true)
.fipsEnabled(true)
.oauthConfig(InstanceOauthConfigArgs.builder()
.clientId("my-client-id")
.clientSecret("my-client-secret")
.build())
.build());
}
}
resources:
looker-instance:
type: gcp:looker:Instance
properties:
name: my-instance-fips
platformEdition: LOOKER_CORE_ENTERPRISE_ANNUAL
region: us-central1
publicIpEnabled: true
fipsEnabled: true
oauthConfig:
clientId: my-client-id
clientSecret: my-client-secret
Setting fipsEnabled to true activates FIPS 140-2 encryption. This feature requires the LOOKER_CORE_ENTERPRISE_ANNUAL edition; it’s not available on standard or trial editions.
Deploy with private networking and customer-managed encryption
Enterprise deployments isolate Looker traffic within a VPC and use customer-managed encryption keys for data sovereignty.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const lookerNetwork = new gcp.compute.Network("looker_network", {name: "looker-network"});
const lookerRange = new gcp.compute.GlobalAddress("looker_range", {
name: "looker-range",
purpose: "VPC_PEERING",
addressType: "INTERNAL",
prefixLength: 20,
network: lookerNetwork.id,
});
const lookerVpcConnection = new gcp.servicenetworking.Connection("looker_vpc_connection", {
network: lookerNetwork.id,
service: "servicenetworking.googleapis.com",
reservedPeeringRanges: [lookerRange.name],
});
const looker_instance = new gcp.looker.Instance("looker-instance", {
name: "my-instance",
platformEdition: "LOOKER_CORE_ENTERPRISE_ANNUAL",
region: "us-central1",
privateIpEnabled: true,
publicIpEnabled: false,
geminiEnabled: true,
reservedRange: lookerRange.name,
consumerNetwork: lookerNetwork.id,
adminSettings: {
allowedEmailDomains: ["google.com"],
},
encryptionConfig: {
kmsKeyName: "looker-kms-key",
},
maintenanceWindow: {
dayOfWeek: "THURSDAY",
startTime: {
hours: 22,
minutes: 0,
seconds: 0,
nanos: 0,
},
},
denyMaintenancePeriod: {
startDate: {
year: 2050,
month: 1,
day: 1,
},
endDate: {
year: 2050,
month: 2,
day: 1,
},
time: {
hours: 10,
minutes: 0,
seconds: 0,
nanos: 0,
},
},
oauthConfig: {
clientId: "my-client-id",
clientSecret: "my-client-secret",
},
}, {
dependsOn: [lookerVpcConnection],
});
const project = gcp.organizations.getProject({});
const cryptoKey = new gcp.kms.CryptoKeyIAMMember("crypto_key", {
cryptoKeyId: "looker-kms-key",
role: "roles/cloudkms.cryptoKeyEncrypterDecrypter",
member: project.then(project => `serviceAccount:service-${project.number}@gcp-sa-looker.iam.gserviceaccount.com`),
});
import pulumi
import pulumi_gcp as gcp
looker_network = gcp.compute.Network("looker_network", name="looker-network")
looker_range = gcp.compute.GlobalAddress("looker_range",
name="looker-range",
purpose="VPC_PEERING",
address_type="INTERNAL",
prefix_length=20,
network=looker_network.id)
looker_vpc_connection = gcp.servicenetworking.Connection("looker_vpc_connection",
network=looker_network.id,
service="servicenetworking.googleapis.com",
reserved_peering_ranges=[looker_range.name])
looker_instance = gcp.looker.Instance("looker-instance",
name="my-instance",
platform_edition="LOOKER_CORE_ENTERPRISE_ANNUAL",
region="us-central1",
private_ip_enabled=True,
public_ip_enabled=False,
gemini_enabled=True,
reserved_range=looker_range.name,
consumer_network=looker_network.id,
admin_settings={
"allowed_email_domains": ["google.com"],
},
encryption_config={
"kms_key_name": "looker-kms-key",
},
maintenance_window={
"day_of_week": "THURSDAY",
"start_time": {
"hours": 22,
"minutes": 0,
"seconds": 0,
"nanos": 0,
},
},
deny_maintenance_period={
"start_date": {
"year": 2050,
"month": 1,
"day": 1,
},
"end_date": {
"year": 2050,
"month": 2,
"day": 1,
},
"time": {
"hours": 10,
"minutes": 0,
"seconds": 0,
"nanos": 0,
},
},
oauth_config={
"client_id": "my-client-id",
"client_secret": "my-client-secret",
},
opts = pulumi.ResourceOptions(depends_on=[looker_vpc_connection]))
project = gcp.organizations.get_project()
crypto_key = gcp.kms.CryptoKeyIAMMember("crypto_key",
crypto_key_id="looker-kms-key",
role="roles/cloudkms.cryptoKeyEncrypterDecrypter",
member=f"serviceAccount:service-{project.number}@gcp-sa-looker.iam.gserviceaccount.com")
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/kms"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/looker"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/servicenetworking"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
lookerNetwork, err := compute.NewNetwork(ctx, "looker_network", &compute.NetworkArgs{
Name: pulumi.String("looker-network"),
})
if err != nil {
return err
}
lookerRange, err := compute.NewGlobalAddress(ctx, "looker_range", &compute.GlobalAddressArgs{
Name: pulumi.String("looker-range"),
Purpose: pulumi.String("VPC_PEERING"),
AddressType: pulumi.String("INTERNAL"),
PrefixLength: pulumi.Int(20),
Network: lookerNetwork.ID(),
})
if err != nil {
return err
}
lookerVpcConnection, err := servicenetworking.NewConnection(ctx, "looker_vpc_connection", &servicenetworking.ConnectionArgs{
Network: lookerNetwork.ID(),
Service: pulumi.String("servicenetworking.googleapis.com"),
ReservedPeeringRanges: pulumi.StringArray{
lookerRange.Name,
},
})
if err != nil {
return err
}
_, err = looker.NewInstance(ctx, "looker-instance", &looker.InstanceArgs{
Name: pulumi.String("my-instance"),
PlatformEdition: pulumi.String("LOOKER_CORE_ENTERPRISE_ANNUAL"),
Region: pulumi.String("us-central1"),
PrivateIpEnabled: pulumi.Bool(true),
PublicIpEnabled: pulumi.Bool(false),
GeminiEnabled: pulumi.Bool(true),
ReservedRange: lookerRange.Name,
ConsumerNetwork: lookerNetwork.ID(),
AdminSettings: &looker.InstanceAdminSettingsArgs{
AllowedEmailDomains: pulumi.StringArray{
pulumi.String("google.com"),
},
},
EncryptionConfig: &looker.InstanceEncryptionConfigArgs{
KmsKeyName: pulumi.String("looker-kms-key"),
},
MaintenanceWindow: &looker.InstanceMaintenanceWindowArgs{
DayOfWeek: pulumi.String("THURSDAY"),
StartTime: &looker.InstanceMaintenanceWindowStartTimeArgs{
Hours: pulumi.Int(22),
Minutes: pulumi.Int(0),
Seconds: pulumi.Int(0),
Nanos: pulumi.Int(0),
},
},
DenyMaintenancePeriod: &looker.InstanceDenyMaintenancePeriodArgs{
StartDate: &looker.InstanceDenyMaintenancePeriodStartDateArgs{
Year: pulumi.Int(2050),
Month: pulumi.Int(1),
Day: pulumi.Int(1),
},
EndDate: &looker.InstanceDenyMaintenancePeriodEndDateArgs{
Year: pulumi.Int(2050),
Month: pulumi.Int(2),
Day: pulumi.Int(1),
},
Time: &looker.InstanceDenyMaintenancePeriodTimeArgs{
Hours: pulumi.Int(10),
Minutes: pulumi.Int(0),
Seconds: pulumi.Int(0),
Nanos: pulumi.Int(0),
},
},
OauthConfig: &looker.InstanceOauthConfigArgs{
ClientId: pulumi.String("my-client-id"),
ClientSecret: pulumi.String("my-client-secret"),
},
}, pulumi.DependsOn([]pulumi.Resource{
lookerVpcConnection,
}))
if err != nil {
return err
}
project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
if err != nil {
return err
}
_, err = kms.NewCryptoKeyIAMMember(ctx, "crypto_key", &kms.CryptoKeyIAMMemberArgs{
CryptoKeyId: pulumi.String("looker-kms-key"),
Role: pulumi.String("roles/cloudkms.cryptoKeyEncrypterDecrypter"),
Member: pulumi.Sprintf("serviceAccount:service-%v@gcp-sa-looker.iam.gserviceaccount.com", project.Number),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var lookerNetwork = new Gcp.Compute.Network("looker_network", new()
{
Name = "looker-network",
});
var lookerRange = new Gcp.Compute.GlobalAddress("looker_range", new()
{
Name = "looker-range",
Purpose = "VPC_PEERING",
AddressType = "INTERNAL",
PrefixLength = 20,
Network = lookerNetwork.Id,
});
var lookerVpcConnection = new Gcp.ServiceNetworking.Connection("looker_vpc_connection", new()
{
Network = lookerNetwork.Id,
Service = "servicenetworking.googleapis.com",
ReservedPeeringRanges = new[]
{
lookerRange.Name,
},
});
var looker_instance = new Gcp.Looker.Instance("looker-instance", new()
{
Name = "my-instance",
PlatformEdition = "LOOKER_CORE_ENTERPRISE_ANNUAL",
Region = "us-central1",
PrivateIpEnabled = true,
PublicIpEnabled = false,
GeminiEnabled = true,
ReservedRange = lookerRange.Name,
ConsumerNetwork = lookerNetwork.Id,
AdminSettings = new Gcp.Looker.Inputs.InstanceAdminSettingsArgs
{
AllowedEmailDomains = new[]
{
"google.com",
},
},
EncryptionConfig = new Gcp.Looker.Inputs.InstanceEncryptionConfigArgs
{
KmsKeyName = "looker-kms-key",
},
MaintenanceWindow = new Gcp.Looker.Inputs.InstanceMaintenanceWindowArgs
{
DayOfWeek = "THURSDAY",
StartTime = new Gcp.Looker.Inputs.InstanceMaintenanceWindowStartTimeArgs
{
Hours = 22,
Minutes = 0,
Seconds = 0,
Nanos = 0,
},
},
DenyMaintenancePeriod = new Gcp.Looker.Inputs.InstanceDenyMaintenancePeriodArgs
{
StartDate = new Gcp.Looker.Inputs.InstanceDenyMaintenancePeriodStartDateArgs
{
Year = 2050,
Month = 1,
Day = 1,
},
EndDate = new Gcp.Looker.Inputs.InstanceDenyMaintenancePeriodEndDateArgs
{
Year = 2050,
Month = 2,
Day = 1,
},
Time = new Gcp.Looker.Inputs.InstanceDenyMaintenancePeriodTimeArgs
{
Hours = 10,
Minutes = 0,
Seconds = 0,
Nanos = 0,
},
},
OauthConfig = new Gcp.Looker.Inputs.InstanceOauthConfigArgs
{
ClientId = "my-client-id",
ClientSecret = "my-client-secret",
},
}, new CustomResourceOptions
{
DependsOn =
{
lookerVpcConnection,
},
});
var project = Gcp.Organizations.GetProject.Invoke();
var cryptoKey = new Gcp.Kms.CryptoKeyIAMMember("crypto_key", new()
{
CryptoKeyId = "looker-kms-key",
Role = "roles/cloudkms.cryptoKeyEncrypterDecrypter",
Member = $"serviceAccount:service-{project.Apply(getProjectResult => getProjectResult.Number)}@gcp-sa-looker.iam.gserviceaccount.com",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.Network;
import com.pulumi.gcp.compute.NetworkArgs;
import com.pulumi.gcp.compute.GlobalAddress;
import com.pulumi.gcp.compute.GlobalAddressArgs;
import com.pulumi.gcp.servicenetworking.Connection;
import com.pulumi.gcp.servicenetworking.ConnectionArgs;
import com.pulumi.gcp.looker.Instance;
import com.pulumi.gcp.looker.InstanceArgs;
import com.pulumi.gcp.looker.inputs.InstanceAdminSettingsArgs;
import com.pulumi.gcp.looker.inputs.InstanceEncryptionConfigArgs;
import com.pulumi.gcp.looker.inputs.InstanceMaintenanceWindowArgs;
import com.pulumi.gcp.looker.inputs.InstanceMaintenanceWindowStartTimeArgs;
import com.pulumi.gcp.looker.inputs.InstanceDenyMaintenancePeriodArgs;
import com.pulumi.gcp.looker.inputs.InstanceDenyMaintenancePeriodStartDateArgs;
import com.pulumi.gcp.looker.inputs.InstanceDenyMaintenancePeriodEndDateArgs;
import com.pulumi.gcp.looker.inputs.InstanceDenyMaintenancePeriodTimeArgs;
import com.pulumi.gcp.looker.inputs.InstanceOauthConfigArgs;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.kms.CryptoKeyIAMMember;
import com.pulumi.gcp.kms.CryptoKeyIAMMemberArgs;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var lookerNetwork = new Network("lookerNetwork", NetworkArgs.builder()
.name("looker-network")
.build());
var lookerRange = new GlobalAddress("lookerRange", GlobalAddressArgs.builder()
.name("looker-range")
.purpose("VPC_PEERING")
.addressType("INTERNAL")
.prefixLength(20)
.network(lookerNetwork.id())
.build());
var lookerVpcConnection = new Connection("lookerVpcConnection", ConnectionArgs.builder()
.network(lookerNetwork.id())
.service("servicenetworking.googleapis.com")
.reservedPeeringRanges(lookerRange.name())
.build());
var looker_instance = new Instance("looker-instance", InstanceArgs.builder()
.name("my-instance")
.platformEdition("LOOKER_CORE_ENTERPRISE_ANNUAL")
.region("us-central1")
.privateIpEnabled(true)
.publicIpEnabled(false)
.geminiEnabled(true)
.reservedRange(lookerRange.name())
.consumerNetwork(lookerNetwork.id())
.adminSettings(InstanceAdminSettingsArgs.builder()
.allowedEmailDomains("google.com")
.build())
.encryptionConfig(InstanceEncryptionConfigArgs.builder()
.kmsKeyName("looker-kms-key")
.build())
.maintenanceWindow(InstanceMaintenanceWindowArgs.builder()
.dayOfWeek("THURSDAY")
.startTime(InstanceMaintenanceWindowStartTimeArgs.builder()
.hours(22)
.minutes(0)
.seconds(0)
.nanos(0)
.build())
.build())
.denyMaintenancePeriod(InstanceDenyMaintenancePeriodArgs.builder()
.startDate(InstanceDenyMaintenancePeriodStartDateArgs.builder()
.year(2050)
.month(1)
.day(1)
.build())
.endDate(InstanceDenyMaintenancePeriodEndDateArgs.builder()
.year(2050)
.month(2)
.day(1)
.build())
.time(InstanceDenyMaintenancePeriodTimeArgs.builder()
.hours(10)
.minutes(0)
.seconds(0)
.nanos(0)
.build())
.build())
.oauthConfig(InstanceOauthConfigArgs.builder()
.clientId("my-client-id")
.clientSecret("my-client-secret")
.build())
.build(), CustomResourceOptions.builder()
.dependsOn(lookerVpcConnection)
.build());
final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
.build());
var cryptoKey = new CryptoKeyIAMMember("cryptoKey", CryptoKeyIAMMemberArgs.builder()
.cryptoKeyId("looker-kms-key")
.role("roles/cloudkms.cryptoKeyEncrypterDecrypter")
.member(String.format("serviceAccount:service-%s@gcp-sa-looker.iam.gserviceaccount.com", project.number()))
.build());
}
}
resources:
looker-instance:
type: gcp:looker:Instance
properties:
name: my-instance
platformEdition: LOOKER_CORE_ENTERPRISE_ANNUAL
region: us-central1
privateIpEnabled: true
publicIpEnabled: false
geminiEnabled: true
reservedRange: ${lookerRange.name}
consumerNetwork: ${lookerNetwork.id}
adminSettings:
allowedEmailDomains:
- google.com
encryptionConfig:
kmsKeyName: looker-kms-key
maintenanceWindow:
dayOfWeek: THURSDAY
startTime:
hours: 22
minutes: 0
seconds: 0
nanos: 0
denyMaintenancePeriod:
startDate:
year: 2050
month: 1
day: 1
endDate:
year: 2050
month: 2
day: 1
time:
hours: 10
minutes: 0
seconds: 0
nanos: 0
oauthConfig:
clientId: my-client-id
clientSecret: my-client-secret
options:
dependsOn:
- ${lookerVpcConnection}
lookerVpcConnection:
type: gcp:servicenetworking:Connection
name: looker_vpc_connection
properties:
network: ${lookerNetwork.id}
service: servicenetworking.googleapis.com
reservedPeeringRanges:
- ${lookerRange.name}
lookerRange:
type: gcp:compute:GlobalAddress
name: looker_range
properties:
name: looker-range
purpose: VPC_PEERING
addressType: INTERNAL
prefixLength: 20
network: ${lookerNetwork.id}
lookerNetwork:
type: gcp:compute:Network
name: looker_network
properties:
name: looker-network
cryptoKey:
type: gcp:kms:CryptoKeyIAMMember
name: crypto_key
properties:
cryptoKeyId: looker-kms-key
role: roles/cloudkms.cryptoKeyEncrypterDecrypter
member: serviceAccount:service-${project.number}@gcp-sa-looker.iam.gserviceaccount.com
variables:
project:
fn::invoke:
function: gcp:organizations:getProject
arguments: {}
This configuration creates VPC peering via servicenetworking.Connection, allocating a reserved IP range for Looker. Setting privateIpEnabled to true and publicIpEnabled to false restricts access to the VPC. The encryptionConfig.kmsKeyName references your KMS key. The dependsOn ensures VPC peering completes before instance creation. The CryptoKeyIAMMember grants the Looker service account permission to use your encryption key.
Connect via Private Service Connect endpoints
Private Service Connect provides private access without VPC peering, avoiding route exposure between networks.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const looker_instance = new gcp.looker.Instance("looker-instance", {
name: "my-instance",
platformEdition: "LOOKER_CORE_ENTERPRISE_ANNUAL",
region: "us-central1",
privateIpEnabled: false,
publicIpEnabled: false,
pscEnabled: true,
oauthConfig: {
clientId: "my-client-id",
clientSecret: "my-client-secret",
},
pscConfig: {
allowedVpcs: ["projects/test-project/global/networks/test"],
},
});
import pulumi
import pulumi_gcp as gcp
looker_instance = gcp.looker.Instance("looker-instance",
name="my-instance",
platform_edition="LOOKER_CORE_ENTERPRISE_ANNUAL",
region="us-central1",
private_ip_enabled=False,
public_ip_enabled=False,
psc_enabled=True,
oauth_config={
"client_id": "my-client-id",
"client_secret": "my-client-secret",
},
psc_config={
"allowed_vpcs": ["projects/test-project/global/networks/test"],
})
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/looker"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := looker.NewInstance(ctx, "looker-instance", &looker.InstanceArgs{
Name: pulumi.String("my-instance"),
PlatformEdition: pulumi.String("LOOKER_CORE_ENTERPRISE_ANNUAL"),
Region: pulumi.String("us-central1"),
PrivateIpEnabled: pulumi.Bool(false),
PublicIpEnabled: pulumi.Bool(false),
PscEnabled: pulumi.Bool(true),
OauthConfig: &looker.InstanceOauthConfigArgs{
ClientId: pulumi.String("my-client-id"),
ClientSecret: pulumi.String("my-client-secret"),
},
PscConfig: &looker.InstancePscConfigArgs{
AllowedVpcs: pulumi.StringArray{
pulumi.String("projects/test-project/global/networks/test"),
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var looker_instance = new Gcp.Looker.Instance("looker-instance", new()
{
Name = "my-instance",
PlatformEdition = "LOOKER_CORE_ENTERPRISE_ANNUAL",
Region = "us-central1",
PrivateIpEnabled = false,
PublicIpEnabled = false,
PscEnabled = true,
OauthConfig = new Gcp.Looker.Inputs.InstanceOauthConfigArgs
{
ClientId = "my-client-id",
ClientSecret = "my-client-secret",
},
PscConfig = new Gcp.Looker.Inputs.InstancePscConfigArgs
{
AllowedVpcs = new[]
{
"projects/test-project/global/networks/test",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.looker.Instance;
import com.pulumi.gcp.looker.InstanceArgs;
import com.pulumi.gcp.looker.inputs.InstanceOauthConfigArgs;
import com.pulumi.gcp.looker.inputs.InstancePscConfigArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var looker_instance = new Instance("looker-instance", InstanceArgs.builder()
.name("my-instance")
.platformEdition("LOOKER_CORE_ENTERPRISE_ANNUAL")
.region("us-central1")
.privateIpEnabled(false)
.publicIpEnabled(false)
.pscEnabled(true)
.oauthConfig(InstanceOauthConfigArgs.builder()
.clientId("my-client-id")
.clientSecret("my-client-secret")
.build())
.pscConfig(InstancePscConfigArgs.builder()
.allowedVpcs("projects/test-project/global/networks/test")
.build())
.build());
}
}
resources:
looker-instance:
type: gcp:looker:Instance
properties:
name: my-instance
platformEdition: LOOKER_CORE_ENTERPRISE_ANNUAL
region: us-central1
privateIpEnabled: false
publicIpEnabled: false
pscEnabled: true
oauthConfig:
clientId: my-client-id
clientSecret: my-client-secret
pscConfig:
allowedVpcs:
- projects/test-project/global/networks/test
Setting pscEnabled to true activates Private Service Connect. The pscConfig.allowedVpcs lists VPC networks that can create endpoints to your instance. Both privateIpEnabled and publicIpEnabled are false; access is exclusively through PSC endpoints.
Beyond these examples
These snippets focus on specific Looker instance features: public and private networking options, maintenance scheduling and access controls, and encryption (FIPS and customer-managed keys). They’re intentionally minimal rather than full analytics deployments.
The examples may reference pre-existing infrastructure such as OAuth client credentials from Google Cloud Console, VPC networks and reserved IP ranges for private instances, and KMS encryption keys and IAM service account bindings. They focus on configuring the instance rather than provisioning everything around it.
To keep things focused, common Looker patterns are omitted, including:
- Gemini AI enablement (geminiEnabled)
- Controlled egress configuration
- User metadata and license allocation
- Custom domain configuration
These omissions are intentional: the goal is to illustrate how each Looker feature is wired, not provide drop-in analytics modules. See the Looker Instance resource reference for all available configuration options.
Let's deploy Google Cloud Looker Instances
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Instance Configuration & Editions
name, region, project, and platformEdition properties are immutable. Changing any of these will force recreation of the instance.fipsEnabled) requires the LOOKER_CORE_ENTERPRISE_ANNUAL edition. User allocation settings (userMetadata) are only available with the LOOKER_CORE_STANDARD edition, which supports up to 50 total users.oauthConfig with clientId and clientSecret properties.Networking & Connectivity
publicIpEnabled: true), private IP with VPC peering (privateIpEnabled: true with consumerNetwork and reservedRange), or Private Service Connect (pscEnabled: true with pscConfig). The Enterprise Full example shows private-only setup, while the PSC example disables both public and private IPs.gcp.compute.Network, a gcp.compute.GlobalAddress with purpose: "VPC_PEERING", and a gcp.servicenetworking.Connection. Then set consumerNetwork to the network ID, reservedRange to the address range name, and use dependsOn to ensure the Connection is created before the Looker instance.pscEnabled: true, privateIpEnabled: false, and publicIpEnabled: false. Configure pscConfig.allowedVpcs with the VPC networks that can connect.Security & Encryption
encryptionConfig.kmsKeyName to your KMS key. You must also grant the service account service-{project-number}@gcp-sa-looker.iam.gserviceaccount.com the roles/cloudkms.cryptoKeyEncrypterDecrypter role on the key using gcp.kms.CryptoKeyIAMMember.fipsEnabled: true. This requires the LOOKER_CORE_ENTERPRISE_ANNUAL platform edition.adminSettings.allowedEmailDomains with a list of allowed domains.Maintenance & Lifecycle
deletionPolicy is set to DEFAULT (the default value), deletion fails when the instance has nested resources. Set deletionPolicy: "FORCE" to delete the instance regardless of nested resources.maintenanceWindow with dayOfWeek and startTime (hours, minutes, seconds, nanos). Maintenance occurs once a month and requires an instance restart.denyMaintenancePeriod with startDate, endDate, and time. You must allow at least 14 days of maintenance availability between any two denial periods.Advanced Features
geminiEnabled: true in your instance configuration.customDomain.domain to your domain name.Using a different cloud?
Explore analytics guides for other cloud providers: