Deploy Google Cloud Looker Instances

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 FREE

Frequently Asked Questions

Instance Configuration & Editions
What properties can't I change after creating a Looker instance?
The name, region, project, and platformEdition properties are immutable and cannot be changed after instance creation.
What are the available platform editions and their differences?
Platform editions determine instance features and size. Options include trial editions (LOOKER_CORE_TRIAL, LOOKER_CORE_TRIAL_STANDARD, LOOKER_CORE_TRIAL_ENTERPRISE, LOOKER_CORE_TRIAL_EMBED), subscription editions (LOOKER_CORE_STANDARD_ANNUAL, LOOKER_CORE_ENTERPRISE_ANNUAL, LOOKER_CORE_EMBED_ANNUAL), and nonprod editions. The default is LOOKER_CORE_TRIAL. Some features like FIPS require ENTERPRISE editions, while user metadata configuration requires STANDARD edition.
What's the difference between DEFAULT and FORCE deletion policies?
Setting deletionPolicy to DEFAULT will fail if the instance has nested resources. Setting it to FORCE deletes the instance regardless of nested resources.
Networking & Connectivity
What networking options are available for Looker instances?
You can configure three networking modes: 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.
How do I connect my Looker instance to a VPC?
Configure 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.
How do I set up Private Service Connect for my instance?
Set 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
How do I enable FIPS 140-2 encryption?
Set fipsEnabled to true and use platformEdition set to LOOKER_CORE_ENTERPRISE_ANNUAL. FIPS encryption is only available with Enterprise editions.
How do I use customer-managed encryption keys with KMS?
Configure 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
What's the constraint on maintenance denial periods?
When configuring denyMaintenancePeriod, you must allow at least 14 days of maintenance availability between any two denial periods.
Can I allocate additional users to my Looker instance?
Yes, using the 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: