Create GCP BigQuery Analytics Hub Listings

The gcp:bigqueryanalyticshub/listing:Listing resource, part of the Pulumi GCP provider, defines a listing within an Analytics Hub data exchange: what data source it shares, who can discover it, and what restrictions apply. This guide focuses on three capabilities: BigQuery dataset and Pub/Sub topic sharing, export restrictions and query logging, and public discovery and regional replication.

Listings belong to DataExchange resources and reference BigQuery datasets, tables, or Pub/Sub topics. The examples are intentionally small. Combine them with your own data exchanges, datasets, and access policies.

Share a BigQuery dataset through Analytics Hub

Most Analytics Hub deployments begin by making a BigQuery dataset discoverable and subscribable within a data exchange.

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

const listing = new gcp.bigqueryanalyticshub.DataExchange("listing", {
    location: "US",
    dataExchangeId: "my_data_exchange",
    displayName: "my_data_exchange",
    description: "example data exchange",
});
const listingDataset = new gcp.bigquery.Dataset("listing", {
    datasetId: "my_listing",
    friendlyName: "my_listing",
    description: "example data exchange",
    location: "US",
});
const listingListing = new gcp.bigqueryanalyticshub.Listing("listing", {
    location: "US",
    dataExchangeId: listing.dataExchangeId,
    listingId: "my_listing",
    displayName: "my_listing",
    description: "example data exchange",
    bigqueryDataset: {
        dataset: listingDataset.id,
    },
});
import pulumi
import pulumi_gcp as gcp

listing = gcp.bigqueryanalyticshub.DataExchange("listing",
    location="US",
    data_exchange_id="my_data_exchange",
    display_name="my_data_exchange",
    description="example data exchange")
listing_dataset = gcp.bigquery.Dataset("listing",
    dataset_id="my_listing",
    friendly_name="my_listing",
    description="example data exchange",
    location="US")
listing_listing = gcp.bigqueryanalyticshub.Listing("listing",
    location="US",
    data_exchange_id=listing.data_exchange_id,
    listing_id="my_listing",
    display_name="my_listing",
    description="example data exchange",
    bigquery_dataset={
        "dataset": listing_dataset.id,
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		listing, err := bigqueryanalyticshub.NewDataExchange(ctx, "listing", &bigqueryanalyticshub.DataExchangeArgs{
			Location:       pulumi.String("US"),
			DataExchangeId: pulumi.String("my_data_exchange"),
			DisplayName:    pulumi.String("my_data_exchange"),
			Description:    pulumi.String("example data exchange"),
		})
		if err != nil {
			return err
		}
		listingDataset, err := bigquery.NewDataset(ctx, "listing", &bigquery.DatasetArgs{
			DatasetId:    pulumi.String("my_listing"),
			FriendlyName: pulumi.String("my_listing"),
			Description:  pulumi.String("example data exchange"),
			Location:     pulumi.String("US"),
		})
		if err != nil {
			return err
		}
		_, err = bigqueryanalyticshub.NewListing(ctx, "listing", &bigqueryanalyticshub.ListingArgs{
			Location:       pulumi.String("US"),
			DataExchangeId: listing.DataExchangeId,
			ListingId:      pulumi.String("my_listing"),
			DisplayName:    pulumi.String("my_listing"),
			Description:    pulumi.String("example data exchange"),
			BigqueryDataset: &bigqueryanalyticshub.ListingBigqueryDatasetArgs{
				Dataset: listingDataset.ID(),
			},
		})
		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 listing = new Gcp.BigQueryAnalyticsHub.DataExchange("listing", new()
    {
        Location = "US",
        DataExchangeId = "my_data_exchange",
        DisplayName = "my_data_exchange",
        Description = "example data exchange",
    });

    var listingDataset = new Gcp.BigQuery.Dataset("listing", new()
    {
        DatasetId = "my_listing",
        FriendlyName = "my_listing",
        Description = "example data exchange",
        Location = "US",
    });

    var listingListing = new Gcp.BigQueryAnalyticsHub.Listing("listing", new()
    {
        Location = "US",
        DataExchangeId = listing.DataExchangeId,
        ListingId = "my_listing",
        DisplayName = "my_listing",
        Description = "example data exchange",
        BigqueryDataset = new Gcp.BigQueryAnalyticsHub.Inputs.ListingBigqueryDatasetArgs
        {
            Dataset = listingDataset.Id,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.bigqueryanalyticshub.DataExchange;
import com.pulumi.gcp.bigqueryanalyticshub.DataExchangeArgs;
import com.pulumi.gcp.bigquery.Dataset;
import com.pulumi.gcp.bigquery.DatasetArgs;
import com.pulumi.gcp.bigqueryanalyticshub.Listing;
import com.pulumi.gcp.bigqueryanalyticshub.ListingArgs;
import com.pulumi.gcp.bigqueryanalyticshub.inputs.ListingBigqueryDatasetArgs;
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 listing = new DataExchange("listing", DataExchangeArgs.builder()
            .location("US")
            .dataExchangeId("my_data_exchange")
            .displayName("my_data_exchange")
            .description("example data exchange")
            .build());

        var listingDataset = new Dataset("listingDataset", DatasetArgs.builder()
            .datasetId("my_listing")
            .friendlyName("my_listing")
            .description("example data exchange")
            .location("US")
            .build());

        var listingListing = new Listing("listingListing", ListingArgs.builder()
            .location("US")
            .dataExchangeId(listing.dataExchangeId())
            .listingId("my_listing")
            .displayName("my_listing")
            .description("example data exchange")
            .bigqueryDataset(ListingBigqueryDatasetArgs.builder()
                .dataset(listingDataset.id())
                .build())
            .build());

    }
}
resources:
  listing:
    type: gcp:bigqueryanalyticshub:DataExchange
    properties:
      location: US
      dataExchangeId: my_data_exchange
      displayName: my_data_exchange
      description: example data exchange
  listingListing:
    type: gcp:bigqueryanalyticshub:Listing
    name: listing
    properties:
      location: US
      dataExchangeId: ${listing.dataExchangeId}
      listingId: my_listing
      displayName: my_listing
      description: example data exchange
      bigqueryDataset:
        dataset: ${listingDataset.id}
  listingDataset:
    type: gcp:bigquery:Dataset
    name: listing
    properties:
      datasetId: my_listing
      friendlyName: my_listing
      description: example data exchange
      location: US

The dataExchangeId connects the listing to its parent exchange. The bigqueryDataset property points to the dataset you’re sharing by its resource ID. Subscribers can then create linked datasets in their own projects that query your data.

Restrict data export from subscribed datasets

Organizations sharing sensitive data often prevent subscribers from exporting query results outside Analytics Hub.

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

const listing = new gcp.bigqueryanalyticshub.DataExchange("listing", {
    location: "US",
    dataExchangeId: "my_data_exchange",
    displayName: "my_data_exchange",
    description: "example data exchange",
});
const listingDataset = new gcp.bigquery.Dataset("listing", {
    datasetId: "my_listing",
    friendlyName: "my_listing",
    description: "example data exchange",
    location: "US",
});
const listingListing = new gcp.bigqueryanalyticshub.Listing("listing", {
    location: "US",
    dataExchangeId: listing.dataExchangeId,
    listingId: "my_listing",
    displayName: "my_listing",
    description: "example data exchange",
    bigqueryDataset: {
        dataset: listingDataset.id,
    },
    restrictedExportConfig: {
        enabled: true,
        restrictQueryResult: true,
    },
});
import pulumi
import pulumi_gcp as gcp

listing = gcp.bigqueryanalyticshub.DataExchange("listing",
    location="US",
    data_exchange_id="my_data_exchange",
    display_name="my_data_exchange",
    description="example data exchange")
listing_dataset = gcp.bigquery.Dataset("listing",
    dataset_id="my_listing",
    friendly_name="my_listing",
    description="example data exchange",
    location="US")
listing_listing = gcp.bigqueryanalyticshub.Listing("listing",
    location="US",
    data_exchange_id=listing.data_exchange_id,
    listing_id="my_listing",
    display_name="my_listing",
    description="example data exchange",
    bigquery_dataset={
        "dataset": listing_dataset.id,
    },
    restricted_export_config={
        "enabled": True,
        "restrict_query_result": True,
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		listing, err := bigqueryanalyticshub.NewDataExchange(ctx, "listing", &bigqueryanalyticshub.DataExchangeArgs{
			Location:       pulumi.String("US"),
			DataExchangeId: pulumi.String("my_data_exchange"),
			DisplayName:    pulumi.String("my_data_exchange"),
			Description:    pulumi.String("example data exchange"),
		})
		if err != nil {
			return err
		}
		listingDataset, err := bigquery.NewDataset(ctx, "listing", &bigquery.DatasetArgs{
			DatasetId:    pulumi.String("my_listing"),
			FriendlyName: pulumi.String("my_listing"),
			Description:  pulumi.String("example data exchange"),
			Location:     pulumi.String("US"),
		})
		if err != nil {
			return err
		}
		_, err = bigqueryanalyticshub.NewListing(ctx, "listing", &bigqueryanalyticshub.ListingArgs{
			Location:       pulumi.String("US"),
			DataExchangeId: listing.DataExchangeId,
			ListingId:      pulumi.String("my_listing"),
			DisplayName:    pulumi.String("my_listing"),
			Description:    pulumi.String("example data exchange"),
			BigqueryDataset: &bigqueryanalyticshub.ListingBigqueryDatasetArgs{
				Dataset: listingDataset.ID(),
			},
			RestrictedExportConfig: &bigqueryanalyticshub.ListingRestrictedExportConfigArgs{
				Enabled:             pulumi.Bool(true),
				RestrictQueryResult: pulumi.Bool(true),
			},
		})
		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 listing = new Gcp.BigQueryAnalyticsHub.DataExchange("listing", new()
    {
        Location = "US",
        DataExchangeId = "my_data_exchange",
        DisplayName = "my_data_exchange",
        Description = "example data exchange",
    });

    var listingDataset = new Gcp.BigQuery.Dataset("listing", new()
    {
        DatasetId = "my_listing",
        FriendlyName = "my_listing",
        Description = "example data exchange",
        Location = "US",
    });

    var listingListing = new Gcp.BigQueryAnalyticsHub.Listing("listing", new()
    {
        Location = "US",
        DataExchangeId = listing.DataExchangeId,
        ListingId = "my_listing",
        DisplayName = "my_listing",
        Description = "example data exchange",
        BigqueryDataset = new Gcp.BigQueryAnalyticsHub.Inputs.ListingBigqueryDatasetArgs
        {
            Dataset = listingDataset.Id,
        },
        RestrictedExportConfig = new Gcp.BigQueryAnalyticsHub.Inputs.ListingRestrictedExportConfigArgs
        {
            Enabled = true,
            RestrictQueryResult = true,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.bigqueryanalyticshub.DataExchange;
import com.pulumi.gcp.bigqueryanalyticshub.DataExchangeArgs;
import com.pulumi.gcp.bigquery.Dataset;
import com.pulumi.gcp.bigquery.DatasetArgs;
import com.pulumi.gcp.bigqueryanalyticshub.Listing;
import com.pulumi.gcp.bigqueryanalyticshub.ListingArgs;
import com.pulumi.gcp.bigqueryanalyticshub.inputs.ListingBigqueryDatasetArgs;
import com.pulumi.gcp.bigqueryanalyticshub.inputs.ListingRestrictedExportConfigArgs;
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 listing = new DataExchange("listing", DataExchangeArgs.builder()
            .location("US")
            .dataExchangeId("my_data_exchange")
            .displayName("my_data_exchange")
            .description("example data exchange")
            .build());

        var listingDataset = new Dataset("listingDataset", DatasetArgs.builder()
            .datasetId("my_listing")
            .friendlyName("my_listing")
            .description("example data exchange")
            .location("US")
            .build());

        var listingListing = new Listing("listingListing", ListingArgs.builder()
            .location("US")
            .dataExchangeId(listing.dataExchangeId())
            .listingId("my_listing")
            .displayName("my_listing")
            .description("example data exchange")
            .bigqueryDataset(ListingBigqueryDatasetArgs.builder()
                .dataset(listingDataset.id())
                .build())
            .restrictedExportConfig(ListingRestrictedExportConfigArgs.builder()
                .enabled(true)
                .restrictQueryResult(true)
                .build())
            .build());

    }
}
resources:
  listing:
    type: gcp:bigqueryanalyticshub:DataExchange
    properties:
      location: US
      dataExchangeId: my_data_exchange
      displayName: my_data_exchange
      description: example data exchange
  listingListing:
    type: gcp:bigqueryanalyticshub:Listing
    name: listing
    properties:
      location: US
      dataExchangeId: ${listing.dataExchangeId}
      listingId: my_listing
      displayName: my_listing
      description: example data exchange
      bigqueryDataset:
        dataset: ${listingDataset.id}
      restrictedExportConfig:
        enabled: true
        restrictQueryResult: true
  listingDataset:
    type: gcp:bigquery:Dataset
    name: listing
    properties:
      datasetId: my_listing
      friendlyName: my_listing
      description: example data exchange
      location: US

The restrictedExportConfig property controls data movement. When enabled is true, subscribers cannot export query results to external storage. Setting restrictQueryResult to true blocks all query result exports, not just table exports.

Share specific tables in a DCR exchange

Data Clean Room (DCR) exchanges require explicit resource selection rather than sharing entire datasets.

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

const listing = new gcp.bigqueryanalyticshub.DataExchange("listing", {
    location: "US",
    dataExchangeId: "dcr_data_exchange",
    displayName: "dcr_data_exchange",
    description: "example dcr data exchange",
    sharingEnvironmentConfig: {
        dcrExchangeConfig: {},
    },
});
const listingDataset = new gcp.bigquery.Dataset("listing", {
    datasetId: "dcr_listing",
    friendlyName: "dcr_listing",
    description: "example dcr data exchange",
    location: "US",
});
const listingTable = new gcp.bigquery.Table("listing", {
    deletionProtection: false,
    tableId: "dcr_listing",
    datasetId: listingDataset.datasetId,
    schema: `[
  {
    \\"name\\": \\"name\\",
    \\"type\\": \\"STRING\\",
    \\"mode\\": \\"NULLABLE\\"
  },
  {
    \\"name\\": \\"post_abbr\\",
    \\"type\\": \\"STRING\\",
    \\"mode\\": \\"NULLABLE\\"
  },
  {
    \\"name\\": \\"date\\",
    \\"type\\": \\"DATE\\",
    \\"mode\\": \\"NULLABLE\\"
  }
]
`,
});
const listingListing = new gcp.bigqueryanalyticshub.Listing("listing", {
    location: "US",
    dataExchangeId: listing.dataExchangeId,
    listingId: "dcr_listing",
    displayName: "dcr_listing",
    description: "example dcr data exchange",
    bigqueryDataset: {
        dataset: listingDataset.id,
        selectedResources: [{
            table: listingTable.id,
        }],
    },
    restrictedExportConfig: {
        enabled: true,
    },
});
import pulumi
import pulumi_gcp as gcp

listing = gcp.bigqueryanalyticshub.DataExchange("listing",
    location="US",
    data_exchange_id="dcr_data_exchange",
    display_name="dcr_data_exchange",
    description="example dcr data exchange",
    sharing_environment_config={
        "dcr_exchange_config": {},
    })
listing_dataset = gcp.bigquery.Dataset("listing",
    dataset_id="dcr_listing",
    friendly_name="dcr_listing",
    description="example dcr data exchange",
    location="US")
listing_table = gcp.bigquery.Table("listing",
    deletion_protection=False,
    table_id="dcr_listing",
    dataset_id=listing_dataset.dataset_id,
    schema="""[
  {
    \"name\": \"name\",
    \"type\": \"STRING\",
    \"mode\": \"NULLABLE\"
  },
  {
    \"name\": \"post_abbr\",
    \"type\": \"STRING\",
    \"mode\": \"NULLABLE\"
  },
  {
    \"name\": \"date\",
    \"type\": \"DATE\",
    \"mode\": \"NULLABLE\"
  }
]
""")
listing_listing = gcp.bigqueryanalyticshub.Listing("listing",
    location="US",
    data_exchange_id=listing.data_exchange_id,
    listing_id="dcr_listing",
    display_name="dcr_listing",
    description="example dcr data exchange",
    bigquery_dataset={
        "dataset": listing_dataset.id,
        "selected_resources": [{
            "table": listing_table.id,
        }],
    },
    restricted_export_config={
        "enabled": True,
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		listing, err := bigqueryanalyticshub.NewDataExchange(ctx, "listing", &bigqueryanalyticshub.DataExchangeArgs{
			Location:       pulumi.String("US"),
			DataExchangeId: pulumi.String("dcr_data_exchange"),
			DisplayName:    pulumi.String("dcr_data_exchange"),
			Description:    pulumi.String("example dcr data exchange"),
			SharingEnvironmentConfig: &bigqueryanalyticshub.DataExchangeSharingEnvironmentConfigArgs{
				DcrExchangeConfig: &bigqueryanalyticshub.DataExchangeSharingEnvironmentConfigDcrExchangeConfigArgs{},
			},
		})
		if err != nil {
			return err
		}
		listingDataset, err := bigquery.NewDataset(ctx, "listing", &bigquery.DatasetArgs{
			DatasetId:    pulumi.String("dcr_listing"),
			FriendlyName: pulumi.String("dcr_listing"),
			Description:  pulumi.String("example dcr data exchange"),
			Location:     pulumi.String("US"),
		})
		if err != nil {
			return err
		}
		listingTable, err := bigquery.NewTable(ctx, "listing", &bigquery.TableArgs{
			DeletionProtection: pulumi.Bool(false),
			TableId:            pulumi.String("dcr_listing"),
			DatasetId:          listingDataset.DatasetId,
			Schema: pulumi.String(`[
  {
    \"name\": \"name\",
    \"type\": \"STRING\",
    \"mode\": \"NULLABLE\"
  },
  {
    \"name\": \"post_abbr\",
    \"type\": \"STRING\",
    \"mode\": \"NULLABLE\"
  },
  {
    \"name\": \"date\",
    \"type\": \"DATE\",
    \"mode\": \"NULLABLE\"
  }
]
`),
		})
		if err != nil {
			return err
		}
		_, err = bigqueryanalyticshub.NewListing(ctx, "listing", &bigqueryanalyticshub.ListingArgs{
			Location:       pulumi.String("US"),
			DataExchangeId: listing.DataExchangeId,
			ListingId:      pulumi.String("dcr_listing"),
			DisplayName:    pulumi.String("dcr_listing"),
			Description:    pulumi.String("example dcr data exchange"),
			BigqueryDataset: &bigqueryanalyticshub.ListingBigqueryDatasetArgs{
				Dataset: listingDataset.ID(),
				SelectedResources: bigqueryanalyticshub.ListingBigqueryDatasetSelectedResourceArray{
					&bigqueryanalyticshub.ListingBigqueryDatasetSelectedResourceArgs{
						Table: listingTable.ID(),
					},
				},
			},
			RestrictedExportConfig: &bigqueryanalyticshub.ListingRestrictedExportConfigArgs{
				Enabled: pulumi.Bool(true),
			},
		})
		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 listing = new Gcp.BigQueryAnalyticsHub.DataExchange("listing", new()
    {
        Location = "US",
        DataExchangeId = "dcr_data_exchange",
        DisplayName = "dcr_data_exchange",
        Description = "example dcr data exchange",
        SharingEnvironmentConfig = new Gcp.BigQueryAnalyticsHub.Inputs.DataExchangeSharingEnvironmentConfigArgs
        {
            DcrExchangeConfig = null,
        },
    });

    var listingDataset = new Gcp.BigQuery.Dataset("listing", new()
    {
        DatasetId = "dcr_listing",
        FriendlyName = "dcr_listing",
        Description = "example dcr data exchange",
        Location = "US",
    });

    var listingTable = new Gcp.BigQuery.Table("listing", new()
    {
        DeletionProtection = false,
        TableId = "dcr_listing",
        DatasetId = listingDataset.DatasetId,
        Schema = @"[
  {
    \""name\"": \""name\"",
    \""type\"": \""STRING\"",
    \""mode\"": \""NULLABLE\""
  },
  {
    \""name\"": \""post_abbr\"",
    \""type\"": \""STRING\"",
    \""mode\"": \""NULLABLE\""
  },
  {
    \""name\"": \""date\"",
    \""type\"": \""DATE\"",
    \""mode\"": \""NULLABLE\""
  }
]
",
    });

    var listingListing = new Gcp.BigQueryAnalyticsHub.Listing("listing", new()
    {
        Location = "US",
        DataExchangeId = listing.DataExchangeId,
        ListingId = "dcr_listing",
        DisplayName = "dcr_listing",
        Description = "example dcr data exchange",
        BigqueryDataset = new Gcp.BigQueryAnalyticsHub.Inputs.ListingBigqueryDatasetArgs
        {
            Dataset = listingDataset.Id,
            SelectedResources = new[]
            {
                new Gcp.BigQueryAnalyticsHub.Inputs.ListingBigqueryDatasetSelectedResourceArgs
                {
                    Table = listingTable.Id,
                },
            },
        },
        RestrictedExportConfig = new Gcp.BigQueryAnalyticsHub.Inputs.ListingRestrictedExportConfigArgs
        {
            Enabled = true,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.bigqueryanalyticshub.DataExchange;
import com.pulumi.gcp.bigqueryanalyticshub.DataExchangeArgs;
import com.pulumi.gcp.bigqueryanalyticshub.inputs.DataExchangeSharingEnvironmentConfigArgs;
import com.pulumi.gcp.bigqueryanalyticshub.inputs.DataExchangeSharingEnvironmentConfigDcrExchangeConfigArgs;
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.bigqueryanalyticshub.Listing;
import com.pulumi.gcp.bigqueryanalyticshub.ListingArgs;
import com.pulumi.gcp.bigqueryanalyticshub.inputs.ListingBigqueryDatasetArgs;
import com.pulumi.gcp.bigqueryanalyticshub.inputs.ListingRestrictedExportConfigArgs;
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 listing = new DataExchange("listing", DataExchangeArgs.builder()
            .location("US")
            .dataExchangeId("dcr_data_exchange")
            .displayName("dcr_data_exchange")
            .description("example dcr data exchange")
            .sharingEnvironmentConfig(DataExchangeSharingEnvironmentConfigArgs.builder()
                .dcrExchangeConfig(DataExchangeSharingEnvironmentConfigDcrExchangeConfigArgs.builder()
                    .build())
                .build())
            .build());

        var listingDataset = new Dataset("listingDataset", DatasetArgs.builder()
            .datasetId("dcr_listing")
            .friendlyName("dcr_listing")
            .description("example dcr data exchange")
            .location("US")
            .build());

        var listingTable = new Table("listingTable", TableArgs.builder()
            .deletionProtection(false)
            .tableId("dcr_listing")
            .datasetId(listingDataset.datasetId())
            .schema("""
[
  {
    \"name\": \"name\",
    \"type\": \"STRING\",
    \"mode\": \"NULLABLE\"
  },
  {
    \"name\": \"post_abbr\",
    \"type\": \"STRING\",
    \"mode\": \"NULLABLE\"
  },
  {
    \"name\": \"date\",
    \"type\": \"DATE\",
    \"mode\": \"NULLABLE\"
  }
]
            """)
            .build());

        var listingListing = new Listing("listingListing", ListingArgs.builder()
            .location("US")
            .dataExchangeId(listing.dataExchangeId())
            .listingId("dcr_listing")
            .displayName("dcr_listing")
            .description("example dcr data exchange")
            .bigqueryDataset(ListingBigqueryDatasetArgs.builder()
                .dataset(listingDataset.id())
                .selectedResources(ListingBigqueryDatasetSelectedResourceArgs.builder()
                    .table(listingTable.id())
                    .build())
                .build())
            .restrictedExportConfig(ListingRestrictedExportConfigArgs.builder()
                .enabled(true)
                .build())
            .build());

    }
}
resources:
  listing:
    type: gcp:bigqueryanalyticshub:DataExchange
    properties:
      location: US
      dataExchangeId: dcr_data_exchange
      displayName: dcr_data_exchange
      description: example dcr data exchange
      sharingEnvironmentConfig:
        dcrExchangeConfig: {}
  listingListing:
    type: gcp:bigqueryanalyticshub:Listing
    name: listing
    properties:
      location: US
      dataExchangeId: ${listing.dataExchangeId}
      listingId: dcr_listing
      displayName: dcr_listing
      description: example dcr data exchange
      bigqueryDataset:
        dataset: ${listingDataset.id}
        selectedResources:
          - table: ${listingTable.id}
      restrictedExportConfig:
        enabled: true
  listingDataset:
    type: gcp:bigquery:Dataset
    name: listing
    properties:
      datasetId: dcr_listing
      friendlyName: dcr_listing
      description: example dcr data exchange
      location: US
  listingTable:
    type: gcp:bigquery:Table
    name: listing
    properties:
      deletionProtection: false
      tableId: dcr_listing
      datasetId: ${listingDataset.datasetId}
      schema: |
        [
          {
            \"name\": \"name\",
            \"type\": \"STRING\",
            \"mode\": \"NULLABLE\"
          },
          {
            \"name\": \"post_abbr\",
            \"type\": \"STRING\",
            \"mode\": \"NULLABLE\"
          },
          {
            \"name\": \"date\",
            \"type\": \"DATE\",
            \"mode\": \"NULLABLE\"
          }
        ]        

DCR exchanges use sharingEnvironmentConfig with dcrExchangeConfig to enable privacy-preserving analytics. The selectedResources array specifies individual tables or routines to share. This granular control supports compliance requirements where only certain columns or aggregations should be accessible.

Log subscriber email addresses for queries

Compliance requirements sometimes mandate tracking which users query shared datasets.

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

const listingLogEmail = new gcp.bigqueryanalyticshub.DataExchange("listing_log_email", {
    location: "US",
    dataExchangeId: "tf_test_log_email_de",
    displayName: "tf_test_log_email_de",
    description: "Example for log email test",
});
const listingLogEmailDataset = new gcp.bigquery.Dataset("listing_log_email", {
    datasetId: "tf_test_log_email_ds",
    friendlyName: "tf_test_log_email_ds",
    description: "Example for log email test",
    location: "US",
});
const listing = new gcp.bigqueryanalyticshub.Listing("listing", {
    location: "US",
    dataExchangeId: listingLogEmail.dataExchangeId,
    listingId: "tf_test_log_email_listing",
    displayName: "tf_test_log_email_listing",
    description: "Example for log email test",
    logLinkedDatasetQueryUserEmail: true,
    bigqueryDataset: {
        dataset: listingLogEmailDataset.id,
    },
});
import pulumi
import pulumi_gcp as gcp

listing_log_email = gcp.bigqueryanalyticshub.DataExchange("listing_log_email",
    location="US",
    data_exchange_id="tf_test_log_email_de",
    display_name="tf_test_log_email_de",
    description="Example for log email test")
listing_log_email_dataset = gcp.bigquery.Dataset("listing_log_email",
    dataset_id="tf_test_log_email_ds",
    friendly_name="tf_test_log_email_ds",
    description="Example for log email test",
    location="US")
listing = gcp.bigqueryanalyticshub.Listing("listing",
    location="US",
    data_exchange_id=listing_log_email.data_exchange_id,
    listing_id="tf_test_log_email_listing",
    display_name="tf_test_log_email_listing",
    description="Example for log email test",
    log_linked_dataset_query_user_email=True,
    bigquery_dataset={
        "dataset": listing_log_email_dataset.id,
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		listingLogEmail, err := bigqueryanalyticshub.NewDataExchange(ctx, "listing_log_email", &bigqueryanalyticshub.DataExchangeArgs{
			Location:       pulumi.String("US"),
			DataExchangeId: pulumi.String("tf_test_log_email_de"),
			DisplayName:    pulumi.String("tf_test_log_email_de"),
			Description:    pulumi.String("Example for log email test"),
		})
		if err != nil {
			return err
		}
		listingLogEmailDataset, err := bigquery.NewDataset(ctx, "listing_log_email", &bigquery.DatasetArgs{
			DatasetId:    pulumi.String("tf_test_log_email_ds"),
			FriendlyName: pulumi.String("tf_test_log_email_ds"),
			Description:  pulumi.String("Example for log email test"),
			Location:     pulumi.String("US"),
		})
		if err != nil {
			return err
		}
		_, err = bigqueryanalyticshub.NewListing(ctx, "listing", &bigqueryanalyticshub.ListingArgs{
			Location:                       pulumi.String("US"),
			DataExchangeId:                 listingLogEmail.DataExchangeId,
			ListingId:                      pulumi.String("tf_test_log_email_listing"),
			DisplayName:                    pulumi.String("tf_test_log_email_listing"),
			Description:                    pulumi.String("Example for log email test"),
			LogLinkedDatasetQueryUserEmail: pulumi.Bool(true),
			BigqueryDataset: &bigqueryanalyticshub.ListingBigqueryDatasetArgs{
				Dataset: listingLogEmailDataset.ID(),
			},
		})
		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 listingLogEmail = new Gcp.BigQueryAnalyticsHub.DataExchange("listing_log_email", new()
    {
        Location = "US",
        DataExchangeId = "tf_test_log_email_de",
        DisplayName = "tf_test_log_email_de",
        Description = "Example for log email test",
    });

    var listingLogEmailDataset = new Gcp.BigQuery.Dataset("listing_log_email", new()
    {
        DatasetId = "tf_test_log_email_ds",
        FriendlyName = "tf_test_log_email_ds",
        Description = "Example for log email test",
        Location = "US",
    });

    var listing = new Gcp.BigQueryAnalyticsHub.Listing("listing", new()
    {
        Location = "US",
        DataExchangeId = listingLogEmail.DataExchangeId,
        ListingId = "tf_test_log_email_listing",
        DisplayName = "tf_test_log_email_listing",
        Description = "Example for log email test",
        LogLinkedDatasetQueryUserEmail = true,
        BigqueryDataset = new Gcp.BigQueryAnalyticsHub.Inputs.ListingBigqueryDatasetArgs
        {
            Dataset = listingLogEmailDataset.Id,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.bigqueryanalyticshub.DataExchange;
import com.pulumi.gcp.bigqueryanalyticshub.DataExchangeArgs;
import com.pulumi.gcp.bigquery.Dataset;
import com.pulumi.gcp.bigquery.DatasetArgs;
import com.pulumi.gcp.bigqueryanalyticshub.Listing;
import com.pulumi.gcp.bigqueryanalyticshub.ListingArgs;
import com.pulumi.gcp.bigqueryanalyticshub.inputs.ListingBigqueryDatasetArgs;
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 listingLogEmail = new DataExchange("listingLogEmail", DataExchangeArgs.builder()
            .location("US")
            .dataExchangeId("tf_test_log_email_de")
            .displayName("tf_test_log_email_de")
            .description("Example for log email test")
            .build());

        var listingLogEmailDataset = new Dataset("listingLogEmailDataset", DatasetArgs.builder()
            .datasetId("tf_test_log_email_ds")
            .friendlyName("tf_test_log_email_ds")
            .description("Example for log email test")
            .location("US")
            .build());

        var listing = new Listing("listing", ListingArgs.builder()
            .location("US")
            .dataExchangeId(listingLogEmail.dataExchangeId())
            .listingId("tf_test_log_email_listing")
            .displayName("tf_test_log_email_listing")
            .description("Example for log email test")
            .logLinkedDatasetQueryUserEmail(true)
            .bigqueryDataset(ListingBigqueryDatasetArgs.builder()
                .dataset(listingLogEmailDataset.id())
                .build())
            .build());

    }
}
resources:
  listingLogEmail:
    type: gcp:bigqueryanalyticshub:DataExchange
    name: listing_log_email
    properties:
      location: US
      dataExchangeId: tf_test_log_email_de
      displayName: tf_test_log_email_de
      description: Example for log email test
  listing:
    type: gcp:bigqueryanalyticshub:Listing
    properties:
      location: US
      dataExchangeId: ${listingLogEmail.dataExchangeId}
      listingId: tf_test_log_email_listing
      displayName: tf_test_log_email_listing
      description: Example for log email test
      logLinkedDatasetQueryUserEmail: true
      bigqueryDataset:
        dataset: ${listingLogEmailDataset.id}
  listingLogEmailDataset:
    type: gcp:bigquery:Dataset
    name: listing_log_email
    properties:
      datasetId: tf_test_log_email_ds
      friendlyName: tf_test_log_email_ds
      description: Example for log email test
      location: US

The logLinkedDatasetQueryUserEmail property enables audit logging. When set to true, every query against the linked dataset logs the subscriber’s email address. This setting cannot be disabled once enabled.

Share Pub/Sub topics as data sources

Analytics Hub can share streaming data sources, not just static datasets.

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

const listing = new gcp.bigqueryanalyticshub.DataExchange("listing", {
    location: "US",
    dataExchangeId: "tf_test_pubsub_data_exchange",
    displayName: "tf_test_pubsub_data_exchange",
    description: "Example for pubsub topic source",
});
const tfTestPubsubTopic = new gcp.pubsub.Topic("tf_test_pubsub_topic", {name: "test_pubsub"});
const listingListing = new gcp.bigqueryanalyticshub.Listing("listing", {
    location: "US",
    dataExchangeId: listing.dataExchangeId,
    listingId: "tf_test_pubsub_listing",
    displayName: "tf_test_pubsub_listing",
    description: "Example for pubsub topic source",
    pubsubTopic: {
        topic: tfTestPubsubTopic.id,
        dataAffinityRegions: [
            "us-central1",
            "europe-west1",
        ],
    },
});
import pulumi
import pulumi_gcp as gcp

listing = gcp.bigqueryanalyticshub.DataExchange("listing",
    location="US",
    data_exchange_id="tf_test_pubsub_data_exchange",
    display_name="tf_test_pubsub_data_exchange",
    description="Example for pubsub topic source")
tf_test_pubsub_topic = gcp.pubsub.Topic("tf_test_pubsub_topic", name="test_pubsub")
listing_listing = gcp.bigqueryanalyticshub.Listing("listing",
    location="US",
    data_exchange_id=listing.data_exchange_id,
    listing_id="tf_test_pubsub_listing",
    display_name="tf_test_pubsub_listing",
    description="Example for pubsub topic source",
    pubsub_topic={
        "topic": tf_test_pubsub_topic.id,
        "data_affinity_regions": [
            "us-central1",
            "europe-west1",
        ],
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		listing, err := bigqueryanalyticshub.NewDataExchange(ctx, "listing", &bigqueryanalyticshub.DataExchangeArgs{
			Location:       pulumi.String("US"),
			DataExchangeId: pulumi.String("tf_test_pubsub_data_exchange"),
			DisplayName:    pulumi.String("tf_test_pubsub_data_exchange"),
			Description:    pulumi.String("Example for pubsub topic source"),
		})
		if err != nil {
			return err
		}
		tfTestPubsubTopic, err := pubsub.NewTopic(ctx, "tf_test_pubsub_topic", &pubsub.TopicArgs{
			Name: pulumi.String("test_pubsub"),
		})
		if err != nil {
			return err
		}
		_, err = bigqueryanalyticshub.NewListing(ctx, "listing", &bigqueryanalyticshub.ListingArgs{
			Location:       pulumi.String("US"),
			DataExchangeId: listing.DataExchangeId,
			ListingId:      pulumi.String("tf_test_pubsub_listing"),
			DisplayName:    pulumi.String("tf_test_pubsub_listing"),
			Description:    pulumi.String("Example for pubsub topic source"),
			PubsubTopic: &bigqueryanalyticshub.ListingPubsubTopicArgs{
				Topic: tfTestPubsubTopic.ID(),
				DataAffinityRegions: pulumi.StringArray{
					pulumi.String("us-central1"),
					pulumi.String("europe-west1"),
				},
			},
		})
		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 listing = new Gcp.BigQueryAnalyticsHub.DataExchange("listing", new()
    {
        Location = "US",
        DataExchangeId = "tf_test_pubsub_data_exchange",
        DisplayName = "tf_test_pubsub_data_exchange",
        Description = "Example for pubsub topic source",
    });

    var tfTestPubsubTopic = new Gcp.PubSub.Topic("tf_test_pubsub_topic", new()
    {
        Name = "test_pubsub",
    });

    var listingListing = new Gcp.BigQueryAnalyticsHub.Listing("listing", new()
    {
        Location = "US",
        DataExchangeId = listing.DataExchangeId,
        ListingId = "tf_test_pubsub_listing",
        DisplayName = "tf_test_pubsub_listing",
        Description = "Example for pubsub topic source",
        PubsubTopic = new Gcp.BigQueryAnalyticsHub.Inputs.ListingPubsubTopicArgs
        {
            Topic = tfTestPubsubTopic.Id,
            DataAffinityRegions = new[]
            {
                "us-central1",
                "europe-west1",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.bigqueryanalyticshub.DataExchange;
import com.pulumi.gcp.bigqueryanalyticshub.DataExchangeArgs;
import com.pulumi.gcp.pubsub.Topic;
import com.pulumi.gcp.pubsub.TopicArgs;
import com.pulumi.gcp.bigqueryanalyticshub.Listing;
import com.pulumi.gcp.bigqueryanalyticshub.ListingArgs;
import com.pulumi.gcp.bigqueryanalyticshub.inputs.ListingPubsubTopicArgs;
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 listing = new DataExchange("listing", DataExchangeArgs.builder()
            .location("US")
            .dataExchangeId("tf_test_pubsub_data_exchange")
            .displayName("tf_test_pubsub_data_exchange")
            .description("Example for pubsub topic source")
            .build());

        var tfTestPubsubTopic = new Topic("tfTestPubsubTopic", TopicArgs.builder()
            .name("test_pubsub")
            .build());

        var listingListing = new Listing("listingListing", ListingArgs.builder()
            .location("US")
            .dataExchangeId(listing.dataExchangeId())
            .listingId("tf_test_pubsub_listing")
            .displayName("tf_test_pubsub_listing")
            .description("Example for pubsub topic source")
            .pubsubTopic(ListingPubsubTopicArgs.builder()
                .topic(tfTestPubsubTopic.id())
                .dataAffinityRegions(                
                    "us-central1",
                    "europe-west1")
                .build())
            .build());

    }
}
resources:
  listing:
    type: gcp:bigqueryanalyticshub:DataExchange
    properties:
      location: US
      dataExchangeId: tf_test_pubsub_data_exchange
      displayName: tf_test_pubsub_data_exchange
      description: Example for pubsub topic source
  tfTestPubsubTopic:
    type: gcp:pubsub:Topic
    name: tf_test_pubsub_topic
    properties:
      name: test_pubsub
  listingListing:
    type: gcp:bigqueryanalyticshub:Listing
    name: listing
    properties:
      location: US
      dataExchangeId: ${listing.dataExchangeId}
      listingId: tf_test_pubsub_listing
      displayName: tf_test_pubsub_listing
      description: Example for pubsub topic source
      pubsubTopic:
        topic: ${tfTestPubsubTopic.id}
        dataAffinityRegions:
          - us-central1
          - europe-west1

The pubsubTopic property specifies a Pub/Sub topic instead of a BigQuery dataset. The dataAffinityRegions array controls where subscribers can consume messages, supporting data residency requirements for streaming workloads.

Make listings publicly discoverable

Public listings appear on the Analytics Hub discovery page for all users, enabling broad data sharing.

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

const listing = new gcp.bigqueryanalyticshub.DataExchange("listing", {
    location: "US",
    dataExchangeId: "my_data_exchange",
    displayName: "my_data_exchange",
    description: "example public listing",
    discoveryType: "DISCOVERY_TYPE_PUBLIC",
});
const listingDataset = new gcp.bigquery.Dataset("listing", {
    datasetId: "my_listing",
    friendlyName: "my_listing",
    description: "example public listing",
    location: "US",
});
const listingListing = new gcp.bigqueryanalyticshub.Listing("listing", {
    location: "US",
    dataExchangeId: listing.dataExchangeId,
    listingId: "my_listing",
    displayName: "my_listing",
    description: "example public listing",
    discoveryType: "DISCOVERY_TYPE_PUBLIC",
    allowOnlyMetadataSharing: false,
    bigqueryDataset: {
        dataset: listingDataset.id,
    },
});
import pulumi
import pulumi_gcp as gcp

listing = gcp.bigqueryanalyticshub.DataExchange("listing",
    location="US",
    data_exchange_id="my_data_exchange",
    display_name="my_data_exchange",
    description="example public listing",
    discovery_type="DISCOVERY_TYPE_PUBLIC")
listing_dataset = gcp.bigquery.Dataset("listing",
    dataset_id="my_listing",
    friendly_name="my_listing",
    description="example public listing",
    location="US")
listing_listing = gcp.bigqueryanalyticshub.Listing("listing",
    location="US",
    data_exchange_id=listing.data_exchange_id,
    listing_id="my_listing",
    display_name="my_listing",
    description="example public listing",
    discovery_type="DISCOVERY_TYPE_PUBLIC",
    allow_only_metadata_sharing=False,
    bigquery_dataset={
        "dataset": listing_dataset.id,
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		listing, err := bigqueryanalyticshub.NewDataExchange(ctx, "listing", &bigqueryanalyticshub.DataExchangeArgs{
			Location:       pulumi.String("US"),
			DataExchangeId: pulumi.String("my_data_exchange"),
			DisplayName:    pulumi.String("my_data_exchange"),
			Description:    pulumi.String("example public listing"),
			DiscoveryType:  pulumi.String("DISCOVERY_TYPE_PUBLIC"),
		})
		if err != nil {
			return err
		}
		listingDataset, err := bigquery.NewDataset(ctx, "listing", &bigquery.DatasetArgs{
			DatasetId:    pulumi.String("my_listing"),
			FriendlyName: pulumi.String("my_listing"),
			Description:  pulumi.String("example public listing"),
			Location:     pulumi.String("US"),
		})
		if err != nil {
			return err
		}
		_, err = bigqueryanalyticshub.NewListing(ctx, "listing", &bigqueryanalyticshub.ListingArgs{
			Location:                 pulumi.String("US"),
			DataExchangeId:           listing.DataExchangeId,
			ListingId:                pulumi.String("my_listing"),
			DisplayName:              pulumi.String("my_listing"),
			Description:              pulumi.String("example public listing"),
			DiscoveryType:            pulumi.String("DISCOVERY_TYPE_PUBLIC"),
			AllowOnlyMetadataSharing: pulumi.Bool(false),
			BigqueryDataset: &bigqueryanalyticshub.ListingBigqueryDatasetArgs{
				Dataset: listingDataset.ID(),
			},
		})
		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 listing = new Gcp.BigQueryAnalyticsHub.DataExchange("listing", new()
    {
        Location = "US",
        DataExchangeId = "my_data_exchange",
        DisplayName = "my_data_exchange",
        Description = "example public listing",
        DiscoveryType = "DISCOVERY_TYPE_PUBLIC",
    });

    var listingDataset = new Gcp.BigQuery.Dataset("listing", new()
    {
        DatasetId = "my_listing",
        FriendlyName = "my_listing",
        Description = "example public listing",
        Location = "US",
    });

    var listingListing = new Gcp.BigQueryAnalyticsHub.Listing("listing", new()
    {
        Location = "US",
        DataExchangeId = listing.DataExchangeId,
        ListingId = "my_listing",
        DisplayName = "my_listing",
        Description = "example public listing",
        DiscoveryType = "DISCOVERY_TYPE_PUBLIC",
        AllowOnlyMetadataSharing = false,
        BigqueryDataset = new Gcp.BigQueryAnalyticsHub.Inputs.ListingBigqueryDatasetArgs
        {
            Dataset = listingDataset.Id,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.bigqueryanalyticshub.DataExchange;
import com.pulumi.gcp.bigqueryanalyticshub.DataExchangeArgs;
import com.pulumi.gcp.bigquery.Dataset;
import com.pulumi.gcp.bigquery.DatasetArgs;
import com.pulumi.gcp.bigqueryanalyticshub.Listing;
import com.pulumi.gcp.bigqueryanalyticshub.ListingArgs;
import com.pulumi.gcp.bigqueryanalyticshub.inputs.ListingBigqueryDatasetArgs;
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 listing = new DataExchange("listing", DataExchangeArgs.builder()
            .location("US")
            .dataExchangeId("my_data_exchange")
            .displayName("my_data_exchange")
            .description("example public listing")
            .discoveryType("DISCOVERY_TYPE_PUBLIC")
            .build());

        var listingDataset = new Dataset("listingDataset", DatasetArgs.builder()
            .datasetId("my_listing")
            .friendlyName("my_listing")
            .description("example public listing")
            .location("US")
            .build());

        var listingListing = new Listing("listingListing", ListingArgs.builder()
            .location("US")
            .dataExchangeId(listing.dataExchangeId())
            .listingId("my_listing")
            .displayName("my_listing")
            .description("example public listing")
            .discoveryType("DISCOVERY_TYPE_PUBLIC")
            .allowOnlyMetadataSharing(false)
            .bigqueryDataset(ListingBigqueryDatasetArgs.builder()
                .dataset(listingDataset.id())
                .build())
            .build());

    }
}
resources:
  listing:
    type: gcp:bigqueryanalyticshub:DataExchange
    properties:
      location: US
      dataExchangeId: my_data_exchange
      displayName: my_data_exchange
      description: example public listing
      discoveryType: DISCOVERY_TYPE_PUBLIC
  listingListing:
    type: gcp:bigqueryanalyticshub:Listing
    name: listing
    properties:
      location: US
      dataExchangeId: ${listing.dataExchangeId}
      listingId: my_listing
      displayName: my_listing
      description: example public listing
      discoveryType: DISCOVERY_TYPE_PUBLIC
      allowOnlyMetadataSharing: false
      bigqueryDataset:
        dataset: ${listingDataset.id}
  listingDataset:
    type: gcp:bigquery:Dataset
    name: listing
    properties:
      datasetId: my_listing
      friendlyName: my_listing
      description: example public listing
      location: US

The discoveryType property controls visibility. DISCOVERY_TYPE_PUBLIC makes the listing discoverable to anyone, while DISCOVERY_TYPE_PRIVATE restricts discovery to users with explicit IAM permissions. The allowOnlyMetadataSharing property, when false, allows full data subscription rather than just metadata access.

Replicate datasets across regions

Global data sharing often requires replicating datasets to multiple regions for performance and compliance.

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

const listing = new gcp.bigqueryanalyticshub.DataExchange("listing", {
    location: "us",
    dataExchangeId: "my_data_exchange",
    displayName: "my_data_exchange",
});
const listingListing = new gcp.bigqueryanalyticshub.Listing("listing", {
    location: "us",
    dataExchangeId: listing.dataExchangeId,
    listingId: "my_listing",
    displayName: "my_listing",
    bigqueryDataset: {
        dataset: "projects/project_id/datasets/my_listing_example2",
        replicaLocations: ["eu"],
    },
});
import pulumi
import pulumi_gcp as gcp

listing = gcp.bigqueryanalyticshub.DataExchange("listing",
    location="us",
    data_exchange_id="my_data_exchange",
    display_name="my_data_exchange")
listing_listing = gcp.bigqueryanalyticshub.Listing("listing",
    location="us",
    data_exchange_id=listing.data_exchange_id,
    listing_id="my_listing",
    display_name="my_listing",
    bigquery_dataset={
        "dataset": "projects/project_id/datasets/my_listing_example2",
        "replica_locations": ["eu"],
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		listing, err := bigqueryanalyticshub.NewDataExchange(ctx, "listing", &bigqueryanalyticshub.DataExchangeArgs{
			Location:       pulumi.String("us"),
			DataExchangeId: pulumi.String("my_data_exchange"),
			DisplayName:    pulumi.String("my_data_exchange"),
		})
		if err != nil {
			return err
		}
		_, err = bigqueryanalyticshub.NewListing(ctx, "listing", &bigqueryanalyticshub.ListingArgs{
			Location:       pulumi.String("us"),
			DataExchangeId: listing.DataExchangeId,
			ListingId:      pulumi.String("my_listing"),
			DisplayName:    pulumi.String("my_listing"),
			BigqueryDataset: &bigqueryanalyticshub.ListingBigqueryDatasetArgs{
				Dataset: pulumi.String("projects/project_id/datasets/my_listing_example2"),
				ReplicaLocations: pulumi.StringArray{
					pulumi.String("eu"),
				},
			},
		})
		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 listing = new Gcp.BigQueryAnalyticsHub.DataExchange("listing", new()
    {
        Location = "us",
        DataExchangeId = "my_data_exchange",
        DisplayName = "my_data_exchange",
    });

    var listingListing = new Gcp.BigQueryAnalyticsHub.Listing("listing", new()
    {
        Location = "us",
        DataExchangeId = listing.DataExchangeId,
        ListingId = "my_listing",
        DisplayName = "my_listing",
        BigqueryDataset = new Gcp.BigQueryAnalyticsHub.Inputs.ListingBigqueryDatasetArgs
        {
            Dataset = "projects/project_id/datasets/my_listing_example2",
            ReplicaLocations = new[]
            {
                "eu",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.bigqueryanalyticshub.DataExchange;
import com.pulumi.gcp.bigqueryanalyticshub.DataExchangeArgs;
import com.pulumi.gcp.bigqueryanalyticshub.Listing;
import com.pulumi.gcp.bigqueryanalyticshub.ListingArgs;
import com.pulumi.gcp.bigqueryanalyticshub.inputs.ListingBigqueryDatasetArgs;
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 listing = new DataExchange("listing", DataExchangeArgs.builder()
            .location("us")
            .dataExchangeId("my_data_exchange")
            .displayName("my_data_exchange")
            .build());

        var listingListing = new Listing("listingListing", ListingArgs.builder()
            .location("us")
            .dataExchangeId(listing.dataExchangeId())
            .listingId("my_listing")
            .displayName("my_listing")
            .bigqueryDataset(ListingBigqueryDatasetArgs.builder()
                .dataset("projects/project_id/datasets/my_listing_example2")
                .replicaLocations("eu")
                .build())
            .build());

    }
}
resources:
  listing:
    type: gcp:bigqueryanalyticshub:DataExchange
    properties:
      location: us
      dataExchangeId: my_data_exchange
      displayName: my_data_exchange
  listingListing:
    type: gcp:bigqueryanalyticshub:Listing
    name: listing
    properties:
      location: us
      dataExchangeId: ${listing.dataExchangeId}
      listingId: my_listing
      displayName: my_listing
      bigqueryDataset:
        dataset: projects/project_id/datasets/my_listing_example2
        replicaLocations:
          - eu

The replicaLocations array specifies where Analytics Hub should replicate the dataset. Subscribers in different regions access local replicas, reducing query latency and supporting data residency requirements. The location property uses multi-region identifiers like “us” or “eu” rather than specific zones.

Beyond these examples

These snippets focus on specific listing-level features: BigQuery dataset and Pub/Sub topic sharing, export restrictions and query logging, and DCR exchanges and public discovery. They’re intentionally minimal rather than full data sharing solutions.

The examples may reference pre-existing infrastructure such as DataExchange resources, BigQuery datasets and tables, and Pub/Sub topics. They focus on configuring the listing rather than provisioning the underlying data sources.

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

  • Publisher and data provider metadata (publisher, dataProvider)
  • Categorization and documentation (categories, documentation, icon)
  • Access request workflows (requestAccess, primaryContact)
  • Commercial marketplace configuration (deleteCommercial)

These omissions are intentional: the goal is to illustrate how each listing feature is wired, not provide drop-in data sharing modules. See the BigQuery Analytics Hub Listing resource reference for all available configuration options.

Let's create GCP BigQuery Analytics Hub Listings

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Immutability & Lifecycle
What properties can't I change after creating a listing?
The following properties are immutable: dataExchangeId, listingId, location, project, and allowOnlyMetadataSharing. You’ll need to recreate the listing to change any of these.
Why am I getting an error when trying to delete a commercial listing?
Commercial listings require deleteCommercial to be set to true before deletion. This acts as a safety guard to prevent accidental deletion of commercial listings.
Can I disable email logging after enabling it?
No, once you set logLinkedDatasetQueryUserEmail to true, it cannot be turned off. All queries on the linked dataset will permanently log the querying user’s email address.
Data Sources & Configuration
What's the difference between bigqueryDataset and pubsubTopic sources?
bigqueryDataset shares BigQuery datasets (tables, views, routines), while pubsubTopic shares Pub/Sub topics. Choose based on your data source type; you can only configure one per listing.
How do I share specific tables or routines instead of the entire dataset?
Use selectedResources within bigqueryDataset to specify individual tables or routines by their IDs.
Can I replicate my listing across multiple regions?
Yes, configure replicaLocations within bigqueryDataset to specify additional regions where the listing should be available.
Discovery & Access Control
What's the difference between DISCOVERY_TYPE_PRIVATE and DISCOVERY_TYPE_PUBLIC?
DISCOVERY_TYPE_PUBLIC makes the listing visible on the discovery page, while DISCOVERY_TYPE_PRIVATE restricts visibility. IAM permissions still control actual access regardless of discovery type.
Can I set discoveryType on a restricted listing?
No, discoveryType cannot be set for listings with restrictedExportConfig enabled.
What does allowOnlyMetadataSharing do?
When set to true, the listing becomes non-subscribable and only metadata is available. Users can view information about the listing but cannot access the actual data.

Using a different cloud?

Explore analytics guides for other cloud providers: