The gcp:looker/instance:Instance resource, part of the Pulumi GCP provider, provisions a Looker (Google Cloud core) instance: its platform edition, region, networking mode, and authentication settings. This guide focuses on four capabilities: OAuth authentication setup, maintenance window scheduling, private networking with VPC peering or PSC, and customer-managed encryption with KMS.
Looker instances require OAuth credentials from the Google Cloud Console and may reference VPC networks, reserved IP ranges, or KMS encryption keys. The examples are intentionally small. Combine them with your own networking infrastructure and security policies.
Create a basic Looker instance with OAuth
Most deployments start with a minimal configuration that establishes the instance name, region, platform edition, and OAuth credentials.
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 the instance’s feature set and pricing model. The oauthConfig block provides the client ID and secret that Looker uses to authenticate users via Google OAuth. Without additional configuration, the instance uses default networking (public IP enabled) and standard maintenance windows.
Configure maintenance windows and access controls
Production instances often schedule maintenance 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 when Looker applies updates (requires instance restart). The denyMaintenancePeriod blocks updates during critical business periods. The adminSettings block restricts authentication to specific email domains, and publicIpEnabled controls whether the instance is accessible from the internet. Setting geminiEnabled to true activates Looker’s AI-powered features.
Enable FIPS 140-2 encryption for compliance
Organizations with federal compliance requirements need FIPS 140-2 validated cryptographic modules.
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, which requires the LOOKER_CORE_ENTERPRISE_ANNUAL platform edition. This ensures all cryptographic operations use federally validated modules, meeting regulatory requirements for government and healthcare workloads.
Deploy with private networking and customer-managed encryption
Enterprise deployments typically isolate Looker in a private VPC with customer-managed encryption keys.
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 (
"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 between Looker and your consumer network. The servicenetworking.Connection establishes the peering relationship using a reserved IP range. Setting privateIpEnabled to true and publicIpEnabled to false ensures Looker is only accessible from within your VPC. The encryptionConfig block specifies a KMS key for encrypting instance data. The example includes an IAM binding that grants the Looker service account permission to use the KMS key.
Connect via Private Service Connect endpoints
Private Service Connect allows clients to access Looker through private endpoints without VPC peering.
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 block lists VPC networks that can create PSC endpoints to reach your Looker instance. This approach simplifies multi-VPC architectures by avoiding the complexity of VPC peering while maintaining private connectivity. Both privateIpEnabled and publicIpEnabled are set to false, making PSC the only access method.
Beyond these examples
These snippets focus on specific Looker instance features: OAuth authentication and access controls, maintenance scheduling and compliance (FIPS), private networking (VPC peering and PSC), and customer-managed encryption. They’re intentionally minimal rather than full analytics platform deployments.
The examples may reference pre-existing infrastructure such as OAuth client credentials from Google Cloud Console, VPC networks and reserved IP ranges, and KMS encryption keys with IAM permissions. They focus on configuring the Looker instance rather than provisioning the surrounding infrastructure.
To keep things focused, common Looker patterns are omitted, including:
- Gemini AI features (geminiEnabled)
- Custom domain configuration (customDomain)
- Controlled egress settings (controlledEgressConfig)
- User metadata and license allocation (userMetadata)
- Periodic export configuration (periodicExportConfig)
- Force deletion policy (deletionPolicy)
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 and cannot be changed after instance creation.LOOKER_CORE_TRIAL. Some features like FIPS require ENTERPRISE editions, while user metadata configuration requires STANDARD edition.deletionPolicy to DEFAULT will fail if the instance has nested resources. Setting it to FORCE deletes the instance regardless of nested resources.Networking & Connectivity
publicIpEnabled for public internet access, privateIpEnabled for VPC peering with private connectivity, or pscEnabled for Private Service Connect. These can be combined based on your connectivity requirements.consumerNetwork with your VPC network ID and reservedRange with an allocated IP address range. Set privateIpEnabled to true. Use dependsOn to reference the servicenetworking.Connection resource to ensure the VPC connection completes before instance creation.pscEnabled to true, privateIpEnabled and publicIpEnabled to false, and configure pscConfig.allowedVpcs with the VPC networks that should be able to connect to your instance.Security & Encryption
fipsEnabled to true and use platformEdition set to LOOKER_CORE_ENTERPRISE_ANNUAL. FIPS encryption is only available with Enterprise editions.encryptionConfig.kmsKeyName with your KMS key identifier. You must also grant the roles/cloudkms.cryptoKeyEncrypterDecrypter role to the Looker service account service-{project-number}@gcp-sa-looker.iam.gserviceaccount.com.Maintenance & User Management
denyMaintenancePeriod, you must allow at least 14 days of maintenance availability between any two denial periods.userMetadata property, but only when platformEdition is set to LOOKER_CORE_STANDARD. You can provision up to 50 total users distributed across Viewer, Standard, and Developer roles.Using a different cloud?
Explore analytics guides for other cloud providers: