Deploy Google Cloud Looker Instances

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 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. Changing any of these will force recreation of the instance.
What platform edition should I choose?
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), annual subscription editions (LOOKER_CORE_STANDARD_ANNUAL, LOOKER_CORE_ENTERPRISE_ANNUAL, LOOKER_CORE_EMBED_ANNUAL), and nonprod variants. The default is LOOKER_CORE_TRIAL. Note that some trial editions are currently unavailable.
What features depend on the platform edition?
FIPS 140-2 encryption (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.
What OAuth configuration is required?
All instances require oauthConfig with clientId and clientSecret properties.
Networking & Connectivity
What are my networking options for a Looker instance?
You have three options: public IP only (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.
How do I set up VPC peering for private connectivity?
Create three resources: a 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.
How do I configure Private Service Connect?
Set pscEnabled: true, privateIpEnabled: false, and publicIpEnabled: false. Configure pscConfig.allowedVpcs with the VPC networks that can connect.
Security & Encryption
How do I enable KMS encryption for my Looker instance?
Set 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.
Can I enable FIPS 140-2 encryption?
Yes, set fipsEnabled: true. This requires the LOOKER_CORE_ENTERPRISE_ANNUAL platform edition.
How do I restrict access by email domain?
Configure adminSettings.allowedEmailDomains with a list of allowed domains.
Maintenance & Lifecycle
Why is my Looker instance failing to delete?
If 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.
How do I schedule maintenance windows?
Configure maintenanceWindow with dayOfWeek and startTime (hours, minutes, seconds, nanos). Maintenance occurs once a month and requires an instance restart.
Can I block maintenance during specific periods?
Yes, use denyMaintenancePeriod with startDate, endDate, and time. You must allow at least 14 days of maintenance availability between any two denial periods.
Advanced Features
How do I enable Gemini AI features?
Set geminiEnabled: true in your instance configuration.
How do I configure a custom domain?
Set customDomain.domain to your domain name.

Using a different cloud?

Explore analytics guides for other cloud providers: