Configure GCP BigQuery Dataset Access

The gcp:bigquery/datasetAccess:DatasetAccess resource, part of the Pulumi GCP provider, grants access to a BigQuery dataset for a single entity: a user, service account, view, routine, or another dataset. This guide focuses on three capabilities: user and service account access, view authorization, and dataset and routine authorization.

This resource attaches to existing datasets and references existing service accounts, views, routines, or other datasets. It’s designed for cases where you can’t compile a full access list in the Dataset resource itself. The examples are intentionally small. Combine them with your own datasets and identity management.

Grant user access with a service account

Teams often grant service accounts specific roles on datasets for automated pipelines or data analysis workflows.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const dataset = new gcp.bigquery.Dataset("dataset", {datasetId: "example_dataset"});
const bqowner = new gcp.serviceaccount.Account("bqowner", {accountId: "bqowner"});
const access = new gcp.bigquery.DatasetAccess("access", {
    datasetId: dataset.datasetId,
    role: "OWNER",
    userByEmail: bqowner.email,
});
import pulumi
import pulumi_gcp as gcp

dataset = gcp.bigquery.Dataset("dataset", dataset_id="example_dataset")
bqowner = gcp.serviceaccount.Account("bqowner", account_id="bqowner")
access = gcp.bigquery.DatasetAccess("access",
    dataset_id=dataset.dataset_id,
    role="OWNER",
    user_by_email=bqowner.email)
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/bigquery"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/serviceaccount"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		dataset, err := bigquery.NewDataset(ctx, "dataset", &bigquery.DatasetArgs{
			DatasetId: pulumi.String("example_dataset"),
		})
		if err != nil {
			return err
		}
		bqowner, err := serviceaccount.NewAccount(ctx, "bqowner", &serviceaccount.AccountArgs{
			AccountId: pulumi.String("bqowner"),
		})
		if err != nil {
			return err
		}
		_, err = bigquery.NewDatasetAccess(ctx, "access", &bigquery.DatasetAccessArgs{
			DatasetId:   dataset.DatasetId,
			Role:        pulumi.String("OWNER"),
			UserByEmail: bqowner.Email,
		})
		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 dataset = new Gcp.BigQuery.Dataset("dataset", new()
    {
        DatasetId = "example_dataset",
    });

    var bqowner = new Gcp.ServiceAccount.Account("bqowner", new()
    {
        AccountId = "bqowner",
    });

    var access = new Gcp.BigQuery.DatasetAccess("access", new()
    {
        DatasetId = dataset.DatasetId,
        Role = "OWNER",
        UserByEmail = bqowner.Email,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.bigquery.Dataset;
import com.pulumi.gcp.bigquery.DatasetArgs;
import com.pulumi.gcp.serviceaccount.Account;
import com.pulumi.gcp.serviceaccount.AccountArgs;
import com.pulumi.gcp.bigquery.DatasetAccess;
import com.pulumi.gcp.bigquery.DatasetAccessArgs;
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 dataset = new Dataset("dataset", DatasetArgs.builder()
            .datasetId("example_dataset")
            .build());

        var bqowner = new Account("bqowner", AccountArgs.builder()
            .accountId("bqowner")
            .build());

        var access = new DatasetAccess("access", DatasetAccessArgs.builder()
            .datasetId(dataset.datasetId())
            .role("OWNER")
            .userByEmail(bqowner.email())
            .build());

    }
}
resources:
  access:
    type: gcp:bigquery:DatasetAccess
    properties:
      datasetId: ${dataset.datasetId}
      role: OWNER
      userByEmail: ${bqowner.email}
  dataset:
    type: gcp:bigquery:Dataset
    properties:
      datasetId: example_dataset
  bqowner:
    type: gcp:serviceaccount:Account
    properties:
      accountId: bqowner

The datasetId property identifies the target dataset. The role property uses legacy format (OWNER, READER, WRITER) rather than roles/bigquery.dataOwner. The userByEmail property specifies the service account email that receives access.

Grant access to a view from another dataset

When a view in one dataset needs to query tables in another dataset, you authorize the view rather than individual users.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const _private = new gcp.bigquery.Dataset("private", {datasetId: "example_dataset"});
const _public = new gcp.bigquery.Dataset("public", {datasetId: "example_dataset2"});
const publicTable = new gcp.bigquery.Table("public", {
    deletionProtection: false,
    datasetId: _public.datasetId,
    tableId: "example_table",
    view: {
        query: "SELECT state FROM [lookerdata:cdc.project_tycho_reports]",
        useLegacySql: false,
    },
});
const access = new gcp.bigquery.DatasetAccess("access", {
    datasetId: _private.datasetId,
    view: {
        projectId: publicTable.project,
        datasetId: _public.datasetId,
        tableId: publicTable.tableId,
    },
});
import pulumi
import pulumi_gcp as gcp

private = gcp.bigquery.Dataset("private", dataset_id="example_dataset")
public = gcp.bigquery.Dataset("public", dataset_id="example_dataset2")
public_table = gcp.bigquery.Table("public",
    deletion_protection=False,
    dataset_id=public.dataset_id,
    table_id="example_table",
    view={
        "query": "SELECT state FROM [lookerdata:cdc.project_tycho_reports]",
        "use_legacy_sql": False,
    })
access = gcp.bigquery.DatasetAccess("access",
    dataset_id=private.dataset_id,
    view={
        "project_id": public_table.project,
        "dataset_id": public.dataset_id,
        "table_id": public_table.table_id,
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/bigquery"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		private, err := bigquery.NewDataset(ctx, "private", &bigquery.DatasetArgs{
			DatasetId: pulumi.String("example_dataset"),
		})
		if err != nil {
			return err
		}
		public, err := bigquery.NewDataset(ctx, "public", &bigquery.DatasetArgs{
			DatasetId: pulumi.String("example_dataset2"),
		})
		if err != nil {
			return err
		}
		publicTable, err := bigquery.NewTable(ctx, "public", &bigquery.TableArgs{
			DeletionProtection: pulumi.Bool(false),
			DatasetId:          public.DatasetId,
			TableId:            pulumi.String("example_table"),
			View: &bigquery.TableViewArgs{
				Query:        pulumi.String("SELECT state FROM [lookerdata:cdc.project_tycho_reports]"),
				UseLegacySql: pulumi.Bool(false),
			},
		})
		if err != nil {
			return err
		}
		_, err = bigquery.NewDatasetAccess(ctx, "access", &bigquery.DatasetAccessArgs{
			DatasetId: private.DatasetId,
			View: &bigquery.DatasetAccessViewArgs{
				ProjectId: publicTable.Project,
				DatasetId: public.DatasetId,
				TableId:   publicTable.TableId,
			},
		})
		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 @private = new Gcp.BigQuery.Dataset("private", new()
    {
        DatasetId = "example_dataset",
    });

    var @public = new Gcp.BigQuery.Dataset("public", new()
    {
        DatasetId = "example_dataset2",
    });

    var publicTable = new Gcp.BigQuery.Table("public", new()
    {
        DeletionProtection = false,
        DatasetId = @public.DatasetId,
        TableId = "example_table",
        View = new Gcp.BigQuery.Inputs.TableViewArgs
        {
            Query = "SELECT state FROM [lookerdata:cdc.project_tycho_reports]",
            UseLegacySql = false,
        },
    });

    var access = new Gcp.BigQuery.DatasetAccess("access", new()
    {
        DatasetId = @private.DatasetId,
        View = new Gcp.BigQuery.Inputs.DatasetAccessViewArgs
        {
            ProjectId = publicTable.Project,
            DatasetId = @public.DatasetId,
            TableId = publicTable.TableId,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.bigquery.Dataset;
import com.pulumi.gcp.bigquery.DatasetArgs;
import com.pulumi.gcp.bigquery.Table;
import com.pulumi.gcp.bigquery.TableArgs;
import com.pulumi.gcp.bigquery.inputs.TableViewArgs;
import com.pulumi.gcp.bigquery.DatasetAccess;
import com.pulumi.gcp.bigquery.DatasetAccessArgs;
import com.pulumi.gcp.bigquery.inputs.DatasetAccessViewArgs;
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 private_ = new Dataset("private", DatasetArgs.builder()
            .datasetId("example_dataset")
            .build());

        var public_ = new Dataset("public", DatasetArgs.builder()
            .datasetId("example_dataset2")
            .build());

        var publicTable = new Table("publicTable", TableArgs.builder()
            .deletionProtection(false)
            .datasetId(public_.datasetId())
            .tableId("example_table")
            .view(TableViewArgs.builder()
                .query("SELECT state FROM [lookerdata:cdc.project_tycho_reports]")
                .useLegacySql(false)
                .build())
            .build());

        var access = new DatasetAccess("access", DatasetAccessArgs.builder()
            .datasetId(private_.datasetId())
            .view(DatasetAccessViewArgs.builder()
                .projectId(publicTable.project())
                .datasetId(public_.datasetId())
                .tableId(publicTable.tableId())
                .build())
            .build());

    }
}
resources:
  access:
    type: gcp:bigquery:DatasetAccess
    properties:
      datasetId: ${private.datasetId}
      view:
        projectId: ${publicTable.project}
        datasetId: ${public.datasetId}
        tableId: ${publicTable.tableId}
  private:
    type: gcp:bigquery:Dataset
    properties:
      datasetId: example_dataset
  public:
    type: gcp:bigquery:Dataset
    properties:
      datasetId: example_dataset2
  publicTable:
    type: gcp:bigquery:Table
    name: public
    properties:
      deletionProtection: false
      datasetId: ${public.datasetId}
      tableId: example_table
      view:
        query: SELECT state FROM [lookerdata:cdc.project_tycho_reports]
        useLegacySql: false

The view block identifies the view by projectId, datasetId, and tableId. When you grant view access, queries against that view can read tables in the target dataset. No role property is required for view authorization.

Authorize all views in another dataset

Instead of authorizing individual views, you can grant access to all views in a dataset at once.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const _private = new gcp.bigquery.Dataset("private", {datasetId: "private"});
const _public = new gcp.bigquery.Dataset("public", {datasetId: "public"});
const access = new gcp.bigquery.DatasetAccess("access", {
    datasetId: _private.datasetId,
    authorizedDataset: {
        dataset: {
            projectId: _public.project,
            datasetId: _public.datasetId,
        },
        targetTypes: ["VIEWS"],
    },
});
import pulumi
import pulumi_gcp as gcp

private = gcp.bigquery.Dataset("private", dataset_id="private")
public = gcp.bigquery.Dataset("public", dataset_id="public")
access = gcp.bigquery.DatasetAccess("access",
    dataset_id=private.dataset_id,
    authorized_dataset={
        "dataset": {
            "project_id": public.project,
            "dataset_id": public.dataset_id,
        },
        "target_types": ["VIEWS"],
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/bigquery"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		private, err := bigquery.NewDataset(ctx, "private", &bigquery.DatasetArgs{
			DatasetId: pulumi.String("private"),
		})
		if err != nil {
			return err
		}
		public, err := bigquery.NewDataset(ctx, "public", &bigquery.DatasetArgs{
			DatasetId: pulumi.String("public"),
		})
		if err != nil {
			return err
		}
		_, err = bigquery.NewDatasetAccess(ctx, "access", &bigquery.DatasetAccessArgs{
			DatasetId: private.DatasetId,
			AuthorizedDataset: &bigquery.DatasetAccessAuthorizedDatasetArgs{
				Dataset: &bigquery.DatasetAccessAuthorizedDatasetDatasetArgs{
					ProjectId: public.Project,
					DatasetId: public.DatasetId,
				},
				TargetTypes: pulumi.StringArray{
					pulumi.String("VIEWS"),
				},
			},
		})
		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 @private = new Gcp.BigQuery.Dataset("private", new()
    {
        DatasetId = "private",
    });

    var @public = new Gcp.BigQuery.Dataset("public", new()
    {
        DatasetId = "public",
    });

    var access = new Gcp.BigQuery.DatasetAccess("access", new()
    {
        DatasetId = @private.DatasetId,
        AuthorizedDataset = new Gcp.BigQuery.Inputs.DatasetAccessAuthorizedDatasetArgs
        {
            Dataset = new Gcp.BigQuery.Inputs.DatasetAccessAuthorizedDatasetDatasetArgs
            {
                ProjectId = @public.Project,
                DatasetId = @public.DatasetId,
            },
            TargetTypes = new[]
            {
                "VIEWS",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.bigquery.Dataset;
import com.pulumi.gcp.bigquery.DatasetArgs;
import com.pulumi.gcp.bigquery.DatasetAccess;
import com.pulumi.gcp.bigquery.DatasetAccessArgs;
import com.pulumi.gcp.bigquery.inputs.DatasetAccessAuthorizedDatasetArgs;
import com.pulumi.gcp.bigquery.inputs.DatasetAccessAuthorizedDatasetDatasetArgs;
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 private_ = new Dataset("private", DatasetArgs.builder()
            .datasetId("private")
            .build());

        var public_ = new Dataset("public", DatasetArgs.builder()
            .datasetId("public")
            .build());

        var access = new DatasetAccess("access", DatasetAccessArgs.builder()
            .datasetId(private_.datasetId())
            .authorizedDataset(DatasetAccessAuthorizedDatasetArgs.builder()
                .dataset(DatasetAccessAuthorizedDatasetDatasetArgs.builder()
                    .projectId(public_.project())
                    .datasetId(public_.datasetId())
                    .build())
                .targetTypes("VIEWS")
                .build())
            .build());

    }
}
resources:
  access:
    type: gcp:bigquery:DatasetAccess
    properties:
      datasetId: ${private.datasetId}
      authorizedDataset:
        dataset:
          projectId: ${public.project}
          datasetId: ${public.datasetId}
        targetTypes:
          - VIEWS
  private:
    type: gcp:bigquery:Dataset
    properties:
      datasetId: private
  public:
    type: gcp:bigquery:Dataset
    properties:
      datasetId: public

The authorizedDataset block identifies the source dataset and specifies targetTypes to limit which resource types receive access. Setting targetTypes to [“VIEWS”] means only views in the public dataset can query the private dataset.

Authorize a routine from another dataset

User-defined functions and stored procedures sometimes need to read tables from another dataset.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const _public = new gcp.bigquery.Dataset("public", {
    datasetId: "public_dataset",
    description: "This dataset is public",
});
const publicRoutine = new gcp.bigquery.Routine("public", {
    datasetId: _public.datasetId,
    routineId: "public_routine",
    routineType: "TABLE_VALUED_FUNCTION",
    language: "SQL",
    definitionBody: "SELECT 1 + value AS value\n",
    arguments: [{
        name: "value",
        argumentKind: "FIXED_TYPE",
        dataType: JSON.stringify({
            typeKind: "INT64",
        }),
    }],
    returnTableType: JSON.stringify({
        columns: [{
            name: "value",
            type: {
                typeKind: "INT64",
            },
        }],
    }),
});
const _private = new gcp.bigquery.Dataset("private", {
    datasetId: "private_dataset",
    description: "This dataset is private",
});
const authorizedRoutine = new gcp.bigquery.DatasetAccess("authorized_routine", {
    datasetId: _private.datasetId,
    routine: {
        projectId: publicRoutine.project,
        datasetId: publicRoutine.datasetId,
        routineId: publicRoutine.routineId,
    },
});
import pulumi
import json
import pulumi_gcp as gcp

public = gcp.bigquery.Dataset("public",
    dataset_id="public_dataset",
    description="This dataset is public")
public_routine = gcp.bigquery.Routine("public",
    dataset_id=public.dataset_id,
    routine_id="public_routine",
    routine_type="TABLE_VALUED_FUNCTION",
    language="SQL",
    definition_body="SELECT 1 + value AS value\n",
    arguments=[{
        "name": "value",
        "argument_kind": "FIXED_TYPE",
        "data_type": json.dumps({
            "typeKind": "INT64",
        }),
    }],
    return_table_type=json.dumps({
        "columns": [{
            "name": "value",
            "type": {
                "typeKind": "INT64",
            },
        }],
    }))
private = gcp.bigquery.Dataset("private",
    dataset_id="private_dataset",
    description="This dataset is private")
authorized_routine = gcp.bigquery.DatasetAccess("authorized_routine",
    dataset_id=private.dataset_id,
    routine={
        "project_id": public_routine.project,
        "dataset_id": public_routine.dataset_id,
        "routine_id": public_routine.routine_id,
    })
package main

import (
	"encoding/json"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/bigquery"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		public, err := bigquery.NewDataset(ctx, "public", &bigquery.DatasetArgs{
			DatasetId:   pulumi.String("public_dataset"),
			Description: pulumi.String("This dataset is public"),
		})
		if err != nil {
			return err
		}
		tmpJSON0, err := json.Marshal(map[string]interface{}{
			"typeKind": "INT64",
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		tmpJSON1, err := json.Marshal(map[string]interface{}{
			"columns": []map[string]interface{}{
				map[string]interface{}{
					"name": "value",
					"type": map[string]interface{}{
						"typeKind": "INT64",
					},
				},
			},
		})
		if err != nil {
			return err
		}
		json1 := string(tmpJSON1)
		publicRoutine, err := bigquery.NewRoutine(ctx, "public", &bigquery.RoutineArgs{
			DatasetId:      public.DatasetId,
			RoutineId:      pulumi.String("public_routine"),
			RoutineType:    pulumi.String("TABLE_VALUED_FUNCTION"),
			Language:       pulumi.String("SQL"),
			DefinitionBody: pulumi.String("SELECT 1 + value AS value\n"),
			Arguments: bigquery.RoutineArgumentArray{
				&bigquery.RoutineArgumentArgs{
					Name:         pulumi.String("value"),
					ArgumentKind: pulumi.String("FIXED_TYPE"),
					DataType:     pulumi.String(json0),
				},
			},
			ReturnTableType: pulumi.String(json1),
		})
		if err != nil {
			return err
		}
		private, err := bigquery.NewDataset(ctx, "private", &bigquery.DatasetArgs{
			DatasetId:   pulumi.String("private_dataset"),
			Description: pulumi.String("This dataset is private"),
		})
		if err != nil {
			return err
		}
		_, err = bigquery.NewDatasetAccess(ctx, "authorized_routine", &bigquery.DatasetAccessArgs{
			DatasetId: private.DatasetId,
			Routine: &bigquery.DatasetAccessRoutineArgs{
				ProjectId: publicRoutine.Project,
				DatasetId: publicRoutine.DatasetId,
				RoutineId: publicRoutine.RoutineId,
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var @public = new Gcp.BigQuery.Dataset("public", new()
    {
        DatasetId = "public_dataset",
        Description = "This dataset is public",
    });

    var publicRoutine = new Gcp.BigQuery.Routine("public", new()
    {
        DatasetId = @public.DatasetId,
        RoutineId = "public_routine",
        RoutineType = "TABLE_VALUED_FUNCTION",
        Language = "SQL",
        DefinitionBody = @"SELECT 1 + value AS value
",
        Arguments = new[]
        {
            new Gcp.BigQuery.Inputs.RoutineArgumentArgs
            {
                Name = "value",
                ArgumentKind = "FIXED_TYPE",
                DataType = JsonSerializer.Serialize(new Dictionary<string, object?>
                {
                    ["typeKind"] = "INT64",
                }),
            },
        },
        ReturnTableType = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["columns"] = new[]
            {
                new Dictionary<string, object?>
                {
                    ["name"] = "value",
                    ["type"] = new Dictionary<string, object?>
                    {
                        ["typeKind"] = "INT64",
                    },
                },
            },
        }),
    });

    var @private = new Gcp.BigQuery.Dataset("private", new()
    {
        DatasetId = "private_dataset",
        Description = "This dataset is private",
    });

    var authorizedRoutine = new Gcp.BigQuery.DatasetAccess("authorized_routine", new()
    {
        DatasetId = @private.DatasetId,
        Routine = new Gcp.BigQuery.Inputs.DatasetAccessRoutineArgs
        {
            ProjectId = publicRoutine.Project,
            DatasetId = publicRoutine.DatasetId,
            RoutineId = publicRoutine.RoutineId,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.bigquery.Dataset;
import com.pulumi.gcp.bigquery.DatasetArgs;
import com.pulumi.gcp.bigquery.Routine;
import com.pulumi.gcp.bigquery.RoutineArgs;
import com.pulumi.gcp.bigquery.inputs.RoutineArgumentArgs;
import com.pulumi.gcp.bigquery.DatasetAccess;
import com.pulumi.gcp.bigquery.DatasetAccessArgs;
import com.pulumi.gcp.bigquery.inputs.DatasetAccessRoutineArgs;
import static com.pulumi.codegen.internal.Serialization.*;
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 public_ = new Dataset("public", DatasetArgs.builder()
            .datasetId("public_dataset")
            .description("This dataset is public")
            .build());

        var publicRoutine = new Routine("publicRoutine", RoutineArgs.builder()
            .datasetId(public_.datasetId())
            .routineId("public_routine")
            .routineType("TABLE_VALUED_FUNCTION")
            .language("SQL")
            .definitionBody("""
SELECT 1 + value AS value
            """)
            .arguments(RoutineArgumentArgs.builder()
                .name("value")
                .argumentKind("FIXED_TYPE")
                .dataType(serializeJson(
                    jsonObject(
                        jsonProperty("typeKind", "INT64")
                    )))
                .build())
            .returnTableType(serializeJson(
                jsonObject(
                    jsonProperty("columns", jsonArray(jsonObject(
                        jsonProperty("name", "value"),
                        jsonProperty("type", jsonObject(
                            jsonProperty("typeKind", "INT64")
                        ))
                    )))
                )))
            .build());

        var private_ = new Dataset("private", DatasetArgs.builder()
            .datasetId("private_dataset")
            .description("This dataset is private")
            .build());

        var authorizedRoutine = new DatasetAccess("authorizedRoutine", DatasetAccessArgs.builder()
            .datasetId(private_.datasetId())
            .routine(DatasetAccessRoutineArgs.builder()
                .projectId(publicRoutine.project())
                .datasetId(publicRoutine.datasetId())
                .routineId(publicRoutine.routineId())
                .build())
            .build());

    }
}
resources:
  public:
    type: gcp:bigquery:Dataset
    properties:
      datasetId: public_dataset
      description: This dataset is public
  publicRoutine:
    type: gcp:bigquery:Routine
    name: public
    properties:
      datasetId: ${public.datasetId}
      routineId: public_routine
      routineType: TABLE_VALUED_FUNCTION
      language: SQL
      definitionBody: |
        SELECT 1 + value AS value        
      arguments:
        - name: value
          argumentKind: FIXED_TYPE
          dataType:
            fn::toJSON:
              typeKind: INT64
      returnTableType:
        fn::toJSON:
          columns:
            - name: value
              type:
                typeKind: INT64
  private:
    type: gcp:bigquery:Dataset
    properties:
      datasetId: private_dataset
      description: This dataset is private
  authorizedRoutine:
    type: gcp:bigquery:DatasetAccess
    name: authorized_routine
    properties:
      datasetId: ${private.datasetId}
      routine:
        projectId: ${publicRoutine.project}
        datasetId: ${publicRoutine.datasetId}
        routineId: ${publicRoutine.routineId}

The routine block identifies the routine by projectId, datasetId, and routineId. Queries executed against that routine can read tables in the target dataset. Like view authorization, no role property is required.

Beyond these examples

These snippets focus on specific dataset access features: user and service account access, view and routine authorization, and dataset-level authorization. They’re intentionally minimal rather than full access control systems.

The examples reference pre-existing infrastructure such as BigQuery datasets (the access resource attaches to existing datasets), and service accounts, views, routines, or other datasets being authorized. They focus on configuring access grants rather than provisioning the underlying resources.

To keep things focused, common access patterns are omitted, including:

  • Group and domain-based access (groupByEmail, domain)
  • Special groups (projectOwners, projectReaders, allAuthenticatedUsers)
  • IAM member syntax (iamMember for allUsers, etc.)
  • Conditional access with CEL expressions (condition block)

These omissions are intentional: the goal is to illustrate how each access type is wired, not provide drop-in access control modules. See the BigQuery DatasetAccess resource reference for all available configuration options.

Let's configure GCP BigQuery Dataset Access

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Resource Conflicts & Lifecycle
How do I avoid conflicts when using DatasetAccess with a Dataset resource?
When using DatasetAccess alongside gcp.bigquery.Dataset, the Dataset resource must either have no access blocks defined or include a lifecycle block with ignore_changes = [access]. Additionally, you cannot modify both resources in the same apply operation.
Role Configuration & Formatting
Why am I seeing a perpetual diff on the role field?
You must use the legacy role format like OWNER instead of roles/bigquery.dataOwner. The API accepts both formats but always returns the legacy format, causing perpetual diffs if you use the modern format.
What does the apiUpdatedMember output mean?
When apiUpdatedMember is true, it indicates that the iamMember you specified was translated to a different member type by the API and is stored in state differently.
Access Types & Configuration
What types of access can I grant to a dataset?
You can grant access using seven different member types: userByEmail, groupByEmail, domain, specialGroup (projectOwners, projectReaders, projectWriters, allAuthenticatedUsers), iamMember, view, routine, or authorizedDataset.
What special groups are available for dataset access?
Four special groups are available: projectOwners, projectReaders, projectWriters, and allAuthenticatedUsers.
Immutability & Updates
Can I modify access configuration after creation?
No, all properties are immutable. Any changes to datasetId, role, or access member properties require replacing the resource.
What happens if I update a view or routine that has granted access?
If a view or routine is updated by any user, you must re-grant access by updating the DatasetAccess resource.
Advanced Access Patterns
How do I grant access to a view from another dataset?
Configure the view property with projectId, datasetId, and tableId pointing to the view. The role field is not required when granting view access.
How do I grant access to all views in another dataset?
Use authorizedDataset with the target dataset’s projectId and datasetId, and set targetTypes to ["VIEWS"].
Can I import existing dataset access configurations?
No, this resource does not support import.

Using a different cloud?

Explore analytics guides for other cloud providers: